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 : // C++ Headers
49 : #include <cassert>
50 : #include <cmath>
51 :
52 : // ObjexxFCL Headers
53 : #include <ObjexxFCL/Array.functions.hh>
54 : #include <ObjexxFCL/Fmath.hh>
55 : // #include <ObjexxFCL/string.functions.hh>
56 :
57 : // EnergyPlus Headers
58 : #include <EnergyPlus/Autosizing/Base.hh>
59 : #include <EnergyPlus/BranchNodeConnections.hh>
60 : #include <EnergyPlus/CondenserLoopTowers.hh>
61 : #include <EnergyPlus/CurveManager.hh>
62 : #include <EnergyPlus/Data/EnergyPlusData.hh>
63 : #include <EnergyPlus/DataBranchAirLoopPlant.hh>
64 : #include <EnergyPlus/DataEnvironment.hh>
65 : #include <EnergyPlus/DataHVACGlobals.hh>
66 : #include <EnergyPlus/DataIPShortCuts.hh>
67 : #include <EnergyPlus/DataLoopNode.hh>
68 : #include <EnergyPlus/DataPrecisionGlobals.hh>
69 : #include <EnergyPlus/DataSizing.hh>
70 : #include <EnergyPlus/DataWater.hh>
71 : #include <EnergyPlus/FaultsManager.hh>
72 : #include <EnergyPlus/FluidProperties.hh>
73 : #include <EnergyPlus/General.hh>
74 : #include <EnergyPlus/GeneralRoutines.hh>
75 : #include <EnergyPlus/GlobalNames.hh>
76 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
77 : #include <EnergyPlus/NodeInputManager.hh>
78 : #include <EnergyPlus/OutAirNodeManager.hh>
79 : #include <EnergyPlus/OutputProcessor.hh>
80 : #include <EnergyPlus/OutputReportPredefined.hh>
81 : #include <EnergyPlus/Plant/DataPlant.hh>
82 : #include <EnergyPlus/PlantUtilities.hh>
83 : #include <EnergyPlus/Psychrometrics.hh>
84 : #include <EnergyPlus/ScheduleManager.hh>
85 : #include <EnergyPlus/UtilityRoutines.hh>
86 : #include <EnergyPlus/WaterManager.hh>
87 :
88 : namespace EnergyPlus {
89 :
90 : namespace CondenserLoopTowers {
91 :
92 : // Module containing the routines dealing with the objects COOLING TOWER:SINGLE SPEED,
93 : // COOLING TOWER:TWO SPEED, and COOLING TOWER:VARIABLE SPEED
94 :
95 : // MODULE INFORMATION:
96 : // AUTHOR Dan Fisher
97 : // DATE WRITTEN April 1998
98 : // MODIFIED Shirey, Raustad: Dec 2000; Shirey, Sept 2002, Raustad Mar 2005
99 : // B Griffith Aug 2006, added water consumption and water system interactions
100 : // T Hong, Aug 2008. Added fluid bypass for single speed cooling tower
101 : // Chandan Sharma, FSEC, February 2010, Added basin heater
102 : // A Flament, July 2010, added multi-cell capability for the 3 types of cooling tower
103 : // RE-ENGINEERED na
104 :
105 : // PURPOSE OF THIS MODULE:
106 : // Model the performance of cooling towers
107 :
108 : std::string const cCoolingTower_SingleSpeed("CoolingTower:SingleSpeed");
109 : std::string const cCoolingTower_TwoSpeed("CoolingTower:TwoSpeed");
110 : std::string const cCoolingTower_VariableSpeed("CoolingTower:VariableSpeed");
111 : std::string const cCoolingTower_VariableSpeedMerkel("CoolingTower:VariableSpeed:Merkel");
112 :
113 272 : CoolingTower *CoolingTower::factory(EnergyPlusData &state, std::string_view objectName)
114 : {
115 : // Process the input data for towers if it hasn't been done already
116 272 : if (state.dataCondenserLoopTowers->GetInput) {
117 245 : GetTowerInput(state);
118 245 : state.dataCondenserLoopTowers->GetInput = false;
119 : }
120 : // Now look for this particular tower in the list
121 272 : auto thisObj = std::find_if(state.dataCondenserLoopTowers->towers.begin(),
122 272 : state.dataCondenserLoopTowers->towers.end(),
123 302 : [&objectName](const CoolingTower &myObj) { return myObj.Name == objectName; });
124 272 : if (thisObj != state.dataCondenserLoopTowers->towers.end()) {
125 272 : return thisObj;
126 : }
127 : // If we didn't find it, fatal
128 : ShowFatalError(state, format("CoolingTowerFactory: Error getting inputs for tower named: {}", objectName)); // LCOV_EXCL_LINE
129 : // Shut up the compiler
130 : return nullptr; // LCOV_EXCL_LINE
131 : }
132 :
133 8831655 : void CoolingTower::simulate(EnergyPlusData &state,
134 : [[maybe_unused]] const PlantLocation &calledFromLocation,
135 : [[maybe_unused]] bool const FirstHVACIteration,
136 : Real64 &CurLoad,
137 : bool const RunFlag)
138 : {
139 8831655 : this->initialize(state);
140 8831655 : switch (this->TowerType) {
141 7735530 : case DataPlant::PlantEquipmentType::CoolingTower_SingleSpd:
142 7735530 : this->calculateSingleSpeedTower(state, CurLoad, RunFlag);
143 7735530 : break;
144 298469 : case DataPlant::PlantEquipmentType::CoolingTower_TwoSpd:
145 298469 : this->calculateTwoSpeedTower(state, CurLoad, RunFlag);
146 298469 : break;
147 716836 : case DataPlant::PlantEquipmentType::CoolingTower_VarSpd:
148 716836 : this->calculateVariableSpeedTower(state, CurLoad, RunFlag);
149 716836 : break;
150 80820 : case DataPlant::PlantEquipmentType::CoolingTower_VarSpdMerkel:
151 80820 : this->calculateMerkelVariableSpeedTower(state, CurLoad, RunFlag);
152 80820 : break;
153 0 : default:
154 0 : ShowFatalError(state, format("Plant Equipment Type specified for {} is not a Cooling Tower.", this->Name));
155 : }
156 8831655 : this->calculateWaterUsage(state);
157 8831655 : this->update(state);
158 8831655 : this->report(state, RunFlag);
159 8831655 : }
160 :
161 1382 : void CoolingTower::getDesignCapacities([[maybe_unused]] EnergyPlusData &state,
162 : [[maybe_unused]] const PlantLocation &calledFromLocation,
163 : Real64 &MaxLoad,
164 : Real64 &MinLoad,
165 : Real64 &OptLoad)
166 : {
167 1382 : MinLoad = 0.0;
168 1382 : MaxLoad = this->TowerNominalCapacity * this->HeatRejectCapNomCapSizingRatio;
169 1382 : OptLoad = this->TowerNominalCapacity;
170 1382 : }
171 :
172 272 : void CoolingTower::getSizingFactor(Real64 &SizFactor)
173 : {
174 272 : SizFactor = this->SizFac;
175 272 : }
176 :
177 1382 : void CoolingTower::onInitLoopEquip(EnergyPlusData &state, [[maybe_unused]] const PlantLocation &calledFromLocation)
178 : {
179 1382 : this->initialize(state);
180 1382 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_VarSpdMerkel) {
181 10 : this->SizeVSMerkelTower(state);
182 : } else {
183 1372 : this->SizeTower(state);
184 : }
185 1382 : }
186 :
187 245 : void GetTowerInput(EnergyPlusData &state)
188 : {
189 :
190 : // SUBROUTINE INFORMATION:
191 : // AUTHOR: Dan Fisher
192 : // DATE WRITTEN: April 1998
193 : // MODIFIED Don Shirey, Jan 2001 and Sept/Oct 2002; Richard Raustad, FSEC, Feb 2005 (added VS tower)
194 : // B. Griffith, Aug. 2006 water consumption modeling and water system connections
195 : // T Hong, Aug. 2008: added fluid bypass for single speed tower
196 : // A Flament, July 2010, added multi-cell capability for the 3 types of cooling tower
197 :
198 : // PURPOSE OF THIS SUBROUTINE:
199 : // Obtains input data for cooling towers and stores it in towers data structure. Additional structure
200 : // (VSTower) stores the coefficients for each VS tower.
201 :
202 : // METHODOLOGY EMPLOYED:
203 : // Uses "Get" routines to read in the data.
204 :
205 : static constexpr std::string_view routineName = "GetTowerInput";
206 :
207 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
208 : int TowerNum; // Tower number, reference counter for towers data array
209 245 : int NumVSCoolToolsModelCoeffs = 0; // Number of CoolTools VS cooling tower coefficient objects
210 245 : int NumVSYorkCalcModelCoeffs = 0; // Number of YorkCalc VS cooling tower coefficient objects
211 : int VSModelCoeffNum; // Specific variable-speed tower coefficient object of interest
212 : int NumAlphas; // Number of elements in the alpha array
213 : int NumNums; // Number of elements in the numeric array
214 : int NumAlphas2; // Number of elements in the alpha2 array
215 : int NumNums2; // Number of elements in the numeric2 array
216 : int IOStat; // IO Status when calling get input subroutine
217 : int CoeffNum; // Index for reading user defined VS tower coefficients
218 245 : bool ErrorsFound(false); // Logical flag set .TRUE. if errors found while getting input data
219 245 : Array1D<Real64> NumArray(33); // Numeric input data array
220 245 : Array1D<Real64> NumArray2(43); // Numeric input data array for VS tower coefficients
221 245 : Array1D_string AlphArray(16); // Character string input data array
222 245 : Array1D_string AlphArray2(1); // Character string input data array for VS tower coefficients
223 :
224 245 : std::unordered_map<std::string, std::string> UniqueSimpleTowerNames;
225 :
226 245 : constexpr std::array<std::string_view, static_cast<int>(EvapLoss::Num)> EvapLossNamesUC{"LOSSFACTOR", "SATURATEDEXIT"};
227 245 : constexpr std::array<std::string_view, static_cast<int>(PIM::Num)> PIMNamesUC{"NOMINALCAPACITY", "UFACTORTIMESAREAANDDESIGNWATERFLOWRATE"};
228 245 : constexpr std::array<std::string_view, static_cast<int>(Blowdown::Num)> BlowDownNamesUC = {"CONCENTRATIONRATIO", "SCHEDULEDRATE"};
229 245 : constexpr std::array<std::string_view, static_cast<int>(CellCtrl::Num)> CellCtrlNamesUC = {"MINIMALCELL", "MAXIMALCELL"};
230 :
231 245 : auto const &s_ip = state.dataInputProcessing->inputProcessor;
232 245 : auto &s_ipsc = state.dataIPShortCut;
233 :
234 : // Get number of all cooling towers specified in the input data file (idf)
235 245 : int NumSingleSpeedTowers = s_ip->getNumObjectsFound(state, cCoolingTower_SingleSpeed);
236 245 : int NumTwoSpeedTowers = s_ip->getNumObjectsFound(state, cCoolingTower_TwoSpeed);
237 245 : int NumVariableSpeedTowers = s_ip->getNumObjectsFound(state, cCoolingTower_VariableSpeed);
238 245 : int NumVSMerkelTowers = s_ip->getNumObjectsFound(state, cCoolingTower_VariableSpeedMerkel);
239 245 : int NumSimpleTowers = NumSingleSpeedTowers + NumTwoSpeedTowers + NumVariableSpeedTowers + NumVSMerkelTowers;
240 :
241 245 : if (NumSimpleTowers <= 0) {
242 0 : ShowFatalError(state,
243 : "No Cooling Tower objects found in input, however, a branch object has specified a cooling tower. Search the input for "
244 : "CoolingTower to determine the cause for this error.");
245 : }
246 :
247 245 : state.dataCondenserLoopTowers->GetInput = false;
248 : // See if load distribution manager has already gotten the input
249 245 : if (allocated(state.dataCondenserLoopTowers->towers)) {
250 0 : return;
251 : }
252 :
253 : // Allocate data structures to hold tower input data, report data and tower inlet conditions
254 245 : state.dataCondenserLoopTowers->towers.allocate(NumSimpleTowers);
255 245 : UniqueSimpleTowerNames.reserve(NumSimpleTowers);
256 : // Allocate variable-speed tower structure with data specific to this type
257 245 : if (NumVariableSpeedTowers > 0) {
258 : // Allow users to input model coefficients other than default
259 17 : NumVSCoolToolsModelCoeffs = s_ip->getNumObjectsFound(state, "CoolingTowerPerformance:CoolTools");
260 17 : NumVSYorkCalcModelCoeffs = s_ip->getNumObjectsFound(state, "CoolingTowerPerformance:YorkCalc");
261 : }
262 :
263 : // Load data structures with cooling tower input data
264 245 : s_ipsc->cCurrentModuleObject = cCoolingTower_SingleSpeed;
265 476 : for (int SingleSpeedTowerNumber = 1; SingleSpeedTowerNumber <= NumSingleSpeedTowers; ++SingleSpeedTowerNumber) {
266 231 : TowerNum = SingleSpeedTowerNumber;
267 693 : s_ip->getObjectItem(state,
268 231 : s_ipsc->cCurrentModuleObject,
269 : SingleSpeedTowerNumber,
270 : AlphArray,
271 : NumAlphas,
272 : NumArray,
273 : NumNums,
274 : IOStat,
275 : _,
276 231 : s_ipsc->lAlphaFieldBlanks,
277 231 : s_ipsc->cAlphaFieldNames,
278 231 : s_ipsc->cNumericFieldNames);
279 :
280 231 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
281 :
282 231 : GlobalNames::VerifyUniqueInterObjectName(
283 462 : state, UniqueSimpleTowerNames, AlphArray(1), s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), ErrorsFound);
284 231 : auto &tower = state.dataCondenserLoopTowers->towers(TowerNum);
285 231 : tower.Name = AlphArray(1);
286 231 : tower.TowerType = DataPlant::PlantEquipmentType::CoolingTower_SingleSpd;
287 231 : tower.TowerMassFlowRateMultiplier = 2.5;
288 231 : tower.WaterInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
289 231 : AlphArray(2),
290 : ErrorsFound,
291 : DataLoopNode::ConnectionObjectType::CoolingTowerSingleSpeed,
292 231 : tower.Name,
293 : DataLoopNode::NodeFluidType::Water,
294 : DataLoopNode::ConnectionType::Inlet,
295 : NodeInputManager::CompFluidStream::Primary,
296 : DataLoopNode::ObjectIsNotParent);
297 462 : tower.WaterOutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
298 231 : AlphArray(3),
299 : ErrorsFound,
300 : DataLoopNode::ConnectionObjectType::CoolingTowerSingleSpeed,
301 231 : tower.Name,
302 : DataLoopNode::NodeFluidType::Water,
303 : DataLoopNode::ConnectionType::Outlet,
304 : NodeInputManager::CompFluidStream::Primary,
305 : DataLoopNode::ObjectIsNotParent);
306 231 : BranchNodeConnections::TestCompSet(state, s_ipsc->cCurrentModuleObject, tower.Name, AlphArray(2), AlphArray(3), "Chilled Water Nodes");
307 231 : tower.DesignWaterFlowRate = NumArray(1);
308 231 : if (tower.DesignWaterFlowRate == DataSizing::AutoSize) {
309 108 : tower.DesignWaterFlowRateWasAutoSized = true;
310 : }
311 231 : tower.HighSpeedAirFlowRate = NumArray(2);
312 231 : if (tower.HighSpeedAirFlowRate == DataSizing::AutoSize) {
313 113 : tower.HighSpeedAirFlowRateWasAutoSized = true;
314 : }
315 231 : tower.HighSpeedFanPower = NumArray(3);
316 231 : if (tower.HighSpeedFanPower == DataSizing::AutoSize) {
317 110 : tower.HighSpeedFanPowerWasAutoSized = true;
318 : }
319 231 : tower.HighSpeedTowerUA = NumArray(4);
320 231 : if (tower.HighSpeedTowerUA == DataSizing::AutoSize) {
321 110 : tower.HighSpeedTowerUAWasAutoSized = true;
322 : }
323 231 : tower.FreeConvAirFlowRate = NumArray(5);
324 231 : if (tower.FreeConvAirFlowRate == DataSizing::AutoSize) {
325 57 : tower.FreeConvAirFlowRateWasAutoSized = true;
326 : }
327 231 : tower.FreeConvAirFlowRateSizingFactor = NumArray(6);
328 231 : tower.FreeConvTowerUA = NumArray(7);
329 231 : if (tower.FreeConvTowerUA == DataSizing::AutoSize) {
330 57 : tower.FreeConvTowerUAWasAutoSized = true;
331 : }
332 231 : tower.FreeConvTowerUASizingFactor = NumArray(8);
333 231 : tower.HeatRejectCapNomCapSizingRatio = NumArray(9);
334 231 : tower.TowerNominalCapacity = NumArray(10);
335 231 : if (tower.TowerNominalCapacity == DataSizing::AutoSize) {
336 0 : tower.TowerNominalCapacityWasAutoSized = true;
337 : }
338 231 : tower.TowerFreeConvNomCap = NumArray(11);
339 231 : if (tower.TowerFreeConvNomCap == DataSizing::AutoSize) {
340 13 : tower.TowerFreeConvNomCapWasAutoSized = true;
341 : }
342 231 : tower.TowerFreeConvNomCapSizingFactor = NumArray(12);
343 231 : if (NumAlphas >= 4) {
344 231 : tower.PerformanceInputMethod_Num = static_cast<PIM>(getEnumValue(PIMNamesUC, Util::makeUPPER(AlphArray(4))));
345 231 : if (tower.PerformanceInputMethod_Num == PIM::Invalid) {
346 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), AlphArray(4));
347 0 : ErrorsFound = true;
348 : }
349 : } else {
350 : // Since Performance Input Method has been omitted then assume it to be UA and DESIGN WATER FLOW RATE
351 0 : tower.PerformanceInputMethod_Num = PIM::UFactor;
352 : }
353 : // cooling tower design inlet conditions
354 231 : tower.DesInletAirDBTemp = NumArray(13);
355 231 : if (tower.DesInletAirDBTemp == 0) {
356 0 : tower.DesInletAirDBTemp = 35.0;
357 0 : tower.TowerInletCondsAutoSize = true;
358 : }
359 231 : tower.DesignInletWB = NumArray(14);
360 231 : if (tower.DesignInletWB == 0) {
361 0 : tower.DesignInletWB = 25.6;
362 0 : tower.TowerInletCondsAutoSize = true;
363 : }
364 231 : tower.DesignApproach = NumArray(15);
365 231 : if (tower.DesignApproach == DataSizing::AutoSize || tower.DesignApproach == 0) {
366 231 : tower.DesignApproach = 3.9;
367 231 : tower.TowerInletCondsAutoSize = true;
368 : }
369 231 : tower.DesignRange = NumArray(16);
370 231 : if (tower.DesignRange == DataSizing::AutoSize || tower.DesignRange == 0) {
371 231 : tower.DesignRange = 5.5;
372 231 : tower.TowerInletCondsAutoSize = true;
373 : }
374 : // set tower design water outlet and inlet temperatures
375 231 : tower.DesOutletWaterTemp = tower.DesignInletWB + tower.DesignApproach;
376 231 : tower.DesInletWaterTemp = tower.DesOutletWaterTemp + tower.DesignRange;
377 : // Basin heater power as a function of temperature must be greater than or equal to 0
378 231 : tower.BasinHeaterPowerFTempDiff = NumArray(17);
379 231 : if (NumArray(17) < 0.0) {
380 0 : ShowSevereCustom(state, eoh, "Basin heater power as a function of temperature difference must be >= 0");
381 0 : ErrorsFound = true;
382 : }
383 :
384 231 : tower.BasinHeaterSetPointTemp = NumArray(18);
385 :
386 231 : if (tower.BasinHeaterPowerFTempDiff > 0.0) {
387 0 : if (NumNums < 18) {
388 0 : tower.BasinHeaterSetPointTemp = 2.0;
389 : }
390 0 : if (tower.BasinHeaterSetPointTemp < 2.0) {
391 0 : ShowWarningCustom(state, eoh, format("{} is less than 2 deg C. Freezing could occur.", s_ipsc->cNumericFieldNames(18)));
392 : }
393 : }
394 :
395 231 : if (!AlphArray(5).empty()) {
396 0 : if ((tower.basinHeaterSched = Sched::GetSchedule(state, AlphArray(5))) == nullptr) {
397 0 : ShowWarningItemNotFound(state,
398 : eoh,
399 0 : s_ipsc->cAlphaFieldNames(5),
400 0 : AlphArray(5),
401 : "Basin heater operation will not be modeled and the simulation continues");
402 : }
403 : }
404 :
405 : // begin water use and systems get input
406 231 : tower.EvapLossMode = static_cast<EvapLoss>(getEnumValue(EvapLossNamesUC, Util::makeUPPER(AlphArray(6))));
407 :
408 231 : tower.UserEvapLossFactor = NumArray(19); // N11 , \field Evaporation Loss Factor
409 231 : tower.DriftLossFraction = NumArray(20) / 100.0; // N12, \field Drift Loss Percent
410 231 : tower.ConcentrationRatio = NumArray(21); // N13, \field Blowdown Concentration Ratio
411 231 : tower.SizFac = NumArray(25); // N17 \field Sizing Factor
412 231 : if (tower.SizFac <= 0.0) {
413 0 : tower.SizFac = 1.0;
414 : }
415 :
416 231 : tower.BlowdownMode = static_cast<Blowdown>(getEnumValue(BlowDownNamesUC, Util::makeUPPER(AlphArray(7))));
417 231 : if (tower.BlowdownMode == Blowdown::Schedule) {
418 0 : if ((tower.blowdownSched = Sched::GetSchedule(state, AlphArray(8))) == nullptr) {
419 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(8), AlphArray(8));
420 0 : ErrorsFound = true;
421 : }
422 : }
423 :
424 231 : if (AlphArray(9).empty()) {
425 229 : tower.SuppliedByWaterSystem = false;
426 : } else { // water from storage tank
427 4 : WaterManager::SetupTankDemandComponent(
428 4 : state, AlphArray(1), s_ipsc->cCurrentModuleObject, AlphArray(9), ErrorsFound, tower.WaterTankID, tower.WaterTankDemandARRID);
429 2 : tower.SuppliedByWaterSystem = true;
430 : }
431 :
432 : // outdoor air inlet node
433 :
434 231 : if (s_ipsc->lAlphaFieldBlanks(10)) {
435 195 : tower.OutdoorAirInletNodeNum = 0;
436 : } else {
437 36 : tower.OutdoorAirInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
438 36 : AlphArray(10),
439 : ErrorsFound,
440 : DataLoopNode::ConnectionObjectType::CoolingTowerSingleSpeed,
441 36 : tower.Name,
442 : DataLoopNode::NodeFluidType::Air,
443 : DataLoopNode::ConnectionType::OutsideAirReference,
444 : NodeInputManager::CompFluidStream::Primary,
445 : DataLoopNode::ObjectIsNotParent);
446 36 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, tower.OutdoorAirInletNodeNum)) {
447 0 : ShowSevereCustom(state,
448 : eoh,
449 0 : format("Outdoor Air Inlet Node Name not valid Outdoor Air Node= {}"
450 : "does not appear in an OutdoorAir:NodeList or as an OutdoorAir:Node.",
451 : AlphArray(10)));
452 0 : ErrorsFound = true;
453 : }
454 : }
455 :
456 : // fluid bypass for single speed tower
457 231 : if (s_ipsc->lAlphaFieldBlanks(11) || AlphArray(11).empty()) {
458 192 : tower.CapacityControl = CapacityCtrl::FanCycling; // FanCycling
459 39 : } else if ((tower.CapacityControl = static_cast<CapacityCtrl>(getEnumValue(CapacityCtrlNamesUC, AlphArray(11)))) ==
460 : CapacityCtrl::Invalid) {
461 0 : tower.CapacityControl = CapacityCtrl::FanCycling;
462 0 : ShowWarningInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(11), AlphArray(11), "The default Fan Cycling is used.");
463 : }
464 :
465 : // added for multi-cell
466 231 : tower.NumCell = NumArray(22);
467 231 : if ((NumNums < 22) && (tower.NumCell == 0)) {
468 : // assume Number of Cells not entered and should be defaulted
469 0 : tower.NumCell = 1;
470 : }
471 231 : tower.MinFracFlowRate = NumArray(23);
472 231 : if ((NumNums < 23) && (tower.MinFracFlowRate == 0.0)) {
473 : // assume Cell Minimum Water Flow Rate Fraction not entered and should be defaulted
474 0 : tower.MinFracFlowRate = 0.33;
475 : }
476 231 : tower.MaxFracFlowRate = NumArray(24);
477 231 : if ((NumNums < 24) && (tower.MaxFracFlowRate == 0.0)) {
478 : // assume Cell Maximum Water Flow Rate Fraction not entered and should be defaulted
479 0 : tower.MaxFracFlowRate = 2.5;
480 : }
481 :
482 : // cell control for single speed tower
483 231 : if (!s_ipsc->lAlphaFieldBlanks(12)) {
484 2 : tower.cellCtrl = static_cast<CellCtrl>(getEnumValue(CellCtrlNamesUC, Util::makeUPPER(AlphArray(12))));
485 : }
486 :
487 : // High speed air flow rate must be greater than free convection air flow rate.
488 : // Can't tell yet if autosized, check later in initialize.
489 231 : if (tower.HighSpeedAirFlowRate <= tower.FreeConvAirFlowRate && tower.HighSpeedAirFlowRate != DataSizing::AutoSize) {
490 0 : ShowSevereCustom(state, eoh, "Free convection air flow rate must be less than the design air flow rate.");
491 0 : ErrorsFound = true;
492 : }
493 :
494 : // Check various inputs if Performance Input Method = "UA and Design Water Flow Rate"
495 231 : if (tower.PerformanceInputMethod_Num == PIM::UFactor) {
496 228 : if (tower.DesignWaterFlowRate == 0.0) {
497 0 : ShowSevereCustom(state, eoh, "Tower performance input method requires a design water flow rate greater than zero.");
498 0 : ErrorsFound = true;
499 : }
500 228 : if (tower.HighSpeedTowerUA <= tower.FreeConvTowerUA && tower.HighSpeedTowerUA != DataSizing::AutoSize) {
501 0 : ShowSevereCustom(state, eoh, "Free convection UA must be less than the design tower UA.");
502 0 : ErrorsFound = true;
503 : }
504 228 : if (tower.FreeConvTowerUA > 0.0 && tower.FreeConvAirFlowRate == 0.0) {
505 0 : ShowSevereCustom(
506 : state, eoh, "Free convection air flow rate must be greater than zero when free convection UA is greater than zero.");
507 0 : ErrorsFound = true;
508 : }
509 3 : } else if (tower.PerformanceInputMethod_Num == PIM::NominalCapacity) {
510 3 : if (tower.TowerNominalCapacity == 0.0) {
511 0 : ShowSevereCustom(state, eoh, "Tower performance input method requires valid nominal capacity.");
512 0 : ErrorsFound = true;
513 : }
514 3 : if (tower.DesignWaterFlowRate != 0.0) {
515 0 : if (tower.DesignWaterFlowRate > 0.0) {
516 0 : ShowWarningCustom(state, eoh, "Nominal capacity input method and design water flow rate have been specified.");
517 : } else {
518 0 : ShowSevereCustom(
519 : state, eoh, "Nominal capacity input method has been specified and design water flow rate is being autosized.");
520 : }
521 0 : ShowContinueError(state, "Design water flow rate will be set according to nominal tower capacity.");
522 : }
523 3 : if (tower.HighSpeedTowerUA != 0.0) {
524 0 : if (tower.HighSpeedTowerUA > 0.0) {
525 0 : ShowWarningCustom(state, eoh, "Nominal tower capacity and design tower UA have been specified.");
526 : } else {
527 0 : ShowSevereCustom(state, eoh, "Nominal tower capacity has been specified and design tower UA is being autosized.");
528 : }
529 0 : ShowContinueError(state, "Design tower UA will be set according to nominal tower capacity.");
530 : }
531 3 : if (tower.FreeConvTowerUA != 0.0) {
532 0 : if (tower.FreeConvTowerUA > 0.0) {
533 0 : ShowWarningCustom(state, eoh, "Nominal capacity input method and free convection UA have been specified.");
534 : } else {
535 0 : ShowSevereCustom(state, eoh, "Nominal capacity input method has been specified and free convection UA is being autosized.");
536 : }
537 0 : ShowContinueError(state, "Free convection UA will be set according to nominal tower capacity.");
538 : }
539 3 : if (tower.TowerFreeConvNomCap >= tower.TowerNominalCapacity) {
540 0 : ShowSevereCustom(state, eoh, "Free convection nominal capacity must be less than the nominal (design) tower capacity.");
541 0 : ErrorsFound = true;
542 : }
543 3 : if (tower.TowerFreeConvNomCap > 0.0 && tower.FreeConvAirFlowRate == 0.0) {
544 0 : ShowSevereCustom(
545 : state, eoh, "Free convection air flow must be greater than zero when tower free convection capacity is specified.");
546 0 : ErrorsFound = true;
547 : }
548 : } else { // Tower performance input method is not specified as a valid "choice"
549 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), AlphArray(4));
550 0 : ErrorsFound = true;
551 : }
552 :
553 231 : if (NumAlphas > 12) {
554 1 : tower.EndUseSubcategory = AlphArray(13);
555 : } else {
556 230 : tower.EndUseSubcategory = "General";
557 : }
558 : } // End Single-Speed Tower Loop
559 :
560 245 : s_ipsc->cCurrentModuleObject = cCoolingTower_TwoSpeed;
561 258 : for (int TwoSpeedTowerNumber = 1; TwoSpeedTowerNumber <= NumTwoSpeedTowers; ++TwoSpeedTowerNumber) {
562 13 : TowerNum = NumSingleSpeedTowers + TwoSpeedTowerNumber;
563 39 : s_ip->getObjectItem(state,
564 13 : s_ipsc->cCurrentModuleObject,
565 : TwoSpeedTowerNumber,
566 : AlphArray,
567 : NumAlphas,
568 : NumArray,
569 : NumNums,
570 : IOStat,
571 : _,
572 13 : s_ipsc->lAlphaFieldBlanks,
573 13 : s_ipsc->cAlphaFieldNames,
574 13 : s_ipsc->cNumericFieldNames);
575 :
576 13 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
577 :
578 13 : GlobalNames::VerifyUniqueInterObjectName(
579 26 : state, UniqueSimpleTowerNames, AlphArray(1), s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), ErrorsFound);
580 :
581 13 : auto &tower = state.dataCondenserLoopTowers->towers(TowerNum);
582 13 : tower.Name = AlphArray(1);
583 13 : tower.TowerType = DataPlant::PlantEquipmentType::CoolingTower_TwoSpd;
584 13 : tower.TowerMassFlowRateMultiplier = 2.5;
585 13 : tower.WaterInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
586 13 : AlphArray(2),
587 : ErrorsFound,
588 : DataLoopNode::ConnectionObjectType::CoolingTowerTwoSpeed,
589 13 : tower.Name,
590 : DataLoopNode::NodeFluidType::Water,
591 : DataLoopNode::ConnectionType::Inlet,
592 : NodeInputManager::CompFluidStream::Primary,
593 : DataLoopNode::ObjectIsNotParent);
594 26 : tower.WaterOutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
595 13 : AlphArray(3),
596 : ErrorsFound,
597 : DataLoopNode::ConnectionObjectType::CoolingTowerTwoSpeed,
598 13 : tower.Name,
599 : DataLoopNode::NodeFluidType::Water,
600 : DataLoopNode::ConnectionType::Outlet,
601 : NodeInputManager::CompFluidStream::Primary,
602 : DataLoopNode::ObjectIsNotParent);
603 13 : BranchNodeConnections::TestCompSet(state, s_ipsc->cCurrentModuleObject, AlphArray(1), AlphArray(2), AlphArray(3), "Chilled Water Nodes");
604 :
605 13 : if (NumAlphas >= 4) {
606 13 : tower.PerformanceInputMethod_Num = static_cast<PIM>(getEnumValue(PIMNamesUC, Util::makeUPPER(AlphArray(4))));
607 : } else {
608 : // Since Performance Input Method has been omitted then assume it to be UA and DESIGN WATER FLOW RATE
609 0 : tower.PerformanceInputMethod_Num = PIM::UFactor;
610 : }
611 13 : tower.DesignWaterFlowRate = NumArray(1);
612 13 : if (tower.DesignWaterFlowRate == DataSizing::AutoSize) {
613 8 : tower.DesignWaterFlowRateWasAutoSized = true;
614 : }
615 13 : tower.HighSpeedAirFlowRate = NumArray(2);
616 13 : if (tower.HighSpeedAirFlowRate == DataSizing::AutoSize) {
617 9 : tower.HighSpeedAirFlowRateWasAutoSized = true;
618 : }
619 13 : tower.HighSpeedFanPower = NumArray(3);
620 13 : if (tower.HighSpeedFanPower == DataSizing::AutoSize) {
621 9 : tower.HighSpeedFanPowerWasAutoSized = true;
622 : }
623 13 : tower.HighSpeedTowerUA = NumArray(4);
624 13 : if (tower.HighSpeedTowerUA == DataSizing::AutoSize) {
625 8 : tower.HighSpeedTowerUAWasAutoSized = true;
626 : }
627 13 : tower.LowSpeedAirFlowRate = NumArray(5);
628 13 : if (tower.LowSpeedAirFlowRate == DataSizing::AutoSize) {
629 9 : tower.LowSpeedAirFlowRateWasAutoSized = true;
630 : }
631 :
632 13 : tower.LowSpeedAirFlowRateSizingFactor = NumArray(6);
633 13 : tower.LowSpeedFanPower = NumArray(7);
634 13 : if (tower.LowSpeedFanPower == DataSizing::AutoSize) {
635 9 : tower.LowSpeedFanPowerWasAutoSized = true;
636 : }
637 13 : tower.LowSpeedFanPowerSizingFactor = NumArray(8);
638 13 : tower.LowSpeedTowerUA = NumArray(9);
639 13 : if (tower.LowSpeedTowerUA == DataSizing::AutoSize) {
640 8 : tower.LowSpeedTowerUAWasAutoSized = true;
641 : }
642 13 : tower.LowSpeedTowerUASizingFactor = NumArray(10);
643 13 : tower.FreeConvAirFlowRate = NumArray(11);
644 13 : if (tower.FreeConvAirFlowRate == DataSizing::AutoSize) {
645 7 : tower.FreeConvAirFlowRateWasAutoSized = true;
646 : }
647 13 : tower.FreeConvAirFlowRateSizingFactor = NumArray(12);
648 13 : tower.FreeConvTowerUA = NumArray(13);
649 13 : if (tower.FreeConvTowerUA == DataSizing::AutoSize) {
650 6 : tower.FreeConvTowerUAWasAutoSized = true;
651 : }
652 13 : tower.FreeConvTowerUASizingFactor = NumArray(14);
653 13 : tower.HeatRejectCapNomCapSizingRatio = NumArray(15);
654 13 : tower.TowerNominalCapacity = NumArray(16);
655 :
656 13 : tower.TowerLowSpeedNomCap = NumArray(17);
657 13 : if (tower.TowerLowSpeedNomCap == DataSizing::AutoSize) {
658 3 : tower.TowerLowSpeedNomCapWasAutoSized = true;
659 : }
660 13 : tower.TowerLowSpeedNomCapSizingFactor = NumArray(18);
661 13 : tower.TowerFreeConvNomCap = NumArray(19);
662 13 : if (tower.TowerFreeConvNomCap == DataSizing::AutoSize) {
663 3 : tower.TowerFreeConvNomCapWasAutoSized = true;
664 : }
665 13 : tower.TowerFreeConvNomCapSizingFactor = NumArray(20);
666 : // cooling tower design inlet conditions
667 13 : tower.DesInletAirDBTemp = NumArray(21);
668 13 : if (tower.DesInletAirDBTemp == 0) {
669 0 : tower.DesInletAirDBTemp = 35.0;
670 0 : tower.TowerInletCondsAutoSize = true;
671 : }
672 13 : tower.DesignInletWB = NumArray(22);
673 13 : if (tower.DesignInletWB == 0) {
674 0 : tower.DesignInletWB = 25.6;
675 0 : tower.TowerInletCondsAutoSize = true;
676 : }
677 13 : tower.DesignApproach = NumArray(23);
678 13 : if (tower.DesignApproach == DataSizing::AutoSize || tower.DesignApproach == 0) {
679 13 : tower.DesignApproach = 3.9;
680 13 : tower.TowerInletCondsAutoSize = true;
681 : }
682 13 : tower.DesignRange = NumArray(24);
683 13 : if (tower.DesignRange == DataSizing::AutoSize || tower.DesignRange == 0) {
684 13 : tower.DesignRange = 5.5;
685 13 : tower.TowerInletCondsAutoSize = true;
686 : }
687 : // set tower design water outlet and inlet temperatures
688 13 : tower.DesOutletWaterTemp = tower.DesignInletWB + tower.DesignApproach;
689 13 : tower.DesInletWaterTemp = tower.DesOutletWaterTemp + tower.DesignRange;
690 : // Basin heater power as a function of temperature must be greater than or equal to 0
691 13 : tower.BasinHeaterPowerFTempDiff = NumArray(25);
692 13 : if (NumArray(25) < 0.0) {
693 0 : ShowSevereCustom(state, eoh, "Basin heater power as a function of temperature difference must be >= 0");
694 0 : ErrorsFound = true;
695 : }
696 :
697 13 : tower.BasinHeaterSetPointTemp = NumArray(26);
698 13 : if (tower.BasinHeaterPowerFTempDiff > 0.0) {
699 0 : if (NumNums < 26) {
700 0 : tower.BasinHeaterSetPointTemp = 2.0;
701 : }
702 0 : if (tower.BasinHeaterSetPointTemp < 2.0) {
703 0 : ShowWarningCustom(state, eoh, format("{} is less than 2 deg C. Freezing could occur.", s_ipsc->cNumericFieldNames(26)));
704 : }
705 : }
706 :
707 13 : if (!AlphArray(5).empty()) {
708 0 : if ((tower.basinHeaterSched = Sched::GetSchedule(state, AlphArray(5))) == nullptr) {
709 0 : ShowWarningItemNotFound(state,
710 : eoh,
711 0 : s_ipsc->cAlphaFieldNames(5),
712 0 : AlphArray(5),
713 : "Basin heater operation will not be modeled and the simulation continues");
714 : }
715 : }
716 :
717 : // begin water use and systems get input
718 13 : tower.EvapLossMode = static_cast<EvapLoss>(getEnumValue(EvapLossNamesUC, Util::makeUPPER(AlphArray(6))));
719 13 : tower.UserEvapLossFactor = NumArray(27); // N23 , \field Evaporation Loss Factor
720 13 : tower.DriftLossFraction = NumArray(28) / 100.0; // N24, \field Drift Loss Percent
721 13 : tower.ConcentrationRatio = NumArray(29); // N17, \field Blowdown Concentration Ratio
722 13 : tower.SizFac = NumArray(33); // N21 \field Sizing Factor
723 13 : if (tower.SizFac <= 0.0) {
724 0 : tower.SizFac = 1.0;
725 : }
726 :
727 13 : tower.BlowdownMode = static_cast<Blowdown>(getEnumValue(BlowDownNamesUC, Util::makeUPPER(AlphArray(7))));
728 13 : if (tower.BlowdownMode == Blowdown::Schedule) {
729 0 : if ((tower.blowdownSched = Sched::GetSchedule(state, AlphArray(8))) == nullptr) {
730 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(8), AlphArray(8));
731 0 : ErrorsFound = true;
732 : }
733 : }
734 :
735 : // added for multi-cell
736 13 : tower.NumCell = NumArray(30);
737 13 : if ((NumNums < 30) && (tower.NumCell == 0)) {
738 : // assume Number of Cells not entered and should be defaulted
739 0 : tower.NumCell = 1;
740 : }
741 13 : tower.MinFracFlowRate = NumArray(31);
742 13 : if ((NumNums < 31) && (tower.MinFracFlowRate == 0.0)) {
743 : // assume Cell Minimum Water Flow Rate Fraction not entered and should be defaulted
744 0 : tower.MinFracFlowRate = 0.33;
745 : }
746 13 : tower.MaxFracFlowRate = NumArray(32);
747 13 : if ((NumNums < 32) && (tower.MaxFracFlowRate == 0.0)) {
748 : // assume Cell Maximum Water Flow Rate Fraction not entered and should be defaulted
749 0 : tower.MaxFracFlowRate = 2.5;
750 : }
751 :
752 : // cell control for two speed tower
753 13 : if (!s_ipsc->lAlphaFieldBlanks(11)) {
754 3 : tower.cellCtrl = static_cast<CellCtrl>(getEnumValue(CellCtrlNamesUC, Util::makeUPPER(AlphArray(11))));
755 : }
756 :
757 13 : if (s_ipsc->lAlphaFieldBlanks(9)) {
758 13 : tower.SuppliedByWaterSystem = false;
759 : } else { // water from storage tank
760 0 : WaterManager::SetupTankDemandComponent(
761 0 : state, AlphArray(1), s_ipsc->cCurrentModuleObject, AlphArray(9), ErrorsFound, tower.WaterTankID, tower.WaterTankDemandARRID);
762 0 : tower.SuppliedByWaterSystem = true;
763 : }
764 :
765 : // outdoor air inlet node
766 13 : if (s_ipsc->lAlphaFieldBlanks(10)) {
767 7 : tower.OutdoorAirInletNodeNum = 0;
768 : } else {
769 6 : tower.OutdoorAirInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
770 6 : AlphArray(10),
771 : ErrorsFound,
772 : DataLoopNode::ConnectionObjectType::CoolingTowerTwoSpeed,
773 6 : tower.Name,
774 : DataLoopNode::NodeFluidType::Air,
775 : DataLoopNode::ConnectionType::OutsideAirReference,
776 : NodeInputManager::CompFluidStream::Primary,
777 : DataLoopNode::ObjectIsNotParent);
778 6 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, tower.OutdoorAirInletNodeNum)) {
779 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(10), AlphArray(10));
780 0 : ErrorsFound = true;
781 : }
782 : }
783 :
784 : // High speed air flow rate must be greater than low speed air flow rate.
785 : // Can't tell yet if autosized, check later in initialize.
786 13 : if (tower.HighSpeedAirFlowRate <= tower.LowSpeedAirFlowRate && tower.HighSpeedAirFlowRate != DataSizing::AutoSize) {
787 0 : ShowSevereError(state,
788 0 : format("{} \"{}\". Low speed air flow rate must be less than the high speed air flow rate.",
789 0 : s_ipsc->cCurrentModuleObject,
790 0 : tower.Name));
791 0 : ErrorsFound = true;
792 : }
793 : // Low speed air flow rate must be greater than free convection air flow rate.
794 : // Can't tell yet if autosized, check later in initialize.
795 13 : if (tower.LowSpeedAirFlowRate <= tower.FreeConvAirFlowRate && tower.LowSpeedAirFlowRate != DataSizing::AutoSize) {
796 0 : ShowSevereError(state,
797 0 : format("{} \"{}\". Free convection air flow rate must be less than the low speed air flow rate.",
798 0 : s_ipsc->cCurrentModuleObject,
799 0 : tower.Name));
800 0 : ErrorsFound = true;
801 : }
802 :
803 : // Check various inputs if Performance Input Method = "UA and Design Water Flow Rate"
804 13 : if (tower.PerformanceInputMethod_Num == PIM::UFactor) {
805 8 : if (tower.DesignWaterFlowRate == 0.0) {
806 0 : ShowSevereError(state,
807 0 : format("{} \"{}\". Tower performance input method requires a design water flow rate greater than zero.",
808 0 : s_ipsc->cCurrentModuleObject,
809 0 : tower.Name));
810 0 : ErrorsFound = true;
811 : }
812 8 : if (tower.HighSpeedTowerUA <= tower.LowSpeedTowerUA && tower.HighSpeedTowerUA != DataSizing::AutoSize) {
813 0 : ShowSevereError(state,
814 0 : format("{} \"{}\". Tower UA at low fan speed must be less than the tower UA at high fan speed.",
815 0 : s_ipsc->cCurrentModuleObject,
816 0 : tower.Name));
817 0 : ErrorsFound = true;
818 : }
819 8 : if (tower.LowSpeedTowerUA <= tower.FreeConvTowerUA && tower.LowSpeedTowerUA != DataSizing::AutoSize) {
820 0 : ShowSevereError(state,
821 0 : format("{} \"{}\". Tower UA at free convection air flow rate must be less than the tower UA at low fan speed.",
822 0 : s_ipsc->cCurrentModuleObject,
823 0 : tower.Name));
824 0 : ErrorsFound = true;
825 : }
826 8 : if (tower.FreeConvTowerUA > 0.0 && tower.FreeConvAirFlowRate == 0.0) {
827 0 : ShowSevereError(
828 : state,
829 0 : format("{} \"{}\". Free convection air flow rate must be greater than zero when free convection UA is greater than zero.",
830 0 : s_ipsc->cCurrentModuleObject,
831 0 : tower.Name));
832 0 : ErrorsFound = true;
833 : }
834 5 : } else if (tower.PerformanceInputMethod_Num == PIM::NominalCapacity) {
835 5 : if (tower.TowerNominalCapacity == 0.0) {
836 0 : ShowSevereError(state,
837 0 : format("{} \"{}\". Tower performance input method requires valid high-speed nominal capacity.",
838 0 : s_ipsc->cCurrentModuleObject,
839 0 : tower.Name));
840 0 : ErrorsFound = true;
841 : }
842 5 : if (tower.TowerLowSpeedNomCap == 0.0) {
843 0 : ShowSevereError(state,
844 0 : format("{} \"{}\". Tower performance input method requires valid low-speed nominal capacity.",
845 0 : s_ipsc->cCurrentModuleObject,
846 0 : tower.Name));
847 0 : ErrorsFound = true;
848 : }
849 5 : if (tower.DesignWaterFlowRate != 0.0) {
850 0 : if (tower.DesignWaterFlowRate > 0.0) {
851 0 : ShowWarningError(state,
852 0 : format("{} \"{}\". Nominal capacity input method and design water flow rate have been specified.",
853 0 : s_ipsc->cCurrentModuleObject,
854 0 : tower.Name));
855 : } else {
856 0 : ShowSevereError(
857 : state,
858 0 : format("{} \"{}\". Nominal capacity input method has been specified and design water flow rate is being autosized.",
859 0 : s_ipsc->cCurrentModuleObject,
860 0 : tower.Name));
861 : }
862 0 : ShowContinueError(state, "Design water flow rate will be set according to nominal tower capacity.");
863 : }
864 5 : if (tower.HighSpeedTowerUA != 0.0) {
865 0 : if (tower.HighSpeedTowerUA > 0.0) {
866 0 : ShowWarningError(state,
867 0 : format("{} \"{}\". Nominal capacity input method and tower UA at high fan speed have been specified.",
868 0 : s_ipsc->cCurrentModuleObject,
869 0 : tower.Name));
870 : } else {
871 0 : ShowSevereError(
872 : state,
873 0 : format("{} \"{}\". Nominal capacity input method has been specified and tower UA at high fan speed is being autosized.",
874 0 : s_ipsc->cCurrentModuleObject,
875 0 : tower.Name));
876 : }
877 0 : ShowContinueError(state, "Tower UA at high fan speed will be set according to nominal tower capacity.");
878 : }
879 5 : if (tower.LowSpeedTowerUA != 0.0) {
880 0 : if (tower.LowSpeedTowerUA > 0.0) {
881 0 : ShowWarningError(state,
882 0 : format("{} \"{}\". Nominal capacity input method and tower UA at low fan speed have been specified.",
883 0 : s_ipsc->cCurrentModuleObject,
884 0 : tower.Name));
885 : } else {
886 0 : ShowSevereError(
887 : state,
888 0 : format("{} \"{}\". Nominal capacity input method has been specified and tower UA at low fan speed is being autosized.",
889 0 : s_ipsc->cCurrentModuleObject,
890 0 : tower.Name));
891 : }
892 0 : ShowContinueError(state, "Tower UA at low fan speed will be set according to nominal tower capacity.");
893 : }
894 5 : if (tower.FreeConvTowerUA != 0.0) {
895 0 : if (tower.FreeConvTowerUA > 0.0) {
896 0 : ShowWarningError(state,
897 0 : format("{} \"{}\". Nominal capacity input method and free convection UA have been specified.",
898 0 : s_ipsc->cCurrentModuleObject,
899 0 : tower.Name));
900 : } else {
901 0 : ShowSevereError(
902 : state,
903 0 : format("{} \"{}\". Nominal capacity input method has been specified and free convection UA is being autosized.",
904 0 : s_ipsc->cCurrentModuleObject,
905 0 : tower.Name));
906 : }
907 0 : ShowContinueError(state, "Free convection UA will be set according to nominal tower capacity.");
908 : }
909 5 : if (tower.TowerLowSpeedNomCap >= tower.TowerNominalCapacity) {
910 0 : ShowSevereError(state,
911 0 : format("{} \"{}\". Low-speed nominal capacity must be less than the high-speed nominal capacity.",
912 0 : s_ipsc->cCurrentModuleObject,
913 0 : tower.Name));
914 0 : ErrorsFound = true;
915 : }
916 5 : if (!tower.TowerLowSpeedNomCapWasAutoSized) {
917 5 : if (tower.TowerFreeConvNomCap >= tower.TowerLowSpeedNomCap) {
918 0 : ShowSevereError(state,
919 0 : format("{} \"{}\". Free convection nominal capacity must be less than the low-speed nominal capacity.",
920 0 : s_ipsc->cCurrentModuleObject,
921 0 : tower.Name));
922 0 : ErrorsFound = true;
923 : }
924 : }
925 5 : if (tower.TowerFreeConvNomCap > 0.0 && tower.FreeConvAirFlowRate == 0.0) {
926 0 : ShowSevereCustom(
927 : state, eoh, "Free convection air flow must be greater than zero when tower free convection capacity is specified.");
928 0 : ErrorsFound = true;
929 : }
930 : } else { // Tower performance input method is not specified as a valid "choice"
931 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), AlphArray(4));
932 0 : ErrorsFound = true;
933 : }
934 :
935 13 : if (NumAlphas > 11) {
936 2 : tower.EndUseSubcategory = AlphArray(12);
937 : } else {
938 11 : tower.EndUseSubcategory = "General";
939 : }
940 : } // End Two-Speed Tower Loop
941 :
942 245 : s_ipsc->cCurrentModuleObject = cCoolingTower_VariableSpeed;
943 271 : for (int VariableSpeedTowerNumber = 1; VariableSpeedTowerNumber <= NumVariableSpeedTowers; ++VariableSpeedTowerNumber) {
944 26 : TowerNum = NumSingleSpeedTowers + NumTwoSpeedTowers + VariableSpeedTowerNumber;
945 78 : s_ip->getObjectItem(state,
946 26 : s_ipsc->cCurrentModuleObject,
947 : VariableSpeedTowerNumber,
948 : AlphArray,
949 : NumAlphas,
950 : NumArray,
951 : NumNums,
952 : IOStat,
953 : _,
954 26 : s_ipsc->lAlphaFieldBlanks,
955 26 : s_ipsc->cAlphaFieldNames,
956 26 : s_ipsc->cNumericFieldNames);
957 26 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
958 26 : GlobalNames::VerifyUniqueInterObjectName(
959 52 : state, UniqueSimpleTowerNames, AlphArray(1), s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), ErrorsFound);
960 :
961 26 : auto &tower = state.dataCondenserLoopTowers->towers(TowerNum);
962 26 : tower.Name = AlphArray(1);
963 26 : tower.TowerType = DataPlant::PlantEquipmentType::CoolingTower_VarSpd;
964 26 : tower.WaterInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
965 26 : AlphArray(2),
966 : ErrorsFound,
967 : DataLoopNode::ConnectionObjectType::CoolingTowerVariableSpeed,
968 26 : AlphArray(1),
969 : DataLoopNode::NodeFluidType::Water,
970 : DataLoopNode::ConnectionType::Inlet,
971 : NodeInputManager::CompFluidStream::Primary,
972 : DataLoopNode::ObjectIsNotParent);
973 52 : tower.WaterOutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
974 26 : AlphArray(3),
975 : ErrorsFound,
976 : DataLoopNode::ConnectionObjectType::CoolingTowerVariableSpeed,
977 26 : AlphArray(1),
978 : DataLoopNode::NodeFluidType::Water,
979 : DataLoopNode::ConnectionType::Outlet,
980 : NodeInputManager::CompFluidStream::Primary,
981 : DataLoopNode::ObjectIsNotParent);
982 26 : BranchNodeConnections::TestCompSet(state, s_ipsc->cCurrentModuleObject, AlphArray(1), AlphArray(2), AlphArray(3), "Chilled Water Nodes");
983 :
984 38 : if ((Util::SameString(AlphArray(4), "CoolToolsUserDefined") || Util::SameString(AlphArray(4), "YorkCalcUserDefined")) &&
985 12 : s_ipsc->lAlphaFieldBlanks(5)) {
986 0 : ShowSevereCustom(state,
987 : eoh,
988 0 : format("A {} must be specified when {} is specified as CoolToolsUserDefined or YorkCalcUserDefined",
989 0 : s_ipsc->cAlphaFieldNames(5),
990 0 : s_ipsc->cAlphaFieldNames(4)));
991 0 : ErrorsFound = true;
992 40 : } else if ((Util::SameString(AlphArray(4), "CoolToolsCrossFlow") || Util::SameString(AlphArray(4), "YorkCalc")) &&
993 14 : !s_ipsc->lAlphaFieldBlanks(5)) {
994 0 : ShowWarningCustom(state,
995 : eoh,
996 : "A Tower Model Coefficient Name is specified and the Tower Model Type is not specified as "
997 : "CoolToolsUserDefined or YorkCalcUserDefined. The CoolingTowerPerformance:CoolTools "
998 : "(orCoolingTowerPerformance:YorkCalc) data object will not be used.");
999 : } else {
1000 26 : tower.ModelCoeffObjectName = AlphArray(5);
1001 : }
1002 :
1003 26 : if (!s_ipsc->lAlphaFieldBlanks(6)) {
1004 25 : tower.FanPowerfAirFlowCurve = Curve::GetCurveIndex(state, AlphArray(6));
1005 25 : if (tower.FanPowerfAirFlowCurve == 0) {
1006 0 : ShowWarningCustom(state,
1007 : eoh,
1008 0 : format("The Fan Power Ratio as a function of Air Flow Rate Ratio Curve Name specified as {} was not found."
1009 : "Fan Power as a function of Air Flow Rate Ratio will default to Fan Power = (Air Flow Rate Ratio)^3."
1010 : "The simulation continues.",
1011 : AlphArray(6)));
1012 : }
1013 : }
1014 :
1015 26 : auto &vstower = state.dataCondenserLoopTowers->towers(TowerNum);
1016 :
1017 26 : if (Util::SameString(AlphArray(4), "CoolToolsCrossFlow")) {
1018 12 : tower.TowerModelType = ModelType::CoolToolsXFModel;
1019 : // set cross-flow model coefficients
1020 : // Outputs approach in C
1021 12 : vstower.Coeff[0] = 0.52049709836241;
1022 12 : vstower.Coeff[1] = -10.617046395344;
1023 12 : vstower.Coeff[2] = 10.7292974722538;
1024 12 : vstower.Coeff[3] = -2.74988377158227;
1025 12 : vstower.Coeff[4] = 4.73629943913743;
1026 12 : vstower.Coeff[5] = -8.25759700874711;
1027 12 : vstower.Coeff[6] = 1.57640938114136;
1028 12 : vstower.Coeff[7] = 6.51119643791324;
1029 12 : vstower.Coeff[8] = 1.50433525206692;
1030 12 : vstower.Coeff[9] = -3.2888529287801;
1031 12 : vstower.Coeff[10] = 0.0257786145353773;
1032 12 : vstower.Coeff[11] = 0.182464289315254;
1033 12 : vstower.Coeff[12] = -0.0818947291400898;
1034 12 : vstower.Coeff[13] = -0.215010003996285;
1035 12 : vstower.Coeff[14] = 0.0186741309635284;
1036 12 : vstower.Coeff[15] = 0.0536824177590012;
1037 12 : vstower.Coeff[16] = -0.00270968955115031;
1038 12 : vstower.Coeff[17] = 0.00112277498589279;
1039 12 : vstower.Coeff[18] = -0.00127758497497718;
1040 12 : vstower.Coeff[19] = 0.0000760420796601607;
1041 12 : vstower.Coeff[20] = 1.43600088336017;
1042 12 : vstower.Coeff[21] = -0.5198695909109;
1043 12 : vstower.Coeff[22] = 0.117339576910507;
1044 12 : vstower.Coeff[23] = 1.50492810819924;
1045 12 : vstower.Coeff[24] = -0.135898905926974;
1046 12 : vstower.Coeff[25] = -0.152577581866506;
1047 12 : vstower.Coeff[26] = -0.0533843828114562;
1048 12 : vstower.Coeff[27] = 0.00493294869565511;
1049 12 : vstower.Coeff[28] = -0.00796260394174197;
1050 12 : vstower.Coeff[29] = 0.000222619828621544;
1051 12 : vstower.Coeff[30] = -0.0543952001568055;
1052 12 : vstower.Coeff[31] = 0.00474266879161693;
1053 12 : vstower.Coeff[32] = -0.0185854671815598;
1054 12 : vstower.Coeff[33] = 0.00115667701293848;
1055 12 : vstower.Coeff[34] = 0.000807370664460284;
1056 :
1057 : // set minimum and maximum boundaries for CoolTools crossflow model input variables
1058 12 : vstower.MinInletAirWBTemp = -1.0;
1059 12 : vstower.MaxInletAirWBTemp = 26.6667;
1060 12 : vstower.MinRangeTemp = 1.1111;
1061 12 : vstower.MaxRangeTemp = 11.1111;
1062 12 : vstower.MinApproachTemp = 1.1111;
1063 12 : vstower.MaxApproachTemp = 11.1111;
1064 12 : vstower.MinWaterFlowRatio = 0.75;
1065 12 : vstower.MaxWaterFlowRatio = 1.25;
1066 :
1067 14 : } else if (Util::SameString(AlphArray(4), "YorkCalc")) {
1068 2 : tower.TowerModelType = ModelType::YorkCalcModel;
1069 : // set counter-flow model coefficients
1070 : // Outputs approach in C
1071 2 : vstower.Coeff[0] = -0.359741205;
1072 2 : vstower.Coeff[1] = -0.055053608;
1073 2 : vstower.Coeff[2] = 0.0023850432;
1074 2 : vstower.Coeff[3] = 0.173926877;
1075 2 : vstower.Coeff[4] = -0.0248473764;
1076 2 : vstower.Coeff[5] = 0.00048430224;
1077 2 : vstower.Coeff[6] = -0.005589849456;
1078 2 : vstower.Coeff[7] = 0.0005770079712;
1079 2 : vstower.Coeff[8] = -0.00001342427256;
1080 2 : vstower.Coeff[9] = 2.84765801111111;
1081 2 : vstower.Coeff[10] = -0.121765149;
1082 2 : vstower.Coeff[11] = 0.0014599242;
1083 2 : vstower.Coeff[12] = 1.680428651;
1084 2 : vstower.Coeff[13] = -0.0166920786;
1085 2 : vstower.Coeff[14] = -0.0007190532;
1086 2 : vstower.Coeff[15] = -0.025485194448;
1087 2 : vstower.Coeff[16] = 0.0000487491696;
1088 2 : vstower.Coeff[17] = 0.00002719234152;
1089 2 : vstower.Coeff[18] = -0.0653766255555556;
1090 2 : vstower.Coeff[19] = -0.002278167;
1091 2 : vstower.Coeff[20] = 0.0002500254;
1092 2 : vstower.Coeff[21] = -0.0910565458;
1093 2 : vstower.Coeff[22] = 0.00318176316;
1094 2 : vstower.Coeff[23] = 0.000038621772;
1095 2 : vstower.Coeff[24] = -0.0034285382352;
1096 2 : vstower.Coeff[25] = 0.00000856589904;
1097 2 : vstower.Coeff[26] = -0.000001516821552;
1098 :
1099 : // set minimum and maximum boundaries for YorkCalc model input variables
1100 2 : vstower.MinInletAirWBTemp = -34.4;
1101 2 : vstower.MaxInletAirWBTemp = 29.4444;
1102 2 : vstower.MinRangeTemp = 1.1111;
1103 2 : vstower.MaxRangeTemp = 22.2222;
1104 2 : vstower.MinApproachTemp = 1.1111;
1105 2 : vstower.MaxApproachTemp = 40.0;
1106 2 : vstower.MinWaterFlowRatio = 0.75;
1107 2 : vstower.MaxWaterFlowRatio = 1.25;
1108 2 : vstower.MaxLiquidToGasRatio = 8.0;
1109 :
1110 12 : } else if (Util::SameString(AlphArray(4), "CoolToolsUserDefined")) {
1111 1 : tower.TowerModelType = ModelType::CoolToolsUserDefined;
1112 : // Nested Get-input routines below. Should pull out of here and read in beforehand.
1113 1 : for (VSModelCoeffNum = 1; VSModelCoeffNum <= NumVSCoolToolsModelCoeffs; ++VSModelCoeffNum) {
1114 1 : s_ip->getObjectItem(
1115 : state, "CoolingTowerPerformance:CoolTools", VSModelCoeffNum, AlphArray2, NumAlphas2, NumArray2, NumNums2, IOStat);
1116 :
1117 1 : if (!Util::SameString(AlphArray2(1), tower.ModelCoeffObjectName)) {
1118 0 : continue;
1119 : }
1120 1 : vstower.FoundModelCoeff = true;
1121 : // verify the correct number of coefficients for the CoolTools model
1122 1 : if (NumNums2 != 43) {
1123 0 : ShowSevereError(state,
1124 0 : format("CoolingTower:VariableSpeed \"{}\". The number of numeric inputs for object "
1125 : "CoolingTowerPerformance:CoolTools \"{}\" must equal 43.",
1126 0 : tower.Name,
1127 0 : tower.ModelCoeffObjectName));
1128 0 : ErrorsFound = true;
1129 : } else {
1130 :
1131 1 : vstower.MinInletAirWBTemp = NumArray2(1);
1132 1 : vstower.MaxInletAirWBTemp = NumArray2(2);
1133 1 : vstower.MinRangeTemp = NumArray2(3);
1134 1 : vstower.MaxRangeTemp = NumArray2(4);
1135 1 : vstower.MinApproachTemp = NumArray2(5);
1136 1 : vstower.MaxApproachTemp = NumArray2(6);
1137 1 : vstower.MinWaterFlowRatio = NumArray2(7);
1138 1 : vstower.MaxWaterFlowRatio = NumArray2(8);
1139 :
1140 36 : for (CoeffNum = 9; CoeffNum <= NumNums2; ++CoeffNum) {
1141 35 : vstower.Coeff[CoeffNum - 9] = NumArray2(CoeffNum);
1142 : }
1143 : }
1144 1 : break;
1145 : }
1146 1 : if (!vstower.FoundModelCoeff) {
1147 0 : ShowSevereError(state,
1148 0 : format("CoolingTower:VariableSpeed \"{}\". User defined name for variable speed cooling tower model coefficients "
1149 : "object not found = {}",
1150 0 : tower.Name,
1151 0 : tower.ModelCoeffObjectName));
1152 0 : ErrorsFound = true;
1153 : }
1154 11 : } else if (Util::SameString(AlphArray(4), "YorkCalcUserDefined")) {
1155 11 : tower.TowerModelType = ModelType::YorkCalcUserDefined;
1156 : // Nested Get-input routines below. Should pull out of here and read in beforehand.
1157 11 : for (VSModelCoeffNum = 1; VSModelCoeffNum <= NumVSYorkCalcModelCoeffs; ++VSModelCoeffNum) {
1158 11 : s_ip->getObjectItem(
1159 : state, "CoolingTowerPerformance:YorkCalc", VSModelCoeffNum, AlphArray2, NumAlphas2, NumArray2, NumNums2, IOStat);
1160 11 : if (!Util::SameString(AlphArray2(1), tower.ModelCoeffObjectName)) {
1161 0 : continue;
1162 : }
1163 11 : vstower.FoundModelCoeff = true;
1164 : // verify the correct number of coefficients for the YorkCalc model
1165 11 : if (NumNums2 != 36) {
1166 0 : ShowSevereError(state,
1167 0 : format("CoolingTower:VariableSpeed \"{}\". The number of numeric inputs for object "
1168 : "CoolingTowerPerformance:YorkCalc \"{}\" must equal 36.",
1169 0 : tower.Name,
1170 0 : tower.ModelCoeffObjectName));
1171 0 : ErrorsFound = true;
1172 : } else {
1173 :
1174 11 : vstower.MinInletAirWBTemp = NumArray2(1);
1175 11 : vstower.MaxInletAirWBTemp = NumArray2(2);
1176 11 : vstower.MinRangeTemp = NumArray2(3);
1177 11 : vstower.MaxRangeTemp = NumArray2(4);
1178 11 : vstower.MinApproachTemp = NumArray2(5);
1179 11 : vstower.MaxApproachTemp = NumArray2(6);
1180 11 : vstower.MinWaterFlowRatio = NumArray2(7);
1181 11 : vstower.MaxWaterFlowRatio = NumArray2(8);
1182 11 : vstower.MaxLiquidToGasRatio = NumArray2(9);
1183 :
1184 308 : for (CoeffNum = 10; CoeffNum <= NumNums2; ++CoeffNum) {
1185 297 : vstower.Coeff[CoeffNum - 10] = NumArray2(CoeffNum);
1186 : }
1187 : }
1188 11 : break;
1189 : }
1190 :
1191 11 : if (!vstower.FoundModelCoeff) {
1192 0 : ShowSevereError(state,
1193 0 : format("{} \"{}\". User defined name for variable speed cooling tower model coefficients object not found = {}",
1194 0 : s_ipsc->cCurrentModuleObject,
1195 0 : tower.Name,
1196 0 : tower.ModelCoeffObjectName));
1197 0 : ErrorsFound = true;
1198 : }
1199 : } else {
1200 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(5), AlphArray(5));
1201 0 : ErrorsFound = true;
1202 : }
1203 :
1204 26 : tower.TowerMassFlowRateMultiplier = vstower.MaxWaterFlowRatio;
1205 :
1206 : // check user defined minimums to be greater than 0
1207 26 : if (vstower.MinApproachTemp < 0.0) {
1208 0 : ShowSevereCustom(state, eoh, "User defined minimum approach temperature must be > 0");
1209 0 : ErrorsFound = true;
1210 : }
1211 26 : if (vstower.MinRangeTemp < 0.0) {
1212 0 : ShowSevereCustom(state, eoh, "User defined minimum range temperature must be > 0");
1213 0 : ErrorsFound = true;
1214 : }
1215 26 : if (vstower.MinWaterFlowRatio < 0.0) {
1216 0 : ShowSevereCustom(state, eoh, "User defined minimum water flow rate ratio must be > 0");
1217 0 : ErrorsFound = true;
1218 : }
1219 :
1220 : // check that the user defined maximums are greater than the minimums
1221 26 : if (vstower.MaxApproachTemp < vstower.MinApproachTemp) {
1222 0 : ShowSevereCustom(state, eoh, "User defined maximum approach temperature must be > the minimum approach temperature");
1223 0 : ErrorsFound = true;
1224 : }
1225 26 : if (vstower.MaxRangeTemp < vstower.MinRangeTemp) {
1226 0 : ShowSevereCustom(state, eoh, "User defined maximum range temperature must be > the minimum range temperature");
1227 0 : ErrorsFound = true;
1228 : }
1229 26 : if (vstower.MaxWaterFlowRatio < vstower.MinWaterFlowRatio) {
1230 0 : ShowSevereCustom(state, eoh, "User defined maximum water flow rate ratio must be > the minimum water flow rate ratio");
1231 0 : ErrorsFound = true;
1232 : }
1233 :
1234 26 : tower.DesignInletWB = NumArray(1);
1235 26 : if (NumArray(1) < vstower.MinInletAirWBTemp || NumArray(1) > vstower.MaxInletAirWBTemp) {
1236 0 : ShowSevereCustom(state,
1237 : eoh,
1238 0 : format("The design inlet air wet-bulb temperature of {:5.2F}"
1239 : "must be within the model limits of {:5.2F} and {:5.2F} degrees C",
1240 0 : tower.DesignInletWB,
1241 0 : vstower.MinInletAirWBTemp,
1242 0 : vstower.MaxInletAirWBTemp));
1243 0 : ErrorsFound = true;
1244 : }
1245 :
1246 26 : tower.DesignApproach = NumArray(2);
1247 26 : if (NumArray(2) < vstower.MinApproachTemp || NumArray(2) > vstower.MaxApproachTemp) {
1248 0 : ShowSevereCustom(state,
1249 : eoh,
1250 0 : format("The design approach temperature of {:5.2F}"
1251 : "must be within the model limits of {:5.2F} and {:5.2F} degrees C",
1252 0 : tower.DesignApproach,
1253 0 : vstower.MinApproachTemp,
1254 0 : vstower.MaxApproachTemp));
1255 0 : ErrorsFound = true;
1256 : }
1257 :
1258 26 : tower.DesignRange = NumArray(3);
1259 26 : if (NumArray(3) < vstower.MinRangeTemp || NumArray(3) > vstower.MaxRangeTemp) {
1260 0 : ShowSevereCustom(state,
1261 : eoh,
1262 0 : format("The design range temperature of {:5.2F}"
1263 : "must be within the model limits of {:5.2F} and {:5.2F} degrees C",
1264 0 : tower.DesignRange,
1265 0 : vstower.MinRangeTemp,
1266 0 : vstower.MaxRangeTemp));
1267 0 : ErrorsFound = true;
1268 : }
1269 :
1270 : // set tower design water outlet and inlet temperatures
1271 26 : tower.DesOutletWaterTemp = tower.DesignInletWB + tower.DesignApproach;
1272 26 : tower.DesInletWaterTemp = tower.DesOutletWaterTemp + tower.DesignRange;
1273 :
1274 26 : tower.DesignWaterFlowRate = NumArray(4);
1275 26 : if (tower.DesignWaterFlowRate == DataSizing::AutoSize) {
1276 15 : tower.DesignWaterFlowRateWasAutoSized = true;
1277 : }
1278 26 : if (NumArray(4) <= 0.0 && NumArray(4) != DataSizing::AutoSize) {
1279 0 : ShowSevereCustom(state, eoh, "Design water flow rate must be > 0");
1280 0 : ErrorsFound = true;
1281 : }
1282 :
1283 26 : tower.HighSpeedAirFlowRate = NumArray(5);
1284 26 : if (tower.HighSpeedAirFlowRate == DataSizing::AutoSize) {
1285 26 : tower.HighSpeedAirFlowRateWasAutoSized = true;
1286 : }
1287 26 : if (NumArray(5) <= 0.0 && NumArray(5) != DataSizing::AutoSize) {
1288 0 : ShowSevereCustom(state, eoh, "Design air flow rate must be > 0");
1289 0 : ErrorsFound = true;
1290 : }
1291 :
1292 26 : tower.HighSpeedFanPower = NumArray(6);
1293 26 : if (tower.HighSpeedFanPower == DataSizing::AutoSize) {
1294 15 : tower.HighSpeedFanPowerWasAutoSized = true;
1295 : }
1296 26 : if (NumArray(6) <= 0.0 && NumArray(6) != DataSizing::AutoSize) {
1297 0 : ShowSevereCustom(state, eoh, "Design fan power must be > 0");
1298 0 : ErrorsFound = true;
1299 : }
1300 :
1301 : // minimum air flow rate fraction must be >= 0.2 and <= 0.5, below this value the tower fan cycles to maintain the setpoint
1302 26 : tower.MinimumVSAirFlowFrac = NumArray(7);
1303 26 : tower.MinimumVSAirFlowFrac = NumArray(7);
1304 26 : if (NumArray(7) < 0.2 || NumArray(7) > 0.5) {
1305 0 : ShowSevereCustom(state, eoh, "Minimum VS air flow rate ratio must be >= 0.2 and <= 0.5");
1306 0 : ErrorsFound = true;
1307 : }
1308 :
1309 : // fraction of tower capacity in free convection regime must be >= to 0 and <= 0.2
1310 26 : tower.FreeConvectionCapacityFraction = NumArray(8);
1311 26 : if (NumArray(8) < 0.0 || NumArray(8) > 0.2) {
1312 0 : ShowSevereCustom(state, eoh, "Fraction of tower capacity in free convection regime must be >= 0 and <= 0.2");
1313 0 : ErrorsFound = true;
1314 : }
1315 :
1316 : // Basin heater power as a function of temperature must be greater than or equal to 0
1317 26 : tower.BasinHeaterPowerFTempDiff = NumArray(9);
1318 26 : if (NumArray(9) < 0.0) {
1319 0 : ShowSevereCustom(state, eoh, "Basin heater power as a function of temperature difference must be >= 0");
1320 0 : ErrorsFound = true;
1321 : }
1322 :
1323 26 : tower.BasinHeaterSetPointTemp = NumArray(10);
1324 26 : if (tower.BasinHeaterPowerFTempDiff > 0.0) {
1325 12 : if (NumNums < 10) {
1326 0 : tower.BasinHeaterSetPointTemp = 2.0;
1327 : }
1328 12 : if (tower.BasinHeaterSetPointTemp < 2.0) {
1329 0 : ShowWarningCustom(state, eoh, format("{} is less than 2 deg C. Freezing could occur.", s_ipsc->cNumericFieldNames(10)));
1330 : }
1331 : }
1332 :
1333 : // Performance Input Method for Variable Speed Towers is assigned to be UA AND DESIGN WATER FLOW RATE
1334 : // for autosizing calculations (see SizeTower)
1335 26 : tower.PerformanceInputMethod_Num = PIM::UFactor;
1336 :
1337 26 : if (!AlphArray(7).empty()) {
1338 0 : if ((tower.basinHeaterSched = Sched::GetSchedule(state, AlphArray(7))) == nullptr) {
1339 0 : ShowWarningItemNotFound(state,
1340 : eoh,
1341 0 : s_ipsc->cAlphaFieldNames(7),
1342 0 : AlphArray(7),
1343 : "Basin heater operation will not be modeled and the simulation continues.");
1344 : }
1345 : }
1346 :
1347 : // begin water use and systems get input
1348 26 : tower.EvapLossMode = static_cast<EvapLoss>(getEnumValue(EvapLossNamesUC, Util::makeUPPER(AlphArray(8))));
1349 26 : tower.UserEvapLossFactor = NumArray(11); // N11 , \field Evaporation Loss Factor
1350 26 : tower.DriftLossFraction = NumArray(12) / 100.0; // N12, \field Drift Loss Percent
1351 26 : tower.ConcentrationRatio = NumArray(13); // N13, \field Blowdown Concentration Ratio
1352 26 : tower.SizFac = NumArray(17); // N14 \field Sizing Factor
1353 26 : if (tower.SizFac <= 0.0) {
1354 0 : tower.SizFac = 1.0;
1355 : }
1356 :
1357 26 : tower.BlowdownMode = static_cast<Blowdown>(getEnumValue(BlowDownNamesUC, Util::makeUPPER(AlphArray(9))));
1358 26 : if (tower.BlowdownMode == Blowdown::Schedule) {
1359 0 : if ((tower.blowdownSched = Sched::GetSchedule(state, AlphArray(10))) == nullptr) {
1360 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(10), AlphArray(10));
1361 0 : ErrorsFound = true;
1362 : }
1363 : }
1364 :
1365 : // added for multi-cell
1366 26 : tower.NumCell = NumArray(14);
1367 26 : if ((NumNums < 14) && (tower.NumCell == 0)) {
1368 : // assume Number of Cells not entered and should be defaulted
1369 0 : tower.NumCell = 1;
1370 : }
1371 26 : tower.MinFracFlowRate = NumArray(15);
1372 26 : if ((NumNums < 15) && (tower.MinFracFlowRate == 0.0)) {
1373 : // assume Cell Minimum Water Flow Rate Fraction not entered and should be defaulted
1374 0 : tower.MinFracFlowRate = 0.33;
1375 : }
1376 26 : tower.MaxFracFlowRate = NumArray(16);
1377 26 : if ((NumNums < 16) && (tower.MaxFracFlowRate == 0.0)) {
1378 : // assume Cell Maximum Water Flow Rate Fraction not entered and should be defaulted
1379 0 : tower.MaxFracFlowRate = 2.5;
1380 : }
1381 :
1382 : // cell control for variable speed tower
1383 26 : if (!s_ipsc->lAlphaFieldBlanks(13)) {
1384 12 : tower.cellCtrl = static_cast<CellCtrl>(getEnumValue(CellCtrlNamesUC, Util::makeUPPER(AlphArray(13))));
1385 : }
1386 :
1387 26 : if (s_ipsc->lAlphaFieldBlanks(11)) {
1388 26 : tower.SuppliedByWaterSystem = false;
1389 : } else { // water from storage tank
1390 0 : WaterManager::SetupTankDemandComponent(
1391 0 : state, AlphArray(1), s_ipsc->cCurrentModuleObject, AlphArray(11), ErrorsFound, tower.WaterTankID, tower.WaterTankDemandARRID);
1392 0 : tower.SuppliedByWaterSystem = true;
1393 : }
1394 :
1395 : // outdoor air inlet node
1396 26 : if (s_ipsc->lAlphaFieldBlanks(12)) {
1397 24 : tower.OutdoorAirInletNodeNum = 0;
1398 : } else {
1399 2 : tower.OutdoorAirInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
1400 2 : AlphArray(12),
1401 : ErrorsFound,
1402 : DataLoopNode::ConnectionObjectType::CoolingTowerVariableSpeed,
1403 2 : tower.Name,
1404 : DataLoopNode::NodeFluidType::Air,
1405 : DataLoopNode::ConnectionType::OutsideAirReference,
1406 : NodeInputManager::CompFluidStream::Primary,
1407 : DataLoopNode::ObjectIsNotParent);
1408 2 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, tower.OutdoorAirInletNodeNum)) {
1409 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(12), AlphArray(12));
1410 0 : ErrorsFound = true;
1411 : }
1412 : }
1413 26 : if (NumAlphas > 13) {
1414 1 : tower.EndUseSubcategory = AlphArray(14);
1415 : } else {
1416 25 : tower.EndUseSubcategory = "General";
1417 : }
1418 :
1419 : } // End Variable-Speed Tower Loop
1420 :
1421 245 : s_ipsc->cCurrentModuleObject = cCoolingTower_VariableSpeedMerkel;
1422 247 : for (int MerkelVSTowerNum = 1; MerkelVSTowerNum <= NumVSMerkelTowers; ++MerkelVSTowerNum) {
1423 2 : TowerNum = NumSingleSpeedTowers + NumTwoSpeedTowers + NumVariableSpeedTowers + MerkelVSTowerNum;
1424 6 : s_ip->getObjectItem(state,
1425 2 : s_ipsc->cCurrentModuleObject,
1426 : MerkelVSTowerNum,
1427 : AlphArray,
1428 : NumAlphas,
1429 : NumArray,
1430 : NumNums,
1431 : IOStat,
1432 2 : s_ipsc->lNumericFieldBlanks,
1433 2 : s_ipsc->lAlphaFieldBlanks,
1434 2 : s_ipsc->cAlphaFieldNames,
1435 2 : s_ipsc->cNumericFieldNames);
1436 :
1437 2 : ErrorObjectHeader eoh{routineName, s_ipsc->cCurrentModuleObject, AlphArray(1)};
1438 2 : GlobalNames::VerifyUniqueInterObjectName(
1439 4 : state, UniqueSimpleTowerNames, AlphArray(1), s_ipsc->cCurrentModuleObject, s_ipsc->cAlphaFieldNames(1), ErrorsFound);
1440 2 : auto &tower = state.dataCondenserLoopTowers->towers(TowerNum);
1441 2 : tower.Name = AlphArray(1);
1442 2 : tower.TowerType = DataPlant::PlantEquipmentType::CoolingTower_VarSpdMerkel;
1443 2 : tower.WaterInletNodeNum = NodeInputManager::GetOnlySingleNode(state,
1444 2 : AlphArray(2),
1445 : ErrorsFound,
1446 : DataLoopNode::ConnectionObjectType::CoolingTowerVariableSpeedMerkel,
1447 2 : AlphArray(1),
1448 : DataLoopNode::NodeFluidType::Water,
1449 : DataLoopNode::ConnectionType::Inlet,
1450 : NodeInputManager::CompFluidStream::Primary,
1451 : DataLoopNode::ObjectIsNotParent);
1452 4 : tower.WaterOutletNodeNum = NodeInputManager::GetOnlySingleNode(state,
1453 2 : AlphArray(3),
1454 : ErrorsFound,
1455 : DataLoopNode::ConnectionObjectType::CoolingTowerVariableSpeedMerkel,
1456 2 : AlphArray(1),
1457 : DataLoopNode::NodeFluidType::Water,
1458 : DataLoopNode::ConnectionType::Outlet,
1459 : NodeInputManager::CompFluidStream::Primary,
1460 : DataLoopNode::ObjectIsNotParent);
1461 2 : BranchNodeConnections::TestCompSet(state, s_ipsc->cCurrentModuleObject, AlphArray(1), AlphArray(2), AlphArray(3), "Chilled Water Nodes");
1462 :
1463 2 : if (Util::SameString(AlphArray(4), "UFactorTimesAreaAndDesignWaterFlowRate")) {
1464 0 : tower.PerformanceInputMethod_Num = PIM::UFactor;
1465 2 : } else if (Util::SameString(AlphArray(4), "NominalCapacity")) {
1466 2 : tower.PerformanceInputMethod_Num = PIM::NominalCapacity;
1467 : } else {
1468 0 : ShowSevereInvalidKey(state, eoh, s_ipsc->cAlphaFieldNames(4), AlphArray(4));
1469 0 : ErrorsFound = true;
1470 : }
1471 :
1472 2 : tower.FanPowerfAirFlowCurve = Curve::GetCurveIndex(state, AlphArray(5));
1473 2 : if (tower.FanPowerfAirFlowCurve == 0) {
1474 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(5), AlphArray(5));
1475 0 : ErrorsFound = true;
1476 : }
1477 :
1478 2 : tower.HeatRejectCapNomCapSizingRatio = NumArray(1);
1479 2 : tower.TowerNominalCapacity = NumArray(2);
1480 2 : if (tower.TowerNominalCapacity == DataSizing::AutoSize) {
1481 2 : tower.TowerNominalCapacityWasAutoSized = true;
1482 : }
1483 2 : tower.TowerFreeConvNomCap = NumArray(3);
1484 2 : if (tower.TowerFreeConvNomCap == DataSizing::AutoSize) {
1485 2 : tower.TowerFreeConvNomCapWasAutoSized = true;
1486 : }
1487 2 : tower.TowerFreeConvNomCapSizingFactor = NumArray(4);
1488 2 : tower.DesignWaterFlowRate = NumArray(5);
1489 2 : if (tower.DesignWaterFlowRate == DataSizing::AutoSize) {
1490 2 : tower.DesignWaterFlowRateWasAutoSized = true;
1491 : }
1492 2 : tower.DesignWaterFlowPerUnitNomCap = NumArray(6);
1493 2 : tower.HighSpeedAirFlowRate = NumArray(7);
1494 2 : if (tower.HighSpeedAirFlowRate == DataSizing::AutoSize) {
1495 2 : tower.HighSpeedAirFlowRateWasAutoSized = true;
1496 : }
1497 2 : tower.DefaultedDesignAirFlowScalingFactor = s_ipsc->lNumericFieldBlanks(8);
1498 2 : tower.DesignAirFlowPerUnitNomCap = NumArray(8);
1499 2 : tower.MinimumVSAirFlowFrac = NumArray(9);
1500 2 : tower.HighSpeedFanPower = NumArray(10);
1501 2 : if (tower.HighSpeedFanPower == DataSizing::AutoSize) {
1502 2 : tower.HighSpeedFanPowerWasAutoSized = true;
1503 : }
1504 2 : tower.DesignFanPowerPerUnitNomCap = NumArray(11);
1505 2 : tower.FreeConvAirFlowRate = NumArray(12);
1506 2 : if (tower.FreeConvAirFlowRate == DataSizing::AutoSize) {
1507 2 : tower.FreeConvAirFlowRateWasAutoSized = true;
1508 : }
1509 2 : tower.FreeConvAirFlowRateSizingFactor = NumArray(13);
1510 2 : tower.HighSpeedTowerUA = NumArray(14);
1511 2 : if (tower.HighSpeedTowerUA == DataSizing::AutoSize) {
1512 0 : tower.HighSpeedTowerUAWasAutoSized = true;
1513 : }
1514 2 : tower.FreeConvTowerUA = NumArray(15);
1515 2 : if (tower.FreeConvTowerUA == DataSizing::AutoSize) {
1516 0 : tower.FreeConvTowerUAWasAutoSized = true;
1517 : }
1518 2 : tower.FreeConvTowerUASizingFactor = NumArray(16);
1519 :
1520 2 : tower.UAModFuncAirFlowRatioCurvePtr = Curve::GetCurveIndex(state, AlphArray(6));
1521 2 : if (tower.UAModFuncAirFlowRatioCurvePtr == 0) {
1522 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(6), AlphArray(6));
1523 0 : ErrorsFound = true;
1524 : }
1525 :
1526 2 : tower.UAModFuncWetBulbDiffCurvePtr = Curve::GetCurveIndex(state, AlphArray(7));
1527 2 : if (tower.UAModFuncWetBulbDiffCurvePtr == 0) {
1528 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(7), AlphArray(7));
1529 0 : ErrorsFound = true;
1530 : }
1531 :
1532 2 : tower.UAModFuncWaterFlowRatioCurvePtr = Curve::GetCurveIndex(state, AlphArray(8));
1533 2 : if (tower.UAModFuncWaterFlowRatioCurvePtr == 0) {
1534 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(8), AlphArray(8));
1535 0 : ErrorsFound = true;
1536 : }
1537 : // cooling tower design inlet conditions
1538 2 : tower.DesInletAirDBTemp = NumArray(17);
1539 2 : if (tower.DesInletAirDBTemp == 0) {
1540 0 : tower.DesInletAirDBTemp = 35.0;
1541 0 : tower.TowerInletCondsAutoSize = true;
1542 : }
1543 2 : tower.DesignInletWB = NumArray(18);
1544 2 : if (tower.DesignInletWB == 0) {
1545 0 : tower.DesignInletWB = 25.6;
1546 0 : tower.TowerInletCondsAutoSize = true;
1547 : }
1548 2 : tower.DesignApproach = NumArray(19);
1549 2 : if (tower.DesignApproach == DataSizing::AutoSize || tower.DesignApproach == 0) {
1550 2 : tower.DesignApproach = 3.9;
1551 2 : tower.TowerInletCondsAutoSize = true;
1552 : }
1553 2 : tower.DesignRange = NumArray(20);
1554 2 : if (tower.DesignRange == DataSizing::AutoSize || tower.DesignRange == 0) {
1555 2 : tower.DesignRange = 5.5;
1556 2 : tower.TowerInletCondsAutoSize = true;
1557 : }
1558 : // set tower design water outlet and inlet temperatures
1559 2 : tower.DesOutletWaterTemp = tower.DesignInletWB + tower.DesignApproach;
1560 2 : tower.DesInletWaterTemp = tower.DesOutletWaterTemp + tower.DesignRange;
1561 : // Basin heater power as a function of temperature must be greater than or equal to 0
1562 2 : tower.BasinHeaterPowerFTempDiff = NumArray(21);
1563 2 : if (NumArray(21) < 0.0) {
1564 0 : ShowSevereCustom(state, eoh, "Basin heater power as a function of temperature difference must be >= 0");
1565 0 : ErrorsFound = true;
1566 : }
1567 :
1568 2 : tower.BasinHeaterSetPointTemp = NumArray(22);
1569 2 : if (tower.BasinHeaterPowerFTempDiff > 0.0) {
1570 0 : if (NumNums < 22) {
1571 0 : tower.BasinHeaterSetPointTemp = 2.0;
1572 : }
1573 0 : if (tower.BasinHeaterSetPointTemp < 2.0) {
1574 0 : ShowWarningCustom(state, eoh, format("{} is less than 2 deg C. Freezing could occur.", s_ipsc->cNumericFieldNames(22)));
1575 : }
1576 : }
1577 :
1578 2 : if (!AlphArray(9).empty()) {
1579 0 : if ((tower.basinHeaterSched = Sched::GetSchedule(state, AlphArray(9))) == nullptr) {
1580 0 : ShowWarningItemNotFound(state,
1581 : eoh,
1582 0 : s_ipsc->cAlphaFieldNames(9),
1583 0 : AlphArray(9),
1584 : "Basin heater operation will not be modeled and the simulation continues");
1585 : }
1586 : }
1587 :
1588 : // begin water use and systems get input
1589 2 : tower.EvapLossMode = static_cast<EvapLoss>(getEnumValue(EvapLossNamesUC, Util::makeUPPER(AlphArray(10))));
1590 2 : tower.UserEvapLossFactor = NumArray(23); // N23 , \field Evaporation Loss Factor
1591 2 : tower.DriftLossFraction = NumArray(24) / 100.0; // N24, \field Drift Loss Percent
1592 2 : tower.ConcentrationRatio = NumArray(25); // N25, \field Blowdown Concentration Ratio
1593 2 : tower.SizFac = NumArray(29); // N29 \field Sizing Factor
1594 2 : if (tower.SizFac <= 0.0) {
1595 0 : tower.SizFac = 1.0;
1596 : }
1597 :
1598 2 : tower.BlowdownMode = static_cast<Blowdown>(getEnumValue(BlowDownNamesUC, Util::makeUPPER(AlphArray(11))));
1599 2 : if (tower.BlowdownMode == Blowdown::Schedule) {
1600 0 : if ((tower.blowdownSched = Sched::GetSchedule(state, AlphArray(12))) == nullptr) {
1601 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(12), AlphArray(12));
1602 0 : ErrorsFound = true;
1603 : }
1604 : }
1605 :
1606 : // added for multi-cell
1607 2 : tower.NumCell = NumArray(26);
1608 2 : if ((NumNums < 26) && (tower.NumCell == 0)) {
1609 : // assume Number of Cells not entered and should be defaulted
1610 0 : tower.NumCell = 1;
1611 : }
1612 2 : tower.MinFracFlowRate = NumArray(27);
1613 2 : if ((NumNums < 27) && (tower.MinFracFlowRate == 0.0)) {
1614 : // assume Cell Minimum Water Flow Rate Fraction not entered and should be defaulted
1615 0 : tower.MinFracFlowRate = 0.33;
1616 : }
1617 2 : tower.MaxFracFlowRate = NumArray(28);
1618 2 : if ((NumNums < 28) && (tower.MaxFracFlowRate == 0.0)) {
1619 : // assume Cell Maximum Water Flow Rate Fraction not entered and should be defaulted
1620 0 : tower.MaxFracFlowRate = 2.5;
1621 : }
1622 2 : tower.TowerMassFlowRateMultiplier = tower.MaxFracFlowRate;
1623 : // cell control for variable speed Merkel tower
1624 2 : if (!s_ipsc->lAlphaFieldBlanks(15)) {
1625 0 : tower.cellCtrl = static_cast<CellCtrl>(getEnumValue(CellCtrlNamesUC, Util::makeUPPER(AlphArray(15))));
1626 : }
1627 :
1628 2 : if (s_ipsc->lAlphaFieldBlanks(13)) {
1629 2 : tower.SuppliedByWaterSystem = false;
1630 : } else { // water from storage tank
1631 0 : WaterManager::SetupTankDemandComponent(
1632 0 : state, AlphArray(1), s_ipsc->cCurrentModuleObject, AlphArray(13), ErrorsFound, tower.WaterTankID, tower.WaterTankDemandARRID);
1633 0 : tower.SuppliedByWaterSystem = true;
1634 : }
1635 :
1636 : // outdoor air inlet node
1637 2 : if (s_ipsc->lAlphaFieldBlanks(14)) {
1638 2 : tower.OutdoorAirInletNodeNum = 0;
1639 : } else {
1640 0 : tower.OutdoorAirInletNodeNum =
1641 0 : NodeInputManager::GetOnlySingleNode(state,
1642 0 : AlphArray(14),
1643 : ErrorsFound,
1644 : DataLoopNode::ConnectionObjectType::CoolingTowerVariableSpeedMerkel,
1645 0 : tower.Name,
1646 : DataLoopNode::NodeFluidType::Air,
1647 : DataLoopNode::ConnectionType::OutsideAirReference,
1648 : NodeInputManager::CompFluidStream::Primary,
1649 : DataLoopNode::ObjectIsNotParent);
1650 0 : if (!OutAirNodeManager::CheckOutAirNodeNumber(state, tower.OutdoorAirInletNodeNum)) {
1651 0 : ShowSevereItemNotFound(state, eoh, s_ipsc->cAlphaFieldNames(14), AlphArray(14));
1652 0 : ErrorsFound = true;
1653 : }
1654 : }
1655 2 : if (NumAlphas > 15) {
1656 0 : tower.EndUseSubcategory = AlphArray(16);
1657 : } else {
1658 2 : tower.EndUseSubcategory = "General";
1659 : }
1660 :
1661 : } // end merkel vs tower loop
1662 :
1663 245 : if (ErrorsFound) {
1664 0 : ShowFatalError(state, "Errors found in getting cooling tower input.");
1665 : }
1666 245 : }
1667 :
1668 272 : void CoolingTower::oneTimeInit(EnergyPlusData &state)
1669 : {
1670 : // Locate the tower on the plant loops for later usage
1671 272 : bool ErrorsFound = false;
1672 272 : PlantUtilities::ScanPlantLoopsForObject(state, this->Name, this->TowerType, this->plantLoc, ErrorsFound, _, _, _, _, _);
1673 272 : if (ErrorsFound) {
1674 0 : ShowFatalError(state, "initialize: Program terminated due to previous condition(s).");
1675 : }
1676 :
1677 : // check if setpoint on outlet node
1678 521 : this->SetpointIsOnOutlet = !((state.dataLoopNodes->Node(this->WaterOutletNodeNum).TempSetPoint == DataLoopNode::SensedNodeFlagValue) &&
1679 249 : (state.dataLoopNodes->Node(this->WaterOutletNodeNum).TempSetPointHi == DataLoopNode::SensedNodeFlagValue));
1680 272 : }
1681 :
1682 1763 : void CoolingTower::initEachEnvironment(EnergyPlusData &state)
1683 : {
1684 : static constexpr std::string_view RoutineName("CoolingTower::initEachEnvironment");
1685 1763 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
1686 1763 : this->DesWaterMassFlowRate = this->DesignWaterFlowRate * rho;
1687 1763 : this->DesWaterMassFlowRatePerCell = this->DesWaterMassFlowRate / this->NumCell;
1688 1763 : PlantUtilities::InitComponentNodes(state, 0.0, this->DesWaterMassFlowRate, this->WaterInletNodeNum, this->WaterOutletNodeNum);
1689 1763 : }
1690 :
1691 8833037 : void CoolingTower::initialize(EnergyPlusData &state)
1692 : {
1693 :
1694 : // SUBROUTINE INFORMATION:
1695 : // AUTHOR Fred Buhl
1696 : // DATE WRITTEN May 2002
1697 : // MODIFIED Don Shirey Sept/Oct 2002, F Buhl Oct 2002
1698 : // RE-ENGINEERED R. Raustad, Oct 2005, moved Max/MinAvail to Init and allowed more than design
1699 : // water flow rate to pass through towers (up to 2.5 and 1.25 times the design flow
1700 : // for 1 or 2-speed and variable speed towers, respectively). Flow multiplier for
1701 : // VS Tower is defaulted to 1.25 and can be reassigned by user.
1702 :
1703 : // PURPOSE OF THIS SUBROUTINE:
1704 : // This subroutine is for initializations of the Cooling Tower components and for
1705 : // final checking of tower inputs (post autosizing)
1706 :
1707 : // METHODOLOGY EMPLOYED:
1708 : // Uses the status flags to trigger initializations.
1709 :
1710 8833037 : if (this->oneTimeFlag) {
1711 272 : this->setupOutputVariables(state);
1712 272 : this->oneTimeInit(state);
1713 272 : this->oneTimeFlag = false;
1714 : }
1715 :
1716 : // Begin environment initializations
1717 8833037 : if (this->envrnFlag && state.dataGlobal->BeginEnvrnFlag && (state.dataPlnt->PlantFirstSizesOkayToFinalize)) {
1718 1763 : this->initEachEnvironment(state);
1719 1763 : this->envrnFlag = false;
1720 : }
1721 :
1722 8833037 : if (!state.dataGlobal->BeginEnvrnFlag) {
1723 8775766 : this->envrnFlag = true;
1724 : }
1725 :
1726 : // Each time initializations
1727 8833037 : this->WaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
1728 :
1729 8833037 : if (this->OutdoorAirInletNodeNum != 0) {
1730 1809382 : this->AirTemp = state.dataLoopNodes->Node(this->OutdoorAirInletNodeNum).Temp;
1731 1809382 : this->AirHumRat = state.dataLoopNodes->Node(this->OutdoorAirInletNodeNum).HumRat;
1732 1809382 : this->AirPress = state.dataLoopNodes->Node(this->OutdoorAirInletNodeNum).Press;
1733 1809382 : this->AirWetBulb = state.dataLoopNodes->Node(this->OutdoorAirInletNodeNum).OutAirWetBulb;
1734 : } else {
1735 7023655 : this->AirTemp = state.dataEnvrn->OutDryBulbTemp;
1736 7023655 : this->AirHumRat = state.dataEnvrn->OutHumRat;
1737 7023655 : this->AirPress = state.dataEnvrn->OutBaroPress;
1738 7023655 : this->AirWetBulb = state.dataEnvrn->OutWetBulbTemp;
1739 : }
1740 :
1741 8833037 : this->WaterMassFlowRate =
1742 8833037 : PlantUtilities::RegulateCondenserCompFlowReqOp(state, this->plantLoc, this->DesWaterMassFlowRate * this->TowerMassFlowRateMultiplier);
1743 :
1744 8833037 : PlantUtilities::SetComponentFlowRate(state, this->WaterMassFlowRate, this->WaterInletNodeNum, this->WaterOutletNodeNum, this->plantLoc);
1745 :
1746 : // Added for fluid bypass. 8/2008
1747 8833037 : this->BypassFraction = 0.0;
1748 8833037 : this->BasinHeaterPower = 0.0;
1749 8833037 : this->airFlowRateRatio = 0.0;
1750 8833037 : }
1751 :
1752 272 : void CoolingTower::setupOutputVariables(EnergyPlusData &state)
1753 : {
1754 : // Set up output variables CurrentModuleObject='CoolingTower:SingleSpeed'
1755 272 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_SingleSpd) {
1756 462 : SetupOutputVariable(state,
1757 : "Cooling Tower Inlet Temperature",
1758 : Constant::Units::C,
1759 231 : this->InletWaterTemp,
1760 : OutputProcessor::TimeStepType::System,
1761 : OutputProcessor::StoreType::Average,
1762 231 : this->Name);
1763 462 : SetupOutputVariable(state,
1764 : "Cooling Tower Outlet Temperature",
1765 : Constant::Units::C,
1766 231 : this->OutletWaterTemp,
1767 : OutputProcessor::TimeStepType::System,
1768 : OutputProcessor::StoreType::Average,
1769 231 : this->Name);
1770 462 : SetupOutputVariable(state,
1771 : "Cooling Tower Mass Flow Rate",
1772 : Constant::Units::kg_s,
1773 231 : this->WaterMassFlowRate,
1774 : OutputProcessor::TimeStepType::System,
1775 : OutputProcessor::StoreType::Average,
1776 231 : this->Name);
1777 462 : SetupOutputVariable(state,
1778 : "Cooling Tower Heat Transfer Rate",
1779 : Constant::Units::W,
1780 231 : this->Qactual,
1781 : OutputProcessor::TimeStepType::System,
1782 : OutputProcessor::StoreType::Average,
1783 231 : this->Name);
1784 462 : SetupOutputVariable(state,
1785 : "Cooling Tower Fan Electricity Rate",
1786 : Constant::Units::W,
1787 231 : this->FanPower,
1788 : OutputProcessor::TimeStepType::System,
1789 : OutputProcessor::StoreType::Average,
1790 231 : this->Name);
1791 462 : SetupOutputVariable(state,
1792 : "Cooling Tower Fan Electricity Energy",
1793 : Constant::Units::J,
1794 231 : this->FanEnergy,
1795 : OutputProcessor::TimeStepType::System,
1796 : OutputProcessor::StoreType::Sum,
1797 231 : this->Name,
1798 : Constant::eResource::Electricity,
1799 : OutputProcessor::Group::Plant,
1800 : OutputProcessor::EndUseCat::HeatRejection,
1801 : this->EndUseSubcategory);
1802 : // Added for fluid bypass
1803 462 : SetupOutputVariable(state,
1804 : "Cooling Tower Bypass Fraction",
1805 : Constant::Units::None,
1806 231 : this->BypassFraction,
1807 : OutputProcessor::TimeStepType::System,
1808 : OutputProcessor::StoreType::Average,
1809 231 : this->Name);
1810 231 : SetupOutputVariable(state,
1811 : "Cooling Tower Operating Cells Count",
1812 : Constant::Units::None,
1813 231 : this->NumCellOn,
1814 : OutputProcessor::TimeStepType::System,
1815 : OutputProcessor::StoreType::Average,
1816 231 : this->Name);
1817 462 : SetupOutputVariable(state,
1818 : "Cooling Tower Fan Cycling Ratio",
1819 : Constant::Units::None,
1820 231 : this->FanCyclingRatio,
1821 : OutputProcessor::TimeStepType::System,
1822 : OutputProcessor::StoreType::Average,
1823 231 : this->Name);
1824 231 : if (this->BasinHeaterPowerFTempDiff > 0.0) {
1825 0 : SetupOutputVariable(state,
1826 : "Cooling Tower Basin Heater Electricity Rate",
1827 : Constant::Units::W,
1828 0 : this->BasinHeaterPower,
1829 : OutputProcessor::TimeStepType::System,
1830 : OutputProcessor::StoreType::Average,
1831 0 : this->Name);
1832 0 : SetupOutputVariable(state,
1833 : "Cooling Tower Basin Heater Electricity Energy",
1834 : Constant::Units::J,
1835 0 : this->BasinHeaterConsumption,
1836 : OutputProcessor::TimeStepType::System,
1837 : OutputProcessor::StoreType::Sum,
1838 0 : this->Name,
1839 : Constant::eResource::Electricity,
1840 : OutputProcessor::Group::Plant,
1841 : OutputProcessor::EndUseCat::HeatRejection,
1842 : "BasinHeater");
1843 : }
1844 : }
1845 :
1846 : // CurrentModuleObject='CoolingTower:TwoSpeed'
1847 272 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_TwoSpd) {
1848 26 : SetupOutputVariable(state,
1849 : "Cooling Tower Inlet Temperature",
1850 : Constant::Units::C,
1851 13 : this->InletWaterTemp,
1852 : OutputProcessor::TimeStepType::System,
1853 : OutputProcessor::StoreType::Average,
1854 13 : this->Name);
1855 26 : SetupOutputVariable(state,
1856 : "Cooling Tower Outlet Temperature",
1857 : Constant::Units::C,
1858 13 : this->OutletWaterTemp,
1859 : OutputProcessor::TimeStepType::System,
1860 : OutputProcessor::StoreType::Average,
1861 13 : this->Name);
1862 26 : SetupOutputVariable(state,
1863 : "Cooling Tower Mass Flow Rate",
1864 : Constant::Units::kg_s,
1865 13 : this->WaterMassFlowRate,
1866 : OutputProcessor::TimeStepType::System,
1867 : OutputProcessor::StoreType::Average,
1868 13 : this->Name);
1869 26 : SetupOutputVariable(state,
1870 : "Cooling Tower Heat Transfer Rate",
1871 : Constant::Units::W,
1872 13 : this->Qactual,
1873 : OutputProcessor::TimeStepType::System,
1874 : OutputProcessor::StoreType::Average,
1875 13 : this->Name);
1876 26 : SetupOutputVariable(state,
1877 : "Cooling Tower Fan Electricity Rate",
1878 : Constant::Units::W,
1879 13 : this->FanPower,
1880 : OutputProcessor::TimeStepType::System,
1881 : OutputProcessor::StoreType::Average,
1882 13 : this->Name);
1883 26 : SetupOutputVariable(state,
1884 : "Cooling Tower Fan Electricity Energy",
1885 : Constant::Units::J,
1886 13 : this->FanEnergy,
1887 : OutputProcessor::TimeStepType::System,
1888 : OutputProcessor::StoreType::Sum,
1889 13 : this->Name,
1890 : Constant::eResource::Electricity,
1891 : OutputProcessor::Group::Plant,
1892 : OutputProcessor::EndUseCat::HeatRejection,
1893 : this->EndUseSubcategory);
1894 26 : SetupOutputVariable(state,
1895 : "Cooling Tower Fan Cycling Ratio",
1896 : Constant::Units::None,
1897 13 : this->FanCyclingRatio,
1898 : OutputProcessor::TimeStepType::System,
1899 : OutputProcessor::StoreType::Average,
1900 13 : this->Name);
1901 13 : SetupOutputVariable(state,
1902 : "Cooling Tower Fan Speed Level",
1903 : Constant::Units::None,
1904 13 : this->SpeedSelected,
1905 : OutputProcessor::TimeStepType::System,
1906 : OutputProcessor::StoreType::Average,
1907 13 : this->Name);
1908 13 : SetupOutputVariable(state,
1909 : "Cooling Tower Operating Cells Count",
1910 : Constant::Units::None,
1911 13 : this->NumCellOn,
1912 : OutputProcessor::TimeStepType::System,
1913 : OutputProcessor::StoreType::Average,
1914 13 : this->Name);
1915 13 : if (this->BasinHeaterPowerFTempDiff > 0.0) {
1916 0 : SetupOutputVariable(state,
1917 : "Cooling Tower Basin Heater Electricity Rate",
1918 : Constant::Units::W,
1919 0 : this->BasinHeaterPower,
1920 : OutputProcessor::TimeStepType::System,
1921 : OutputProcessor::StoreType::Average,
1922 0 : this->Name);
1923 0 : SetupOutputVariable(state,
1924 : "Cooling Tower Basin Heater Electricity Energy",
1925 : Constant::Units::J,
1926 0 : this->BasinHeaterConsumption,
1927 : OutputProcessor::TimeStepType::System,
1928 : OutputProcessor::StoreType::Sum,
1929 0 : this->Name,
1930 : Constant::eResource::Electricity,
1931 : OutputProcessor::Group::Plant,
1932 : OutputProcessor::EndUseCat::HeatRejection,
1933 : "BasinHeater");
1934 : }
1935 : }
1936 :
1937 : // CurrentModuleObject='CoolingTower:VariableSpeed'
1938 272 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_VarSpd) {
1939 52 : SetupOutputVariable(state,
1940 : "Cooling Tower Inlet Temperature",
1941 : Constant::Units::C,
1942 26 : this->InletWaterTemp,
1943 : OutputProcessor::TimeStepType::System,
1944 : OutputProcessor::StoreType::Average,
1945 26 : this->Name);
1946 52 : SetupOutputVariable(state,
1947 : "Cooling Tower Outlet Temperature",
1948 : Constant::Units::C,
1949 26 : this->OutletWaterTemp,
1950 : OutputProcessor::TimeStepType::System,
1951 : OutputProcessor::StoreType::Average,
1952 26 : this->Name);
1953 52 : SetupOutputVariable(state,
1954 : "Cooling Tower Mass Flow Rate",
1955 : Constant::Units::kg_s,
1956 26 : this->WaterMassFlowRate,
1957 : OutputProcessor::TimeStepType::System,
1958 : OutputProcessor::StoreType::Average,
1959 26 : this->Name);
1960 52 : SetupOutputVariable(state,
1961 : "Cooling Tower Heat Transfer Rate",
1962 : Constant::Units::W,
1963 26 : this->Qactual,
1964 : OutputProcessor::TimeStepType::System,
1965 : OutputProcessor::StoreType::Average,
1966 26 : this->Name);
1967 52 : SetupOutputVariable(state,
1968 : "Cooling Tower Fan Electricity Rate",
1969 : Constant::Units::W,
1970 26 : this->FanPower,
1971 : OutputProcessor::TimeStepType::System,
1972 : OutputProcessor::StoreType::Average,
1973 26 : this->Name);
1974 52 : SetupOutputVariable(state,
1975 : "Cooling Tower Fan Electricity Energy",
1976 : Constant::Units::J,
1977 26 : this->FanEnergy,
1978 : OutputProcessor::TimeStepType::System,
1979 : OutputProcessor::StoreType::Sum,
1980 26 : this->Name,
1981 : Constant::eResource::Electricity,
1982 : OutputProcessor::Group::Plant,
1983 : OutputProcessor::EndUseCat::HeatRejection,
1984 : this->EndUseSubcategory);
1985 52 : SetupOutputVariable(state,
1986 : "Cooling Tower Air Flow Rate Ratio",
1987 : Constant::Units::None,
1988 26 : this->AirFlowRatio,
1989 : OutputProcessor::TimeStepType::System,
1990 : OutputProcessor::StoreType::Average,
1991 26 : this->Name);
1992 52 : SetupOutputVariable(state,
1993 : "Cooling Tower Fan Part Load Ratio",
1994 : Constant::Units::None,
1995 26 : this->FanCyclingRatio,
1996 : OutputProcessor::TimeStepType::System,
1997 : OutputProcessor::StoreType::Average,
1998 26 : this->Name);
1999 26 : SetupOutputVariable(state,
2000 : "Cooling Tower Operating Cells Count",
2001 : Constant::Units::None,
2002 26 : this->NumCellOn,
2003 : OutputProcessor::TimeStepType::System,
2004 : OutputProcessor::StoreType::Average,
2005 26 : this->Name);
2006 26 : if (this->BasinHeaterPowerFTempDiff > 0.0) {
2007 24 : SetupOutputVariable(state,
2008 : "Cooling Tower Basin Heater Electricity Rate",
2009 : Constant::Units::W,
2010 12 : this->BasinHeaterPower,
2011 : OutputProcessor::TimeStepType::System,
2012 : OutputProcessor::StoreType::Average,
2013 12 : this->Name);
2014 24 : SetupOutputVariable(state,
2015 : "Cooling Tower Basin Heater Electricity Energy",
2016 : Constant::Units::J,
2017 12 : this->BasinHeaterConsumption,
2018 : OutputProcessor::TimeStepType::System,
2019 : OutputProcessor::StoreType::Sum,
2020 12 : this->Name,
2021 : Constant::eResource::Electricity,
2022 : OutputProcessor::Group::Plant,
2023 : OutputProcessor::EndUseCat::HeatRejection,
2024 : "BasinHeater");
2025 : }
2026 : }
2027 :
2028 : // CurrentModuleObject='CoolingTower:VariableSpeed:Merkel'
2029 272 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_VarSpdMerkel) {
2030 4 : SetupOutputVariable(state,
2031 : "Cooling Tower Inlet Temperature",
2032 : Constant::Units::C,
2033 2 : this->InletWaterTemp,
2034 : OutputProcessor::TimeStepType::System,
2035 : OutputProcessor::StoreType::Average,
2036 2 : this->Name);
2037 4 : SetupOutputVariable(state,
2038 : "Cooling Tower Outlet Temperature",
2039 : Constant::Units::C,
2040 2 : this->OutletWaterTemp,
2041 : OutputProcessor::TimeStepType::System,
2042 : OutputProcessor::StoreType::Average,
2043 2 : this->Name);
2044 4 : SetupOutputVariable(state,
2045 : "Cooling Tower Mass Flow Rate",
2046 : Constant::Units::kg_s,
2047 2 : this->WaterMassFlowRate,
2048 : OutputProcessor::TimeStepType::System,
2049 : OutputProcessor::StoreType::Average,
2050 2 : this->Name);
2051 4 : SetupOutputVariable(state,
2052 : "Cooling Tower Heat Transfer Rate",
2053 : Constant::Units::W,
2054 2 : this->Qactual,
2055 : OutputProcessor::TimeStepType::System,
2056 : OutputProcessor::StoreType::Average,
2057 2 : this->Name);
2058 4 : SetupOutputVariable(state,
2059 : "Cooling Tower Fan Electricity Rate",
2060 : Constant::Units::W,
2061 2 : this->FanPower,
2062 : OutputProcessor::TimeStepType::System,
2063 : OutputProcessor::StoreType::Average,
2064 2 : this->Name);
2065 4 : SetupOutputVariable(state,
2066 : "Cooling Tower Fan Electricity Energy",
2067 : Constant::Units::J,
2068 2 : this->FanEnergy,
2069 : OutputProcessor::TimeStepType::System,
2070 : OutputProcessor::StoreType::Sum,
2071 2 : this->Name,
2072 : Constant::eResource::Electricity,
2073 : OutputProcessor::Group::Plant,
2074 : OutputProcessor::EndUseCat::HeatRejection,
2075 : this->EndUseSubcategory);
2076 4 : SetupOutputVariable(state,
2077 : "Cooling Tower Fan Speed Ratio",
2078 : Constant::Units::None,
2079 2 : this->AirFlowRatio,
2080 : OutputProcessor::TimeStepType::System,
2081 : OutputProcessor::StoreType::Average,
2082 2 : this->Name);
2083 :
2084 2 : SetupOutputVariable(state,
2085 : "Cooling Tower Operating Cells Count",
2086 : Constant::Units::None,
2087 2 : this->NumCellOn,
2088 : OutputProcessor::TimeStepType::System,
2089 : OutputProcessor::StoreType::Average,
2090 2 : this->Name);
2091 2 : if (this->BasinHeaterPowerFTempDiff > 0.0) {
2092 0 : SetupOutputVariable(state,
2093 : "Cooling Tower Basin Heater Electricity Rate",
2094 : Constant::Units::W,
2095 0 : this->BasinHeaterPower,
2096 : OutputProcessor::TimeStepType::System,
2097 : OutputProcessor::StoreType::Average,
2098 0 : this->Name);
2099 0 : SetupOutputVariable(state,
2100 : "Cooling Tower Basin Heater Electricity Energy",
2101 : Constant::Units::J,
2102 0 : this->BasinHeaterConsumption,
2103 : OutputProcessor::TimeStepType::System,
2104 : OutputProcessor::StoreType::Sum,
2105 0 : this->Name,
2106 : Constant::eResource::Electricity,
2107 : OutputProcessor::Group::Plant,
2108 : OutputProcessor::EndUseCat::HeatRejection,
2109 : "BasinHeater");
2110 : }
2111 : }
2112 : // setup common water reporting for all types of towers.
2113 272 : if (this->SuppliedByWaterSystem) {
2114 4 : SetupOutputVariable(state,
2115 : "Cooling Tower Make Up Water Volume Flow Rate",
2116 : Constant::Units::m3_s,
2117 2 : this->MakeUpVdot,
2118 : OutputProcessor::TimeStepType::System,
2119 : OutputProcessor::StoreType::Average,
2120 2 : this->Name);
2121 4 : SetupOutputVariable(state,
2122 : "Cooling Tower Make Up Water Volume",
2123 : Constant::Units::m3,
2124 2 : this->MakeUpVol,
2125 : OutputProcessor::TimeStepType::System,
2126 : OutputProcessor::StoreType::Sum,
2127 2 : this->Name);
2128 4 : SetupOutputVariable(state,
2129 : "Cooling Tower Storage Tank Water Volume Flow Rate",
2130 : Constant::Units::m3_s,
2131 2 : this->TankSupplyVdot,
2132 : OutputProcessor::TimeStepType::System,
2133 : OutputProcessor::StoreType::Average,
2134 2 : this->Name);
2135 4 : SetupOutputVariable(state,
2136 : "Cooling Tower Storage Tank Water Volume",
2137 : Constant::Units::m3,
2138 2 : this->TankSupplyVol,
2139 : OutputProcessor::TimeStepType::System,
2140 : OutputProcessor::StoreType::Sum,
2141 2 : this->Name,
2142 : Constant::eResource::Water,
2143 : OutputProcessor::Group::Plant,
2144 : OutputProcessor::EndUseCat::HeatRejection);
2145 4 : SetupOutputVariable(state,
2146 : "Cooling Tower Starved Storage Tank Water Volume Flow Rate",
2147 : Constant::Units::m3_s,
2148 2 : this->StarvedMakeUpVdot,
2149 : OutputProcessor::TimeStepType::System,
2150 : OutputProcessor::StoreType::Average,
2151 2 : this->Name);
2152 4 : SetupOutputVariable(state,
2153 : "Cooling Tower Starved Storage Tank Water Volume",
2154 : Constant::Units::m3,
2155 2 : this->StarvedMakeUpVol,
2156 : OutputProcessor::TimeStepType::System,
2157 : OutputProcessor::StoreType::Sum,
2158 2 : this->Name);
2159 4 : SetupOutputVariable(state,
2160 : "Cooling Tower Make Up Mains Water Volume",
2161 : Constant::Units::m3,
2162 2 : this->StarvedMakeUpVol,
2163 : OutputProcessor::TimeStepType::System,
2164 : OutputProcessor::StoreType::Sum,
2165 2 : this->Name,
2166 : Constant::eResource::MainsWater,
2167 : OutputProcessor::Group::Plant,
2168 : OutputProcessor::EndUseCat::HeatRejection);
2169 : } else { // tower water from mains and gets metered
2170 540 : SetupOutputVariable(state,
2171 : "Cooling Tower Make Up Water Volume Flow Rate",
2172 : Constant::Units::m3_s,
2173 270 : this->MakeUpVdot,
2174 : OutputProcessor::TimeStepType::System,
2175 : OutputProcessor::StoreType::Average,
2176 270 : this->Name);
2177 540 : SetupOutputVariable(state,
2178 : "Cooling Tower Make Up Water Volume",
2179 : Constant::Units::m3,
2180 270 : this->MakeUpVol,
2181 : OutputProcessor::TimeStepType::System,
2182 : OutputProcessor::StoreType::Sum,
2183 270 : this->Name,
2184 : Constant::eResource::Water,
2185 : OutputProcessor::Group::Plant,
2186 : OutputProcessor::EndUseCat::HeatRejection);
2187 540 : SetupOutputVariable(state,
2188 : "Cooling Tower Make Up Mains Water Volume",
2189 : Constant::Units::m3,
2190 270 : this->MakeUpVol,
2191 : OutputProcessor::TimeStepType::System,
2192 : OutputProcessor::StoreType::Sum,
2193 270 : this->Name,
2194 : Constant::eResource::MainsWater,
2195 : OutputProcessor::Group::Plant,
2196 : OutputProcessor::EndUseCat::HeatRejection);
2197 : }
2198 :
2199 544 : SetupOutputVariable(state,
2200 : "Cooling Tower Water Evaporation Volume Flow Rate",
2201 : Constant::Units::m3_s,
2202 272 : this->EvaporationVdot,
2203 : OutputProcessor::TimeStepType::System,
2204 : OutputProcessor::StoreType::Average,
2205 272 : this->Name);
2206 544 : SetupOutputVariable(state,
2207 : "Cooling Tower Water Evaporation Volume",
2208 : Constant::Units::m3,
2209 272 : this->EvaporationVol,
2210 : OutputProcessor::TimeStepType::System,
2211 : OutputProcessor::StoreType::Sum,
2212 272 : this->Name);
2213 544 : SetupOutputVariable(state,
2214 : "Cooling Tower Water Drift Volume Flow Rate",
2215 : Constant::Units::m3_s,
2216 272 : this->DriftVdot,
2217 : OutputProcessor::TimeStepType::System,
2218 : OutputProcessor::StoreType::Average,
2219 272 : this->Name);
2220 544 : SetupOutputVariable(state,
2221 : "Cooling Tower Water Drift Volume",
2222 : Constant::Units::m3,
2223 272 : this->DriftVol,
2224 : OutputProcessor::TimeStepType::System,
2225 : OutputProcessor::StoreType::Sum,
2226 272 : this->Name);
2227 544 : SetupOutputVariable(state,
2228 : "Cooling Tower Water Blowdown Volume Flow Rate",
2229 : Constant::Units::m3_s,
2230 272 : this->BlowdownVdot,
2231 : OutputProcessor::TimeStepType::System,
2232 : OutputProcessor::StoreType::Average,
2233 272 : this->Name);
2234 544 : SetupOutputVariable(state,
2235 : "Cooling Tower Water Blowdown Volume",
2236 : Constant::Units::m3,
2237 272 : this->BlowdownVol,
2238 : OutputProcessor::TimeStepType::System,
2239 : OutputProcessor::StoreType::Sum,
2240 272 : this->Name);
2241 544 : SetupOutputVariable(state,
2242 : "Cooling Tower Approach",
2243 : Constant::Units::C,
2244 272 : this->coolingTowerApproach,
2245 : OutputProcessor::TimeStepType::System,
2246 : OutputProcessor::StoreType::Average,
2247 272 : this->Name);
2248 544 : SetupOutputVariable(state,
2249 : "Cooling Tower Range",
2250 : Constant::Units::C,
2251 272 : this->coolingTowerRange,
2252 : OutputProcessor::TimeStepType::System,
2253 : OutputProcessor::StoreType::Average,
2254 272 : this->Name);
2255 272 : }
2256 :
2257 1372 : void CoolingTower::SizeTower(EnergyPlusData &state)
2258 : {
2259 :
2260 : // SUBROUTINE INFORMATION:
2261 : // AUTHOR Fred Buhl
2262 : // DATE WRITTEN May 2002
2263 : // MODIFIED Don Shirey, Sept/Oct 2002; Richard Raustad, Feb 2005
2264 :
2265 : // PURPOSE OF THIS SUBROUTINE:
2266 : // This subroutine is for sizing Cooling Tower Components for which capacities and flow rates
2267 : // have not been specified in the input. This subroutine also calculates tower UA if the user
2268 : // has specified tower performance via the "Nominal Capacity" method.
2269 :
2270 : // METHODOLOGY EMPLOYED:
2271 : // Obtains condenser flow rate from the plant sizing array. If tower performance is specified
2272 : // via the "Nominal Capacity" method, the water flow rate is directly proportional to capacity.
2273 :
2274 : // SUBROUTINE PARAMETER DEFINITIONS:
2275 :
2276 1372 : int constexpr MaxIte(500); // Maximum number of iterations
2277 1372 : Real64 constexpr Acc(0.0001); // Accuracy of result
2278 : static constexpr std::string_view RoutineName("SizeTower");
2279 :
2280 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
2281 : int SolFla; // Flag of solver
2282 1372 : Real64 DesTowerLoad(0.0); // Design tower load [W]
2283 : Real64 UA0; // Lower bound for UA [W/C]
2284 : Real64 UA1; // Upper bound for UA [W/C]
2285 : Real64 UA; // Calculated UA value
2286 : Real64 DesTowerInletWaterTemp; // design tower inlet water temperature
2287 : Real64 DesTowerExitWaterTemp; // design tower exit water temperature
2288 : Real64 DesTowerWaterDeltaT; // design tower temperature range
2289 : Real64 DesTowerApproachFromPlant; // design tower approach temperature from plant sizing object
2290 1372 : Real64 TolTemp(0.04); // DeltaT and DesignApproach diffs tolerance between plant sizing data and user input in cooling tower
2291 : // for warning message reporting purpose only
2292 :
2293 1372 : Real64 tmpDesignWaterFlowRate = this->DesignWaterFlowRate;
2294 1372 : Real64 tmpHighSpeedFanPower = this->HighSpeedFanPower;
2295 1372 : Real64 tmpHighSpeedAirFlowRate = this->HighSpeedAirFlowRate;
2296 1372 : Real64 tmpLowSpeedAirFlowRate = this->LowSpeedAirFlowRate;
2297 :
2298 1372 : auto &PlantSizData(state.dataSize->PlantSizData);
2299 :
2300 : // Find the appropriate Plant Sizing object
2301 1372 : int PltSizCondNum = this->plantLoc.loop->PlantSizNum;
2302 :
2303 1372 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_SingleSpd ||
2304 195 : this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_TwoSpd) {
2305 1242 : if (this->TowerInletCondsAutoSize) {
2306 1242 : if (PltSizCondNum > 0) {
2307 : // use plant sizing data
2308 697 : DesTowerExitWaterTemp = PlantSizData(PltSizCondNum).ExitTemp;
2309 697 : DesTowerInletWaterTemp = DesTowerExitWaterTemp + PlantSizData(PltSizCondNum).DeltaT;
2310 697 : DesTowerWaterDeltaT = PlantSizData(PltSizCondNum).DeltaT;
2311 : } else {
2312 : // set hard wired input assumptions
2313 : // AssumedDeltaT = 11.0;
2314 : // AssumedExitTemp = 21.0;
2315 545 : DesTowerWaterDeltaT = 11.0;
2316 545 : DesTowerExitWaterTemp = 21.0;
2317 545 : DesTowerInletWaterTemp = DesTowerExitWaterTemp + DesTowerWaterDeltaT;
2318 : }
2319 : } else {
2320 : // use tower sizing data
2321 0 : DesTowerExitWaterTemp = this->DesOutletWaterTemp;
2322 0 : DesTowerInletWaterTemp = this->DesInletWaterTemp;
2323 0 : DesTowerWaterDeltaT = this->DesignRange;
2324 0 : if (PltSizCondNum > 0) {
2325 : // check the tower range against the plant sizing data
2326 0 : if (std::abs(DesTowerWaterDeltaT - PlantSizData(PltSizCondNum).DeltaT) > TolTemp) {
2327 0 : ShowWarningError(state,
2328 0 : format("Error when autosizing the load for cooling tower = {}. Tower Design Range Temperature is different "
2329 : "from the Design Loop Delta Temperature.",
2330 0 : this->Name));
2331 0 : ShowContinueError(state, format("Tower Design Range Temperature specified in tower = {}", this->Name));
2332 0 : ShowContinueError(state,
2333 0 : format("is inconsistent with Design Loop Delta Temperature specified in Sizing:Plant object = {}.",
2334 0 : PlantSizData(PltSizCondNum).PlantLoopName));
2335 0 : ShowContinueError(state, format("..The Design Range Temperature specified in tower is = {:.2T}", this->DesignRange));
2336 0 : ShowContinueError(state,
2337 0 : format("..The Design Loop Delta Temperature specified in plant sizing data is = {:.2T}",
2338 0 : PlantSizData(PltSizCondNum).DeltaT));
2339 : }
2340 : // check if the tower approach is different from plant sizing data
2341 0 : DesTowerApproachFromPlant = PlantSizData(PltSizCondNum).ExitTemp - this->DesignInletWB;
2342 0 : if (std::abs(DesTowerApproachFromPlant - this->DesignApproach) > TolTemp) {
2343 0 : ShowWarningError(state,
2344 0 : format("Error when autosizing the UA for cooling tower = {}. Tower Design Approach Temperature is "
2345 : "inconsistent with Approach from Plant Sizing Data.",
2346 0 : this->Name));
2347 0 : ShowContinueError(state,
2348 0 : format("The Design Approach Temperature from inputs specified in Sizing:Plant object = {}",
2349 0 : PlantSizData(PltSizCondNum).PlantLoopName));
2350 0 : ShowContinueError(state, format("is inconsistent with Design Approach Temperature specified in tower = {}.", this->Name));
2351 0 : ShowContinueError(state,
2352 0 : format("..The Design Approach Temperature from inputs specified is = {:.2T}", DesTowerApproachFromPlant));
2353 0 : ShowContinueError(state, format("..The Design Approach Temperature specified in tower is = {:.2T}", this->DesignApproach));
2354 : }
2355 : }
2356 : }
2357 : } else { // CoolingTower_VariableSpeed
2358 130 : if (PltSizCondNum > 0) {
2359 : // use plant sizing data
2360 130 : DesTowerExitWaterTemp = PlantSizData(PltSizCondNum).ExitTemp;
2361 130 : DesTowerInletWaterTemp = DesTowerExitWaterTemp + PlantSizData(PltSizCondNum).DeltaT;
2362 130 : DesTowerWaterDeltaT = PlantSizData(PltSizCondNum).DeltaT;
2363 : } else {
2364 : // set hard wired input assumptions
2365 0 : DesTowerWaterDeltaT = 11.0;
2366 0 : DesTowerExitWaterTemp = 21.0;
2367 0 : DesTowerInletWaterTemp = DesTowerExitWaterTemp + DesTowerWaterDeltaT;
2368 : }
2369 : }
2370 :
2371 1372 : if (this->PerformanceInputMethod_Num == PIM::UFactor && (!this->HighSpeedTowerUAWasAutoSized)) {
2372 720 : if (PltSizCondNum > 0) {
2373 215 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, DesTowerExitWaterTemp, RoutineName);
2374 215 : Real64 const Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
2375 215 : DesTowerLoad = rho * Cp * this->DesignWaterFlowRate * DesTowerWaterDeltaT;
2376 215 : this->TowerNominalCapacity = DesTowerLoad / this->HeatRejectCapNomCapSizingRatio;
2377 :
2378 : } else {
2379 505 : Real64 AssumedDeltaT = DesTowerWaterDeltaT;
2380 505 : Real64 AssumedExitTemp = DesTowerExitWaterTemp;
2381 :
2382 505 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, AssumedExitTemp, RoutineName);
2383 505 : Real64 const Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, AssumedExitTemp, RoutineName);
2384 :
2385 505 : DesTowerLoad = rho * Cp * this->DesignWaterFlowRate * AssumedDeltaT;
2386 505 : this->TowerNominalCapacity = DesTowerLoad / this->HeatRejectCapNomCapSizingRatio;
2387 : }
2388 : }
2389 :
2390 1372 : if (this->DesignWaterFlowRateWasAutoSized) {
2391 677 : if (PltSizCondNum > 0) {
2392 677 : if (PlantSizData(PltSizCondNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
2393 430 : tmpDesignWaterFlowRate = PlantSizData(PltSizCondNum).DesVolFlowRate * this->SizFac;
2394 430 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2395 153 : this->DesignWaterFlowRate = tmpDesignWaterFlowRate;
2396 : }
2397 : } else {
2398 247 : tmpDesignWaterFlowRate = 0.0;
2399 247 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2400 0 : this->DesignWaterFlowRate = tmpDesignWaterFlowRate;
2401 : }
2402 : }
2403 677 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2404 262 : BaseSizer::reportSizerOutput(state,
2405 131 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2406 : this->Name,
2407 : "Design Water Flow Rate [m3/s]",
2408 : this->DesignWaterFlowRate);
2409 : }
2410 677 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2411 22 : BaseSizer::reportSizerOutput(state,
2412 11 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2413 : this->Name,
2414 : "Initial Design Water Flow Rate [m3/s]",
2415 : this->DesignWaterFlowRate);
2416 : }
2417 : } else {
2418 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2419 0 : ShowSevereError(state, format("Autosizing error for cooling tower object = {}", this->Name));
2420 0 : ShowFatalError(state, "Autosizing of cooling tower condenser flow rate requires a loop Sizing:Plant object.");
2421 : }
2422 : }
2423 : }
2424 :
2425 1372 : if (this->PerformanceInputMethod_Num == PIM::NominalCapacity) {
2426 : // Design water flow rate is assumed to be 3 gpm per ton (SI equivalent 5.382E-8 m3/s per watt)
2427 40 : this->DesignWaterFlowRate = 5.382e-8 * this->TowerNominalCapacity;
2428 40 : tmpDesignWaterFlowRate = this->DesignWaterFlowRate;
2429 40 : if (Util::SameString(DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)], "CoolingTower:SingleSpeed")) {
2430 15 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2431 6 : BaseSizer::reportSizerOutput(state,
2432 3 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2433 : this->Name,
2434 : "Design Water Flow Rate based on tower nominal capacity [m3/s]",
2435 : this->DesignWaterFlowRate);
2436 : }
2437 15 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2438 0 : BaseSizer::reportSizerOutput(state,
2439 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2440 : this->Name,
2441 : "Initial Design Water Flow Rate based on tower nominal capacity [m3/s]",
2442 : this->DesignWaterFlowRate);
2443 : }
2444 25 : } else if (Util::SameString(DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)], "CoolingTower:TwoSpeed")) {
2445 25 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2446 10 : BaseSizer::reportSizerOutput(state,
2447 5 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2448 : this->Name,
2449 : "Design Water Flow Rate based on tower high-speed nominal capacity [m3/s]",
2450 : this->DesignWaterFlowRate);
2451 : }
2452 25 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2453 0 : BaseSizer::reportSizerOutput(state,
2454 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2455 : this->Name,
2456 : "Initial Design Water Flow Rate based on tower high-speed nominal capacity [m3/s]",
2457 : this->DesignWaterFlowRate);
2458 : }
2459 : }
2460 : }
2461 :
2462 1372 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->WaterInletNodeNum, tmpDesignWaterFlowRate);
2463 :
2464 1372 : if (this->HighSpeedFanPowerWasAutoSized) {
2465 : // We assume the nominal fan power is 0.0105 times the design load
2466 692 : if (this->PerformanceInputMethod_Num == PIM::NominalCapacity) {
2467 5 : this->HighSpeedFanPower = 0.0105 * this->TowerNominalCapacity;
2468 5 : tmpHighSpeedFanPower = this->HighSpeedFanPower;
2469 : } else {
2470 687 : if (PltSizCondNum > 0) {
2471 687 : if (PlantSizData(PltSizCondNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
2472 437 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
2473 437 : Real64 const Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
2474 437 : DesTowerLoad = rho * Cp * tmpDesignWaterFlowRate * DesTowerWaterDeltaT;
2475 437 : tmpHighSpeedFanPower = 0.0105 * DesTowerLoad;
2476 437 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2477 155 : this->HighSpeedFanPower = tmpHighSpeedFanPower;
2478 : }
2479 : } else {
2480 250 : tmpHighSpeedFanPower = 0.0;
2481 250 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2482 0 : this->HighSpeedFanPower = tmpHighSpeedFanPower;
2483 : }
2484 : }
2485 : } else {
2486 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2487 0 : ShowSevereError(state, "Autosizing of cooling tower fan power requires a loop Sizing:Plant object.");
2488 0 : ShowFatalError(state, format(" Occurs in cooling tower object= {}", this->Name));
2489 : }
2490 : }
2491 : }
2492 692 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_SingleSpd ||
2493 120 : this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_VarSpd) {
2494 647 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2495 250 : BaseSizer::reportSizerOutput(state,
2496 125 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2497 : this->Name,
2498 : "Fan Power at Design Air Flow Rate [W]",
2499 : this->HighSpeedFanPower);
2500 : }
2501 658 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2502 22 : BaseSizer::reportSizerOutput(state,
2503 11 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2504 : this->Name,
2505 : "Initial Fan Power at Design Air Flow Rate [W]",
2506 : this->HighSpeedFanPower);
2507 : }
2508 45 : } else if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_TwoSpd) {
2509 45 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2510 18 : BaseSizer::reportSizerOutput(state,
2511 9 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2512 : this->Name,
2513 : "Fan Power at High Fan Speed [W]",
2514 : this->HighSpeedFanPower);
2515 : }
2516 45 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2517 0 : BaseSizer::reportSizerOutput(state,
2518 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2519 : this->Name,
2520 : "Initial Fan Power at High Fan Speed [W]",
2521 : this->HighSpeedFanPower);
2522 : }
2523 : }
2524 : }
2525 :
2526 1372 : if (this->HighSpeedAirFlowRateWasAutoSized) {
2527 : // Plant Sizing Object is not required to AUTOSIZE this field since its simply a multiple of another field.
2528 762 : tmpHighSpeedAirFlowRate = tmpHighSpeedFanPower * 0.5 * (101325.0 / state.dataEnvrn->StdBaroPress) / 190.0;
2529 762 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2530 170 : this->HighSpeedAirFlowRate = tmpHighSpeedAirFlowRate;
2531 : }
2532 :
2533 762 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_SingleSpd ||
2534 175 : this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_VarSpd) {
2535 717 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2536 278 : BaseSizer::reportSizerOutput(state,
2537 139 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2538 : this->Name,
2539 : "Design Air Flow Rate [m3/s]",
2540 : this->HighSpeedAirFlowRate);
2541 : }
2542 728 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2543 22 : BaseSizer::reportSizerOutput(state,
2544 11 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2545 : this->Name,
2546 : "Initial Design Air Flow Rate [m3/s]",
2547 : this->HighSpeedAirFlowRate);
2548 : }
2549 45 : } else if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_TwoSpd) {
2550 45 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2551 18 : BaseSizer::reportSizerOutput(state,
2552 9 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2553 : this->Name,
2554 : "Air Flow Rate at High Fan Speed [m3/s]",
2555 : this->HighSpeedAirFlowRate);
2556 : }
2557 45 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2558 0 : BaseSizer::reportSizerOutput(state,
2559 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2560 : this->Name,
2561 : "Initial Air Flow Rate at High Fan Speed [m3/s]",
2562 : this->HighSpeedAirFlowRate);
2563 : }
2564 : }
2565 : }
2566 :
2567 1372 : if (this->HighSpeedTowerUAWasAutoSized) {
2568 612 : if (PltSizCondNum > 0) {
2569 612 : if (PlantSizData(PltSizCondNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
2570 391 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
2571 391 : Real64 const Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
2572 391 : DesTowerLoad = rho * Cp * tmpDesignWaterFlowRate * DesTowerWaterDeltaT;
2573 : // This conditional statement is to trap when the user specified condenser/tower water design setpoint
2574 : // temperature is less than design inlet air wet bulb temperature
2575 391 : if (PlantSizData(PltSizCondNum).ExitTemp <= this->DesignInletWB) {
2576 0 : ShowSevereError(state,
2577 0 : format("Error when autosizing the UA value for cooling tower = {}. Design Loop Exit Temperature must be "
2578 : "greater than {:.2T} C when autosizing the tower UA.",
2579 0 : this->Name,
2580 0 : this->DesignInletWB));
2581 0 : ShowContinueError(state,
2582 0 : format("The Design Loop Exit Temperature specified in Sizing:Plant object = {} ({:.2T} C)",
2583 0 : PlantSizData(PltSizCondNum).PlantLoopName,
2584 0 : PlantSizData(PltSizCondNum).ExitTemp));
2585 0 : ShowContinueError(
2586 0 : state, format("is less than or equal to the design inlet air wet-bulb temperature of {:.2T} C.", this->DesignInletWB));
2587 0 : ShowContinueError(state,
2588 0 : format("If using HVACTemplate:Plant:ChilledWaterLoop, then check that input field Condenser Water Design "
2589 : "Setpoint must be > {:.2T} C if autosizing the cooling tower.",
2590 0 : this->DesignInletWB));
2591 0 : ShowFatalError(state, format("Autosizing of cooling tower fails for tower = {}.", this->Name));
2592 : }
2593 :
2594 391 : Real64 const solveDesignWaterMassFlow = rho * tmpDesignWaterFlowRate; // design water mass flow rate
2595 391 : UA0 = 0.0001 * DesTowerLoad; // Assume deltaT = 10000K (limit)
2596 391 : UA1 = DesTowerLoad; // Assume deltaT = 1K
2597 391 : this->WaterTemp = DesTowerInletWaterTemp;
2598 391 : this->AirTemp = this->DesInletAirDBTemp; // 35.0;
2599 391 : this->AirWetBulb = this->DesignInletWB; // 25.6;
2600 391 : this->AirPress = state.dataEnvrn->StdBaroPress;
2601 391 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
2602 9680 : auto f1 = [&state, this, DesTowerLoad, solveDesignWaterMassFlow, tmpHighSpeedAirFlowRate, Cp](Real64 UA) {
2603 : Real64 const OutWaterTemp =
2604 9680 : this->calculateSimpleTowerOutletTemp(state, solveDesignWaterMassFlow, tmpHighSpeedAirFlowRate, UA);
2605 9680 : Real64 const CoolingOutput = Cp * solveDesignWaterMassFlow * (this->WaterTemp - OutWaterTemp); // tower cooling output [W]
2606 9680 : return (DesTowerLoad - CoolingOutput) / DesTowerLoad;
2607 391 : };
2608 391 : General::SolveRoot(state, Acc, MaxIte, SolFla, UA, f1, UA0, UA1);
2609 391 : if (SolFla == -1) {
2610 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower UA");
2611 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
2612 391 : } else if (SolFla == -2) {
2613 0 : ShowSevereError(state, "Bad starting values for UA");
2614 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
2615 : }
2616 :
2617 391 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2618 140 : this->HighSpeedTowerUA = UA;
2619 : }
2620 391 : this->TowerNominalCapacity = DesTowerLoad / this->HeatRejectCapNomCapSizingRatio;
2621 : } else {
2622 221 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2623 0 : this->HighSpeedTowerUA = 0.0;
2624 : }
2625 : }
2626 612 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_SingleSpd) {
2627 572 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2628 220 : BaseSizer::reportSizerOutput(state,
2629 110 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2630 : this->Name,
2631 : "U-Factor Times Area Value at Design Air Flow Rate [W/C]",
2632 : this->HighSpeedTowerUA);
2633 : }
2634 572 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2635 22 : BaseSizer::reportSizerOutput(state,
2636 11 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2637 : this->Name,
2638 : "Initial U-Factor Times Area Value at Design Air Flow Rate [W/C]",
2639 : this->HighSpeedTowerUA);
2640 : }
2641 40 : } else if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_TwoSpd) {
2642 40 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2643 16 : BaseSizer::reportSizerOutput(state,
2644 8 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2645 : this->Name,
2646 : "U-Factor Times Area Value at High Fan Speed [W/C]",
2647 : this->HighSpeedTowerUA);
2648 : }
2649 40 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2650 0 : BaseSizer::reportSizerOutput(state,
2651 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2652 : this->Name,
2653 : "Initial U-Factor Times Area Value at High Fan Speed [W/C]",
2654 : this->HighSpeedTowerUA);
2655 : }
2656 : }
2657 : } else {
2658 0 : if (this->DesignWaterFlowRate >= HVAC::SmallWaterVolFlow) {
2659 :
2660 0 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
2661 0 : Real64 const Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
2662 0 : DesTowerLoad = rho * Cp * tmpDesignWaterFlowRate * DesTowerWaterDeltaT;
2663 : // This conditional statement is to trap when the user specified condenser/tower water design setpoint
2664 : // temperature is less than design inlet air wet bulb temperature
2665 : // Note JM 2018-11-22
2666 : // * If actually user-specified:
2667 : // this->DesOutletWaterTemp = this->DesignInletWB
2668 : // + this->DesignApproach;
2669 : // DesTowerExitWaterTemp = this->DesOutletWaterTemp;
2670 : // => This basically means that approach is negative, which is impossible (must be > 0 per IDD)
2671 : // * If not, hardcoded above to 21C
2672 0 : if (DesTowerExitWaterTemp <= this->DesignInletWB) {
2673 0 : ShowSevereError(state,
2674 0 : format("Error when autosizing the UA value for cooling tower = {}. Design Tower Exit Temperature must be "
2675 : "greater than {:.2T} C when autosizing the tower UA.",
2676 0 : this->Name,
2677 0 : this->DesignInletWB));
2678 0 : ShowContinueError(state, format("The User-specified Design Loop Exit Temperature={:.2T}", DesTowerExitWaterTemp));
2679 0 : ShowContinueError(
2680 0 : state, format("is less than or equal to the design inlet air wet-bulb temperature of {:.2T} C.", this->DesignInletWB));
2681 :
2682 0 : if (this->TowerInletCondsAutoSize) {
2683 0 : ShowContinueError(state,
2684 0 : format("Because you did not specify the Design Approach Temperature, and you do not have a "
2685 : "Sizing:Plant object, it was defaulted to {:.2T} C.",
2686 : DesTowerExitWaterTemp));
2687 : } else {
2688 : // Should never get there...
2689 0 : ShowContinueError(state,
2690 0 : format("The Design Loop Exit Temperature is the sum of the design air inlet wet-bulb temperature= "
2691 : "{:.2T} C plus the cooling tower design approach temperature = {:.2T}C.",
2692 0 : this->DesignInletWB,
2693 0 : this->DesignApproach));
2694 : }
2695 0 : ShowContinueError(state,
2696 0 : format("If using HVACTemplate:Plant:ChilledWaterLoop, then check that input field Condenser Water Design "
2697 : "Setpoint must be > {:.2T} C if autosizing the cooling tower.",
2698 0 : this->DesignInletWB));
2699 0 : ShowFatalError(state, format("Autosizing of cooling tower fails for tower = {}.", this->Name));
2700 : }
2701 :
2702 0 : Real64 const solveWaterMassFlow = rho * tmpDesignWaterFlowRate; // design water mass flow rate
2703 0 : UA0 = 0.0001 * DesTowerLoad; // Assume deltaT = 10000K (limit)
2704 0 : UA1 = DesTowerLoad; // Assume deltaT = 1K
2705 0 : this->WaterTemp = DesTowerInletWaterTemp;
2706 0 : this->AirTemp = this->DesInletAirDBTemp; // 35.0;
2707 0 : this->AirWetBulb = this->DesignInletWB; // 25.6;
2708 0 : this->AirPress = state.dataEnvrn->StdBaroPress;
2709 0 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
2710 0 : auto f = [&state, this, DesTowerLoad, solveWaterMassFlow, tmpHighSpeedAirFlowRate, Cp](Real64 UA) {
2711 0 : Real64 const OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterMassFlow, tmpHighSpeedAirFlowRate, UA);
2712 0 : Real64 const CoolingOutput = Cp * solveWaterMassFlow * (this->WaterTemp - OutWaterTemp); // tower cooling output [W]
2713 0 : return (DesTowerLoad - CoolingOutput) / DesTowerLoad;
2714 0 : };
2715 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, UA, f, UA0, UA1);
2716 0 : if (SolFla == -1) {
2717 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower UA");
2718 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
2719 0 : } else if (SolFla == -2) {
2720 0 : ShowSevereError(state, "Bad starting values for UA");
2721 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
2722 : }
2723 :
2724 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2725 0 : this->HighSpeedTowerUA = UA;
2726 : }
2727 0 : this->TowerNominalCapacity = DesTowerLoad / this->HeatRejectCapNomCapSizingRatio;
2728 : } else {
2729 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2730 0 : this->HighSpeedTowerUA = 0.0;
2731 : }
2732 : }
2733 0 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_SingleSpd) {
2734 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2735 0 : BaseSizer::reportSizerOutput(state,
2736 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2737 : this->Name,
2738 : "U-Factor Times Area Value at Design Air Flow Rate [W/C]",
2739 : this->HighSpeedTowerUA);
2740 : }
2741 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2742 0 : BaseSizer::reportSizerOutput(state,
2743 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2744 : this->Name,
2745 : "Initial U-Factor Times Area Value at Design Air Flow Rate [W/C]",
2746 : this->HighSpeedTowerUA);
2747 : }
2748 0 : } else if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_TwoSpd) {
2749 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2750 0 : BaseSizer::reportSizerOutput(state,
2751 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2752 : this->Name,
2753 : "U-Factor Times Area Value at High Fan Speed [W/C]",
2754 : this->HighSpeedTowerUA);
2755 : }
2756 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2757 0 : BaseSizer::reportSizerOutput(state,
2758 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2759 : this->Name,
2760 : "Initial U-Factor Times Area Value at High Fan Speed [W/C]",
2761 : this->HighSpeedTowerUA);
2762 : }
2763 : }
2764 : }
2765 : }
2766 :
2767 1372 : if (this->PerformanceInputMethod_Num == PIM::NominalCapacity) {
2768 40 : if (this->DesignWaterFlowRate >= HVAC::SmallWaterVolFlow) {
2769 : // nominal capacity doesn't include compressor heat; predefined factor was 1.25 W heat rejection per W of delivered cooling but now is
2770 : // a user input
2771 40 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, 29.44, RoutineName); // 85F design exiting water temp
2772 40 : Real64 const Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, 29.44, RoutineName); // 85F design exiting water temp
2773 :
2774 40 : DesTowerLoad = this->TowerNominalCapacity * this->HeatRejectCapNomCapSizingRatio;
2775 40 : Real64 const solveWaterFlowRate = rho * tmpDesignWaterFlowRate; // design water mass flow rate
2776 40 : UA0 = 0.0001 * DesTowerLoad; // Assume deltaT = 10000K (limit)
2777 40 : UA1 = DesTowerLoad; // Assume deltaT = 1K
2778 40 : this->WaterTemp = this->DesInletWaterTemp; // 35.0; // 95F design inlet water temperature
2779 40 : this->AirTemp = this->DesInletAirDBTemp; // 95F design inlet air dry-bulb temp
2780 40 : this->AirWetBulb = this->DesignInletWB; // 78F design inlet air wet-bulb temp
2781 40 : this->AirPress = state.dataEnvrn->StdBaroPress;
2782 40 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
2783 1005 : auto f = [&state, this, DesTowerLoad, solveWaterFlowRate, tmpHighSpeedAirFlowRate, Cp](Real64 UA) {
2784 1005 : Real64 const OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterFlowRate, tmpHighSpeedAirFlowRate, UA);
2785 1005 : Real64 const CoolingOutput = Cp * solveWaterFlowRate * (this->WaterTemp - OutWaterTemp); // tower cooling output [W]
2786 1005 : return (DesTowerLoad - CoolingOutput) / DesTowerLoad;
2787 40 : };
2788 40 : General::SolveRoot(state, Acc, MaxIte, SolFla, UA, f, UA0, UA1);
2789 40 : if (SolFla == -1) {
2790 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower UA");
2791 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
2792 40 : } else if (SolFla == -2) {
2793 0 : ShowSevereError(state, "Bad starting values for UA");
2794 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
2795 : }
2796 40 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2797 8 : this->HighSpeedTowerUA = UA;
2798 : }
2799 : } else {
2800 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2801 0 : this->HighSpeedTowerUA = 0.0;
2802 : }
2803 : }
2804 40 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_SingleSpd) {
2805 15 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2806 6 : BaseSizer::reportSizerOutput(state,
2807 3 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2808 : this->Name,
2809 : "U-Factor Times Area Value at Design Air Flow Rate [W/C]",
2810 : this->HighSpeedTowerUA);
2811 : }
2812 15 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2813 0 : BaseSizer::reportSizerOutput(state,
2814 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2815 : this->Name,
2816 : "Initial U-Factor Times Area Value at Design Air Flow Rate [W/C]",
2817 : this->HighSpeedTowerUA);
2818 : }
2819 25 : } else if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_TwoSpd) {
2820 25 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2821 10 : BaseSizer::reportSizerOutput(state,
2822 5 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2823 : this->Name,
2824 : "U-Factor Times Area Value at High Fan Speed [W/C]",
2825 : this->HighSpeedTowerUA);
2826 : }
2827 25 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2828 0 : BaseSizer::reportSizerOutput(state,
2829 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2830 : this->Name,
2831 : "Initial U-Factor Times Area Value at High Fan Speed [W/C]",
2832 : this->HighSpeedTowerUA);
2833 : }
2834 : }
2835 : }
2836 :
2837 1372 : if (this->LowSpeedAirFlowRateWasAutoSized) {
2838 :
2839 45 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2840 9 : this->LowSpeedAirFlowRate = this->LowSpeedAirFlowRateSizingFactor * this->HighSpeedAirFlowRate;
2841 9 : tmpLowSpeedAirFlowRate = this->LowSpeedAirFlowRate;
2842 9 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2843 18 : BaseSizer::reportSizerOutput(state,
2844 9 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2845 : this->Name,
2846 : "Low Fan Speed Air Flow Rate [m3/s]",
2847 : this->LowSpeedAirFlowRate);
2848 : }
2849 9 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2850 0 : BaseSizer::reportSizerOutput(state,
2851 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2852 : this->Name,
2853 : "Initial Low Fan Speed Air Flow Rate [m3/s]",
2854 : this->LowSpeedAirFlowRate);
2855 : }
2856 : } else {
2857 36 : tmpLowSpeedAirFlowRate = this->LowSpeedAirFlowRateSizingFactor * tmpHighSpeedAirFlowRate;
2858 : }
2859 : }
2860 :
2861 1372 : if (this->LowSpeedFanPowerWasAutoSized) {
2862 45 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2863 9 : this->LowSpeedFanPower = this->LowSpeedFanPowerSizingFactor * this->HighSpeedFanPower;
2864 9 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2865 18 : BaseSizer::reportSizerOutput(state,
2866 9 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2867 : this->Name,
2868 : "Fan Power at Low Fan Speed [W]",
2869 : this->LowSpeedFanPower);
2870 : }
2871 9 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2872 0 : BaseSizer::reportSizerOutput(state,
2873 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2874 : this->Name,
2875 : "Initial Fan Power at Low Fan Speed [W]",
2876 : this->LowSpeedFanPower);
2877 : }
2878 : }
2879 : }
2880 :
2881 1372 : if (this->LowSpeedTowerUAWasAutoSized && state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2882 8 : this->LowSpeedTowerUA = this->LowSpeedTowerUASizingFactor * this->HighSpeedTowerUA;
2883 8 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2884 16 : BaseSizer::reportSizerOutput(state,
2885 8 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2886 : this->Name,
2887 : "U-Factor Times Area Value at Low Fan Speed [W/K]",
2888 : this->LowSpeedTowerUA);
2889 : }
2890 8 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2891 0 : BaseSizer::reportSizerOutput(state,
2892 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2893 : this->Name,
2894 : "Initial U-Factor Times Area Value at Low Fan Speed [W/K]",
2895 : this->LowSpeedTowerUA);
2896 : }
2897 : }
2898 :
2899 1372 : if (this->PerformanceInputMethod_Num == PIM::NominalCapacity) {
2900 40 : if (this->TowerLowSpeedNomCapWasAutoSized) {
2901 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2902 0 : this->TowerLowSpeedNomCap = this->TowerLowSpeedNomCapSizingFactor * this->TowerNominalCapacity;
2903 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2904 0 : BaseSizer::reportSizerOutput(state,
2905 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2906 : this->Name,
2907 : "Low Speed Nominal Capacity [W]",
2908 : this->TowerLowSpeedNomCap);
2909 : }
2910 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2911 0 : BaseSizer::reportSizerOutput(state,
2912 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2913 : this->Name,
2914 : "Initial Low Speed Nominal Capacity [W]",
2915 : this->TowerLowSpeedNomCap);
2916 : }
2917 : }
2918 : }
2919 40 : if (this->TowerFreeConvNomCapWasAutoSized) {
2920 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2921 0 : this->TowerFreeConvNomCap = this->TowerFreeConvNomCapSizingFactor * this->TowerNominalCapacity;
2922 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2923 0 : BaseSizer::reportSizerOutput(state,
2924 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2925 : this->Name,
2926 : "Free Convection Nominal Capacity [W]",
2927 : this->TowerFreeConvNomCap);
2928 : }
2929 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2930 0 : BaseSizer::reportSizerOutput(state,
2931 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2932 : this->Name,
2933 : "Initial Free Convection Nominal Capacity [W]",
2934 : this->TowerFreeConvNomCap);
2935 : }
2936 : }
2937 : }
2938 : }
2939 :
2940 1412 : if (this->PerformanceInputMethod_Num == PIM::NominalCapacity &&
2941 1412 : Util::SameString(DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)], "CoolingTower:TwoSpeed")) {
2942 25 : if (this->DesignWaterFlowRate >= HVAC::SmallWaterVolFlow && this->TowerLowSpeedNomCap > 0.0) {
2943 :
2944 : // nominal capacity doesn't include compressor heat; predefined factor was 1.25 W heat rejection per W of evap cooling but now is a
2945 : // user input
2946 25 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, 29.44, RoutineName); // 85F design exiting water temp
2947 25 : Real64 const Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, 29.44, RoutineName); // 85F design exiting water temp
2948 25 : DesTowerLoad = this->TowerLowSpeedNomCap * this->HeatRejectCapNomCapSizingRatio;
2949 25 : Real64 const solveWaterFlow = rho * tmpDesignWaterFlowRate; // design water mass flow rate
2950 25 : UA0 = 0.0001 * DesTowerLoad; // Assume deltaT = 10000K (limit)
2951 25 : UA1 = DesTowerLoad; // Assume deltaT = 1K
2952 25 : this->WaterTemp = this->DesInletWaterTemp; // 35.0; // 95F design inlet water temperature
2953 25 : this->AirTemp = this->DesInletAirDBTemp; // 35.0; // 95F design inlet air dry-bulb temp
2954 25 : this->AirWetBulb = this->DesignInletWB; // 25.6; // 78F design inlet air wet-bulb temp
2955 25 : this->AirPress = state.dataEnvrn->StdBaroPress;
2956 25 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
2957 350 : auto f = [&state, this, DesTowerLoad, solveWaterFlow, tmpLowSpeedAirFlowRate, Cp](Real64 UA) {
2958 350 : Real64 const OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterFlow, tmpLowSpeedAirFlowRate, UA);
2959 350 : Real64 const CoolingOutput = Cp * solveWaterFlow * (this->WaterTemp - OutWaterTemp); // tower cooling output [W]
2960 350 : return (DesTowerLoad - CoolingOutput) / DesTowerLoad;
2961 25 : };
2962 25 : General::SolveRoot(state, Acc, MaxIte, SolFla, UA, f, UA0, UA1);
2963 25 : if (SolFla == -1) {
2964 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower UA");
2965 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
2966 25 : } else if (SolFla == -2) {
2967 0 : ShowSevereError(state, "Bad starting values for UA");
2968 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
2969 : }
2970 25 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2971 5 : this->LowSpeedTowerUA = UA;
2972 : }
2973 25 : } else {
2974 0 : this->LowSpeedTowerUA = 0.0;
2975 : }
2976 25 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2977 10 : BaseSizer::reportSizerOutput(state,
2978 5 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2979 : this->Name,
2980 : "Low Fan Speed U-Factor Times Area Value [W/K]",
2981 : this->LowSpeedTowerUA);
2982 : }
2983 25 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
2984 0 : BaseSizer::reportSizerOutput(state,
2985 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2986 : this->Name,
2987 : "Initial Low Fan Speed U-Factor Times Area Value [W/K]",
2988 : this->LowSpeedTowerUA);
2989 : }
2990 : }
2991 :
2992 1372 : if (this->FreeConvAirFlowRateWasAutoSized) {
2993 336 : this->FreeConvAirFlowRate = this->FreeConvAirFlowRateSizingFactor * tmpHighSpeedAirFlowRate;
2994 336 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
2995 80 : this->FreeConvAirFlowRate = this->FreeConvAirFlowRateSizingFactor * this->HighSpeedAirFlowRate;
2996 80 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
2997 128 : BaseSizer::reportSizerOutput(state,
2998 64 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
2999 : this->Name,
3000 : "Free Convection Regime Air Flow Rate [m3/s]",
3001 : this->FreeConvAirFlowRate);
3002 : }
3003 80 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3004 16 : BaseSizer::reportSizerOutput(state,
3005 8 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3006 : this->Name,
3007 : "Initial Free Convection Regime Air Flow Rate [m3/s]",
3008 : this->FreeConvAirFlowRate);
3009 : }
3010 : }
3011 : }
3012 :
3013 1372 : if (this->FreeConvTowerUAWasAutoSized) {
3014 331 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3015 79 : this->FreeConvTowerUA = this->FreeConvTowerUASizingFactor * this->HighSpeedTowerUA;
3016 79 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3017 126 : BaseSizer::reportSizerOutput(state,
3018 63 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3019 : this->Name,
3020 : "Free Convection U-Factor Times Area Value [W/K]",
3021 : this->FreeConvTowerUA);
3022 : }
3023 79 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3024 16 : BaseSizer::reportSizerOutput(state,
3025 8 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3026 : this->Name,
3027 : "Initial Free Convection U-Factor Times Area Value [W/K]",
3028 : this->FreeConvTowerUA);
3029 : }
3030 : }
3031 : }
3032 :
3033 1372 : if (this->PerformanceInputMethod_Num == PIM::NominalCapacity) {
3034 40 : if (this->DesignWaterFlowRate >= HVAC::SmallWaterVolFlow && this->TowerFreeConvNomCap > 0.0) {
3035 : // nominal capacity doesn't include compressor heat; predefined factor was 1.25 W heat rejection per W of evap cooling but now user
3036 : // input
3037 25 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, 29.44, RoutineName); // 85F design exiting water temp
3038 25 : Real64 const Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, 29.44, RoutineName); // 85F design exiting water temp
3039 25 : DesTowerLoad = this->TowerFreeConvNomCap * this->HeatRejectCapNomCapSizingRatio;
3040 25 : Real64 const solveWaterFlow = rho * this->DesignWaterFlowRate; // design water mass flow rate
3041 25 : UA0 = 0.0001 * DesTowerLoad; // Assume deltaT = 10000K (limit)
3042 25 : UA1 = DesTowerLoad; // Assume deltaT = 1K
3043 25 : this->WaterTemp = this->DesInletWaterTemp; // 35.0; // 95F design inlet water temperature
3044 25 : this->AirTemp = this->DesInletAirDBTemp; // 35.0; // 95F design inlet air dry-bulb temp
3045 25 : this->AirWetBulb = this->DesignInletWB; // 25.6; // 78F design inlet air wet-bulb temp
3046 25 : this->AirPress = state.dataEnvrn->StdBaroPress;
3047 25 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
3048 280 : auto f = [&state, this, DesTowerLoad, solveWaterFlow, Cp](Real64 UA) {
3049 280 : Real64 const OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterFlow, this->FreeConvAirFlowRate, UA);
3050 280 : Real64 const CoolingOutput = Cp * solveWaterFlow * (this->WaterTemp - OutWaterTemp); // tower cooling output [W]
3051 280 : return (DesTowerLoad - CoolingOutput) / DesTowerLoad;
3052 25 : };
3053 25 : General::SolveRoot(state, Acc, MaxIte, SolFla, UA, f, UA0, UA1);
3054 25 : if (SolFla == -1) {
3055 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower UA");
3056 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
3057 25 : } else if (SolFla == -2) {
3058 0 : ShowSevereError(state, "Bad starting values for UA calculations");
3059 0 : ShowContinueError(state, "Tower inlet design water temperature assumed to be 35.0 C.");
3060 0 : ShowContinueError(state, "Tower inlet design air dry-bulb temperature assumed to be 35.0 C.");
3061 0 : ShowContinueError(state, "Tower inlet design air wet-bulb temperature assumed to be 25.6 C.");
3062 0 : ShowContinueError(state,
3063 0 : format("Tower load assumed to be {:.3T} times free convection capacity of {:.0T} W.",
3064 0 : this->HeatRejectCapNomCapSizingRatio,
3065 0 : this->TowerFreeConvNomCap));
3066 :
3067 : Real64 OutWaterTemp; // outlet water temperature during sizing [C]
3068 :
3069 0 : OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterFlow, this->FreeConvAirFlowRate, UA0);
3070 0 : Real64 CoolingOutput = Cp * solveWaterFlow * (this->WaterTemp - OutWaterTemp); // tower capacity during sizing [W]
3071 0 : ShowContinueError(state, format("Tower capacity at lower UA guess ({:.4T}) = {:.0T} W.", UA0, CoolingOutput));
3072 :
3073 0 : OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterFlow, this->FreeConvAirFlowRate, UA1);
3074 0 : CoolingOutput = Cp * solveWaterFlow * (this->WaterTemp - OutWaterTemp);
3075 0 : ShowContinueError(state, format("Tower capacity at upper UA guess ({:.4T}) = {:.0T} W.", UA1, CoolingOutput));
3076 :
3077 0 : if (CoolingOutput < DesTowerLoad) {
3078 0 : ShowContinueError(state, "Free convection capacity should be less than tower capacity at upper UA guess.");
3079 : }
3080 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
3081 : }
3082 25 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3083 5 : this->FreeConvTowerUA = UA;
3084 : }
3085 25 : } else {
3086 15 : this->FreeConvTowerUA = 0.0;
3087 : }
3088 40 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3089 16 : BaseSizer::reportSizerOutput(state,
3090 8 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3091 : this->Name,
3092 : "U-Factor Times Area Value at Free Convection Air Flow Rate [W/C]",
3093 : this->FreeConvTowerUA);
3094 : }
3095 40 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3096 0 : BaseSizer::reportSizerOutput(state,
3097 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3098 : this->Name,
3099 : "Initial U-Factor Times Area Value at Free Convection Air Flow Rate [W/C]",
3100 : this->FreeConvTowerUA);
3101 : }
3102 : }
3103 :
3104 : // calibrate variable speed tower model based on user input by finding calibration water flow rate ratio that
3105 : // yields an approach temperature that matches user input
3106 1372 : if (Util::SameString(DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)], "CoolingTower:VariableSpeed")) {
3107 :
3108 : // check range for water flow rate ratio (make sure RegulaFalsi converges)
3109 130 : Real64 MaxWaterFlowRateRatio = 0.5; // maximum water flow rate ratio which yields desired approach temp
3110 130 : Real64 Tapproach = 0.0; // temporary tower approach temp variable [C]
3111 130 : Real64 const FlowRateRatioStep = (this->MaxWaterFlowRatio - this->MinWaterFlowRatio) / 10.0;
3112 130 : bool ModelCalibrated = true;
3113 130 : Real64 ModelWaterFlowRatioMax = this->MaxWaterFlowRatio * 4.0; // maximum water flow rate ratio used for model calibration
3114 : // find a flow rate large enough to provide an approach temperature > than the user defined approach
3115 130 : Real64 WaterFlowRateRatio(0.0); // tower water flow rate ratio
3116 1405 : while (Tapproach < this->DesignApproach && MaxWaterFlowRateRatio <= ModelWaterFlowRatioMax) {
3117 1275 : WaterFlowRateRatio = MaxWaterFlowRateRatio;
3118 1275 : Tapproach = this->calculateVariableSpeedApproach(state, WaterFlowRateRatio, 1.0, this->DesignInletWB, this->DesignRange);
3119 1275 : if (Tapproach < this->DesignApproach) {
3120 1145 : MaxWaterFlowRateRatio += FlowRateRatioStep;
3121 : }
3122 : // a water flow rate large enough to provide an approach temperature > than the user defined approach does not exist
3123 : // within the tolerances specified by the user
3124 1275 : if ((MaxWaterFlowRateRatio == 0.5 && Tapproach > this->DesignApproach) || MaxWaterFlowRateRatio >= ModelWaterFlowRatioMax) {
3125 0 : ModelCalibrated = false;
3126 0 : break;
3127 : }
3128 : }
3129 :
3130 130 : Real64 WaterFlowRatio(0.0); // tower water flow rate ratio found during model calibration
3131 :
3132 130 : if (ModelCalibrated) {
3133 540 : auto f = [&state, this](Real64 FlowRatio) {
3134 540 : Real64 Tact = this->calculateVariableSpeedApproach(state, FlowRatio, 1.0, this->DesignInletWB, this->DesignRange);
3135 540 : return this->DesignApproach - Tact;
3136 130 : };
3137 130 : General::SolveRoot(state, Acc, MaxIte, SolFla, WaterFlowRatio, f, DataPrecisionGlobals::constant_pointfive, MaxWaterFlowRateRatio);
3138 :
3139 130 : if (SolFla == -1) {
3140 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower water flow ratio during calibration");
3141 0 : ShowContinueError(state,
3142 : "Inlet air wet-bulb, range, and/or approach temperature does not allow calibration of water flow rate ratio "
3143 : "for this variable-speed cooling tower.");
3144 0 : ShowFatalError(state, format("Cooling tower calibration failed for tower {}", this->Name));
3145 130 : } else if (SolFla == -2) {
3146 0 : ShowSevereError(state, "Bad starting values for cooling tower water flow rate ratio calibration.");
3147 0 : ShowContinueError(state,
3148 : "Inlet air wet-bulb, range, and/or approach temperature does not allow calibration of water flow rate ratio "
3149 : "for this variable-speed cooling tower.");
3150 0 : ShowFatalError(state, format("Cooling tower calibration failed for tower {}.", this->Name));
3151 : }
3152 : } else {
3153 0 : ShowSevereError(state, "Bad starting values for cooling tower water flow rate ratio calibration.");
3154 0 : ShowContinueError(state, "Design inlet air wet-bulb or range temperature must be modified to achieve the design approach");
3155 0 : ShowContinueError(state,
3156 0 : format("A water flow rate ratio of {:.6F} was calculated to yield an approach temperature of {:.2F}.",
3157 : WaterFlowRateRatio,
3158 : Tapproach));
3159 0 : ShowFatalError(state, format("Cooling tower calibration failed for tower {}.", this->Name));
3160 : }
3161 :
3162 130 : this->CalibratedWaterFlowRate = this->DesignWaterFlowRate / WaterFlowRatio;
3163 :
3164 130 : if (WaterFlowRatio < this->MinWaterFlowRatio || WaterFlowRatio > this->MaxWaterFlowRatio) {
3165 0 : ShowWarningError(state,
3166 0 : format("CoolingTower:VariableSpeed, \"{}\" the calibrated water flow rate ratio is determined to be {:9.6F}. This "
3167 : "is outside the valid range of {:.2F} to {:.2F}.",
3168 0 : this->Name,
3169 : WaterFlowRatio,
3170 0 : this->MinWaterFlowRatio,
3171 0 : this->MaxWaterFlowRatio));
3172 : }
3173 :
3174 : Real64 const rho =
3175 130 : this->plantLoc.loop->glycol->getDensity(state, (this->DesignInletWB + this->DesignApproach + this->DesignRange), RoutineName);
3176 : Real64 const Cp =
3177 130 : this->plantLoc.loop->glycol->getSpecificHeat(state, (this->DesignInletWB + this->DesignApproach + this->DesignRange), RoutineName);
3178 :
3179 130 : this->TowerNominalCapacity = ((rho * tmpDesignWaterFlowRate) * Cp * this->DesignRange);
3180 130 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3181 52 : BaseSizer::reportSizerOutput(state,
3182 26 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3183 : this->Name,
3184 : "Nominal Capacity [W]",
3185 : this->TowerNominalCapacity);
3186 : }
3187 130 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3188 0 : BaseSizer::reportSizerOutput(state,
3189 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3190 : this->Name,
3191 : "Initial Nominal Capacity [W]",
3192 : this->TowerNominalCapacity);
3193 : }
3194 130 : this->FreeConvAirFlowRate = this->MinimumVSAirFlowFrac * this->HighSpeedAirFlowRate;
3195 :
3196 130 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3197 52 : BaseSizer::reportSizerOutput(state,
3198 26 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3199 : this->Name,
3200 : "Air Flow Rate in free convection regime [m3/s]",
3201 : this->FreeConvAirFlowRate);
3202 : }
3203 130 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3204 0 : BaseSizer::reportSizerOutput(state,
3205 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3206 : this->Name,
3207 : "Initial Air Flow Rate in free convection regime [m3/s]",
3208 : this->FreeConvAirFlowRate);
3209 : }
3210 130 : this->TowerFreeConvNomCap = this->TowerNominalCapacity * this->FreeConvectionCapacityFraction;
3211 :
3212 130 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3213 52 : BaseSizer::reportSizerOutput(state,
3214 26 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3215 : this->Name,
3216 : "Tower capacity in free convection regime at design conditions [W]",
3217 : this->TowerFreeConvNomCap);
3218 : }
3219 130 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3220 0 : BaseSizer::reportSizerOutput(state,
3221 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3222 : this->Name,
3223 : "Initial Tower capacity in free convection regime at design conditions [W]",
3224 : this->TowerFreeConvNomCap);
3225 : }
3226 : }
3227 1372 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3228 : // create predefined report
3229 540 : OutputReportPredefined::PreDefTableEntry(
3230 540 : state, state.dataOutRptPredefined->pdchMechType, this->Name, DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)]);
3231 270 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomCap, this->Name, this->TowerNominalCapacity);
3232 :
3233 : // create std 229 new table for cooling towers and fluid coolers
3234 540 : OutputReportPredefined::PreDefTableEntry(
3235 540 : state, state.dataOutRptPredefined->pdchCTFCType, this->Name, DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)]);
3236 540 : OutputReportPredefined::PreDefTableEntry(state,
3237 270 : state.dataOutRptPredefined->pdchCTFCCondLoopName,
3238 : this->Name,
3239 540 : (this->plantLoc.loop != nullptr) ? this->plantLoc.loop->Name : "N/A");
3240 540 : OutputReportPredefined::PreDefTableEntry(state,
3241 270 : state.dataOutRptPredefined->pdchCTFCCondLoopBranchName,
3242 : this->Name,
3243 540 : (this->plantLoc.branch != nullptr) ? plantLoc.branch->Name : "N/A");
3244 540 : OutputReportPredefined::PreDefTableEntry(state,
3245 270 : state.dataOutRptPredefined->pdchCTFCFluidType,
3246 : this->Name,
3247 270 : this->plantLoc.loop->glycol->Name); // Fluid Name more reasonable than FluidType
3248 270 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchCTFCRange, this->Name, this->DesignRange);
3249 270 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchCTFCApproach, this->Name, this->DesignApproach);
3250 540 : OutputReportPredefined::PreDefTableEntry(
3251 270 : state, state.dataOutRptPredefined->pdchCTFCDesFanPwr, this->Name, this->HighSpeedFanPower); // equivalent to Design Fan Power?
3252 270 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchCTFCDesInletAirWBT, this->Name, this->DesignInletWB);
3253 540 : OutputReportPredefined::PreDefTableEntry(
3254 540 : state, state.dataOutRptPredefined->pdchCTFCDesWaterFlowRate, this->Name, this->DesignWaterFlowRate, 6);
3255 270 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchCTFCLevWaterSPTemp, this->Name, this->DesOutletWaterTemp);
3256 : }
3257 :
3258 : // input error checking
3259 1372 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3260 270 : bool ErrorsFound = false;
3261 270 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_SingleSpd) {
3262 231 : if (this->DesignWaterFlowRate > 0.0) {
3263 231 : if (this->FreeConvAirFlowRate >= this->HighSpeedAirFlowRate) {
3264 0 : ShowSevereError(state,
3265 0 : format("{} \"{}\". Free convection air flow rate must be less than the design air flow rate.",
3266 : cCoolingTower_SingleSpeed,
3267 0 : this->Name));
3268 0 : ErrorsFound = true;
3269 : }
3270 231 : if (this->FreeConvTowerUA >= this->HighSpeedTowerUA) {
3271 0 : ShowSevereError(
3272 : state,
3273 0 : format("{} \"{}\". Free convection UA must be less than the design tower UA.", cCoolingTower_SingleSpeed, this->Name));
3274 0 : ErrorsFound = true;
3275 : }
3276 : }
3277 : }
3278 :
3279 270 : if (this->TowerType == DataPlant::PlantEquipmentType::CoolingTower_TwoSpd) {
3280 13 : if (this->DesignWaterFlowRate > 0.0) {
3281 13 : if (this->HighSpeedAirFlowRate <= this->LowSpeedAirFlowRate) {
3282 0 : ShowSevereError(state,
3283 0 : format("{} \"{}\". Low speed air flow rate must be less than the high speed air flow rate.",
3284 : cCoolingTower_TwoSpeed,
3285 0 : this->Name));
3286 0 : ErrorsFound = true;
3287 : }
3288 13 : if (this->LowSpeedAirFlowRate <= this->FreeConvAirFlowRate) {
3289 0 : ShowSevereError(state,
3290 0 : format("{} \"{}\". Free convection air flow rate must be less than the low speed air flow rate.",
3291 : cCoolingTower_TwoSpeed,
3292 0 : this->Name));
3293 0 : ErrorsFound = true;
3294 : }
3295 13 : if (this->HighSpeedTowerUA <= this->LowSpeedTowerUA) {
3296 0 : ShowSevereError(state,
3297 0 : format("{} \"{}\". Tower UA at low fan speed must be less than the tower UA at high fan speed.",
3298 : cCoolingTower_TwoSpeed,
3299 0 : this->Name));
3300 0 : ErrorsFound = true;
3301 : }
3302 13 : if (this->LowSpeedTowerUA <= this->FreeConvTowerUA) {
3303 0 : ShowSevereError(
3304 : state,
3305 0 : format("{} \"{}\". Tower UA at free convection air flow rate must be less than the tower UA at low fan speed.",
3306 : cCoolingTower_TwoSpeed,
3307 0 : this->Name));
3308 0 : ErrorsFound = true;
3309 : }
3310 : }
3311 : }
3312 270 : if (ErrorsFound) {
3313 0 : ShowFatalError(state, "initialize: Program terminated due to previous condition(s).");
3314 : }
3315 : }
3316 1372 : }
3317 :
3318 10 : void CoolingTower::SizeVSMerkelTower(EnergyPlusData &state)
3319 : {
3320 :
3321 : // SUBROUTINE PARAMETER DEFINITIONS:
3322 10 : int constexpr MaxIte(500); // Maximum number of iterations
3323 10 : Real64 constexpr Acc(0.0001); // Accuracy of result
3324 : static constexpr std::string_view RoutineName("SizeTower");
3325 :
3326 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
3327 : int SolFla; // Flag of solver
3328 : Real64 tmpHighSpeedFanPower;
3329 :
3330 : Real64 UA0; // Lower bound for UA [W/C]
3331 : Real64 UA1; // Upper bound for UA [W/C]
3332 : Real64 DesTowerLoad; // Design tower load [W]
3333 10 : Real64 Cp(0); // local specific heat for fluid
3334 10 : Real64 rho(0); // local density for fluid
3335 : Real64 UA; // Calculated UA value
3336 : Real64 DesTowerInletWaterTemp; // design tower inlet water temperature
3337 : Real64 DesTowerExitWaterTemp; // design tower exit water temperature
3338 : Real64 DesTowerWaterDeltaT; // design tower temperature range
3339 : Real64 DesTowerApproachFromPlant; // design tower approach temperature from plant sizing object
3340 10 : Real64 TolTemp(0.04); // DeltaT and DesignApproach diffs tolerance between plant sizing data and user input in cooling tower
3341 : // for warning message reporting purpose only
3342 :
3343 : // Find the appropriate Plant Sizing object
3344 10 : int PltSizCondNum = this->plantLoc.loop->PlantSizNum;
3345 :
3346 10 : Real64 tmpNomTowerCap = this->TowerNominalCapacity;
3347 10 : Real64 tmpDesignWaterFlowRate = this->DesignWaterFlowRate;
3348 10 : Real64 tmpTowerFreeConvNomCap = this->TowerFreeConvNomCap;
3349 10 : Real64 tmpDesignAirFlowRate = this->HighSpeedAirFlowRate;
3350 10 : Real64 tmpFreeConvAirFlowRate = this->FreeConvAirFlowRate;
3351 10 : Real64 DesTowerInletAirWBTemp = this->DesignInletWB;
3352 10 : Real64 DesTowerInletAirDBTemp = this->DesInletAirDBTemp;
3353 :
3354 10 : auto const &PlantSizData(state.dataSize->PlantSizData);
3355 :
3356 10 : if (this->TowerInletCondsAutoSize) {
3357 10 : if (PltSizCondNum > 0) {
3358 : // use plant sizing data
3359 10 : DesTowerExitWaterTemp = PlantSizData(PltSizCondNum).ExitTemp;
3360 10 : DesTowerInletWaterTemp = DesTowerExitWaterTemp + PlantSizData(PltSizCondNum).DeltaT;
3361 10 : DesTowerWaterDeltaT = PlantSizData(PltSizCondNum).DeltaT;
3362 : } else {
3363 : // set default values to replace hard wired input assumptions
3364 0 : DesTowerExitWaterTemp = this->DesOutletWaterTemp;
3365 0 : DesTowerInletWaterTemp = this->DesInletWaterTemp;
3366 0 : DesTowerWaterDeltaT = this->DesignRange;
3367 : }
3368 : } else {
3369 : // use tower sizing data
3370 0 : DesTowerExitWaterTemp = this->DesOutletWaterTemp;
3371 0 : DesTowerInletWaterTemp = this->DesInletWaterTemp;
3372 0 : DesTowerWaterDeltaT = this->DesignRange;
3373 0 : if (PltSizCondNum > 0) {
3374 : // check the tower range against the plant sizing data
3375 0 : if (std::abs(DesTowerWaterDeltaT - PlantSizData(PltSizCondNum).DeltaT) > TolTemp) {
3376 0 : ShowWarningError(state,
3377 0 : format("Error when autosizing the load for cooling tower = {}. Tower Design Range Temperature is different from "
3378 : "the Design Loop Delta Temperature.",
3379 0 : this->Name));
3380 0 : ShowContinueError(state, format("Tower Design Range Temperature specified in tower = {}", this->Name));
3381 0 : ShowContinueError(state,
3382 0 : format("is inconsistent with Design Loop Delta Temperature specified in Sizing:Plant object = {}.",
3383 0 : PlantSizData(PltSizCondNum).PlantLoopName));
3384 0 : ShowContinueError(state, format("..The Design Range Temperature specified in tower is = {:.2T}", this->DesignRange));
3385 0 : ShowContinueError(
3386 : state,
3387 0 : format("..The Design Loop Delta Temperature specified in plant sizing data is = {:.2T}", PlantSizData(PltSizCondNum).DeltaT));
3388 : }
3389 : // check if the tower approach is different from plant sizing data
3390 0 : DesTowerApproachFromPlant = PlantSizData(PltSizCondNum).ExitTemp - this->DesignInletWB;
3391 0 : if (std::abs(DesTowerApproachFromPlant - this->DesignApproach) > TolTemp) {
3392 0 : ShowWarningError(state,
3393 0 : format("Error when autosizing the UA for cooling tower = {}. Tower Design Approach Temperature is inconsistent "
3394 : "with Approach from Plant Sizing Data.",
3395 0 : this->Name));
3396 0 : ShowContinueError(state,
3397 0 : format("The Design Approach Temperature from inputs specified in Sizing:Plant object = {}",
3398 0 : PlantSizData(PltSizCondNum).PlantLoopName));
3399 0 : ShowContinueError(state, format("is inconsistent with Design Approach Temperature specified in tower = {}.", this->Name));
3400 0 : ShowContinueError(state,
3401 0 : format("..The Design Approach Temperature from inputs specified is = {:.2T}", DesTowerApproachFromPlant));
3402 0 : ShowContinueError(state, format("..The Design Approach Temperature specified in tower is = {:.2T}", this->DesignApproach));
3403 : }
3404 : }
3405 : }
3406 :
3407 10 : if (this->PerformanceInputMethod_Num == PIM::NominalCapacity) {
3408 :
3409 10 : if (PltSizCondNum > 0) { // get nominal capacity from PlantSizData(PltSizCondNum)%DeltaT and PlantSizData(PltSizCondNum)%DesVolFlowRate
3410 10 : if (PlantSizData(PltSizCondNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
3411 6 : rho = this->plantLoc.loop->glycol->getDensity(state, DesTowerExitWaterTemp, RoutineName);
3412 6 : Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
3413 6 : DesTowerLoad = rho * Cp * PlantSizData(PltSizCondNum).DesVolFlowRate * DesTowerWaterDeltaT * this->SizFac;
3414 6 : tmpNomTowerCap = DesTowerLoad / this->HeatRejectCapNomCapSizingRatio;
3415 : } else {
3416 4 : if (this->TowerNominalCapacityWasAutoSized) {
3417 4 : tmpNomTowerCap = 0.0;
3418 : }
3419 : }
3420 : } else { // PltSizCondNum = 0
3421 0 : if (!this->TowerInletCondsAutoSize) { // can use design data entered into tower object
3422 0 : if (this->DesignWaterFlowRate >= HVAC::SmallWaterVolFlow) {
3423 0 : rho = this->plantLoc.loop->glycol->getDensity(state, DesTowerExitWaterTemp, RoutineName);
3424 0 : Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
3425 0 : DesTowerLoad = rho * Cp * this->DesignWaterFlowRate * DesTowerWaterDeltaT * this->SizFac;
3426 0 : tmpNomTowerCap = DesTowerLoad / this->HeatRejectCapNomCapSizingRatio;
3427 : } else {
3428 0 : if (this->TowerNominalCapacityWasAutoSized) {
3429 0 : tmpNomTowerCap = 0.0;
3430 : }
3431 : }
3432 : } else { // do not have enough data to size.
3433 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize && this->TowerNominalCapacityWasAutoSized) {
3434 0 : ShowSevereError(state, format("Autosizing error for cooling tower object = {}", this->Name));
3435 0 : ShowFatalError(state, "Autosizing of cooling tower nominal capacity requires a loop Sizing:Plant object.");
3436 : }
3437 : }
3438 : }
3439 10 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3440 2 : if (this->TowerNominalCapacityWasAutoSized) {
3441 2 : this->TowerNominalCapacity = tmpNomTowerCap;
3442 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3443 4 : BaseSizer::reportSizerOutput(state,
3444 2 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3445 : this->Name,
3446 : "Design Nominal Capacity [W]",
3447 : tmpNomTowerCap);
3448 : }
3449 2 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3450 0 : BaseSizer::reportSizerOutput(state,
3451 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3452 : this->Name,
3453 : "Initial Design Nominal Capacity [W]",
3454 : this->TowerNominalCapacity);
3455 : }
3456 : } else { // Hard-sized with sizing data
3457 0 : if (this->TowerNominalCapacity > 0.0 && tmpNomTowerCap > 0.0) {
3458 0 : Real64 NomCapUser(0.0);
3459 0 : NomCapUser = this->TowerNominalCapacity;
3460 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3461 0 : BaseSizer::reportSizerOutput(state,
3462 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3463 : this->Name,
3464 : "Design Nominal Capacity [W]",
3465 : tmpNomTowerCap,
3466 : "User-Specified Nominal Capacity [W]",
3467 : NomCapUser);
3468 0 : if (state.dataGlobal->DisplayExtraWarnings) {
3469 0 : if ((std::abs(tmpNomTowerCap - NomCapUser) / NomCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
3470 0 : ShowMessage(state, format("SizeVSMerkelTower: Potential issue with equipment sizing for {}", this->Name));
3471 0 : ShowContinueError(state, format("User-Specified Nominal Capacity of {:.2R} [W]", NomCapUser));
3472 0 : ShowContinueError(state, format("differs from Design Size Nominal Capacity of {:.2R} [W]", tmpNomTowerCap));
3473 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
3474 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
3475 : }
3476 : }
3477 0 : tmpNomTowerCap = NomCapUser;
3478 : }
3479 : }
3480 : }
3481 : }
3482 :
3483 10 : tmpTowerFreeConvNomCap = tmpNomTowerCap * this->TowerFreeConvNomCapSizingFactor;
3484 10 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3485 2 : if (this->TowerFreeConvNomCapWasAutoSized) {
3486 2 : this->TowerFreeConvNomCap = tmpTowerFreeConvNomCap;
3487 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3488 4 : BaseSizer::reportSizerOutput(state,
3489 2 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3490 : this->Name,
3491 : "Design Free Convection Nominal Capacity [W]",
3492 : this->TowerFreeConvNomCap);
3493 : }
3494 2 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3495 0 : BaseSizer::reportSizerOutput(state,
3496 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3497 : this->Name,
3498 : "Initial Design Free Convection Nominal Capacity [W]",
3499 : this->TowerFreeConvNomCap);
3500 : }
3501 : } else { // Hard-sized with sizing data
3502 0 : if (this->TowerFreeConvNomCap > 0.0 && tmpTowerFreeConvNomCap > 0.0) {
3503 0 : Real64 NomCapUser(0.0);
3504 0 : NomCapUser = this->TowerFreeConvNomCap;
3505 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3506 0 : BaseSizer::reportSizerOutput(state,
3507 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3508 : this->Name,
3509 : "Design Free Convection Nominal Capacity [W]",
3510 : tmpTowerFreeConvNomCap,
3511 : "User-Specified Free Convection Nominal Capacity [W]",
3512 : NomCapUser);
3513 0 : if (state.dataGlobal->DisplayExtraWarnings) {
3514 0 : if ((std::abs(tmpTowerFreeConvNomCap - NomCapUser) / NomCapUser) > state.dataSize->AutoVsHardSizingThreshold) {
3515 0 : ShowMessage(state, format("SizeVSMerkelTower: Potential issue with equipment sizing for {}", this->Name));
3516 0 : ShowContinueError(state, format("User-Specified Free Convection Nominal Capacity of {:.2R} [W]", NomCapUser));
3517 0 : ShowContinueError(
3518 : state,
3519 0 : format("differs from Design Size Free Convection Nominal Capacity of {:.2R} [W]", tmpTowerFreeConvNomCap));
3520 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
3521 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
3522 : }
3523 : }
3524 0 : tmpTowerFreeConvNomCap = NomCapUser;
3525 : }
3526 : }
3527 : }
3528 : }
3529 :
3530 10 : tmpDesignWaterFlowRate = tmpNomTowerCap * this->DesignWaterFlowPerUnitNomCap;
3531 10 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3532 2 : if (this->DesignWaterFlowRateWasAutoSized) {
3533 : // for nominal cap input method, get design water flow rate from nominal cap and scalable sizing factor
3534 :
3535 2 : this->DesignWaterFlowRate = tmpDesignWaterFlowRate;
3536 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3537 4 : BaseSizer::reportSizerOutput(state,
3538 2 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3539 : this->Name,
3540 : "Design Water Flow Rate [m3/s]",
3541 : this->DesignWaterFlowRate);
3542 : }
3543 2 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3544 0 : BaseSizer::reportSizerOutput(state,
3545 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3546 : this->Name,
3547 : "Initial Design Water Flow Rate [m3/s]",
3548 : this->DesignWaterFlowRate);
3549 : }
3550 :
3551 : } else { // Hard-sized with sizing data
3552 0 : if (this->DesignWaterFlowRate > 0.0 && tmpDesignWaterFlowRate > 0.0) {
3553 0 : Real64 NomDesWaterFlowUser(0.0);
3554 0 : NomDesWaterFlowUser = this->DesignWaterFlowRate;
3555 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3556 0 : BaseSizer::reportSizerOutput(state,
3557 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3558 : this->Name,
3559 : "Design Water Flow Rate [m3/s]",
3560 : this->DesignWaterFlowRate,
3561 : "User-Specified Design Water Flow Rate [m3/s]",
3562 : NomDesWaterFlowUser);
3563 0 : if (state.dataGlobal->DisplayExtraWarnings) {
3564 0 : if ((std::abs(tmpDesignWaterFlowRate - NomDesWaterFlowUser) / NomDesWaterFlowUser) >
3565 0 : state.dataSize->AutoVsHardSizingThreshold) {
3566 0 : ShowMessage(state, format("SizeVSMerkelTower: Potential issue with equipment sizing for {}", this->Name));
3567 0 : ShowContinueError(state, format("User-Specified Design Water Flow Rate of {:.2R} [m3/s]", NomDesWaterFlowUser));
3568 0 : ShowContinueError(state, format("differs from Design Water Flow Rate of {:.2R} [m3/s]", tmpDesignWaterFlowRate));
3569 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
3570 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
3571 : }
3572 : }
3573 0 : tmpDesignWaterFlowRate = NomDesWaterFlowUser;
3574 : }
3575 : }
3576 : }
3577 : }
3578 :
3579 10 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->WaterInletNodeNum, tmpDesignWaterFlowRate);
3580 :
3581 10 : if (this->DefaultedDesignAirFlowScalingFactor) {
3582 0 : tmpDesignAirFlowRate = tmpNomTowerCap * this->DesignAirFlowPerUnitNomCap * (101325.0 / state.dataEnvrn->StdBaroPress);
3583 : } else {
3584 10 : tmpDesignAirFlowRate = tmpNomTowerCap * this->DesignAirFlowPerUnitNomCap;
3585 : }
3586 10 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3587 2 : if (this->HighSpeedAirFlowRateWasAutoSized) {
3588 2 : this->HighSpeedAirFlowRate = tmpDesignAirFlowRate;
3589 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3590 4 : BaseSizer::reportSizerOutput(state,
3591 2 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3592 : this->Name,
3593 : "Design Air Flow Rate [m3/s]",
3594 : this->HighSpeedAirFlowRate);
3595 : }
3596 2 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3597 0 : BaseSizer::reportSizerOutput(state,
3598 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3599 : this->Name,
3600 : "Initial Design Air Flow Rate [m3/s]",
3601 : this->HighSpeedAirFlowRate);
3602 : }
3603 : } else { // Hard-sized with sizing data
3604 0 : Real64 DesignAirFlowRateUser(0.0);
3605 0 : DesignAirFlowRateUser = this->HighSpeedAirFlowRate;
3606 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3607 0 : BaseSizer::reportSizerOutput(state,
3608 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3609 : this->Name,
3610 : "Design Air Flow Rate [m3/s]",
3611 : tmpDesignAirFlowRate,
3612 : "User-Specified Design Air Flow Rate [m3/s]",
3613 : DesignAirFlowRateUser);
3614 0 : if (state.dataGlobal->DisplayExtraWarnings) {
3615 0 : if ((std::abs(tmpDesignAirFlowRate - DesignAirFlowRateUser) / DesignAirFlowRateUser) >
3616 0 : state.dataSize->AutoVsHardSizingThreshold) {
3617 0 : ShowMessage(state, format("SizeVSMerkelTower: Potential issue with equipment sizing for {}", this->Name));
3618 0 : ShowContinueError(state, format("User-Specified Design Air Flow Rate of {:.2R} [m3/s]", DesignAirFlowRateUser));
3619 0 : ShowContinueError(state, format("differs from Design Air Flow Rate of {:.2R} [m3/s]", tmpDesignAirFlowRate));
3620 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
3621 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
3622 : }
3623 : }
3624 0 : tmpDesignAirFlowRate = DesignAirFlowRateUser;
3625 : }
3626 : }
3627 : }
3628 10 : tmpFreeConvAirFlowRate = tmpDesignAirFlowRate * this->FreeConvAirFlowRateSizingFactor;
3629 :
3630 10 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3631 2 : if (this->FreeConvAirFlowRateWasAutoSized) {
3632 2 : this->FreeConvAirFlowRate = tmpFreeConvAirFlowRate;
3633 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3634 4 : BaseSizer::reportSizerOutput(state,
3635 2 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3636 : this->Name,
3637 : "Design Free Convection Regime Air Flow Rate [m3/s]",
3638 : this->FreeConvAirFlowRate);
3639 : }
3640 2 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3641 0 : BaseSizer::reportSizerOutput(state,
3642 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3643 : this->Name,
3644 : "Initial Design Free Convection Regime Air Flow Rate [m3/s]",
3645 : this->FreeConvAirFlowRate);
3646 : }
3647 : } else { // Hard-sized with sizing data
3648 0 : Real64 FreeConvAirFlowUser(0.0);
3649 0 : FreeConvAirFlowUser = this->FreeConvAirFlowRate;
3650 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3651 0 : BaseSizer::reportSizerOutput(state,
3652 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3653 : this->Name,
3654 : "Design Free Convection Regime Air Flow Rate [m3/s]",
3655 : tmpFreeConvAirFlowRate,
3656 : "User-Specified Design Free Convection Regime Air Flow Rate [m3/s]",
3657 : FreeConvAirFlowUser);
3658 0 : if (state.dataGlobal->DisplayExtraWarnings) {
3659 0 : if ((std::abs(tmpFreeConvAirFlowRate - FreeConvAirFlowUser) / FreeConvAirFlowUser) >
3660 0 : state.dataSize->AutoVsHardSizingThreshold) {
3661 0 : ShowMessage(state, format("SizeVSMerkelTower: Potential issue with equipment sizing for {}", this->Name));
3662 0 : ShowContinueError(
3663 : state,
3664 0 : format("User-Specified Design Free Convection Regime Air Flow Rate of {:.2R} [m3/s]", FreeConvAirFlowUser));
3665 0 : ShowContinueError(
3666 : state,
3667 0 : format("differs from Design Free Convection Regime Air Flow Rate of {:.2R} [m3/s]", tmpFreeConvAirFlowRate));
3668 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
3669 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
3670 : }
3671 : }
3672 0 : tmpFreeConvAirFlowRate = FreeConvAirFlowUser;
3673 : }
3674 : }
3675 : }
3676 :
3677 : // now calculate UA values from nominal capacities and flow rates
3678 10 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3679 2 : if (PltSizCondNum > 0) { // user has a plant sizing object
3680 2 : Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
3681 2 : this->WaterTemp = DesTowerInletWaterTemp;
3682 : } else { // probably no plant sizing object
3683 0 : Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, Constant::InitConvTemp, RoutineName);
3684 0 : this->WaterTemp = DesTowerInletWaterTemp; // 35.0; // design condition
3685 : }
3686 2 : rho = this->plantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
3687 :
3688 : // full speed fan tower UA
3689 2 : Real64 const solveLoad = tmpNomTowerCap * this->HeatRejectCapNomCapSizingRatio;
3690 2 : Real64 const solveWaterFlow = rho * tmpDesignWaterFlowRate; // design water mass flow rate
3691 2 : UA0 = 0.0001 * solveLoad; // Assume deltaT = 10000K (limit)
3692 2 : UA1 = solveLoad; // Assume deltaT = 1K
3693 :
3694 2 : this->AirTemp = this->DesInletAirDBTemp; // 35.0;
3695 2 : this->AirWetBulb = this->DesignInletWB; // 25.6;
3696 2 : this->AirPress = state.dataEnvrn->StdBaroPress;
3697 2 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
3698 72 : auto f = [&state, this, solveLoad, solveWaterFlow, tmpDesignAirFlowRate, Cp](Real64 UA) {
3699 72 : Real64 const OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterFlow, tmpDesignAirFlowRate, UA);
3700 72 : Real64 const CoolingOutput = Cp * solveWaterFlow * (this->WaterTemp - OutWaterTemp); // tower cooling output [W]
3701 72 : return (solveLoad - CoolingOutput) / solveLoad;
3702 2 : };
3703 2 : General::SolveRoot(state, Acc, MaxIte, SolFla, UA, f, UA0, UA1);
3704 2 : if (SolFla == -1) {
3705 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower UA");
3706 0 : ShowFatalError(state, format("calculating cooling tower UA failed for tower {}", this->Name));
3707 2 : } else if (SolFla == -2) {
3708 0 : ShowSevereError(state, "Bad starting values for UA");
3709 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
3710 : }
3711 2 : this->HighSpeedTowerUA = UA;
3712 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3713 4 : BaseSizer::reportSizerOutput(state,
3714 2 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3715 : this->Name,
3716 : "U-Factor Times Area Value at Full Speed Air Flow Rate [W/C]",
3717 : this->HighSpeedTowerUA);
3718 : }
3719 2 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3720 0 : BaseSizer::reportSizerOutput(state,
3721 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3722 : this->Name,
3723 : "Initial U-Factor Times Area Value at Full Speed Air Flow Rate [W/C]",
3724 : this->HighSpeedTowerUA);
3725 : }
3726 : // free convection tower UA
3727 2 : Real64 const solveLoad1 = tmpTowerFreeConvNomCap * this->HeatRejectCapNomCapSizingRatio;
3728 2 : Real64 solveWaterFlow1 = rho * tmpDesignWaterFlowRate; // design water mass flow rate
3729 2 : UA0 = 0.0001 * solveLoad1; // Assume deltaT = 10000K (limit)
3730 2 : UA0 = max(UA0, 1.0); // limit to 1.0
3731 2 : UA1 = solveLoad1; // Assume deltaT = 1K
3732 2 : this->AirTemp = this->DesInletAirDBTemp; // 35.0;
3733 2 : this->AirWetBulb = this->DesignInletWB; // 25.6;
3734 2 : this->AirPress = state.dataEnvrn->StdBaroPress;
3735 2 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
3736 62 : auto f2 = [&state, this, solveLoad1, solveWaterFlow1, tmpFreeConvAirFlowRate, Cp](Real64 UA) {
3737 62 : Real64 const OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterFlow1, tmpFreeConvAirFlowRate, UA);
3738 62 : Real64 const CoolingOutput = Cp * solveWaterFlow1 * (this->WaterTemp - OutWaterTemp); // tower cooling output [W]
3739 62 : return (solveLoad1 - CoolingOutput) / solveLoad1;
3740 2 : };
3741 2 : General::SolveRoot(state, Acc, MaxIte, SolFla, UA, f2, UA0, UA1);
3742 2 : if (SolFla == -1) {
3743 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower free convection UA");
3744 0 : ShowFatalError(state, format("calculating cooling tower UA failed for tower {}", this->Name));
3745 2 : } else if (SolFla == -2) {
3746 0 : ShowSevereError(state, "Bad starting values for UA");
3747 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for free convection tower {}", this->Name));
3748 : }
3749 2 : this->FreeConvTowerUA = UA;
3750 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3751 4 : BaseSizer::reportSizerOutput(state,
3752 2 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3753 : this->Name,
3754 : "U-Factor Times Area Value at Free Convection Air Flow Rate [W/C]",
3755 : this->FreeConvTowerUA);
3756 : }
3757 2 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3758 0 : BaseSizer::reportSizerOutput(state,
3759 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3760 : this->Name,
3761 : "Initial U-Factor Times Area Value at Free Convection Air Flow Rate [W/C]",
3762 : this->FreeConvTowerUA);
3763 : }
3764 : }
3765 0 : } else if (this->PerformanceInputMethod_Num == PIM::UFactor) {
3766 : // UA input method
3767 :
3768 0 : if (this->DesignWaterFlowRateWasAutoSized) { // get from plant sizing
3769 : // UA input method using plant sizing for flow rate, whereas Nominal capacity method uses scalable sizing factor per cap
3770 0 : if (PltSizCondNum > 0) {
3771 0 : if (PlantSizData(PltSizCondNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
3772 0 : tmpDesignWaterFlowRate = PlantSizData(PltSizCondNum).DesVolFlowRate * this->SizFac;
3773 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3774 0 : this->DesignWaterFlowRate = tmpDesignWaterFlowRate;
3775 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3776 0 : BaseSizer::reportSizerOutput(state,
3777 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3778 : this->Name,
3779 : "Design Water Flow Rate [m3/s]",
3780 : this->DesignWaterFlowRate);
3781 : }
3782 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3783 0 : BaseSizer::reportSizerOutput(state,
3784 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3785 : this->Name,
3786 : "Initial Design Water Flow Rate [m3/s]",
3787 : this->DesignWaterFlowRate);
3788 : }
3789 : }
3790 : } else {
3791 0 : tmpDesignWaterFlowRate = 0.0;
3792 : }
3793 :
3794 : } else {
3795 0 : if (!this->TowerInletCondsAutoSize) {
3796 0 : if (this->DesignWaterFlowRate >= HVAC::SmallWaterVolFlow) {
3797 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3798 0 : this->DesignWaterFlowRate = tmpDesignWaterFlowRate;
3799 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3800 0 : BaseSizer::reportSizerOutput(state,
3801 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3802 : this->Name,
3803 : "Design Water Flow Rate [m3/s]",
3804 : this->DesignWaterFlowRate);
3805 : }
3806 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3807 0 : BaseSizer::reportSizerOutput(state,
3808 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3809 : this->Name,
3810 : "Initial Design Water Flow Rate [m3/s]",
3811 : this->DesignWaterFlowRate);
3812 : }
3813 : }
3814 : } else {
3815 0 : tmpDesignWaterFlowRate = 0.0;
3816 : }
3817 : } else {
3818 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3819 0 : ShowSevereError(state, format("Autosizing error for cooling tower object = {}", this->Name));
3820 0 : ShowFatalError(state, "Autosizing of cooling tower nominal capacity requires a loop Sizing:Plant object.");
3821 : }
3822 : }
3823 : }
3824 : }
3825 0 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->WaterInletNodeNum, tmpDesignWaterFlowRate);
3826 :
3827 0 : if (this->HighSpeedTowerUAWasAutoSized) {
3828 : // get nominal capacity from PlantSizData(PltSizCondNum)%DeltaT and PlantSizData(PltSizCondNum)%DesVolFlowRate
3829 0 : if (PltSizCondNum > 0) {
3830 0 : if (PlantSizData(PltSizCondNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
3831 0 : rho = this->plantLoc.loop->glycol->getDensity(state, DesTowerExitWaterTemp, RoutineName);
3832 0 : Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
3833 0 : DesTowerLoad = rho * Cp * PlantSizData(PltSizCondNum).DesVolFlowRate * DesTowerWaterDeltaT * this->SizFac;
3834 0 : tmpNomTowerCap = DesTowerLoad / this->HeatRejectCapNomCapSizingRatio;
3835 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3836 0 : this->TowerNominalCapacity = tmpNomTowerCap;
3837 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3838 0 : BaseSizer::reportSizerOutput(state,
3839 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3840 : this->Name,
3841 : "Nominal Capacity [W]",
3842 : this->TowerNominalCapacity);
3843 : }
3844 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3845 0 : BaseSizer::reportSizerOutput(state,
3846 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3847 : this->Name,
3848 : "Initial Nominal Capacity [W]",
3849 : this->TowerNominalCapacity);
3850 : }
3851 : }
3852 : } else {
3853 0 : tmpNomTowerCap = 0.0;
3854 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3855 0 : this->TowerNominalCapacity = tmpNomTowerCap;
3856 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3857 0 : BaseSizer::reportSizerOutput(state,
3858 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3859 : this->Name,
3860 : "Nominal Capacity [W]",
3861 : this->TowerNominalCapacity);
3862 : }
3863 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3864 0 : BaseSizer::reportSizerOutput(state,
3865 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3866 : this->Name,
3867 : "Initial Nominal Capacity [W]",
3868 : this->TowerNominalCapacity);
3869 : }
3870 : }
3871 : }
3872 : } else {
3873 0 : if (!this->TowerInletCondsAutoSize) {
3874 0 : if (this->DesignWaterFlowRate >= HVAC::SmallWaterVolFlow) {
3875 0 : rho = this->plantLoc.loop->glycol->getDensity(state, DesTowerExitWaterTemp, RoutineName);
3876 0 : Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
3877 0 : DesTowerLoad = rho * Cp * this->DesignWaterFlowRate * DesTowerWaterDeltaT * this->SizFac;
3878 0 : tmpNomTowerCap = DesTowerLoad / this->HeatRejectCapNomCapSizingRatio;
3879 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3880 0 : this->TowerNominalCapacity = tmpNomTowerCap;
3881 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3882 0 : BaseSizer::reportSizerOutput(state,
3883 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3884 : this->Name,
3885 : "Nominal Capacity [W]",
3886 : this->TowerNominalCapacity);
3887 : }
3888 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3889 0 : BaseSizer::reportSizerOutput(state,
3890 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3891 : this->Name,
3892 : "Initial Nominal Capacity [W]",
3893 : this->TowerNominalCapacity);
3894 : }
3895 : }
3896 : } else {
3897 0 : tmpNomTowerCap = 0.0;
3898 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3899 0 : this->TowerNominalCapacity = tmpNomTowerCap;
3900 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3901 0 : BaseSizer::reportSizerOutput(state,
3902 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3903 : this->Name,
3904 : "Nominal Capacity [W]",
3905 : this->TowerNominalCapacity);
3906 : }
3907 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3908 0 : BaseSizer::reportSizerOutput(state,
3909 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3910 : this->Name,
3911 : "Initial Nominal Capacity [W]",
3912 : this->TowerNominalCapacity);
3913 : }
3914 : }
3915 : }
3916 : } else {
3917 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3918 0 : ShowSevereError(state, format("Autosizing error for cooling tower object = {}", this->Name));
3919 0 : ShowFatalError(state, "Autosizing of cooling tower nominal capacity requires a loop Sizing:Plant object.");
3920 : }
3921 : }
3922 : }
3923 0 : if (this->TowerFreeConvNomCapWasAutoSized) {
3924 0 : tmpTowerFreeConvNomCap = tmpNomTowerCap * this->TowerFreeConvNomCapSizingFactor;
3925 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3926 0 : this->TowerFreeConvNomCap = tmpTowerFreeConvNomCap;
3927 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3928 0 : BaseSizer::reportSizerOutput(state,
3929 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3930 : this->Name,
3931 : "Free Convection Nominal Capacity [W]",
3932 : this->TowerFreeConvNomCap);
3933 : }
3934 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3935 0 : BaseSizer::reportSizerOutput(state,
3936 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3937 : this->Name,
3938 : "Initial Free Convection Nominal Capacity [W]",
3939 : this->TowerFreeConvNomCap);
3940 : }
3941 : }
3942 : }
3943 0 : if (this->HighSpeedAirFlowRateWasAutoSized) {
3944 0 : if (this->DefaultedDesignAirFlowScalingFactor) {
3945 0 : tmpDesignAirFlowRate = tmpNomTowerCap * this->DesignAirFlowPerUnitNomCap * (101325.0 / state.dataEnvrn->StdBaroPress);
3946 : } else {
3947 0 : tmpDesignAirFlowRate = tmpNomTowerCap * this->DesignAirFlowPerUnitNomCap;
3948 : }
3949 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3950 0 : this->HighSpeedAirFlowRate = tmpDesignAirFlowRate;
3951 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3952 0 : BaseSizer::reportSizerOutput(state,
3953 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3954 : this->Name,
3955 : "Design Air Flow Rate [m3/s]",
3956 : this->HighSpeedAirFlowRate);
3957 : }
3958 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3959 0 : BaseSizer::reportSizerOutput(state,
3960 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3961 : this->Name,
3962 : "Initial Design Air Flow Rate [m3/s]",
3963 : this->HighSpeedAirFlowRate);
3964 : }
3965 : }
3966 : }
3967 0 : if (this->FreeConvAirFlowRateWasAutoSized) {
3968 0 : tmpFreeConvAirFlowRate = tmpDesignAirFlowRate * this->FreeConvAirFlowRateSizingFactor;
3969 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3970 0 : this->FreeConvAirFlowRate = tmpFreeConvAirFlowRate;
3971 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
3972 0 : BaseSizer::reportSizerOutput(state,
3973 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3974 : this->Name,
3975 : "Free Convection Regime Air Flow Rate [m3/s]",
3976 : this->FreeConvAirFlowRate);
3977 : }
3978 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
3979 0 : BaseSizer::reportSizerOutput(state,
3980 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
3981 : this->Name,
3982 : "Initial Free Convection Regime Air Flow Rate [m3/s]",
3983 : this->FreeConvAirFlowRate);
3984 : }
3985 : }
3986 : }
3987 : // now calculate UA values from nominal capacities and flow rates
3988 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
3989 0 : rho = this->plantLoc.loop->glycol->getDensity(state, Constant::InitConvTemp, RoutineName);
3990 0 : Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
3991 : // full speed fan tower UA
3992 0 : Real64 const solveLoad = tmpNomTowerCap * this->HeatRejectCapNomCapSizingRatio;
3993 0 : Real64 const solveWaterFlow = rho * tmpDesignWaterFlowRate; // design water mass flow rate
3994 0 : UA0 = 0.0001 * solveLoad; // Assume deltaT = 10000K (limit)
3995 0 : UA1 = solveLoad; // Assume deltaT = 1K
3996 0 : this->WaterTemp = DesTowerInletWaterTemp;
3997 0 : this->AirTemp = this->DesInletAirDBTemp; // 35.0;
3998 0 : this->AirWetBulb = this->DesignInletWB; // 25.6;
3999 0 : this->AirPress = state.dataEnvrn->StdBaroPress;
4000 0 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
4001 0 : auto f = [&state, this, solveLoad, solveWaterFlow, tmpDesignAirFlowRate, Cp](Real64 UA) {
4002 0 : Real64 const OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterFlow, tmpDesignAirFlowRate, UA);
4003 0 : Real64 const CoolingOutput = Cp * solveWaterFlow * (this->WaterTemp - OutWaterTemp); // tower cooling output [W]
4004 0 : return (solveLoad - CoolingOutput) / solveLoad;
4005 0 : };
4006 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, UA, f, UA0, UA1);
4007 0 : if (SolFla == -1) {
4008 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower UA");
4009 0 : ShowFatalError(state, format("calculating cooling tower UA failed for tower {}", this->Name));
4010 0 : } else if (SolFla == -2) {
4011 0 : ShowSevereError(state, "Bad starting values for UA");
4012 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for tower {}", this->Name));
4013 : }
4014 0 : this->HighSpeedTowerUA = UA;
4015 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4016 0 : BaseSizer::reportSizerOutput(state,
4017 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4018 : this->Name,
4019 : "U-Factor Times Area Value at Full Speed Air Flow Rate [W/C]",
4020 : this->HighSpeedTowerUA);
4021 : }
4022 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4023 0 : BaseSizer::reportSizerOutput(state,
4024 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4025 : this->Name,
4026 : "Initial U-Factor Times Area Value at Full Speed Air Flow Rate [W/C]",
4027 : this->HighSpeedTowerUA);
4028 : }
4029 : // free convection tower UA
4030 0 : Real64 const solveLoad2 = tmpTowerFreeConvNomCap * this->HeatRejectCapNomCapSizingRatio;
4031 0 : Real64 const solveWaterFlow2 = rho * tmpDesignWaterFlowRate; // design water mass flow rate
4032 0 : UA0 = 0.0001 * solveLoad2; // Assume deltaT = 10000K (limit)
4033 0 : UA1 = solveLoad2; // Assume deltaT = 1K
4034 0 : this->WaterTemp = DesTowerInletWaterTemp;
4035 0 : this->AirTemp = DesTowerInletAirDBTemp; // 35.0;
4036 0 : this->AirWetBulb = DesTowerInletAirWBTemp; // 25.6;
4037 0 : this->AirPress = state.dataEnvrn->StdBaroPress;
4038 0 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
4039 0 : auto f3 = [&state, this, solveLoad2, solveWaterFlow2, tmpFreeConvAirFlowRate, Cp](Real64 UA) {
4040 0 : Real64 const OutWaterTemp = this->calculateSimpleTowerOutletTemp(state, solveWaterFlow2, tmpFreeConvAirFlowRate, UA);
4041 0 : Real64 const CoolingOutput = Cp * solveWaterFlow2 * (this->WaterTemp - OutWaterTemp); // tower cooling output [W]
4042 0 : return (solveLoad2 - CoolingOutput) / solveLoad2;
4043 0 : };
4044 0 : General::SolveRoot(state, Acc, MaxIte, SolFla, UA, f3, UA0, UA1);
4045 0 : if (SolFla == -1) {
4046 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower free convection UA");
4047 0 : ShowFatalError(state, format("calculating cooling tower UA failed for tower {}", this->Name));
4048 0 : } else if (SolFla == -2) {
4049 0 : ShowSevereError(state, "Bad starting values for UA");
4050 0 : ShowFatalError(state, format("Autosizing of cooling tower UA failed for free convection tower {}", this->Name));
4051 : }
4052 0 : this->LowSpeedTowerUA = UA;
4053 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4054 0 : BaseSizer::reportSizerOutput(state,
4055 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4056 : this->Name,
4057 : "U-Factor Times Area Value at Free Convection Air Flow Rate [W/C]",
4058 : this->FreeConvTowerUA);
4059 : }
4060 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4061 0 : BaseSizer::reportSizerOutput(state,
4062 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4063 : this->Name,
4064 : "Initial U-Factor Times Area Value at Free Convection Air Flow Rate [W/C]",
4065 : this->FreeConvTowerUA);
4066 : }
4067 : }
4068 :
4069 : } else { // full speed UA given
4070 :
4071 0 : if (this->FreeConvTowerUAWasAutoSized) { // determine from scalable sizing factor
4072 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
4073 0 : this->FreeConvTowerUA = this->HighSpeedTowerUA * this->FreeConvTowerUASizingFactor;
4074 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4075 0 : BaseSizer::reportSizerOutput(state,
4076 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4077 : this->Name,
4078 : "U-Factor Times Area Value at Free Convection Air Flow Rate [W/C]",
4079 : this->FreeConvTowerUA);
4080 : }
4081 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4082 0 : BaseSizer::reportSizerOutput(state,
4083 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4084 : this->Name,
4085 : "Initial U-Factor Times Area Value at Free Convection Air Flow Rate [W/C]",
4086 : this->FreeConvTowerUA);
4087 : }
4088 : }
4089 : }
4090 : Real64 OutWaterTemp;
4091 0 : if (this->HighSpeedAirFlowRateWasAutoSized) { // given UA but not air flow rate
4092 : // need an air flow rate to find capacity from UA but flow rate is scaled off capacity
4093 : // get nominal capacity from PlantSizData(PltSizCondNum)%DeltaT and PlantSizData(PltSizCondNum)%DesVolFlowRate
4094 0 : if (PltSizCondNum > 0) {
4095 0 : if (PlantSizData(PltSizCondNum).DesVolFlowRate >= HVAC::SmallWaterVolFlow) {
4096 0 : rho = this->plantLoc.loop->glycol->getDensity(state, DesTowerExitWaterTemp, RoutineName);
4097 0 : Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
4098 0 : DesTowerLoad = rho * Cp * PlantSizData(PltSizCondNum).DesVolFlowRate * DesTowerWaterDeltaT;
4099 0 : tmpNomTowerCap = DesTowerLoad / this->HeatRejectCapNomCapSizingRatio;
4100 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
4101 0 : this->TowerNominalCapacity = tmpNomTowerCap;
4102 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4103 0 : BaseSizer::reportSizerOutput(state,
4104 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4105 : this->Name,
4106 : "Nominal Capacity [W]",
4107 : this->TowerNominalCapacity);
4108 : }
4109 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4110 0 : BaseSizer::reportSizerOutput(state,
4111 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4112 : this->Name,
4113 : "Initial Nominal Capacity [W]",
4114 : this->TowerNominalCapacity);
4115 : }
4116 : }
4117 : } else {
4118 0 : tmpNomTowerCap = rho = Cp = 0.0; // rho and Cp added: Used below
4119 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
4120 0 : this->TowerNominalCapacity = tmpNomTowerCap;
4121 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4122 0 : BaseSizer::reportSizerOutput(state,
4123 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4124 : this->Name,
4125 : "Nominal Capacity [W]",
4126 : this->TowerNominalCapacity);
4127 : }
4128 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4129 0 : BaseSizer::reportSizerOutput(state,
4130 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4131 : this->Name,
4132 : "Initial Nominal Capacity [W]",
4133 : this->TowerNominalCapacity);
4134 : }
4135 : }
4136 : }
4137 :
4138 : } else {
4139 0 : tmpNomTowerCap = 0.0; // Suppress uninitialized warnings
4140 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
4141 0 : ShowSevereError(state, format("Autosizing error for cooling tower object = {}", this->Name));
4142 0 : ShowFatalError(state, "Autosizing of cooling tower nominal capacity requires a loop Sizing:Plant object.");
4143 : }
4144 : }
4145 :
4146 0 : if (this->DefaultedDesignAirFlowScalingFactor) {
4147 0 : tmpDesignAirFlowRate = tmpNomTowerCap * this->DesignAirFlowPerUnitNomCap * (101325.0 / state.dataEnvrn->StdBaroPress);
4148 : } else {
4149 0 : tmpDesignAirFlowRate = tmpNomTowerCap * this->DesignAirFlowPerUnitNomCap;
4150 : }
4151 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
4152 0 : this->HighSpeedAirFlowRate = tmpDesignAirFlowRate;
4153 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4154 0 : BaseSizer::reportSizerOutput(state,
4155 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4156 : this->Name,
4157 : "Design Air Flow Rate [m3/s]",
4158 : this->HighSpeedAirFlowRate);
4159 : }
4160 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4161 0 : BaseSizer::reportSizerOutput(state,
4162 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4163 : this->Name,
4164 : "Initial Design Air Flow Rate [m3/s]",
4165 : this->HighSpeedAirFlowRate);
4166 : }
4167 : }
4168 :
4169 : } else { // UA and Air flow rate given, so find Nominal Cap from running model
4170 :
4171 0 : rho = this->plantLoc.loop->glycol->getDensity(state, DesTowerExitWaterTemp, RoutineName);
4172 0 : Cp = this->plantLoc.loop->glycol->getSpecificHeat(state, DesTowerExitWaterTemp, RoutineName);
4173 :
4174 0 : this->WaterTemp = DesTowerInletWaterTemp;
4175 0 : this->AirTemp = DesTowerInletAirDBTemp; // 35.0;
4176 0 : this->AirWetBulb = DesTowerInletAirWBTemp; // 25.6;
4177 0 : this->AirPress = state.dataEnvrn->StdBaroPress;
4178 0 : this->AirHumRat = Psychrometrics::PsyWFnTdbTwbPb(state, this->AirTemp, this->AirWetBulb, this->AirPress);
4179 : OutWaterTemp =
4180 0 : this->calculateSimpleTowerOutletTemp(state, rho * tmpDesignWaterFlowRate, this->HighSpeedAirFlowRate, this->HighSpeedTowerUA);
4181 0 : tmpNomTowerCap = Cp * rho * tmpDesignWaterFlowRate * (this->WaterTemp - OutWaterTemp);
4182 0 : tmpNomTowerCap /= this->HeatRejectCapNomCapSizingRatio;
4183 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
4184 0 : this->TowerNominalCapacity = tmpNomTowerCap;
4185 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4186 0 : BaseSizer::reportSizerOutput(state,
4187 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4188 : this->Name,
4189 : "Nominal Capacity [W]",
4190 : this->TowerNominalCapacity);
4191 : }
4192 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4193 0 : BaseSizer::reportSizerOutput(state,
4194 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4195 : this->Name,
4196 : "Initial Nominal Capacity [W]",
4197 : this->TowerNominalCapacity);
4198 : }
4199 : }
4200 :
4201 : } // both UA and air flow rate given
4202 :
4203 0 : if (this->FreeConvAirFlowRateWasAutoSized) {
4204 0 : tmpFreeConvAirFlowRate = tmpDesignAirFlowRate * this->FreeConvAirFlowRateSizingFactor;
4205 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
4206 0 : this->FreeConvAirFlowRate = tmpFreeConvAirFlowRate;
4207 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4208 0 : BaseSizer::reportSizerOutput(state,
4209 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4210 : this->Name,
4211 : "Free Convection Regime Air Flow Rate [m3/s]",
4212 : this->FreeConvAirFlowRate);
4213 : }
4214 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4215 0 : BaseSizer::reportSizerOutput(state,
4216 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4217 : this->Name,
4218 : "Initial Free Convection Regime Air Flow Rate [m3/s]",
4219 : this->FreeConvAirFlowRate);
4220 : }
4221 : }
4222 : }
4223 :
4224 : OutWaterTemp =
4225 0 : this->calculateSimpleTowerOutletTemp(state, rho * tmpDesignWaterFlowRate, tmpFreeConvAirFlowRate, this->FreeConvTowerUA);
4226 0 : tmpTowerFreeConvNomCap = Cp * rho * tmpDesignWaterFlowRate * (this->WaterTemp - OutWaterTemp);
4227 0 : tmpTowerFreeConvNomCap /= this->HeatRejectCapNomCapSizingRatio;
4228 0 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
4229 0 : this->TowerFreeConvNomCap = tmpTowerFreeConvNomCap;
4230 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4231 0 : BaseSizer::reportSizerOutput(state,
4232 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4233 : this->Name,
4234 : "Free Convection Nominal Capacity [W]",
4235 : this->TowerFreeConvNomCap);
4236 : }
4237 0 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4238 0 : BaseSizer::reportSizerOutput(state,
4239 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4240 : this->Name,
4241 : "Initial Free Convection Nominal Capacity [W]",
4242 : this->TowerFreeConvNomCap);
4243 : }
4244 : }
4245 : }
4246 : }
4247 :
4248 10 : tmpHighSpeedFanPower = tmpNomTowerCap * this->DesignFanPowerPerUnitNomCap;
4249 10 : if (state.dataPlnt->PlantFirstSizesOkayToFinalize) {
4250 2 : if (this->HighSpeedFanPowerWasAutoSized) {
4251 :
4252 2 : this->HighSpeedFanPower = tmpHighSpeedFanPower;
4253 2 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4254 4 : BaseSizer::reportSizerOutput(state,
4255 2 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4256 : this->Name,
4257 : "Design Fan Power [W]",
4258 : this->HighSpeedFanPower);
4259 : }
4260 2 : if (state.dataPlnt->PlantFirstSizesOkayToReport) {
4261 0 : BaseSizer::reportSizerOutput(state,
4262 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4263 : this->Name,
4264 : "Initial Design Fan Power [W]",
4265 : this->HighSpeedFanPower);
4266 : }
4267 : } else { // Hard-sized with sizing data
4268 0 : Real64 HighSpeedFanPowerUser(0.0);
4269 0 : HighSpeedFanPowerUser = this->HighSpeedAirFlowRate;
4270 0 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4271 0 : BaseSizer::reportSizerOutput(state,
4272 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
4273 : this->Name,
4274 : "Design Fan Power [W]",
4275 : tmpHighSpeedFanPower,
4276 : "User-Specified Design Fan Power [W]",
4277 : HighSpeedFanPowerUser);
4278 0 : if (state.dataGlobal->DisplayExtraWarnings) {
4279 0 : if ((std::abs(tmpHighSpeedFanPower - HighSpeedFanPowerUser) / HighSpeedFanPowerUser) >
4280 0 : state.dataSize->AutoVsHardSizingThreshold) {
4281 0 : ShowMessage(state, format("SizeVSMerkelTower: Potential issue with equipment sizing for {}", this->Name));
4282 0 : ShowContinueError(state, format("User-Specified Design Fan Power of {:.2R} [W]", HighSpeedFanPowerUser));
4283 0 : ShowContinueError(state, format("differs from Design Fan Power of {:.2R} [W]", tmpHighSpeedFanPower));
4284 0 : ShowContinueError(state, "This may, or may not, indicate mismatched component sizes.");
4285 0 : ShowContinueError(state, "Verify that the value entered is intended and is consistent with other components.");
4286 : }
4287 : }
4288 : }
4289 : }
4290 : }
4291 :
4292 10 : if (state.dataPlnt->PlantFinalSizesOkayToReport) {
4293 : // create predefined report
4294 4 : OutputReportPredefined::PreDefTableEntry(
4295 4 : state, state.dataOutRptPredefined->pdchMechType, this->Name, DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)]);
4296 2 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchMechNomCap, this->Name, this->TowerNominalCapacity);
4297 :
4298 : // create std 229 new table for cooling towers and fluid coolers
4299 4 : OutputReportPredefined::PreDefTableEntry(
4300 4 : state, state.dataOutRptPredefined->pdchCTFCType, this->Name, DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)]);
4301 4 : OutputReportPredefined::PreDefTableEntry(state,
4302 2 : state.dataOutRptPredefined->pdchCTFCCondLoopName,
4303 : this->Name,
4304 4 : (this->plantLoc.loop != nullptr) ? this->plantLoc.loop->Name : "N/A");
4305 4 : OutputReportPredefined::PreDefTableEntry(state,
4306 2 : state.dataOutRptPredefined->pdchCTFCCondLoopBranchName,
4307 : this->Name,
4308 4 : (this->plantLoc.branch != nullptr) ? plantLoc.branch->Name : "N/A");
4309 4 : OutputReportPredefined::PreDefTableEntry(state,
4310 2 : state.dataOutRptPredefined->pdchCTFCFluidType,
4311 : this->Name,
4312 2 : this->plantLoc.loop->glycol->Name); // Fluid Name more reasonable than FluidType
4313 2 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchCTFCRange, this->Name, this->DesignRange);
4314 2 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchCTFCApproach, this->Name, this->DesignApproach);
4315 4 : OutputReportPredefined::PreDefTableEntry(
4316 2 : state, state.dataOutRptPredefined->pdchCTFCDesFanPwr, this->Name, this->HighSpeedFanPower); // equivalent to Design Fan Power?
4317 2 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchCTFCDesInletAirWBT, this->Name, this->DesignInletWB);
4318 4 : OutputReportPredefined::PreDefTableEntry(
4319 4 : state, state.dataOutRptPredefined->pdchCTFCDesWaterFlowRate, this->Name, this->DesignWaterFlowRate, 6);
4320 2 : OutputReportPredefined::PreDefTableEntry(state, state.dataOutRptPredefined->pdchCTFCLevWaterSPTemp, this->Name, this->DesOutletWaterTemp);
4321 : }
4322 10 : } // namespace CondenserLoopTowers
4323 :
4324 7735530 : void CoolingTower::calculateSingleSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag)
4325 : {
4326 :
4327 : // SUBROUTINE INFORMATION:
4328 : // AUTHOR Dan Fisher
4329 : // DATE WRITTEN Sept. 1998
4330 : // MODIFIED Aug. 2008, T Hong, Added fluid bypass for single speed cooling tower
4331 : // The OutletWaterTemp from calculateSimpleTowerOutletTemp can be lower than 0 degreeC
4332 : // which may not be allowed in practice if water is the tower fluid.
4333 : // Chandan Sharma, FSEC, February 2010, Added basin heater
4334 : // Jul. 2010, A Flament, added multi-cell capability for the 3 types of cooling tower
4335 : // Jun. 2016, R Zhang, Applied the condenser supply water temperature sensor fault model
4336 : // Jul. 2016, R Zhang, Applied the cooling tower fouling fault model
4337 : // RE-ENGINEERED Jan. 2001, Richard Raustad
4338 :
4339 : // PURPOSE OF THIS SUBROUTINE:
4340 : // To simulate the operation of a single-speed fan cooling tower.
4341 :
4342 : // METHODOLOGY EMPLOYED:
4343 : // The cooling tower is modeled using effectiveness-NTU relationships for
4344 : // counterflow heat exchangers based on Merkel's theory.
4345 : // The subroutine calculates the period of time required to meet a
4346 : // leaving water temperature setpoint. It assumes that part-load
4347 : // operation represents a linear interpolation of two steady-state regimes.
4348 : // Cyclic losses are neglected. The period of time required to meet the
4349 : // leaving water temperature setpoint is used to determine the required
4350 : // fan power and energy. Free convection regime is also modeled. This
4351 : // occurs when the pump is operating and the fan is off. If free convection
4352 : // regime cooling is all that is required for a given time step, the leaving
4353 : // water temperature is allowed to fall below the leaving water temperature
4354 : // setpoint (free cooling). At times when the cooling tower fan is required,
4355 : // the leaving water temperature is at or above the setpoint.
4356 : // A RunFlag is passed by the upper level manager to indicate the ON/OFF status,
4357 : // or schedule, of the cooling tower. If the tower is OFF, outlet water
4358 : // temperature and flow rate are passed through the model from inlet node to
4359 : // outlet node without intervention (with the exception of free convection
4360 : // where water temperature is allowed to float below the outlet water set
4361 : // point). Reports are also updated with fan power and energy being zero.
4362 : // When the RunFlag indicates an ON condition for the cooling tower, the
4363 : // mass flow rate and water temperature are read from the inlet node of the
4364 : // cooling tower (water-side). The outdoor air wet-bulb temperature is used
4365 : // as the entering condition to the cooling tower (air-side). Input deck
4366 : // parameters are read for the free convection regime (pump ON and fan OFF)
4367 : // and a leaving water temperature is calculated. If the leaving water temperature
4368 : // is at or below the setpoint, the calculated leaving water temperature is
4369 : // placed on the outlet node and no fan power is used. If the calculated leaving
4370 : // water temperature is above the setpoint, the cooling tower fan is turned on
4371 : // and design parameters are used to again calculate the leaving water temperature.
4372 : // If the calculated leaving water temperature is below the setpoint, a fan
4373 : // run-time fraction is calculated and used to determine fan power. The leaving
4374 : // water temperature setpoint is placed on the outlet node. If the calculated
4375 : // leaving water temperature is at or above the setpoint, the calculated
4376 : // leaving water temperature is placed on the outlet node and the fan runs at
4377 : // full power. Water mass flow rate is passed from inlet node to outlet node
4378 : // with no intervention.
4379 : // If a tower has multiple cells, the specified inputs of or the autosized capacity
4380 : // and air/water flow rates are for the entire tower. The number of cells to operate
4381 : // is first determined based on the user entered minimal and maximal water flow fractions
4382 : // per cell. If the loads are not met, more cells (if available) will operate to meet
4383 : // the loads. Inside each cell, the capacity controls still apply. Each cell operates
4384 : // in the same way.
4385 :
4386 : // REFERENCES:
4387 : // ASHRAE HVAC1KIT: A Toolkit for Primary HVAC System Energy Calculation. 1999.
4388 :
4389 : // SUBROUTINE PARAMETER DEFINITIONS:
4390 : static constexpr std::string_view RoutineName("calculateSingleSpeedTower");
4391 7735530 : int constexpr MaxIteration(100); // Maximum fluid bypass iteration calculations
4392 : static constexpr std::string_view MaxItChar("100");
4393 7735530 : Real64 constexpr BypassFractionThreshold(0.01); // Threshold to stop bypass iteration
4394 7735530 : Real64 constexpr OWTLowerLimit(0.0); // The limit of tower exit fluid temperature used in the fluid bypass
4395 : // calculation to avoid fluid freezing. For water, it is 0 degreeC,
4396 : // for glycols, it can be much lower. The fluid type is stored at the loop.
4397 : // Current choices are Water and Steam, needs to expand for glycols
4398 :
4399 : // set inlet and outlet nodes
4400 7735530 : this->Qactual = 0.0;
4401 7735530 : this->FanPower = 0.0;
4402 7735530 : this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
4403 :
4404 7735530 : Real64 freeConvTowerUA = this->FreeConvTowerUA;
4405 7735530 : Real64 highSpeedTowerUA = this->HighSpeedTowerUA;
4406 :
4407 : // water temperature setpoint
4408 7735530 : Real64 TempSetPoint = 0.0;
4409 7735530 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
4410 7627677 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
4411 7627677 : if (this->SetpointIsOnOutlet) {
4412 329043 : TempSetPoint = state.dataLoopNodes->Node(this->WaterOutletNodeNum).TempSetPoint;
4413 : } else {
4414 7298634 : TempSetPoint = this->plantLoc.side->TempSetPoint;
4415 : }
4416 7627677 : } break;
4417 107853 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand: {
4418 107853 : if (this->SetpointIsOnOutlet) {
4419 29668 : TempSetPoint = state.dataLoopNodes->Node(this->WaterOutletNodeNum).TempSetPointHi;
4420 : } else {
4421 78185 : TempSetPoint = this->plantLoc.side->TempSetPointHi;
4422 : }
4423 107853 : } break;
4424 0 : default:
4425 0 : break;
4426 : }
4427 :
4428 : // If there is a fault of condenser SWT Sensor
4429 7735530 : if (this->FaultyCondenserSWTFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
4430 0 : (!state.dataGlobal->KickOffSimulation)) {
4431 0 : int FaultIndex = this->FaultyCondenserSWTIndex;
4432 0 : Real64 TowerOutletTemp_ff = TempSetPoint;
4433 :
4434 : // calculate the sensor offset using fault information
4435 0 : this->FaultyCondenserSWTOffset = state.dataFaultsMgr->FaultsCondenserSWTSensor(FaultIndex).CalFaultOffsetAct(state);
4436 : // update the TempSetPoint
4437 0 : TempSetPoint = TowerOutletTemp_ff - this->FaultyCondenserSWTOffset;
4438 : }
4439 :
4440 : // If there is a fault of cooling tower fouling
4441 7738770 : if (this->FaultyTowerFoulingFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
4442 3240 : (!state.dataGlobal->KickOffSimulation)) {
4443 3240 : int FaultIndex = this->FaultyTowerFoulingIndex;
4444 3240 : Real64 FreeConvTowerUA_ff = this->FreeConvTowerUA;
4445 3240 : Real64 HighSpeedTowerUA_ff = this->HighSpeedTowerUA;
4446 :
4447 : // calculate the Faulty Tower Fouling Factor using fault information
4448 3240 : this->FaultyTowerFoulingFactor = state.dataFaultsMgr->FaultsTowerFouling(FaultIndex).CalFaultyTowerFoulingFactor(state);
4449 :
4450 : // update the tower UA values at faulty cases
4451 3240 : freeConvTowerUA = FreeConvTowerUA_ff * this->FaultyTowerFoulingFactor;
4452 3240 : highSpeedTowerUA = HighSpeedTowerUA_ff * this->FaultyTowerFoulingFactor;
4453 : }
4454 :
4455 : // Added for fluid bypass. First assume no fluid bypass
4456 7735530 : int BypassFlag = 0; // Flag indicator for fluid bypass (0 - no bypass, 1 - bypass)
4457 7735530 : Real64 BypassFraction2 = 0.0;
4458 7735530 : this->BypassFraction = 0.0;
4459 :
4460 : // Added for multi-cell. Determine the number of cells operating
4461 7735530 : int NumCellMin(0);
4462 7735530 : int NumCellMax(0);
4463 7735530 : Real64 WaterMassFlowRatePerCellMin = 0.0;
4464 7735530 : if (this->DesWaterMassFlowRate > 0.0) {
4465 7730495 : WaterMassFlowRatePerCellMin = this->DesWaterMassFlowRate * this->MinFracFlowRate / this->NumCell;
4466 7730495 : Real64 WaterMassFlowRatePerCellMax = this->DesWaterMassFlowRate * this->MaxFracFlowRate / this->NumCell;
4467 :
4468 : // round it up to the nearest integer
4469 7730495 : NumCellMin = min(int((this->WaterMassFlowRate / WaterMassFlowRatePerCellMax) + 0.9999), this->NumCell);
4470 7730495 : NumCellMax = min(int((this->WaterMassFlowRate / WaterMassFlowRatePerCellMin) + 0.9999), this->NumCell);
4471 : }
4472 : // cap min at 1
4473 7735530 : if (NumCellMin <= 0) {
4474 3945600 : NumCellMin = 1;
4475 : }
4476 7735530 : if (NumCellMax <= 0) {
4477 3945600 : NumCellMax = 1;
4478 : }
4479 7735530 : if (this->cellCtrl == CellCtrl::MinCell) {
4480 40284 : this->NumCellOn = NumCellMin;
4481 : } else {
4482 7695246 : this->NumCellOn = NumCellMax;
4483 : }
4484 7735530 : Real64 WaterMassFlowRatePerCell = this->WaterMassFlowRate / this->NumCellOn;
4485 :
4486 : // Do not RETURN here if flow rate is less than SmallMassFlow. Check basin heater and then RETURN.
4487 :
4488 7735530 : bool returnFlagSet = false;
4489 7735530 : this->checkMassFlowAndLoad(state, MyLoad, RunFlag, returnFlagSet);
4490 7735530 : if (returnFlagSet) {
4491 4260985 : return;
4492 : }
4493 :
4494 3474545 : bool IncrNumCellFlag = true; // determine if yes or no we increase the number of cells // set value to true to enter in the loop
4495 :
4496 3474545 : Real64 UAdesign = 0.0; // UA value at design conditions (entered by user or calculated)
4497 : Real64 OutletWaterTempOFF;
4498 3474545 : Real64 FanModeFrac = 0.0;
4499 3474545 : Real64 AirFlowRate = 0.0;
4500 6962074 : while (IncrNumCellFlag) {
4501 3487529 : IncrNumCellFlag = false;
4502 :
4503 : // Initialize local variables to the free convection design values
4504 3487529 : UAdesign = freeConvTowerUA / this->NumCell;
4505 3487529 : AirFlowRate = this->FreeConvAirFlowRate / this->NumCell;
4506 3487529 : OutletWaterTempOFF = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
4507 3487529 : this->OutletWaterTemp = OutletWaterTempOFF;
4508 3487529 : FanModeFrac = 0.0;
4509 :
4510 3487529 : OutletWaterTempOFF = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRate, UAdesign);
4511 :
4512 : // Assume Setpoint was met using free convection regime (pump ON and fan OFF)
4513 3487529 : this->FanPower = 0.0;
4514 3487529 : this->OutletWaterTemp = OutletWaterTempOFF;
4515 :
4516 3487529 : if (OutletWaterTempOFF > TempSetPoint) {
4517 : // Setpoint was not met (or free conv. not used), turn on cooling tower fan
4518 3475955 : UAdesign = highSpeedTowerUA / this->NumCell;
4519 3475955 : AirFlowRate = this->HighSpeedAirFlowRate / this->NumCell;
4520 :
4521 : // The fan power is for all cells operating
4522 3475955 : Real64 const FanPowerOn = this->HighSpeedFanPower * this->NumCellOn / this->NumCell;
4523 :
4524 3475955 : this->OutletWaterTemp = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRate, UAdesign);
4525 :
4526 3475955 : if (this->OutletWaterTemp <= TempSetPoint) {
4527 760334 : if (this->CapacityControl == CapacityCtrl::FanCycling || this->OutletWaterTemp <= OWTLowerLimit) {
4528 : // Setpoint was met with pump ON and fan ON, calculate run-time fraction
4529 760230 : FanModeFrac = (TempSetPoint - OutletWaterTempOFF) / (this->OutletWaterTemp - OutletWaterTempOFF);
4530 760230 : this->FanPower = FanModeFrac * FanPowerOn;
4531 760230 : this->OutletWaterTemp = TempSetPoint;
4532 : } else {
4533 : // FluidBypass, fan runs at full speed for the entire time step
4534 104 : FanModeFrac = 1.0;
4535 104 : this->FanPower = FanPowerOn;
4536 104 : BypassFlag = 1;
4537 : }
4538 : } else {
4539 : // Setpoint was not met, cooling tower ran at full capacity
4540 2715621 : FanModeFrac = 1.0;
4541 2715621 : this->FanPower = FanPowerOn;
4542 : // if possible increase the number of cells and do the calculations again with the new water mass flow rate per cell
4543 2715621 : if (this->NumCellOn < this->NumCell && (this->WaterMassFlowRate / (this->NumCellOn + 1)) >= WaterMassFlowRatePerCellMin) {
4544 12984 : ++this->NumCellOn;
4545 12984 : WaterMassFlowRatePerCell = this->WaterMassFlowRate / this->NumCellOn;
4546 12984 : IncrNumCellFlag = true;
4547 : }
4548 : }
4549 11574 : } else if (OutletWaterTempOFF < TempSetPoint) {
4550 : // Need to bypass in free convection cooling mode if bypass is allowed
4551 11574 : if (this->CapacityControl == CapacityCtrl::FluidBypass) {
4552 58 : if (OutletWaterTempOFF > OWTLowerLimit) {
4553 58 : BypassFlag = 1;
4554 : }
4555 : }
4556 : }
4557 : }
4558 :
4559 : // Calculate bypass fraction since OWTLowerLimit < OutletWaterTemp < TempSetPoint.
4560 : // The iteration ends when the number of iterations exceeds the limit or the difference
4561 : // between the new and old bypass fractions is less than the threshold.
4562 3474545 : if (BypassFlag == 1) {
4563 : // Inlet water temperature lower than setpoint, assume 100% bypass, tower fan off
4564 162 : if (this->InletWaterTemp <= TempSetPoint) {
4565 0 : this->FanPower = 0.0;
4566 0 : this->BypassFraction = 1.0;
4567 0 : this->OutletWaterTemp = this->InletWaterTemp;
4568 : } else {
4569 162 : if (std::abs(this->InletWaterTemp - this->OutletWaterTemp) <= 0.01) {
4570 : // Outlet temp is close enough to inlet temp, assume 100% bypass, tower fan off
4571 0 : this->BypassFraction = 1.0;
4572 0 : this->FanPower = 0.0;
4573 : } else {
4574 162 : Real64 bypassFraction = (TempSetPoint - this->OutletWaterTemp) / (this->InletWaterTemp - this->OutletWaterTemp);
4575 162 : if (bypassFraction > 1.0 || bypassFraction < 0.0) {
4576 : // Bypass cannot meet setpoint, assume no bypass
4577 0 : this->BypassFraction = 0.0;
4578 : } else {
4579 162 : int NumIteration = 0;
4580 162 : Real64 BypassFractionPrev = bypassFraction;
4581 162 : Real64 OutletWaterTempPrev = this->OutletWaterTemp;
4582 484 : while (NumIteration < MaxIteration) {
4583 484 : ++NumIteration;
4584 : // need to iterate for the new OutletWaterTemp while bypassing tower water
4585 484 : this->OutletWaterTemp =
4586 484 : this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell * (1.0 - bypassFraction), AirFlowRate, UAdesign);
4587 : // Calc new BypassFraction based on the new OutletWaterTemp
4588 484 : if (std::abs(this->OutletWaterTemp - OWTLowerLimit) <= 0.01) {
4589 0 : BypassFraction2 = bypassFraction;
4590 0 : break;
4591 484 : } else if (this->OutletWaterTemp < OWTLowerLimit) {
4592 : // Set OutletWaterTemp = OWTLowerLimit, and use linear interpolation to calculate the bypassFraction
4593 40 : BypassFraction2 = BypassFractionPrev - (BypassFractionPrev - bypassFraction) * (OutletWaterTempPrev - OWTLowerLimit) /
4594 40 : (OutletWaterTempPrev - this->OutletWaterTemp);
4595 80 : this->OutletWaterTemp = this->calculateSimpleTowerOutletTemp(
4596 40 : state, WaterMassFlowRatePerCell * (1.0 - BypassFraction2), AirFlowRate, UAdesign);
4597 40 : if (this->OutletWaterTemp < OWTLowerLimit) {
4598 : // Use previous iteration values
4599 40 : BypassFraction2 = BypassFractionPrev;
4600 40 : this->OutletWaterTemp = OutletWaterTempPrev;
4601 : }
4602 40 : break;
4603 : } else {
4604 444 : BypassFraction2 = (TempSetPoint - this->OutletWaterTemp) / (this->InletWaterTemp - this->OutletWaterTemp);
4605 : }
4606 :
4607 : // Compare two BypassFraction to determine when to stop
4608 444 : if (std::abs(BypassFraction2 - bypassFraction) <= BypassFractionThreshold) {
4609 122 : break;
4610 : }
4611 322 : BypassFractionPrev = bypassFraction;
4612 322 : OutletWaterTempPrev = this->OutletWaterTemp;
4613 322 : bypassFraction = BypassFraction2;
4614 : }
4615 162 : if (NumIteration > MaxIteration) {
4616 0 : ShowWarningError(
4617 0 : state, format("Cooling tower fluid bypass iteration exceeds maximum limit of {} for {}", MaxItChar, this->Name));
4618 : }
4619 162 : this->BypassFraction = BypassFraction2;
4620 : // may not meet TempSetPoint due to limit of tower outlet temp to OWTLowerLimit
4621 162 : this->OutletWaterTemp = (1.0 - BypassFraction2) * this->OutletWaterTemp + BypassFraction2 * this->InletWaterTemp;
4622 : }
4623 : }
4624 : }
4625 : }
4626 :
4627 : // output the fraction of the time step the fan is ON
4628 3474545 : this->FanCyclingRatio = FanModeFrac;
4629 : // Should this be water inlet node num?????
4630 : Real64 const CpWater =
4631 3474545 : this->plantLoc.loop->glycol->getSpecificHeat(state, state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp, RoutineName);
4632 :
4633 3474545 : this->Qactual = this->WaterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - this->OutletWaterTemp);
4634 3474545 : this->airFlowRateRatio = (AirFlowRate * this->NumCell) / this->HighSpeedAirFlowRate;
4635 : }
4636 :
4637 298469 : void CoolingTower::calculateTwoSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag)
4638 : {
4639 :
4640 : // SUBROUTINE INFORMATION:
4641 : // AUTHOR Dan Fisher
4642 : // DATE WRITTEN Sept. 1998
4643 : // MODIFIED Feb. 2010, Chandan Sharma, FSEC, Added basin heater
4644 : // Jul. 2010, A Flament, added multi-cell capability for the 3 types of cooling tower
4645 : // Jun. 2016, R Zhang, Applied the condenser supply water temperature sensor fault model
4646 : // Jul. 2016, R Zhang, Applied the cooling tower fouling fault model
4647 :
4648 : // PURPOSE OF THIS SUBROUTINE:
4649 : // To simulate the operation of a cooling tower with a two-speed fan.
4650 :
4651 : // METHODOLOGY EMPLOYED:
4652 : // The cooling tower is modeled using effectiveness-NTU relationships for
4653 : // counterflow heat exchangers based on Merkel's theory.
4654 : // The subroutine calculates the period of time required to meet a
4655 : // leaving water temperature setpoint. It assumes that part-load
4656 : // operation represents a linear interpolation of three steady-state regimes
4657 : // (high-speed fan operation, low-speed fan operation and free convection regime).
4658 : // Cyclic losses are neglected. The period of time required to meet the
4659 : // leaving water temperature setpoint is used to determine the required
4660 : // fan power and energy. Free convection regime is also modeled. This
4661 : // occurs when the pump is operating and the fan is off. If free convection
4662 : // regime cooling is all that is required for a given time step, the leaving
4663 : // water temperature is allowed to fall below the leaving water temperature
4664 : // setpoint (free cooling). At times when the cooling tower fan is required,
4665 : // the leaving water temperature is at or above the setpoint.
4666 : // A RunFlag is passed by the upper level manager to indicate the ON/OFF status,
4667 : // or schedule, of the cooling tower. If the tower is OFF, outlet water
4668 : // temperature and flow rate are passed through the model from inlet node to
4669 : // outlet node without intervention (with the exception of free convection
4670 : // where water temperature is allowed to float below the outlet water set
4671 : // point). Reports are also updated with fan power and fan energy being zero.
4672 : // When the RunFlag indicates an ON condition for the cooling tower, the
4673 : // mass flow rate and water temperature are read from the inlet node of the
4674 : // cooling tower (water-side). The outdoor air wet-bulb temperature is used
4675 : // as the entering condition to the cooling tower (air-side). Input deck
4676 : // parameters are read for the free convection regime (pump ON and fan OFF)
4677 : // and a leaving water temperature is calculated. If the leaving water temperature
4678 : // is at or below the setpoint, the calculated leaving water temperature is
4679 : // placed on the outlet node and no fan power is used. If the calculated leaving
4680 : // water temperature is above the setpoint, the cooling tower fan is turned on
4681 : // and parameters for low fan speed are used to again calculate the leaving
4682 : // water temperature. If the calculated leaving water temperature is
4683 : // below the setpoint, a fan run-time fraction (FanModeFrac) is calculated and
4684 : // used to determine fan power. The leaving water temperature setpoint is placed
4685 : // on the outlet node. If the calculated leaving water temperature is at or above
4686 : // the setpoint, the cooling tower fan is turned on 'high speed' and the routine is
4687 : // repeated. If the calculated leaving water temperature is below the setpoint,
4688 : // a fan run-time fraction is calculated for the second stage fan and fan power
4689 : // is calculated as FanModeFrac*HighSpeedFanPower+(1-FanModeFrac)*LowSpeedFanPower.
4690 : // If the calculated leaving water temperature is above the leaving water temp.
4691 : // setpoint, the calculated leaving water temperature is placed on the outlet
4692 : // node and the fan runs at full power (High Speed Fan Power). Water mass flow
4693 : // rate is passed from inlet node to outlet node with no intervention.
4694 : // If a tower has multiple cells, the specified inputs of or the autosized capacity
4695 : // and air/water flow rates are for the entire tower. The number of cells to operate
4696 : // is first determined based on the user entered minimal and maximal water flow fractions
4697 : // per cell. If the loads are not met, more cells (if available) will operate to meet
4698 : // the loads. Each cell operates in same way - same fan speed etc.
4699 : // REFERENCES:
4700 : // ASHRAE HVAC1KIT: A Toolkit for Primary HVAC System Energy Calculation. 1999.
4701 :
4702 : // SUBROUTINE PARAMETER DEFINITIONS:
4703 : static constexpr std::string_view RoutineName("calculateTwoSpeedTower");
4704 :
4705 : // init
4706 298469 : this->Qactual = 0.0;
4707 298469 : this->FanPower = 0.0;
4708 298469 : this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
4709 :
4710 298469 : Real64 freeConvTowerUA = this->FreeConvTowerUA;
4711 298469 : Real64 highSpeedTowerUA = this->HighSpeedTowerUA;
4712 :
4713 : // water temperature setpoint
4714 298469 : Real64 TempSetPoint = 0.0;
4715 298469 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
4716 280831 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
4717 280831 : if (this->SetpointIsOnOutlet) {
4718 31852 : TempSetPoint = state.dataLoopNodes->Node(this->WaterOutletNodeNum).TempSetPoint;
4719 : } else {
4720 248979 : TempSetPoint = this->plantLoc.side->TempSetPoint;
4721 : }
4722 280831 : } break;
4723 17638 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand: {
4724 17638 : if (this->SetpointIsOnOutlet) {
4725 17638 : TempSetPoint = state.dataLoopNodes->Node(this->WaterOutletNodeNum).TempSetPointHi;
4726 : } else {
4727 0 : TempSetPoint = this->plantLoc.side->TempSetPointHi;
4728 : }
4729 17638 : } break;
4730 0 : default:
4731 0 : break;
4732 : }
4733 :
4734 : // If there is a fault of condenser SWT Sensor
4735 301381 : if (this->FaultyCondenserSWTFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
4736 2912 : (!state.dataGlobal->KickOffSimulation)) {
4737 2912 : int FaultIndex = this->FaultyCondenserSWTIndex;
4738 2912 : Real64 TowerOutletTemp_ff = TempSetPoint;
4739 :
4740 : // calculate the sensor offset using fault information
4741 2912 : this->FaultyCondenserSWTOffset = state.dataFaultsMgr->FaultsCondenserSWTSensor(FaultIndex).CalFaultOffsetAct(state);
4742 : // update the TempSetPoint
4743 2912 : TempSetPoint = TowerOutletTemp_ff - this->FaultyCondenserSWTOffset;
4744 : }
4745 :
4746 : // If there is a fault of cooling tower fouling
4747 298469 : if (this->FaultyTowerFoulingFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
4748 0 : (!state.dataGlobal->KickOffSimulation)) {
4749 0 : int FaultIndex = this->FaultyTowerFoulingIndex;
4750 0 : Real64 FreeConvTowerUA_ff = this->FreeConvTowerUA;
4751 0 : Real64 HighSpeedTowerUA_ff = this->HighSpeedTowerUA;
4752 :
4753 : // calculate the Faulty Tower Fouling Factor using fault information
4754 0 : this->FaultyTowerFoulingFactor = state.dataFaultsMgr->FaultsTowerFouling(FaultIndex).CalFaultyTowerFoulingFactor(state);
4755 :
4756 : // update the tower UA values at faulty cases
4757 0 : freeConvTowerUA = FreeConvTowerUA_ff * this->FaultyTowerFoulingFactor;
4758 0 : highSpeedTowerUA = HighSpeedTowerUA_ff * this->FaultyTowerFoulingFactor;
4759 : }
4760 :
4761 : // Do not RETURN here if flow rate is less than SmallMassFlow. Check basin heater and then RETURN.
4762 298469 : if (this->plantLoc.side->FlowLock == DataPlant::FlowLock::Unlocked) {
4763 261298 : return; // TODO: WTF
4764 : }
4765 149198 : bool returnFlagSet = false;
4766 149198 : this->checkMassFlowAndLoad(state, MyLoad, RunFlag, returnFlagSet);
4767 149198 : if (returnFlagSet) {
4768 112027 : return;
4769 : }
4770 :
4771 : // Added for multi-cell. Determine the number of cells operating
4772 37171 : Real64 WaterMassFlowRatePerCellMin = 0.0;
4773 : Real64 WaterMassFlowRatePerCellMax;
4774 37171 : int NumCellMin(0);
4775 37171 : int NumCellMax(0);
4776 : Real64 WaterMassFlowRatePerCell;
4777 37171 : if (this->DesWaterMassFlowRate > 0.0) {
4778 37171 : WaterMassFlowRatePerCellMin = this->DesWaterMassFlowRate * this->MinFracFlowRate / this->NumCell;
4779 37171 : WaterMassFlowRatePerCellMax = this->DesWaterMassFlowRate * this->MaxFracFlowRate / this->NumCell;
4780 :
4781 : // round it up to the nearest integer
4782 37171 : NumCellMin = min(int((this->WaterMassFlowRate / WaterMassFlowRatePerCellMax) + 0.9999), this->NumCell);
4783 37171 : NumCellMax = min(int((this->WaterMassFlowRate / WaterMassFlowRatePerCellMin) + 0.9999), this->NumCell);
4784 : }
4785 :
4786 : // cap min at 1
4787 37171 : if (NumCellMin <= 0) {
4788 0 : NumCellMin = 1;
4789 : }
4790 37171 : if (NumCellMax <= 0) {
4791 0 : NumCellMax = 1;
4792 : }
4793 :
4794 37171 : if (this->cellCtrl == CellCtrl::MinCell) {
4795 3428 : this->NumCellOn = NumCellMin;
4796 : } else {
4797 33743 : this->NumCellOn = NumCellMax;
4798 : }
4799 :
4800 37171 : WaterMassFlowRatePerCell = this->WaterMassFlowRate / this->NumCellOn;
4801 :
4802 37171 : bool IncrNumCellFlag = true;
4803 :
4804 37171 : Real64 AirFlowRate = 0.0;
4805 37171 : Real64 FanModeFrac = 0.0;
4806 37171 : int SpeedSel = 0;
4807 118005 : while (IncrNumCellFlag) {
4808 43663 : IncrNumCellFlag = false;
4809 :
4810 : // set local variable for tower
4811 43663 : Real64 UAdesign = freeConvTowerUA / this->NumCell; // where is NumCellOn?
4812 43663 : AirFlowRate = this->FreeConvAirFlowRate / this->NumCell;
4813 43663 : this->WaterMassFlowRate = state.dataLoopNodes->Node(this->WaterInletNodeNum).MassFlowRate;
4814 43663 : Real64 OutletWaterTemp1stStage = this->OutletWaterTemp;
4815 43663 : Real64 OutletWaterTemp2ndStage = this->OutletWaterTemp;
4816 43663 : FanModeFrac = 0.0;
4817 :
4818 43663 : Real64 OutletWaterTempOFF = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRate, UAdesign);
4819 :
4820 : // Setpoint was met using free convection regime (pump ON and fan OFF)
4821 43663 : this->FanPower = 0.0;
4822 43663 : this->OutletWaterTemp = OutletWaterTempOFF;
4823 :
4824 43663 : if (OutletWaterTempOFF > TempSetPoint) {
4825 : // Setpoint was not met (or free conv. not used),turn on cooling tower 1st stage fan
4826 43276 : UAdesign = this->LowSpeedTowerUA / this->NumCell;
4827 43276 : AirFlowRate = this->LowSpeedAirFlowRate / this->NumCell;
4828 43276 : Real64 const FanPowerLow = this->LowSpeedFanPower * this->NumCellOn / this->NumCell;
4829 :
4830 43276 : OutletWaterTemp1stStage = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRate, UAdesign);
4831 :
4832 43276 : if (OutletWaterTemp1stStage <= TempSetPoint) {
4833 : // Setpoint was met with pump ON and fan ON 1st stage, calculate fan mode fraction
4834 5307 : FanModeFrac = (TempSetPoint - OutletWaterTempOFF) / (OutletWaterTemp1stStage - OutletWaterTempOFF);
4835 5307 : this->FanPower = FanModeFrac * FanPowerLow;
4836 5307 : this->OutletWaterTemp = TempSetPoint;
4837 5307 : this->Qactual *= FanModeFrac;
4838 5307 : SpeedSel = 1;
4839 : } else {
4840 : // Setpoint was not met, turn on cooling tower 2nd stage fan
4841 37969 : UAdesign = highSpeedTowerUA / this->NumCell;
4842 37969 : AirFlowRate = this->HighSpeedAirFlowRate / this->NumCell;
4843 37969 : Real64 const FanPowerHigh = this->HighSpeedFanPower * this->NumCellOn / this->NumCell;
4844 :
4845 37969 : OutletWaterTemp2ndStage = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRate, UAdesign);
4846 :
4847 37969 : if ((OutletWaterTemp2ndStage <= TempSetPoint) && UAdesign > 0.0) {
4848 : // Setpoint was met with pump ON and fan ON 2nd stage, calculate fan mode fraction
4849 9034 : FanModeFrac = (TempSetPoint - OutletWaterTemp1stStage) / (OutletWaterTemp2ndStage - OutletWaterTemp1stStage);
4850 9034 : this->FanPower = (FanModeFrac * FanPowerHigh) + (1.0 - FanModeFrac) * FanPowerLow;
4851 9034 : this->OutletWaterTemp = TempSetPoint;
4852 9034 : SpeedSel = 2;
4853 : } else {
4854 : // Setpoint was not met, cooling tower ran at full capacity
4855 28935 : this->OutletWaterTemp = OutletWaterTemp2ndStage;
4856 28935 : this->FanPower = FanPowerHigh;
4857 28935 : SpeedSel = 2;
4858 28935 : FanModeFrac = 1.0;
4859 : // if possible increase the number of cells and do the calculations again with the new water mass flow rate per cell
4860 28935 : if (this->NumCellOn < this->NumCell && (this->WaterMassFlowRate / (this->NumCellOn + 1)) >= WaterMassFlowRatePerCellMin) {
4861 6492 : ++this->NumCellOn;
4862 6492 : WaterMassFlowRatePerCell = this->WaterMassFlowRate / this->NumCellOn;
4863 6492 : IncrNumCellFlag = true;
4864 : }
4865 : }
4866 : }
4867 : }
4868 : }
4869 :
4870 : // output the fraction of the time step the fan is ON
4871 37171 : this->FanCyclingRatio = FanModeFrac;
4872 37171 : this->SpeedSelected = SpeedSel;
4873 :
4874 : Real64 const CpWater =
4875 37171 : this->plantLoc.loop->glycol->getSpecificHeat(state, state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp, RoutineName);
4876 37171 : this->Qactual = this->WaterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - this->OutletWaterTemp);
4877 37171 : this->airFlowRateRatio = (AirFlowRate * this->NumCell) / this->HighSpeedAirFlowRate;
4878 : }
4879 :
4880 716836 : void CoolingTower::calculateVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag)
4881 : {
4882 :
4883 : // SUBROUTINE INFORMATION:
4884 : // AUTHOR Richard Raustad
4885 : // DATE WRITTEN Feb 2005
4886 : // MODIFIED Jul. 2010, A Flament, added multi-cell capability for the 3 types of cooling tower
4887 : // Jul. 2010, B Griffith, general fluid props
4888 : // Jun. 2016, R Zhang, Applied the condenser supply water temperature sensor fault model
4889 : // Jul. 2016, R Zhang, Applied the cooling tower fouling fault model
4890 :
4891 : // PURPOSE OF THIS SUBROUTINE:
4892 : // To simulate the operation of a variable-speed fan cooling tower.
4893 :
4894 : // METHODOLOGY EMPLOYED:
4895 : // For each simulation time step, a desired range temperature (Twater,inlet-Twater,setpoint) and desired approach
4896 : // temperature (Twater,setpoint-Tair,WB) is calculated which meets the outlet water temperature setpoint. This
4897 : // desired range and approach temperature also provides a balance point for the empirical model where:
4898 : // Tair,WB + Twater,range + Tapproach = Node(WaterInletNode)%Temp
4899 : // Calculation of water outlet temperature uses one of the following equations:
4900 : // Twater,outlet = Tair,WB + Tapproach (1) or
4901 : // Twater,outlet = Twater,inlet - Twater,range (2)
4902 : // If a solution (or balance) is found, these 2 calculation methods are equal. Equation 2 is used to calculate
4903 : // the outlet water temperature in the free convection regime and at the minimum or maximum fan speed so that
4904 : // if a solution is not reached, the outlet water temperature is approximately equal to the inlet water temperature
4905 : // and the tower fan must be varied to meet the setpoint. Equation 1 is used when the fan speed is varied between
4906 : // the minimum and maximum fan speed to meet the outlet water temperature setpoint.
4907 : // The outlet water temperature in the free convection regime is first calculated to see if the setpoint is met.
4908 : // If the setpoint is met, the fan is OFF and the outlet water temperature is allowed to float below the set
4909 : // point temperature. If the setpoint is not met, the outlet water temperature is re-calculated at the minimum
4910 : // fan speed. If the setpoint is met, the fan is cycled to exactly meet the outlet water temperature setpoint.
4911 : // If the setpoint is not met at the minimum fan speed, the outlet water temperature is re-calculated at the
4912 : // maximum fan speed. If the setpoint at the maximum fan speed is not met, the fan runs at maximum speed the
4913 : // entire time step. If the setpoint is met at the maximum fan speed, the fan speed is varied to meet the setpoint.
4914 : // If a tower has multiple cells, the specified inputs of or the autosized capacity
4915 : // and air/water flow rates are for the entire tower. The number of cells to operate
4916 : // is first determined based on the user entered minimal and maximal water flow fractions
4917 : // per cell. If the loads are not met, more cells (if available) will operate to meet
4918 : // the loads. Inside each cell, the fan speed varies in the same way.
4919 : // REFERENCES:
4920 : // Benton, D.J., Bowmand, C.F., Hydeman, M., Miller, P.,
4921 : // "An Improved Cooling Tower Algorithm for the CoolToolsTM Simulation Model".
4922 : // ASHRAE Transactions 2002, V. 108, Pt. 1.
4923 : // York International Corporation, "YORKcalcTM Software, Chiller-Plant Energy-Estimating Program",
4924 : // Form 160.00-SG2 (0502). 2002.
4925 :
4926 : // SUBROUTINE PARAMETER DEFINITIONS:
4927 :
4928 716836 : int constexpr MaxIte(500); // Maximum number of iterations
4929 716836 : Real64 constexpr Acc(0.0001); // Accuracy of result
4930 : static constexpr std::string_view RoutineName("calculateVariableSpeedTower");
4931 :
4932 : // Added for multi-cell. Determine the number of cells operating
4933 716836 : Real64 WaterMassFlowRatePerCellMin = 0.0;
4934 : Real64 WaterMassFlowRatePerCellMax;
4935 716836 : int NumCellMin(0);
4936 716836 : int NumCellMax(0);
4937 : Real64 WaterMassFlowRatePerCell;
4938 716836 : if (this->DesWaterMassFlowRate > 0.0) {
4939 716331 : WaterMassFlowRatePerCellMin = this->DesWaterMassFlowRate * this->MinFracFlowRate / this->NumCell;
4940 716331 : WaterMassFlowRatePerCellMax = this->DesWaterMassFlowRate * this->MaxFracFlowRate / this->NumCell;
4941 :
4942 : // round it up to the nearest integer
4943 716331 : NumCellMin = min(int((this->WaterMassFlowRate / WaterMassFlowRatePerCellMax) + 0.9999), this->NumCell);
4944 716331 : NumCellMax = min(int((this->WaterMassFlowRate / WaterMassFlowRatePerCellMin) + 0.9999), this->NumCell);
4945 : }
4946 :
4947 : // cap min at 1
4948 716836 : if (NumCellMin <= 0) {
4949 453190 : NumCellMin = 1;
4950 : }
4951 716836 : if (NumCellMax <= 0) {
4952 453190 : NumCellMax = 1;
4953 : }
4954 :
4955 716836 : if (this->cellCtrl == CellCtrl::MinCell) {
4956 23390 : this->NumCellOn = NumCellMin;
4957 : } else {
4958 693446 : this->NumCellOn = NumCellMax;
4959 : }
4960 :
4961 716836 : WaterMassFlowRatePerCell = this->WaterMassFlowRate / this->NumCellOn;
4962 :
4963 : // Initialize subroutine variables
4964 716836 : this->Qactual = 0.0;
4965 716836 : this->FanPower = 0.0;
4966 716836 : this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
4967 :
4968 716836 : this->WaterUsage = 0.0;
4969 716836 : Real64 Twb = this->AirWetBulb;
4970 716836 : Real64 TwbCapped = this->AirWetBulb;
4971 :
4972 : // water temperature setpoint
4973 716836 : Real64 TempSetPoint(0.0); // Outlet water temperature setpoint (C)
4974 716836 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
4975 716836 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
4976 716836 : TempSetPoint = this->plantLoc.side->TempSetPoint;
4977 716836 : } break;
4978 0 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand: {
4979 0 : TempSetPoint = this->plantLoc.side->TempSetPointHi;
4980 0 : } break;
4981 0 : default: {
4982 0 : assert(false);
4983 : } break;
4984 : }
4985 :
4986 : // If there is a fault of condenser SWT Sensor
4987 716836 : if (this->FaultyCondenserSWTFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
4988 0 : (!state.dataGlobal->KickOffSimulation)) {
4989 0 : int FaultIndex = this->FaultyCondenserSWTIndex;
4990 0 : Real64 TowerOutletTemp_ff = TempSetPoint;
4991 :
4992 : // calculate the sensor offset using fault information
4993 0 : this->FaultyCondenserSWTOffset = state.dataFaultsMgr->FaultsCondenserSWTSensor(FaultIndex).CalFaultOffsetAct(state);
4994 : // update the TempSetPoint
4995 0 : TempSetPoint = TowerOutletTemp_ff - this->FaultyCondenserSWTOffset;
4996 : }
4997 :
4998 716836 : Real64 Tr = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - TempSetPoint;
4999 716836 : Real64 Ta = TempSetPoint - this->AirWetBulb;
5000 :
5001 : // Do not RETURN here if flow rate is less than MassFlowTolerance. Check basin heater and then RETURN.
5002 716836 : if (this->plantLoc.side->FlowLock == DataPlant::FlowLock::Unlocked) {
5003 588131 : return; // TODO: WTF
5004 : }
5005 :
5006 358340 : bool returnFlagSet = false;
5007 358340 : this->checkMassFlowAndLoad(state, MyLoad, RunFlag, returnFlagSet);
5008 358340 : if (returnFlagSet) {
5009 229635 : return;
5010 : }
5011 :
5012 : // loop to increment NumCell if we cannot meet the setpoint with the actual number of cells calculated above
5013 128705 : bool IncrNumCellFlag = true;
5014 : Real64 OutletWaterTempOFF; // Outlet water temperature with fan OFF (C)
5015 128705 : Real64 OutletWaterTempON = 0.0; // Outlet water temperature with fan ON at maximum fan speed (C)
5016 128705 : Real64 FreeConvectionCapFrac = 0.0; // fraction of tower capacity in free convection
5017 128705 : Real64 WaterFlowRateRatioCapped = 0.0; // Water flow rate ratio passed to VS tower model
5018 : Real64 TrCapped; // range temp passed to VS tower model
5019 : Real64 TaCapped; // approach temp passed to VS tower model
5020 263806 : while (IncrNumCellFlag) {
5021 135101 : IncrNumCellFlag = false;
5022 : // Initialize inlet node water properties
5023 : Real64 const WaterDensity =
5024 135101 : this->plantLoc.loop->glycol->getDensity(state, state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp, RoutineName);
5025 135101 : Real64 const WaterFlowRateRatio = WaterMassFlowRatePerCell / (WaterDensity * this->CalibratedWaterFlowRate / this->NumCell);
5026 :
5027 : // check independent inputs with respect to model boundaries
5028 135101 : this->checkModelBounds(state, Twb, Tr, Ta, WaterFlowRateRatio, TwbCapped, TrCapped, TaCapped, WaterFlowRateRatioCapped);
5029 :
5030 : // determine the free convection capacity by finding the outlet temperature at full air flow and multiplying
5031 : // the tower's full capacity temperature difference by the percentage of tower capacity in free convection
5032 : // regime specified by the user
5033 :
5034 135101 : this->airFlowRateRatio = 1.0;
5035 135101 : OutletWaterTempOFF = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
5036 135101 : this->OutletWaterTemp = OutletWaterTempOFF;
5037 135101 : FreeConvectionCapFrac = this->FreeConvectionCapacityFraction;
5038 135101 : OutletWaterTempON = this->calculateVariableTowerOutletTemp(state, WaterFlowRateRatioCapped, this->airFlowRateRatio, TwbCapped);
5039 :
5040 135101 : if (OutletWaterTempON > TempSetPoint) {
5041 66873 : this->FanCyclingRatio = 1.0;
5042 66873 : this->airFlowRateRatio = 1.0;
5043 66873 : this->FanPower = this->HighSpeedFanPower * this->NumCellOn / this->NumCell;
5044 66873 : this->OutletWaterTemp = OutletWaterTempON;
5045 : // if possible increase the number of cells and do the calculations again with the new water mass flow rate per cell
5046 66873 : if (this->NumCellOn < this->NumCell && (this->WaterMassFlowRate / (this->NumCellOn + 1)) > WaterMassFlowRatePerCellMin) {
5047 6396 : ++this->NumCellOn;
5048 6396 : WaterMassFlowRatePerCell = this->WaterMassFlowRate / this->NumCellOn;
5049 6396 : IncrNumCellFlag = true;
5050 : }
5051 : }
5052 : }
5053 :
5054 : // find the correct air ratio only if full flow is too much
5055 128705 : if (OutletWaterTempON < TempSetPoint) {
5056 : // outlet water temperature is calculated in the free convection regime
5057 68228 : OutletWaterTempOFF = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp -
5058 68228 : FreeConvectionCapFrac * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - OutletWaterTempON);
5059 : // fan is OFF
5060 68228 : this->FanCyclingRatio = 0.0;
5061 : // air flow ratio is assumed to be the fraction of tower capacity in the free convection regime (fan is OFF but air is flowing)
5062 68228 : this->airFlowRateRatio = FreeConvectionCapFrac;
5063 :
5064 : // Assume setpoint was met using free convection regime (pump ON and fan OFF)
5065 68228 : this->FanPower = 0.0;
5066 68228 : this->OutletWaterTemp = OutletWaterTempOFF;
5067 :
5068 68228 : if (OutletWaterTempOFF > TempSetPoint) {
5069 : // Setpoint was not met, turn on cooling tower fan at minimum fan speed
5070 :
5071 65864 : this->airFlowRateRatio = this->MinimumVSAirFlowFrac;
5072 : Real64 OutletWaterTempMIN; // Outlet water temperature with fan at minimum speed (C)
5073 65864 : OutletWaterTempMIN = this->calculateVariableTowerOutletTemp(state, WaterFlowRateRatioCapped, this->airFlowRateRatio, TwbCapped);
5074 :
5075 65864 : if (OutletWaterTempMIN < TempSetPoint) {
5076 : // if setpoint was exceeded, cycle the fan at minimum air flow to meet the setpoint temperature
5077 29872 : if (this->FanPowerfAirFlowCurve == 0) {
5078 0 : this->FanPower = pow_3(this->airFlowRateRatio) * this->HighSpeedFanPower * this->NumCellOn / this->NumCell;
5079 : } else {
5080 29872 : Real64 const FanCurveValue = Curve::CurveValue(state, this->FanPowerfAirFlowCurve, this->airFlowRateRatio);
5081 29872 : this->FanPower = max(0.0, (this->HighSpeedFanPower * FanCurveValue)) * this->NumCellOn / this->NumCell;
5082 : }
5083 : // fan is cycling ON and OFF at the minimum fan speed. Adjust fan power and air flow rate ratio according to cycling rate
5084 29872 : this->FanCyclingRatio = ((OutletWaterTempOFF - TempSetPoint) / (OutletWaterTempOFF - OutletWaterTempMIN));
5085 29872 : this->FanPower *= this->FanCyclingRatio;
5086 29872 : this->OutletWaterTemp = TempSetPoint;
5087 29872 : this->airFlowRateRatio =
5088 29872 : (this->FanCyclingRatio * this->MinimumVSAirFlowFrac) + ((1 - this->FanCyclingRatio) * FreeConvectionCapFrac);
5089 : } else {
5090 : // if setpoint was not met at minimum fan speed, set fan speed to maximum
5091 35992 : this->airFlowRateRatio = 1.0;
5092 : // fan will not cycle and runs the entire time step
5093 35992 : this->FanCyclingRatio = 1.0;
5094 :
5095 35992 : this->OutletWaterTemp =
5096 35992 : this->calculateVariableTowerOutletTemp(state, WaterFlowRateRatioCapped, this->airFlowRateRatio, TwbCapped);
5097 :
5098 : // Setpoint was met with pump ON and fan ON at full flow
5099 : // Calculate the fraction of full air flow to exactly meet the setpoint temperature
5100 354365 : auto f = [&state, this, WaterFlowRateRatioCapped, TwbCapped, Tr, Ta](Real64 FlowRatio) {
5101 354365 : Real64 TapproachActual = this->calculateVariableSpeedApproach(state, WaterFlowRateRatioCapped, FlowRatio, TwbCapped, Tr);
5102 354365 : return Ta - TapproachActual;
5103 35992 : };
5104 35992 : int SolFla = 0;
5105 35992 : General::SolveRoot(state, Acc, MaxIte, SolFla, this->airFlowRateRatio, f, this->MinimumVSAirFlowFrac, 1.0);
5106 35992 : if (SolFla == -1) {
5107 0 : if (!state.dataGlobal->WarmupFlag) {
5108 0 : ShowWarningError(
5109 : state,
5110 0 : format("Cooling tower iteration limit exceeded when calculating air flow rate ratio for tower {}", this->Name));
5111 : }
5112 : // IF RegulaFalsi cannot find a solution then provide detailed output for debugging
5113 35992 : } else if (SolFla == -2) {
5114 0 : if (!state.dataGlobal->WarmupFlag) {
5115 :
5116 0 : if (this->CoolingTowerAFRRFailedCount < 1) {
5117 0 : ++this->CoolingTowerAFRRFailedCount;
5118 0 : ShowWarningError(
5119 : state,
5120 0 : format("CoolingTower:VariableSpeed \"{}\" - Cooling tower air flow rate ratio calculation failed ", this->Name));
5121 0 : ShowContinueError(state,
5122 0 : format("...with conditions as Twb = {:5.2F}, Trange = {:5.2F}, Tapproach = {:5.2F}, and water flow "
5123 : "rate ratio = {:5.2F}",
5124 : TwbCapped,
5125 : Tr,
5126 : Ta,
5127 : WaterFlowRateRatioCapped));
5128 0 : ShowContinueError(state, "...a solution could not be found within the valid range of air flow rate ratios");
5129 0 : ShowContinueErrorTimeStamp(
5130 0 : state, format(" ...Valid air flow rate ratio range = {:5.2F} to 1.0.", this->MinimumVSAirFlowFrac));
5131 0 : ShowContinueError(state, "...Consider modifying the design approach or design range temperature for this tower.");
5132 : } else {
5133 0 : ShowRecurringWarningErrorAtEnd(state,
5134 0 : "CoolingTower:VariableSpeed \"" + this->Name +
5135 : "\" - Cooling tower air flow rate ratio calculation failed error continues.",
5136 0 : this->CoolingTowerAFRRFailedIndex);
5137 : }
5138 : }
5139 : }
5140 :
5141 : // Use theoretical cubic for determination of fan power if user has not specified a fan power ratio curve
5142 35992 : if (this->FanPowerfAirFlowCurve == 0) {
5143 0 : this->FanPower = pow_3(this->airFlowRateRatio) * this->HighSpeedFanPower * this->NumCellOn / this->NumCell;
5144 : } else {
5145 35992 : Real64 const FanCurveValue = Curve::CurveValue(state, this->FanPowerfAirFlowCurve, this->airFlowRateRatio);
5146 35992 : this->FanPower = max(0.0, (this->HighSpeedFanPower * FanCurveValue)) * this->NumCellOn / this->NumCell;
5147 : }
5148 : // outlet water temperature is calculated as the inlet air wet-bulb temperature plus tower approach temperature
5149 35992 : this->OutletWaterTemp = Twb + Ta;
5150 : }
5151 : }
5152 : }
5153 :
5154 : Real64 const CpWater =
5155 128705 : this->plantLoc.loop->glycol->getSpecificHeat(state, state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp, RoutineName);
5156 128705 : this->Qactual = this->WaterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - this->OutletWaterTemp);
5157 :
5158 : // calculate end time of current time step
5159 128705 : Real64 const CurrentEndTime = state.dataGlobal->CurrentTime + state.dataHVACGlobal->SysTimeElapsed;
5160 :
5161 : // Print warning messages only when valid and only for the first occurrence. Let summary provide statistics.
5162 : // Wait for next time step to print warnings. If simulation iterates, print out
5163 : // the warning for the last iteration only. Must wait for next time step to accomplish this.
5164 : // If a warning occurs and the simulation down shifts, the warning is not valid.
5165 128705 : if (CurrentEndTime > this->CurrentEndTimeLast && state.dataHVACGlobal->TimeStepSys >= this->TimeStepSysLast) {
5166 0 : if (this->PrintLGMessage) {
5167 0 : ++this->VSErrorCountFlowFrac;
5168 : // Show single warning and pass additional info to ShowRecurringWarningErrorAtEnd
5169 0 : if (this->VSErrorCountFlowFrac < 2) {
5170 0 : ShowWarningError(state, this->LGBuffer1);
5171 0 : ShowContinueError(state, this->LGBuffer2);
5172 : } else {
5173 0 : ShowRecurringWarningErrorAtEnd(state,
5174 0 : format("{} \"{}\" - Liquid to gas ratio is out of range error continues...",
5175 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5176 0 : this->Name),
5177 0 : this->ErrIndexLG,
5178 0 : this->LGLast,
5179 0 : this->LGLast);
5180 : }
5181 : }
5182 : }
5183 :
5184 : // save last system time step and last end time of current time step (used to determine if warning is valid)
5185 128705 : this->TimeStepSysLast = state.dataHVACGlobal->TimeStepSys;
5186 128705 : this->CurrentEndTimeLast = CurrentEndTime;
5187 :
5188 : // warn user on first occurrence if flow fraction is greater than maximum for the YorkCalc model, use recurring warning stats
5189 128705 : if (this->TowerModelType == ModelType::YorkCalcModel || this->TowerModelType == ModelType::YorkCalcUserDefined) {
5190 45324 : this->PrintLGMessage = false;
5191 : // Do not report error message in free convection regime
5192 45324 : if (this->airFlowRateRatio > this->MinimumVSAirFlowFrac) {
5193 28838 : Real64 const FlowFraction = WaterFlowRateRatioCapped / this->airFlowRateRatio;
5194 : // Flow fractions greater than a MaxLiquidToGasRatio of 8 are not reliable using the YorkCalc model
5195 28838 : if (FlowFraction > this->MaxLiquidToGasRatio) {
5196 : // Report warnings only during actual simulation
5197 0 : if (!state.dataGlobal->WarmupFlag) {
5198 0 : this->PrintLGMessage = true;
5199 0 : this->LGBuffer1 = format("{} \"{}\" - Liquid to gas ratio (L/G) is out of range at {:5.2F}.",
5200 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5201 0 : this->Name,
5202 0 : FlowFraction);
5203 0 : this->LGBuffer2 = format(" ...Valid maximum ratio = {:5.2F}. Occurrence info = {}, {} {}",
5204 0 : this->MaxLiquidToGasRatio,
5205 0 : state.dataEnvrn->EnvironmentName,
5206 0 : state.dataEnvrn->CurMnDy,
5207 0 : General::CreateSysTimeIntervalString(state));
5208 :
5209 0 : this->LGLast = FlowFraction;
5210 : }
5211 : }
5212 : }
5213 : }
5214 : }
5215 :
5216 80820 : void CoolingTower::calculateMerkelVariableSpeedTower(EnergyPlusData &state, Real64 &MyLoad, bool RunFlag)
5217 : {
5218 :
5219 : // SUBROUTINE INFORMATION:
5220 : // AUTHOR B.Griffith
5221 : // DATE WRITTEN August 2013
5222 : // MODIFIED Jun. 2016, R Zhang, Applied the condenser supply water temperature sensor fault model
5223 : // Jul. 2016, R Zhang, Applied the cooling tower fouling fault model
5224 :
5225 : // PURPOSE OF THIS SUBROUTINE:
5226 : // Calculate variable speed tower model using Merkel's theory with UA adjustments developed by Scheier
5227 :
5228 : // METHODOLOGY EMPLOYED:
5229 : // Find a fan speed that operates the tower to meet MyLoad
5230 :
5231 : // SUBROUTINE PARAMETER DEFINITIONS:
5232 80820 : Real64 constexpr DesignWetBulb(25.56); // tower outdoor air entering wetbulb for design [C]
5233 80820 : int constexpr MaxIte(500); // Maximum number of iterations for solver
5234 80820 : Real64 constexpr Acc(1.e-3); // Accuracy of solver result
5235 : static constexpr std::string_view RoutineName("calculateMerkelVariableSpeedTower");
5236 :
5237 : Real64 const CpWater =
5238 80820 : this->plantLoc.loop->glycol->getSpecificHeat(state, state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp, RoutineName);
5239 80820 : this->Qactual = 0.0;
5240 80820 : this->FanPower = 0.0;
5241 80820 : this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
5242 :
5243 80820 : Real64 freeConvTowerUA = this->FreeConvTowerUA;
5244 80820 : Real64 highSpeedTowerUA = this->HighSpeedTowerUA;
5245 :
5246 : // If there is a fault of condenser SWT Sensor
5247 80820 : if (this->FaultyCondenserSWTFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
5248 0 : (!state.dataGlobal->KickOffSimulation)) {
5249 0 : int FaultIndex = this->FaultyCondenserSWTIndex;
5250 : // calculate the sensor offset using fault information
5251 0 : this->FaultyCondenserSWTOffset = state.dataFaultsMgr->FaultsCondenserSWTSensor(FaultIndex).CalFaultOffsetAct(state);
5252 : }
5253 :
5254 : // If there is a fault of cooling tower fouling
5255 80820 : if (this->FaultyTowerFoulingFlag && (!state.dataGlobal->WarmupFlag) && (!state.dataGlobal->DoingSizing) &&
5256 0 : (!state.dataGlobal->KickOffSimulation)) {
5257 0 : int FaultIndex = this->FaultyTowerFoulingIndex;
5258 0 : Real64 FreeConvTowerUA_ff = this->FreeConvTowerUA;
5259 0 : Real64 HighSpeedTowerUA_ff = this->HighSpeedTowerUA;
5260 :
5261 : // calculate the Faulty Tower Fouling Factor using fault information
5262 0 : this->FaultyTowerFoulingFactor = state.dataFaultsMgr->FaultsTowerFouling(FaultIndex).CalFaultyTowerFoulingFactor(state);
5263 :
5264 : // update the tower UA values at faulty cases
5265 0 : freeConvTowerUA = FreeConvTowerUA_ff * this->FaultyTowerFoulingFactor;
5266 0 : highSpeedTowerUA = HighSpeedTowerUA_ff * this->FaultyTowerFoulingFactor;
5267 : }
5268 :
5269 80820 : Real64 WaterMassFlowRatePerCellMin = 0.0;
5270 : Real64 WaterMassFlowRatePerCellMax;
5271 :
5272 : // Added for multi-cell. Determine the number of cells operating
5273 80820 : int NumCellMin = 0;
5274 80820 : int NumCellMax = 0;
5275 : Real64 WaterMassFlowRatePerCell;
5276 : Real64 UAdesignPerCell;
5277 : Real64 AirFlowRatePerCell;
5278 80820 : if (this->DesWaterMassFlowRate > 0.0) {
5279 80760 : WaterMassFlowRatePerCellMin = this->DesWaterMassFlowRate * this->MinFracFlowRate / this->NumCell;
5280 80760 : WaterMassFlowRatePerCellMax = this->DesWaterMassFlowRate * this->MaxFracFlowRate / this->NumCell;
5281 :
5282 : // round it up to the nearest integer
5283 80760 : NumCellMin = min(int((this->WaterMassFlowRate / WaterMassFlowRatePerCellMax) + 0.9999), this->NumCell);
5284 80760 : NumCellMax = min(int((this->WaterMassFlowRate / WaterMassFlowRatePerCellMin) + 0.9999), this->NumCell);
5285 : }
5286 :
5287 : // cap min at 1
5288 80820 : if (NumCellMin <= 0) {
5289 50654 : NumCellMin = 1;
5290 : }
5291 80820 : if (NumCellMax <= 0) {
5292 50654 : NumCellMax = 1;
5293 : }
5294 :
5295 80820 : if (this->cellCtrl == CellCtrl::MinCell) {
5296 0 : this->NumCellOn = NumCellMin;
5297 : } else {
5298 80820 : this->NumCellOn = NumCellMax;
5299 : }
5300 :
5301 80820 : WaterMassFlowRatePerCell = this->WaterMassFlowRate / this->NumCellOn;
5302 :
5303 80820 : if ((std::abs(MyLoad) <= HVAC::SmallLoad) || !RunFlag) {
5304 : // tower doesn't need to do anything
5305 50654 : this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
5306 50654 : this->FanPower = 0.0;
5307 50654 : this->airFlowRateRatio = 0.0;
5308 50654 : this->Qactual = 0.0;
5309 50654 : CalcBasinHeaterPower(
5310 50654 : state, this->BasinHeaterPowerFTempDiff, this->basinHeaterSched, this->BasinHeaterSetPointTemp, this->BasinHeaterPower);
5311 50654 : return;
5312 30166 : } else if (this->WaterMassFlowRate <= DataBranchAirLoopPlant::MassFlowTolerance || (MyLoad > HVAC::SmallLoad)) {
5313 : // for multiple cells, we assume that it's a common basin
5314 0 : CalcBasinHeaterPower(
5315 0 : state, this->BasinHeaterPowerFTempDiff, this->basinHeaterSched, this->BasinHeaterSetPointTemp, this->BasinHeaterPower);
5316 0 : return;
5317 : }
5318 :
5319 : // first find free convection cooling rate
5320 30166 : UAdesignPerCell = freeConvTowerUA / this->NumCell;
5321 30166 : AirFlowRatePerCell = this->FreeConvAirFlowRate / this->NumCell;
5322 30166 : this->WaterMassFlowRate = state.dataLoopNodes->Node(this->WaterInletNodeNum).MassFlowRate;
5323 30166 : Real64 OutletWaterTempOFF = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRatePerCell, UAdesignPerCell);
5324 :
5325 30166 : Real64 FreeConvQdot = this->WaterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - OutletWaterTempOFF);
5326 30166 : this->FanPower = 0.0;
5327 :
5328 30166 : if (std::abs(MyLoad) <= FreeConvQdot) { // can meet load with free convection and fan off
5329 :
5330 78 : this->OutletWaterTemp = OutletWaterTempOFF;
5331 78 : this->airFlowRateRatio = 0.0;
5332 78 : this->Qactual = FreeConvQdot;
5333 78 : CalcBasinHeaterPower(
5334 78 : state, this->BasinHeaterPowerFTempDiff, this->basinHeaterSched, this->BasinHeaterSetPointTemp, this->BasinHeaterPower);
5335 :
5336 78 : return;
5337 : }
5338 :
5339 : // next find full fan speed cooling rate
5340 30088 : UAdesignPerCell = highSpeedTowerUA / this->NumCell;
5341 30088 : AirFlowRatePerCell = this->HighSpeedAirFlowRate / this->NumCell;
5342 30088 : this->airFlowRateRatio = 1.0;
5343 30088 : Real64 WaterFlowRateRatio = WaterMassFlowRatePerCell / this->DesWaterMassFlowRatePerCell;
5344 30088 : Real64 UAwetbulbAdjFac = Curve::CurveValue(state, this->UAModFuncWetBulbDiffCurvePtr, (DesignWetBulb - this->AirWetBulb));
5345 30088 : Real64 UAairflowAdjFac = Curve::CurveValue(state, this->UAModFuncAirFlowRatioCurvePtr, this->airFlowRateRatio);
5346 30088 : Real64 UAwaterflowAdjFac = Curve::CurveValue(state, this->UAModFuncWaterFlowRatioCurvePtr, WaterFlowRateRatio);
5347 30088 : Real64 UAadjustedPerCell = UAdesignPerCell * UAwetbulbAdjFac * UAairflowAdjFac * UAwaterflowAdjFac;
5348 30088 : this->OutletWaterTemp = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRatePerCell, UAadjustedPerCell);
5349 : Real64 FullSpeedFanQdot =
5350 30088 : this->WaterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - this->OutletWaterTemp);
5351 30088 : Real64 FanPowerAdjustFac = 0.0;
5352 30088 : if (FullSpeedFanQdot <= std::abs(MyLoad)) { // full speed is what we want.
5353 :
5354 29130 : if ((FullSpeedFanQdot + HVAC::SmallLoad) < std::abs(MyLoad) && (this->NumCellOn < this->NumCell) &&
5355 0 : ((this->WaterMassFlowRate / (this->NumCellOn + 1)) >= WaterMassFlowRatePerCellMin)) {
5356 : // If full fan and not meeting setpoint, then increase number of cells until all are used or load is satisfied
5357 0 : bool IncrNumCellFlag = true; // set value to true to enter in the loop
5358 0 : while (IncrNumCellFlag) {
5359 0 : ++this->NumCellOn;
5360 0 : WaterMassFlowRatePerCell = this->WaterMassFlowRate / this->NumCellOn;
5361 0 : WaterFlowRateRatio = WaterMassFlowRatePerCell / this->DesWaterMassFlowRatePerCell;
5362 0 : UAwaterflowAdjFac = Curve::CurveValue(state, this->UAModFuncWaterFlowRatioCurvePtr, WaterFlowRateRatio);
5363 0 : UAadjustedPerCell = UAdesignPerCell * UAwetbulbAdjFac * UAairflowAdjFac * UAwaterflowAdjFac;
5364 0 : this->OutletWaterTemp =
5365 0 : this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRatePerCell, UAadjustedPerCell);
5366 0 : IncrNumCellFlag = (FullSpeedFanQdot + HVAC::SmallLoad) < std::abs(MyLoad) && (this->NumCellOn < this->NumCell) &&
5367 0 : ((this->WaterMassFlowRate / (this->NumCellOn + 1)) >= WaterMassFlowRatePerCellMin);
5368 : }
5369 0 : FullSpeedFanQdot =
5370 0 : this->WaterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - this->OutletWaterTemp);
5371 : }
5372 29130 : this->Qactual = FullSpeedFanQdot;
5373 29130 : CalcBasinHeaterPower(
5374 29130 : state, this->BasinHeaterPowerFTempDiff, this->basinHeaterSched, this->BasinHeaterSetPointTemp, this->BasinHeaterPower);
5375 : // now calculate fan power
5376 29130 : FanPowerAdjustFac = Curve::CurveValue(state, this->FanPowerfAirFlowCurve, this->airFlowRateRatio);
5377 29130 : this->FanPower = this->HighSpeedFanPower * FanPowerAdjustFac * this->NumCellOn / this->NumCell;
5378 :
5379 29130 : return;
5380 : }
5381 :
5382 : // next find minimum air flow ratio cooling rate
5383 958 : this->airFlowRateRatio = this->MinimumVSAirFlowFrac;
5384 958 : AirFlowRatePerCell = this->airFlowRateRatio * this->HighSpeedAirFlowRate / this->NumCell;
5385 958 : UAairflowAdjFac = Curve::CurveValue(state, this->UAModFuncAirFlowRatioCurvePtr, this->airFlowRateRatio);
5386 958 : UAadjustedPerCell = UAdesignPerCell * UAwetbulbAdjFac * UAairflowAdjFac * UAwaterflowAdjFac;
5387 958 : this->OutletWaterTemp = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRatePerCell, UAadjustedPerCell);
5388 : Real64 MinSpeedFanQdot =
5389 958 : this->WaterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - this->OutletWaterTemp);
5390 :
5391 958 : if (std::abs(MyLoad) <= MinSpeedFanQdot) { // min fan speed already exceeds load)
5392 164 : this->Qactual = MinSpeedFanQdot;
5393 164 : CalcBasinHeaterPower(
5394 164 : state, this->BasinHeaterPowerFTempDiff, this->basinHeaterSched, this->BasinHeaterSetPointTemp, this->BasinHeaterPower);
5395 : // now calculate fan power
5396 164 : FanPowerAdjustFac = Curve::CurveValue(state, this->FanPowerfAirFlowCurve, this->airFlowRateRatio);
5397 164 : this->FanPower = this->HighSpeedFanPower * FanPowerAdjustFac * this->NumCellOn / this->NumCell;
5398 164 : return;
5399 : }
5400 :
5401 794 : if ((MinSpeedFanQdot < std::abs(MyLoad)) && (std::abs(MyLoad) < FullSpeedFanQdot)) {
5402 : // load can be refined by modulating fan speed, call regula-falsi
5403 8036 : auto f = [&state, this, MyLoad, WaterMassFlowRatePerCell, UAdesignPerCell, UAwetbulbAdjFac, UAwaterflowAdjFac, CpWater](
5404 : Real64 airFlowRateRatioLocal) {
5405 7242 : Real64 const AirFlowRatePerCell = airFlowRateRatioLocal * this->HighSpeedAirFlowRate / this->NumCell;
5406 7242 : Real64 const UAairflowAdjFac = Curve::CurveValue(state, this->UAModFuncAirFlowRatioCurvePtr, airFlowRateRatioLocal);
5407 7242 : Real64 const UAadjustedPerCell = UAdesignPerCell * UAwetbulbAdjFac * UAairflowAdjFac * UAwaterflowAdjFac;
5408 : Real64 OutletWaterTempTrial =
5409 7242 : this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRatePerCell, UAadjustedPerCell);
5410 : Real64 const Qdot =
5411 7242 : this->WaterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - OutletWaterTempTrial);
5412 7242 : return std::abs(MyLoad) - Qdot;
5413 794 : };
5414 794 : int SolFla = 0;
5415 794 : General::SolveRoot(state, Acc, MaxIte, SolFla, this->airFlowRateRatio, f, this->MinimumVSAirFlowFrac, 1.0);
5416 :
5417 794 : if (SolFla == -1) {
5418 0 : if (!state.dataGlobal->WarmupFlag) {
5419 0 : if (this->VSMerkelAFRErrorIter < 1) {
5420 0 : ++this->VSMerkelAFRErrorIter;
5421 0 : ShowWarningError(state,
5422 0 : format("{} - Iteration limit exceeded calculating variable speed fan ratio for unit = {}",
5423 : cCoolingTower_VariableSpeedMerkel,
5424 0 : this->Name));
5425 0 : ShowContinueError(state,
5426 0 : format("Estimated air flow ratio = {:.4R}",
5427 0 : (std::abs(MyLoad) - MinSpeedFanQdot) / (FullSpeedFanQdot - MinSpeedFanQdot)));
5428 0 : ShowContinueError(state, format("Calculated air flow ratio = {:.4R}", this->airFlowRateRatio));
5429 0 : ShowContinueErrorTimeStamp(state,
5430 : "The calculated air flow ratio will be used and the simulation continues. Occurrence info:");
5431 : }
5432 0 : ShowRecurringWarningErrorAtEnd(
5433 : state,
5434 0 : cCoolingTower_VariableSpeedMerkel + " \"" + this->Name +
5435 : "\" - Iteration limit exceeded calculating air flow ratio error continues. air flow ratio statistics follow.",
5436 0 : this->VSMerkelAFRErrorIterIndex,
5437 0 : this->airFlowRateRatio,
5438 0 : this->airFlowRateRatio);
5439 : }
5440 794 : } else if (SolFla == -2) {
5441 0 : this->airFlowRateRatio = (std::abs(MyLoad) - MinSpeedFanQdot) / (FullSpeedFanQdot - MinSpeedFanQdot);
5442 0 : if (!state.dataGlobal->WarmupFlag) {
5443 0 : if (this->VSMerkelAFRErrorFail < 1) {
5444 0 : ++this->VSMerkelAFRErrorFail;
5445 0 : ShowWarningError(state,
5446 0 : format("{} - solver failed calculating variable speed fan ratio for unit = {}",
5447 : cCoolingTower_VariableSpeedMerkel,
5448 0 : this->Name));
5449 0 : ShowContinueError(state, format("Estimated air flow ratio = {:.4R}", this->airFlowRateRatio));
5450 0 : ShowContinueErrorTimeStamp(state, "The estimated air flow ratio will be used and the simulation continues. Occurrence info:");
5451 : }
5452 0 : ShowRecurringWarningErrorAtEnd(
5453 : state,
5454 0 : cCoolingTower_VariableSpeedMerkel + " \"" + this->Name +
5455 : "\" - solver failed calculating air flow ratio error continues. air flow ratio statistics follow.",
5456 0 : this->VSMerkelAFRErrorFailIndex,
5457 0 : this->airFlowRateRatio,
5458 0 : this->airFlowRateRatio);
5459 : }
5460 : }
5461 :
5462 : // now rerun to get performance with AirFlowRateRatio
5463 794 : AirFlowRatePerCell = this->airFlowRateRatio * this->HighSpeedAirFlowRate / this->NumCell;
5464 :
5465 794 : UAairflowAdjFac = Curve::CurveValue(state, this->UAModFuncAirFlowRatioCurvePtr, this->airFlowRateRatio);
5466 794 : UAadjustedPerCell = UAdesignPerCell * UAwetbulbAdjFac * UAairflowAdjFac * UAwaterflowAdjFac;
5467 :
5468 794 : this->OutletWaterTemp = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRatePerCell, UAadjustedPerCell);
5469 794 : this->Qactual = this->WaterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - this->OutletWaterTemp);
5470 794 : CalcBasinHeaterPower(
5471 794 : state, this->BasinHeaterPowerFTempDiff, this->basinHeaterSched, this->BasinHeaterSetPointTemp, this->BasinHeaterPower);
5472 :
5473 : // now calculate fan power
5474 794 : FanPowerAdjustFac = Curve::CurveValue(state, this->FanPowerfAirFlowCurve, this->airFlowRateRatio);
5475 794 : this->FanPower = this->HighSpeedFanPower * FanPowerAdjustFac * this->NumCellOn / this->NumCell;
5476 : }
5477 : }
5478 :
5479 7169613 : Real64 CoolingTower::calculateSimpleTowerOutletTemp(EnergyPlusData &state,
5480 : Real64 const waterMassFlowRate,
5481 : Real64 const AirFlowRate,
5482 : Real64 const UAdesign)
5483 : {
5484 :
5485 : // SUBROUTINE INFORMATION:
5486 : // AUTHOR Dan Fisher
5487 : // DATE WRITTEN Sept. 1998
5488 : // RE-ENGINEERED Shirey, Raustad, Jan 2001
5489 :
5490 : // PURPOSE OF THIS SUBROUTINE:
5491 : // See purpose for Single Speed or Two Speed tower model
5492 :
5493 : // METHODOLOGY EMPLOYED:
5494 : // See methodology for Single Speed or Two Speed tower model
5495 :
5496 : // REFERENCES:
5497 : // Merkel, F. 1925. Verduftungskuhlung. VDI Forschungsarbeiten, Nr 275, Berlin.
5498 : // ASHRAE 1999. HVAC1KIT: A Toolkit for Primary HVAC System Energy Calculations.
5499 :
5500 : // SUBROUTINE PARAMETER DEFINITIONS:
5501 : static constexpr std::string_view RoutineName("calculateSimpleTowerOutletTemp");
5502 :
5503 : // initialize some local variables
5504 7169613 : Real64 QactualLocal = 0.0; // Actual heat transfer rate between tower water and air [W]
5505 :
5506 : // set local tower inlet and outlet temperature variables
5507 7169613 : this->InletWaterTemp = this->WaterTemp;
5508 7169613 : Real64 OutletWaterTempLocal = this->InletWaterTemp;
5509 7169613 : Real64 InletAirTemp = this->AirTemp; // Dry-bulb temperature of air entering the tower [C]
5510 7169613 : Real64 InletAirWetBulb = this->AirWetBulb; // Wetbulb temp of entering moist air [C]
5511 :
5512 7169613 : if (UAdesign == 0.0) {
5513 2053263 : return OutletWaterTempLocal;
5514 : }
5515 :
5516 : // set water and air properties
5517 5116350 : Real64 AirDensity = Psychrometrics::PsyRhoAirFnPbTdbW(state, this->AirPress, InletAirTemp, this->AirHumRat); // Density of air [kg/m3]
5518 5116350 : Real64 AirMassFlowRate = AirFlowRate * AirDensity; // Mass flow rate of air [kg/s]
5519 5116350 : Real64 CpAir = Psychrometrics::PsyCpAirFnW(this->AirHumRat); // Heat capacity of air [J/kg/K]
5520 5116350 : Real64 CpWater = this->plantLoc.loop->glycol->getSpecificHeat(state, this->WaterTemp, RoutineName); // Heat capacity of water [J/kg/K]
5521 : Real64 InletAirEnthalpy =
5522 5116350 : Psychrometrics::PsyHFnTdbRhPb(state, this->AirWetBulb, 1.0, this->AirPress); // Enthalpy of entering moist air [J/kg]
5523 :
5524 : // initialize exiting wet bulb temperature before iterating on final solution
5525 5116350 : Real64 OutletAirWetBulb = InletAirWetBulb + 6.0; // Wetbulb temp of exiting moist air [C]
5526 :
5527 : // Calculate mass flow rates
5528 5116350 : if (waterMassFlowRate <= 0.0) {
5529 0 : OutletWaterTempLocal = this->InletWaterTemp;
5530 0 : return OutletWaterTempLocal;
5531 : }
5532 :
5533 5116350 : Real64 MdotCpWater = waterMassFlowRate * CpWater; // Water mass flow rate times the heat capacity [W/K]
5534 :
5535 5116350 : int Iter = 0;
5536 : Real64 OutletAirEnthalpy; // Enthalpy of exiting moist air [J/kg]
5537 5116350 : Real64 WetBulbError = 1.0; // Calculated error for exiting wet-bulb temperature between iterations [delta K/K]
5538 5116350 : Real64 DeltaTwb = 1.0; // Absolute value of difference between inlet and outlet air wet-bulb temp [C]
5539 : Real64 OutletAirWetBulbLast; // temporary Wetbulb temp of exiting moist air [C]
5540 5116350 : int constexpr IterMax(50); // Maximum number of iterations allowed
5541 5116350 : Real64 constexpr WetBulbTolerance(0.00001); // Maximum error for exiting wet-bulb temperature between iterations [delta K/K]
5542 5116350 : Real64 constexpr DeltaTwbTolerance(0.001); // Maximum error (tolerance) in DeltaTwb for iteration convergence [C]
5543 20230825 : while ((WetBulbError > WetBulbTolerance) && (Iter <= IterMax) && (DeltaTwb > DeltaTwbTolerance)) {
5544 15114475 : ++Iter;
5545 : // OutletAirEnthalpy = PsyHFnTdbRhPb(OutletAirWetBulb,1.0,OutBaroPress)
5546 15114475 : OutletAirEnthalpy = Psychrometrics::PsyHFnTdbRhPb(state, OutletAirWetBulb, 1.0, this->AirPress);
5547 : // calculate the airside specific heat and capacity
5548 15114475 : Real64 const CpAirside =
5549 15114475 : (OutletAirEnthalpy - InletAirEnthalpy) /
5550 15114475 : (OutletAirWetBulb - InletAirWetBulb); // Delta enthalpy of the tower air divides by delta air wet-bulb temp [J/kg/K]
5551 15114475 : Real64 const AirCapacity = AirMassFlowRate * CpAirside; // MdotCp of air through the tower
5552 : // calculate the minimum to maximum capacity ratios of airside and waterside
5553 15114475 : Real64 const CapacityRatioMin = min(AirCapacity, MdotCpWater); // Minimum capacity of airside and waterside
5554 15114475 : Real64 const CapacityRatioMax = max(AirCapacity, MdotCpWater); // Maximum capacity of airside and waterside
5555 15114475 : Real64 const CapacityRatio = CapacityRatioMin / CapacityRatioMax; // Ratio of minimum to maximum capacity
5556 : // Calculate heat transfer coefficient and number of transfer units (NTU)
5557 15114475 : Real64 const UAactual = UAdesign * CpAirside / CpAir; // UA value at actual conditions [W/C]
5558 15114475 : Real64 const NumTransferUnits = UAactual / CapacityRatioMin; // Number of transfer Units [NTU]
5559 : // calculate heat exchanger effectiveness
5560 : Real64 effectiveness; // Effectiveness of the heat exchanger [-]
5561 15114475 : if (CapacityRatio <= 0.995) {
5562 15104410 : Real64 Exponent = NumTransferUnits * (1.0 - CapacityRatio);
5563 15104410 : if (Exponent >= 700.0) {
5564 0 : effectiveness = NumTransferUnits / (1.0 + NumTransferUnits);
5565 : } else {
5566 15104410 : effectiveness = (1.0 - std::exp(-1.0 * NumTransferUnits * (1.0 - CapacityRatio))) /
5567 15104410 : (1.0 - CapacityRatio * std::exp(-1.0 * NumTransferUnits * (1.0 - CapacityRatio)));
5568 : }
5569 : } else {
5570 10065 : effectiveness = NumTransferUnits / (1.0 + NumTransferUnits);
5571 : }
5572 : // calculate water to air heat transfer and store last exiting WB temp of air
5573 15114475 : QactualLocal = effectiveness * CapacityRatioMin * (this->InletWaterTemp - InletAirWetBulb);
5574 15114475 : OutletAirWetBulbLast = OutletAirWetBulb;
5575 : // calculate new exiting wet bulb temperature of airstream
5576 15114475 : OutletAirWetBulb = InletAirWetBulb + QactualLocal / AirCapacity;
5577 : // Check error tolerance and exit if satisfied
5578 15114475 : DeltaTwb = std::abs(OutletAirWetBulb - InletAirWetBulb);
5579 : // Add KelvinConv to denominator below convert OutletAirWetBulbLast to Kelvin to avoid divide by zero.
5580 : // Wet bulb error units are delta K/K
5581 15114475 : WetBulbError = std::abs((OutletAirWetBulb - OutletAirWetBulbLast) / (OutletAirWetBulbLast + Constant::Kelvin));
5582 : }
5583 :
5584 5116350 : if (QactualLocal >= 0.0) {
5585 5116270 : OutletWaterTempLocal = this->InletWaterTemp - QactualLocal / MdotCpWater;
5586 : } else {
5587 80 : OutletWaterTempLocal = this->InletWaterTemp;
5588 : }
5589 5116350 : return OutletWaterTempLocal;
5590 : }
5591 :
5592 236957 : Real64 CoolingTower::calculateVariableTowerOutletTemp(EnergyPlusData &state,
5593 : Real64 const WaterFlowRateRatio, // current water flow rate ratio (capped if applicable)
5594 : Real64 const airFlowRateRatioLocal, // current air flow rate ratio
5595 : Real64 const Twb // current inlet air wet-bulb temperature (C, capped if applicable)
5596 : )
5597 : {
5598 :
5599 : // SUBROUTINE INFORMATION:
5600 : // AUTHOR Richard Raustad, FSEC
5601 : // DATE WRITTEN Feb. 2005
5602 :
5603 : // PURPOSE OF THIS SUBROUTINE:
5604 : // To calculate the leaving water temperature of the variable speed cooling tower.
5605 :
5606 : // METHODOLOGY EMPLOYED:
5607 : // The range temperature is varied to determine balance point where model output (Tapproach),
5608 : // range temperature and inlet air wet-bulb temperature show a balance as:
5609 : // Twb + Tapproach + Trange = Node(WaterInletNode)%Temp
5610 :
5611 : // REFERENCES:
5612 : // Benton, D.J., Bowmand, C.F., Hydeman, M., Miller, P.,
5613 : // "An Improved Cooling Tower Algorithm for the CoolToolsTM Simulation Model".
5614 : // ASHRAE Transactions 2002, V. 108, Pt. 1.
5615 : // York International Corporation, "YORKcalcTM Software, Chiller-Plant Energy-Estimating Program",
5616 : // Form 160.00-SG2 (0502). 2002.
5617 :
5618 : // SUBROUTINE PARAMETER DEFINITIONS:
5619 236957 : int constexpr MaxIte(500); // Maximum number of iterations
5620 236957 : Real64 constexpr Acc(0.0001); // Accuracy of result
5621 :
5622 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5623 236957 : Real64 constexpr VSTowerMaxRangeTemp(22.2222); // set VS cooling tower range maximum value used for solver
5624 :
5625 : // determine tower outlet water temperature
5626 : Real64 Tr; // range temperature which results in an energy balance
5627 2015889 : auto f = [&state, this, WaterFlowRateRatio, airFlowRateRatioLocal, Twb](Real64 Trange) {
5628 : // call model to determine approach temperature given other independent variables (range temp is being varied to find balance)
5629 2015889 : Real64 Tapproach = this->calculateVariableSpeedApproach(state, WaterFlowRateRatio, airFlowRateRatioLocal, Twb, Trange);
5630 : // calculate residual based on a balance where Twb + Ta + Tr = Inlet Water Temp
5631 2015889 : return (Twb + Tapproach + Trange) - this->WaterTemp;
5632 236957 : };
5633 236957 : int SolFla = 0;
5634 236957 : General::SolveRoot(state, Acc, MaxIte, SolFla, Tr, f, 0.001, max(this->MaxRangeTemp, VSTowerMaxRangeTemp));
5635 :
5636 : // calculate outlet temperature
5637 236957 : Real64 outletWaterTempLocal = this->WaterTemp - min(Tr, this->MaxRangeTemp);
5638 :
5639 236957 : if (SolFla == -1) {
5640 0 : ShowSevereError(state, "Iteration limit exceeded in calculating tower nominal capacity at minimum air flow ratio");
5641 0 : ShowContinueError(
5642 : state,
5643 : "Design inlet air wet-bulb or approach temperature must be modified to achieve an acceptable range at the minimum air flow rate");
5644 0 : ShowContinueError(state, format("Cooling tower simulation failed to converge for tower {}", this->Name));
5645 236957 : } else if (SolFla == -2) {
5646 : // bad starting value means that solution corresponds to a range that is beyond
5647 : // the bounds of the model; The maximum range is used here
5648 369 : outletWaterTempLocal = this->WaterTemp - this->MaxRangeTemp;
5649 369 : ++this->VSErrorCountTRCalc;
5650 369 : if (this->VSErrorCountTRCalc < 2) {
5651 18 : ShowWarningError(
5652 : state,
5653 18 : format("The range for the cooling tower {} likely exceeds the bounds of the model. The maximum range of the model is used {}.",
5654 9 : this->Name,
5655 9 : this->MaxRangeTemp));
5656 18 : ShowContinueError(state,
5657 18 : format(" ... Occurrence info = {}, {} {}",
5658 9 : state.dataEnvrn->EnvironmentName,
5659 9 : state.dataEnvrn->CurMnDy,
5660 18 : General::CreateSysTimeIntervalString(state)));
5661 : } else {
5662 2880 : ShowRecurringWarningErrorAtEnd(
5663 : state,
5664 720 : format("The range for the cooling tower {} likely exceeds the bounds of the model. The maximum range of the model is used {}",
5665 360 : this->Name,
5666 360 : this->MaxRangeTemp),
5667 360 : this->ErrIndexTRCalc);
5668 : }
5669 : }
5670 236957 : return outletWaterTempLocal;
5671 : }
5672 :
5673 2372069 : Real64 CoolingTower::calculateVariableSpeedApproach(EnergyPlusData &state,
5674 : Real64 const PctWaterFlow, // Water flow ratio of cooling tower
5675 : Real64 const airFlowRatioLocal, // Air flow ratio of cooling tower
5676 : Real64 const Twb, // Inlet air wet-bulb temperature [C]
5677 : Real64 const Tr // Cooling tower range (outlet water temp minus inlet air wet-bulb temp) [C]
5678 : )
5679 : {
5680 : // FUNCTION INFORMATION:
5681 : // AUTHOR Richard Raustad, FSEC
5682 : // DATE WRITTEN Feb. 2005
5683 :
5684 : // PURPOSE OF THIS FUNCTION:
5685 : // Calculate tower approach temperature (e.g. outlet water temp minus inlet air wet-bulb temp)
5686 : // given air flow ratio, water flow ratio, inlet air wet-bulb temp, and tower range.
5687 :
5688 : // METHODOLOGY EMPLOYED:
5689 : // Calculation method used empirical models from CoolTools or York to determine performance
5690 : // of variable speed (variable air flow rate) cooling towers.
5691 :
5692 : // REFERENCES:
5693 : // Benton, D.J., Bowmand, C.F., Hydeman, M., Miller, P.,
5694 : // "An Improved Cooling Tower Algorithm for the CoolToolsTM Simulation Model".
5695 : // ASHRAE Transactions 2002, V. 108, Pt. 1.
5696 : // York International Corporation, "YORKcalcTM Software, Chiller-Plant Energy-Estimating Program",
5697 : // Form 160.00-SG2 (0502). 2002.
5698 :
5699 2372069 : if (this->TowerModelType == ModelType::YorkCalcModel || this->TowerModelType == ModelType::YorkCalcUserDefined) {
5700 958621 : Real64 PctAirFlow = airFlowRatioLocal;
5701 958621 : Real64 FlowFactor = PctWaterFlow / PctAirFlow;
5702 958621 : return this->Coeff[0] + this->Coeff[1] * Twb + this->Coeff[2] * Twb * Twb + this->Coeff[3] * Tr + this->Coeff[4] * Twb * Tr +
5703 958621 : this->Coeff[5] * Twb * Twb * Tr + this->Coeff[6] * Tr * Tr + this->Coeff[7] * Twb * Tr * Tr +
5704 958621 : this->Coeff[8] * Twb * Twb * Tr * Tr + this->Coeff[9] * FlowFactor + this->Coeff[10] * Twb * FlowFactor +
5705 958621 : this->Coeff[11] * Twb * Twb * FlowFactor + this->Coeff[12] * Tr * FlowFactor + this->Coeff[13] * Twb * Tr * FlowFactor +
5706 958621 : this->Coeff[14] * Twb * Twb * Tr * FlowFactor + this->Coeff[15] * Tr * Tr * FlowFactor +
5707 958621 : this->Coeff[16] * Twb * Tr * Tr * FlowFactor + this->Coeff[17] * Twb * Twb * Tr * Tr * FlowFactor +
5708 958621 : this->Coeff[18] * FlowFactor * FlowFactor + this->Coeff[19] * Twb * FlowFactor * FlowFactor +
5709 958621 : this->Coeff[20] * Twb * Twb * FlowFactor * FlowFactor + this->Coeff[21] * Tr * FlowFactor * FlowFactor +
5710 958621 : this->Coeff[22] * Twb * Tr * FlowFactor * FlowFactor + this->Coeff[23] * Twb * Twb * Tr * FlowFactor * FlowFactor +
5711 958621 : this->Coeff[24] * Tr * Tr * FlowFactor * FlowFactor + this->Coeff[25] * Twb * Tr * Tr * FlowFactor * FlowFactor +
5712 958621 : this->Coeff[26] * Twb * Twb * Tr * Tr * FlowFactor * FlowFactor;
5713 :
5714 : } else { // empirical model is CoolTools format
5715 : // the CoolTools model actually uses PctFanPower = AirFlowRatio^3 as an input to the model
5716 1413448 : Real64 PctAirFlow = pow_3(airFlowRatioLocal);
5717 1413448 : return this->Coeff[0] + this->Coeff[1] * PctAirFlow + this->Coeff[2] * PctAirFlow * PctAirFlow +
5718 1413448 : this->Coeff[3] * PctAirFlow * PctAirFlow * PctAirFlow + this->Coeff[4] * PctWaterFlow +
5719 1413448 : this->Coeff[5] * PctAirFlow * PctWaterFlow + this->Coeff[6] * PctAirFlow * PctAirFlow * PctWaterFlow +
5720 1413448 : this->Coeff[7] * PctWaterFlow * PctWaterFlow + this->Coeff[8] * PctAirFlow * PctWaterFlow * PctWaterFlow +
5721 1413448 : this->Coeff[9] * PctWaterFlow * PctWaterFlow * PctWaterFlow + this->Coeff[10] * Twb + this->Coeff[11] * PctAirFlow * Twb +
5722 1413448 : this->Coeff[12] * PctAirFlow * PctAirFlow * Twb + this->Coeff[13] * PctWaterFlow * Twb +
5723 1413448 : this->Coeff[14] * PctAirFlow * PctWaterFlow * Twb + this->Coeff[15] * PctWaterFlow * PctWaterFlow * Twb +
5724 1413448 : this->Coeff[16] * Twb * Twb + this->Coeff[17] * PctAirFlow * Twb * Twb + this->Coeff[18] * PctWaterFlow * Twb * Twb +
5725 1413448 : this->Coeff[19] * Twb * Twb * Twb + this->Coeff[20] * Tr + this->Coeff[21] * PctAirFlow * Tr +
5726 1413448 : this->Coeff[22] * PctAirFlow * PctAirFlow * Tr + this->Coeff[23] * PctWaterFlow * Tr +
5727 1413448 : this->Coeff[24] * PctAirFlow * PctWaterFlow * Tr + this->Coeff[25] * PctWaterFlow * PctWaterFlow * Tr +
5728 1413448 : this->Coeff[26] * Twb * Tr + this->Coeff[27] * PctAirFlow * Twb * Tr + this->Coeff[28] * PctWaterFlow * Twb * Tr +
5729 1413448 : this->Coeff[29] * Twb * Twb * Tr + this->Coeff[30] * Tr * Tr + this->Coeff[31] * PctAirFlow * Tr * Tr +
5730 1413448 : this->Coeff[32] * PctWaterFlow * Tr * Tr + this->Coeff[33] * Twb * Tr * Tr + this->Coeff[34] * Tr * Tr * Tr;
5731 : }
5732 : }
5733 :
5734 135101 : void CoolingTower::checkModelBounds(EnergyPlusData &state,
5735 : Real64 Twb, // current inlet air wet-bulb temperature (C)
5736 : Real64 Tr, // requested range temperature for current time step (C)
5737 : Real64 Ta, // requested approach temperature for current time step (C)
5738 : Real64 WaterFlowRateRatio, // current water flow rate ratio at water inlet node
5739 : Real64 &TwbCapped, // bounded value of inlet air wet-bulb temperature (C)
5740 : Real64 &TrCapped, // bounded value of range temperature (C)
5741 : Real64 &TaCapped, // bounded value of approach temperature (C)
5742 : Real64 &WaterFlowRateRatioCapped // bounded value of water flow rate ratio
5743 : )
5744 : {
5745 :
5746 : // SUBROUTINE INFORMATION:
5747 : // AUTHOR Richard Raustad
5748 : // DATE WRITTEN Feb 2005
5749 :
5750 : // PURPOSE OF THIS SUBROUTINE:
5751 : // To verify that the empirical model's independent variables are within the limits used during the
5752 : // development of the empirical model.
5753 :
5754 : // METHODOLOGY EMPLOYED:
5755 : // The empirical models used for simulating a variable speed cooling tower are based on a limited data set.
5756 : // Extrapolation of empirical models can cause instability and the independent variables may need to be limited.
5757 : // The range of each independent variable is provided either by the CoolTools or York model limits, or
5758 : // specified by the user if the model is User Defined (in either the CoolTools or York model format).
5759 : // These limits are tested in this subroutine each time step and returned for use by the calling routine.
5760 : // The independent variables capped here may or may not be passed to the empirical model in the calling
5761 : // routine depending on their use.
5762 :
5763 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
5764 135101 : std::string OutputChar; // character string for warning messages
5765 135101 : std::string OutputCharLo; // character string for warning messages
5766 135101 : std::string OutputCharHi; // character string for warning messages
5767 135101 : std::string TrimValue; // character string for warning messages
5768 : // current end time is compared with last to see if time step changed
5769 :
5770 : // initialize capped variables in case independent variables are in bounds
5771 135101 : TwbCapped = Twb;
5772 135101 : TrCapped = Tr;
5773 135101 : TaCapped = Ta;
5774 135101 : WaterFlowRateRatioCapped = WaterFlowRateRatio;
5775 :
5776 : // calculate end time of current time step
5777 135101 : Real64 CurrentEndTime = state.dataGlobal->CurrentTime + state.dataHVACGlobal->SysTimeElapsed;
5778 :
5779 : // Print warning messages only when valid and only for the first occurrence. Let summary provide statistics.
5780 : // Wait for next time step to print warnings. If simulation iterates, print out
5781 : // the warning for the last iteration only. Must wait for next time step to accomplish this.
5782 : // If a warning occurs and the simulation down shifts, the warning is not valid.
5783 135101 : if (CurrentEndTime > this->CurrentEndTimeLast && state.dataHVACGlobal->TimeStepSys >= this->TimeStepSysLast) {
5784 19602 : if (this->PrintTrMessage) {
5785 635 : ++this->VSErrorCountTR;
5786 635 : if (this->VSErrorCountTR < 2) {
5787 15 : ShowWarningError(state, this->TrBuffer1);
5788 15 : ShowContinueError(state, this->TrBuffer2);
5789 15 : ShowContinueError(state, this->TrBuffer3);
5790 30 : ShowContinueError(state, " ...Range temperatures outside model boundaries may not adversely affect tower performance.");
5791 45 : ShowContinueError(state, " ...This is not an unexpected occurrence when simulating actual conditions.");
5792 : } else {
5793 4960 : ShowRecurringWarningErrorAtEnd(state,
5794 1240 : format("{} \"{}\" - Tower range temperature is out of range error continues...",
5795 620 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5796 620 : this->Name),
5797 620 : this->ErrIndexTR,
5798 620 : this->TrLast,
5799 620 : this->TrLast);
5800 : }
5801 : }
5802 19602 : if (this->PrintTwbMessage) {
5803 94 : ++this->VSErrorCountIAWB;
5804 94 : if (this->VSErrorCountIAWB < 6) {
5805 5 : ShowWarningError(state, this->TwbBuffer1);
5806 5 : ShowContinueError(state, this->TwbBuffer2);
5807 5 : ShowContinueError(state, this->TwbBuffer3);
5808 15 : ShowContinueError(state, " ...Wet-bulb temperatures outside model boundaries may not adversely affect tower performance.");
5809 : } else {
5810 712 : ShowRecurringWarningErrorAtEnd(state,
5811 178 : format("{} \"{}\" - Inlet air wet-bulb temperature is out of range error continues...",
5812 89 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5813 89 : this->Name),
5814 89 : this->ErrIndexIAWB,
5815 89 : this->TwbLast,
5816 89 : this->TwbLast);
5817 : }
5818 : }
5819 19602 : if (this->PrintTaMessage) {
5820 927 : ++this->VSErrorCountTA;
5821 927 : if (this->VSErrorCountTA < 2) {
5822 13 : ShowWarningError(state, this->TaBuffer1);
5823 13 : ShowContinueError(state, this->TaBuffer2);
5824 13 : ShowContinueError(state, this->TaBuffer3);
5825 26 : ShowContinueError(state, " ...Approach temperatures outside model boundaries may not adversely affect tower performance.");
5826 39 : ShowContinueError(state, " ...This is not an unexpected occurrence when simulating actual conditions.");
5827 : } else {
5828 7312 : ShowRecurringWarningErrorAtEnd(state,
5829 1828 : format("{} \"{}\" - Tower approach temperature is out of range error continues...",
5830 914 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5831 914 : this->Name),
5832 914 : this->ErrIndexTA,
5833 914 : this->TaLast,
5834 914 : this->TaLast);
5835 : }
5836 : }
5837 19602 : if (this->PrintWFRRMessage) {
5838 13 : ++this->VSErrorCountWFRR;
5839 13 : if (this->VSErrorCountWFRR < 6) {
5840 13 : ShowWarningError(state, this->WFRRBuffer1);
5841 13 : ShowContinueError(state, this->WFRRBuffer2);
5842 13 : ShowContinueError(state, this->WFRRBuffer3);
5843 39 : ShowContinueError(state, " ...Water flow rate ratios outside model boundaries may not adversely affect tower performance.");
5844 : } else {
5845 0 : ShowRecurringWarningErrorAtEnd(state,
5846 0 : format("{} \"{}\" - Water flow rate ratio is out of range error continues...",
5847 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5848 0 : this->Name),
5849 0 : this->ErrIndexWFRR,
5850 0 : this->WaterFlowRateRatioLast,
5851 0 : this->WaterFlowRateRatioLast);
5852 : }
5853 : }
5854 : }
5855 :
5856 : // save last system time step and last end time of current time step (used to determine if warning is valid)
5857 135101 : this->TimeStepSysLast = state.dataHVACGlobal->TimeStepSys;
5858 135101 : this->CurrentEndTimeLast = CurrentEndTime;
5859 :
5860 : // check boundaries of independent variables and post warnings to individual buffers to print at end of time step
5861 135101 : if (Twb < this->MinInletAirWBTemp || Twb > this->MaxInletAirWBTemp) {
5862 1023 : OutputChar = format("{:.2R}", Twb);
5863 1023 : OutputCharLo = format("{:.2R}", this->MinInletAirWBTemp);
5864 1023 : OutputCharHi = format("{:.2R}", this->MaxInletAirWBTemp);
5865 1023 : if (Twb < this->MinInletAirWBTemp) {
5866 1023 : TwbCapped = this->MinInletAirWBTemp;
5867 : }
5868 1023 : if (Twb > this->MaxInletAirWBTemp) {
5869 0 : TwbCapped = this->MaxInletAirWBTemp;
5870 : }
5871 2046 : if (!state.dataGlobal->WarmupFlag) {
5872 95 : this->PrintTwbMessage = true;
5873 190 : this->TwbBuffer1 = format("{} \"{}\" - Inlet air wet-bulb temperature is outside model boundaries at {}.",
5874 95 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5875 95 : this->Name,
5876 95 : OutputChar);
5877 190 : this->TwbBuffer2 = " ...Valid range = " + OutputCharLo + " to " + OutputCharHi +
5878 285 : ". Occurrence info = " + state.dataEnvrn->EnvironmentName + ", " + state.dataEnvrn->CurMnDy + ' ' +
5879 285 : General::CreateSysTimeIntervalString(state);
5880 95 : TrimValue = format("{:.6R}", TwbCapped);
5881 95 : this->TwbBuffer3 = " ...Inlet air wet-bulb temperature passed to the model = " + TrimValue;
5882 95 : this->TwbLast = Twb;
5883 : } else {
5884 928 : this->PrintTwbMessage = false;
5885 : }
5886 : } else {
5887 134078 : this->PrintTwbMessage = false;
5888 : }
5889 :
5890 135101 : if (Tr < this->MinRangeTemp || Tr > this->MaxRangeTemp) {
5891 19789 : OutputChar = format("{:.2R}", Tr);
5892 19789 : OutputCharLo = format("{:.2R}", this->MinRangeTemp);
5893 19789 : OutputCharHi = format("{:.2R}", this->MaxRangeTemp);
5894 19789 : if (Tr < this->MinRangeTemp) {
5895 19789 : TrCapped = this->MinRangeTemp;
5896 : }
5897 19789 : if (Tr > this->MaxRangeTemp) {
5898 0 : TrCapped = this->MaxRangeTemp;
5899 : }
5900 39578 : if (!state.dataGlobal->WarmupFlag) {
5901 3813 : this->PrintTrMessage = true;
5902 7626 : this->TrBuffer1 = format("{} \"{}\" - Tower range temperature is outside model boundaries at {}.",
5903 3813 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5904 3813 : this->Name,
5905 3813 : OutputChar);
5906 7626 : this->TrBuffer2 = " ...Valid range = " + OutputCharLo + " to " + OutputCharHi +
5907 11439 : ". Occurrence info = " + state.dataEnvrn->EnvironmentName + ", " + state.dataEnvrn->CurMnDy + ' ' +
5908 11439 : General::CreateSysTimeIntervalString(state);
5909 3813 : TrimValue = format("{:.5R}", Tr);
5910 3813 : this->TrBuffer3 = " ...Tower range temperature passed to the model = " + TrimValue;
5911 3813 : this->TrLast = Tr;
5912 : } else {
5913 15976 : this->PrintTrMessage = false;
5914 : }
5915 : } else {
5916 115312 : this->PrintTrMessage = false;
5917 : }
5918 :
5919 135101 : if (Ta < this->MinApproachTemp || Ta > this->MaxApproachTemp) {
5920 46051 : OutputChar = format("{:.2R}", Ta);
5921 46051 : OutputCharLo = format("{:.2R}", this->MinApproachTemp);
5922 46051 : OutputCharHi = format("{:.2R}", this->MaxApproachTemp);
5923 46051 : if (Ta < this->MinApproachTemp) {
5924 41458 : TaCapped = this->MinApproachTemp;
5925 : }
5926 46051 : if (Ta > this->MaxApproachTemp) {
5927 4593 : TaCapped = this->MaxApproachTemp;
5928 : }
5929 92102 : if (!state.dataGlobal->WarmupFlag) {
5930 7002 : this->PrintTaMessage = true;
5931 14004 : this->TaBuffer1 = format("{} \"{}\" - Tower approach temperature is outside model boundaries at {}.",
5932 7002 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5933 7002 : this->Name,
5934 7002 : OutputChar);
5935 14004 : this->TaBuffer2 = " ...Valid range = " + OutputCharLo + " to " + OutputCharHi +
5936 21006 : ". Occurrence info = " + state.dataEnvrn->EnvironmentName + ", " + state.dataEnvrn->CurMnDy + ' ' +
5937 21006 : General::CreateSysTimeIntervalString(state);
5938 7002 : TrimValue = format("{:.5R}", Ta);
5939 7002 : this->TaBuffer3 = " ...Tower approach temperature passed to the model = " + TrimValue;
5940 7002 : this->TaLast = Ta;
5941 : } else {
5942 39049 : this->PrintTaMessage = false;
5943 : }
5944 : } else {
5945 89050 : this->PrintTaMessage = false;
5946 : }
5947 :
5948 135101 : if (this->TowerModelType == ModelType::YorkCalcModel || this->TowerModelType == ModelType::YorkCalcUserDefined) {
5949 : // Water flow rate ratio warning not valid for YorkCalc model, print liquid to gas ratio
5950 : // warning instead (bottom of Subroutine VariableSpeedTower)
5951 51720 : this->PrintWFRRMessage = false;
5952 : } else {
5953 83381 : if (WaterFlowRateRatio < this->MinWaterFlowRatio || WaterFlowRateRatio > this->MaxWaterFlowRatio) {
5954 840 : OutputChar = format("{:.2R}", WaterFlowRateRatio);
5955 840 : OutputCharLo = format("{:.2R}", this->MinWaterFlowRatio);
5956 840 : OutputCharHi = format("{:.2R}", this->MaxWaterFlowRatio);
5957 840 : if (WaterFlowRateRatio < this->MinWaterFlowRatio) {
5958 840 : WaterFlowRateRatioCapped = this->MinWaterFlowRatio;
5959 : }
5960 840 : if (WaterFlowRateRatio > this->MaxWaterFlowRatio) {
5961 0 : WaterFlowRateRatioCapped = this->MaxWaterFlowRatio;
5962 : }
5963 1680 : if (!state.dataGlobal->WarmupFlag) {
5964 54 : this->PrintWFRRMessage = true;
5965 108 : this->WFRRBuffer1 = format("{} \"{}\" - Water flow rate ratio is outside model boundaries at {}.",
5966 54 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
5967 54 : this->Name,
5968 54 : OutputChar);
5969 108 : this->WFRRBuffer2 = " ...Valid range = " + OutputCharLo + " to " + OutputCharHi +
5970 162 : ". Occurrence info = " + state.dataEnvrn->EnvironmentName + ", " + state.dataEnvrn->CurMnDy + ' ' +
5971 162 : General::CreateSysTimeIntervalString(state);
5972 54 : TrimValue = format("{:.5R}", WaterFlowRateRatioCapped);
5973 54 : this->WFRRBuffer3 = " ...Water flow rate ratio passed to the model = " + TrimValue;
5974 54 : this->WaterFlowRateRatioLast = WaterFlowRateRatio;
5975 : } else {
5976 786 : this->PrintWFRRMessage = false;
5977 : }
5978 : } else {
5979 82541 : this->PrintWFRRMessage = false;
5980 : }
5981 : }
5982 135101 : }
5983 :
5984 8831655 : void CoolingTower::calculateWaterUsage(EnergyPlusData &state)
5985 : {
5986 :
5987 : // SUBROUTINE INFORMATION:
5988 : // AUTHOR B. Griffith
5989 : // DATE WRITTEN August 2006
5990 : // MODIFIED T Hong, Aug. 2008. Added fluid bypass for single speed cooling tower
5991 : // A Flament, July 2010. Added multi-cell capability
5992 :
5993 : // PURPOSE OF THIS SUBROUTINE:
5994 : // Collect tower water usage calculations for reuse by all the tower models.
5995 :
5996 : // REFERENCES:
5997 : // Code for this routine started from VariableSpeedTower
5998 :
5999 : // SUBROUTINE PARAMETER DEFINITIONS:
6000 : static constexpr std::string_view RoutineName("calculateWaterUsage");
6001 :
6002 8831655 : Real64 EvapVdot = 0.0;
6003 8831655 : Real64 AverageWaterTemp = (this->InletWaterTemp + this->OutletWaterTemp) / 2.0;
6004 :
6005 : // Set water and air properties
6006 8831655 : if (this->EvapLossMode == EvapLoss::MoistTheory) {
6007 :
6008 8814761 : Real64 const AirDensity = Psychrometrics::PsyRhoAirFnPbTdbW(state, this->AirPress, this->AirTemp, this->AirHumRat);
6009 8814761 : Real64 const AirMassFlowRate = this->airFlowRateRatio * this->HighSpeedAirFlowRate * AirDensity * this->NumCellOn / this->NumCell;
6010 8814761 : Real64 const InletAirEnthalpy = Psychrometrics::PsyHFnTdbRhPb(state, this->AirWetBulb, 1.0, this->AirPress);
6011 :
6012 8814761 : if (AirMassFlowRate > 0.0) {
6013 : // Calculate outlet air conditions for determining water usage
6014 :
6015 3659365 : Real64 const OutletAirEnthalpy = InletAirEnthalpy + this->Qactual / AirMassFlowRate;
6016 3659365 : Real64 const OutletAirTSat = Psychrometrics::PsyTsatFnHPb(state, OutletAirEnthalpy, this->AirPress);
6017 3659365 : Real64 const OutletAirHumRatSat = Psychrometrics::PsyWFnTdbH(state, OutletAirTSat, OutletAirEnthalpy);
6018 :
6019 : // calculate specific humidity ratios (HUMRAT to mass of moist air not dry air)
6020 3659365 : Real64 const InSpecificHumRat = this->AirHumRat / (1 + this->AirHumRat);
6021 3659365 : Real64 const OutSpecificHumRat = OutletAirHumRatSat / (1 + OutletAirHumRatSat);
6022 :
6023 : // calculate average air temp for density call
6024 3659365 : Real64 const TairAvg = (this->AirTemp + OutletAirTSat) / 2.0;
6025 :
6026 : // Amount of water evaporated, get density water at air temp or 4 C if too cold
6027 3659365 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, max(TairAvg, 4.0), RoutineName);
6028 :
6029 3659365 : EvapVdot = (AirMassFlowRate * (OutSpecificHumRat - InSpecificHumRat)) / rho; // [m3/s]
6030 3659365 : if (EvapVdot < 0.0) {
6031 38 : EvapVdot = 0.0;
6032 : }
6033 : } else {
6034 5155396 : EvapVdot = 0.0;
6035 : }
6036 :
6037 16894 : } else if (this->EvapLossMode == EvapLoss::UserFactor) {
6038 16894 : Real64 const rho = this->plantLoc.loop->glycol->getDensity(state, AverageWaterTemp, RoutineName);
6039 :
6040 16894 : EvapVdot = this->UserEvapLossFactor * (this->InletWaterTemp - this->OutletWaterTemp) * (this->WaterMassFlowRate / rho);
6041 16894 : if (EvapVdot < 0.0) {
6042 0 : EvapVdot = 0.0;
6043 : }
6044 : } else {
6045 : // should never come here
6046 : }
6047 :
6048 : // amount of water lost due to drift
6049 8831655 : Real64 driftVdot = this->DesignWaterFlowRate * this->NumCellOn / this->NumCell * this->DriftLossFraction * this->airFlowRateRatio;
6050 :
6051 8831655 : Real64 BlowDownVdot(0.0);
6052 8831655 : if (this->BlowdownMode == Blowdown::Schedule) {
6053 : // Amount of water lost due to blow down (purging contaminants from tower basin)
6054 0 : BlowDownVdot = (this->blowdownSched != nullptr) ? this->blowdownSched->getCurrentVal() : 0.0;
6055 8831655 : } else if (this->BlowdownMode == Blowdown::Concentration) {
6056 8831655 : if (this->ConcentrationRatio > 2.0) { // protect divide by zero
6057 8831655 : BlowDownVdot = EvapVdot / (this->ConcentrationRatio - 1) - driftVdot;
6058 : } else {
6059 0 : BlowDownVdot = EvapVdot - driftVdot;
6060 : }
6061 8831655 : if (BlowDownVdot < 0.0) {
6062 106490 : BlowDownVdot = 0.0;
6063 : }
6064 : } else {
6065 : // should never come here
6066 : }
6067 :
6068 : // Added for fluid bypass
6069 8831655 : if (this->CapacityControl == CapacityCtrl::FluidBypass) {
6070 359740 : if (this->EvapLossMode == EvapLoss::UserFactor) {
6071 0 : EvapVdot *= (1 - this->BypassFraction);
6072 : }
6073 359740 : driftVdot *= (1 - this->BypassFraction);
6074 359740 : BlowDownVdot *= (1 - this->BypassFraction);
6075 : }
6076 :
6077 8831655 : Real64 const makeUpVdot = EvapVdot + driftVdot + BlowDownVdot;
6078 :
6079 : // set demand request in Water Storage if needed
6080 8831655 : Real64 StarvedVdot = 0.0;
6081 8831655 : Real64 tankSupplyVdot = 0.0;
6082 8831655 : if (this->SuppliedByWaterSystem) {
6083 :
6084 : // set demand request
6085 35076 : state.dataWaterData->WaterStorage(this->WaterTankID).VdotRequestDemand(this->WaterTankDemandARRID) = makeUpVdot;
6086 :
6087 35076 : Real64 const AvailTankVdot = state.dataWaterData->WaterStorage(this->WaterTankID)
6088 35076 : .VdotAvailDemand(this->WaterTankDemandARRID); // check what tank can currently provide
6089 :
6090 35076 : tankSupplyVdot = makeUpVdot; // init
6091 35076 : if (AvailTankVdot < makeUpVdot) { // calculate starved flow
6092 20214 : StarvedVdot = makeUpVdot - AvailTankVdot;
6093 20214 : tankSupplyVdot = AvailTankVdot;
6094 : }
6095 : } else { // supplied by mains
6096 : }
6097 :
6098 : // total water usage
6099 : // update report variables
6100 8831655 : this->EvaporationVdot = EvapVdot;
6101 8831655 : this->EvaporationVol = EvapVdot * (state.dataHVACGlobal->TimeStepSysSec);
6102 8831655 : this->DriftVdot = driftVdot;
6103 8831655 : this->DriftVol = driftVdot * (state.dataHVACGlobal->TimeStepSysSec);
6104 8831655 : this->BlowdownVdot = BlowDownVdot;
6105 8831655 : this->BlowdownVol = BlowDownVdot * (state.dataHVACGlobal->TimeStepSysSec);
6106 8831655 : this->MakeUpVdot = makeUpVdot;
6107 8831655 : this->MakeUpVol = makeUpVdot * (state.dataHVACGlobal->TimeStepSysSec);
6108 8831655 : this->TankSupplyVdot = tankSupplyVdot;
6109 8831655 : this->TankSupplyVol = tankSupplyVdot * (state.dataHVACGlobal->TimeStepSysSec);
6110 8831655 : this->StarvedMakeUpVdot = StarvedVdot;
6111 8831655 : this->StarvedMakeUpVol = StarvedVdot * (state.dataHVACGlobal->TimeStepSysSec);
6112 8831655 : this->coolingTowerApproach = this->OutletWaterTemp - this->AirWetBulb;
6113 8831655 : this->coolingTowerRange = this->InletWaterTemp - this->OutletWaterTemp;
6114 8831655 : }
6115 :
6116 8831655 : void CoolingTower::update(EnergyPlusData &state)
6117 : {
6118 :
6119 : // SUBROUTINE INFORMATION:
6120 : // AUTHOR: Dan Fisher
6121 : // DATE WRITTEN: October 1998
6122 :
6123 : // PURPOSE OF THIS SUBROUTINE:
6124 : // This subroutine is for passing results to the outlet water node.
6125 :
6126 : // set node information
6127 8831655 : PlantUtilities::SafeCopyPlantNode(state, this->WaterInletNodeNum, this->WaterOutletNodeNum);
6128 8831655 : state.dataLoopNodes->Node(this->WaterOutletNodeNum).Temp = this->OutletWaterTemp;
6129 :
6130 8831655 : if (this->plantLoc.side->FlowLock == DataPlant::FlowLock::Unlocked || state.dataGlobal->WarmupFlag) {
6131 8199115 : return;
6132 : }
6133 :
6134 : // Check flow rate through tower and compare to design flow rate, show warning if greater than Design * Multiplier
6135 632540 : if (state.dataLoopNodes->Node(this->WaterOutletNodeNum).MassFlowRate > this->DesWaterMassFlowRate * this->TowerMassFlowRateMultiplier) {
6136 0 : ++this->HighMassFlowErrorCount;
6137 0 : if (this->HighMassFlowErrorCount < 2) {
6138 0 : ShowWarningError(state, format("{} \"{}\"", DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)], this->Name));
6139 0 : ShowContinueError(state, " Condenser Loop Mass Flow Rate is much greater than the towers design mass flow rate.");
6140 0 : ShowContinueError(
6141 0 : state, format(" Condenser Loop Mass Flow Rate = {:.6T}", state.dataLoopNodes->Node(this->WaterOutletNodeNum).MassFlowRate));
6142 0 : ShowContinueError(state, format(" Tower Design Mass Flow Rate = {:.6T}", this->DesWaterMassFlowRate));
6143 0 : ShowContinueErrorTimeStamp(state, "");
6144 : } else {
6145 0 : ShowRecurringWarningErrorAtEnd(
6146 : state,
6147 0 : format("{} \"{}\" Condenser Loop Mass Flow Rate is much greater than the towers design mass flow rate error continues...",
6148 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
6149 0 : this->Name),
6150 0 : this->HighMassFlowErrorIndex,
6151 0 : state.dataLoopNodes->Node(this->WaterOutletNodeNum).MassFlowRate,
6152 0 : state.dataLoopNodes->Node(this->WaterOutletNodeNum).MassFlowRate);
6153 : }
6154 : }
6155 :
6156 : // Check if OutletWaterTemp is below the minimum condenser loop temp and warn user
6157 632540 : Real64 const LoopMinTemp = this->plantLoc.loop->MinTemp;
6158 632540 : bool const outletWaterTempTooLow = this->OutletWaterTemp < LoopMinTemp;
6159 632540 : bool const flowIsOn = this->WaterMassFlowRate > 0.0;
6160 632540 : if (outletWaterTempTooLow && flowIsOn) {
6161 770 : ++this->OutletWaterTempErrorCount;
6162 770 : if (this->OutletWaterTempErrorCount < 2) {
6163 7 : ShowWarningError(state, format("{} \"{}\"", DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)], this->Name));
6164 14 : ShowContinueError(
6165 : state,
6166 14 : format("Cooling tower water outlet temperature ({:.2F} C) is below the specified minimum condenser loop temp of {:.2F} C",
6167 7 : this->OutletWaterTemp,
6168 : LoopMinTemp));
6169 21 : ShowContinueErrorTimeStamp(state, "");
6170 : } else {
6171 6104 : ShowRecurringWarningErrorAtEnd(
6172 : state,
6173 1526 : format("{} \"{}\" Cooling tower water outlet temperature is below the specified minimum condenser loop temp error continues...",
6174 763 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
6175 763 : this->Name),
6176 763 : this->OutletWaterTempErrorIndex,
6177 763 : this->OutletWaterTemp,
6178 763 : this->OutletWaterTemp);
6179 : }
6180 : }
6181 :
6182 : // Check if water mass flow rate is small (e.g. no flow) and warn user
6183 632540 : if (this->WaterMassFlowRate > 0.0 && this->WaterMassFlowRate <= DataBranchAirLoopPlant::MassFlowTolerance) {
6184 0 : ++this->SmallWaterMassFlowErrorCount;
6185 0 : if (this->SmallWaterMassFlowErrorCount < 2) {
6186 0 : ShowWarningError(state, format("{} \"{}\"", DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)], this->Name));
6187 0 : ShowContinueError(state, "Cooling tower water mass flow rate near zero.");
6188 0 : ShowContinueErrorTimeStamp(state, "");
6189 0 : ShowContinueError(state, format("Actual Mass flow = {:.2T}", this->WaterMassFlowRate));
6190 : } else {
6191 0 : ShowRecurringWarningErrorAtEnd(state,
6192 0 : format("{} \"{}\" Cooling tower water mass flow rate near zero error continues...",
6193 0 : DataPlant::PlantEquipTypeNames[static_cast<int>(this->TowerType)],
6194 0 : this->Name),
6195 0 : this->SmallWaterMassFlowErrorIndex,
6196 0 : this->WaterMassFlowRate,
6197 0 : this->WaterMassFlowRate);
6198 : }
6199 : }
6200 : }
6201 :
6202 8831655 : void CoolingTower::report(EnergyPlusData &state, bool const RunFlag)
6203 : {
6204 :
6205 : // SUBROUTINE INFORMATION:
6206 : // AUTHOR: Dan Fisher
6207 : // DATE WRITTEN: October 1998
6208 :
6209 : // PURPOSE OF THIS SUBROUTINE:
6210 : // This subroutine updates the report variables for the tower.
6211 :
6212 8831655 : Real64 const ReportingConstant = state.dataHVACGlobal->TimeStepSysSec;
6213 :
6214 8831655 : if (!RunFlag) {
6215 4657680 : this->InletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
6216 4657680 : this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
6217 4657680 : this->Qactual = 0.0;
6218 4657680 : this->FanPower = 0.0;
6219 4657680 : this->FanEnergy = 0.0;
6220 4657680 : this->AirFlowRatio = 0.0;
6221 4657680 : this->WaterAmountUsed = 0.0;
6222 4657680 : this->BasinHeaterConsumption = this->BasinHeaterPower * ReportingConstant;
6223 4657680 : this->FanCyclingRatio = 0.0;
6224 4657680 : this->BypassFraction = 0.0; // added for fluid bypass
6225 4657680 : this->NumCellOn = 0;
6226 4657680 : this->SpeedSelected = 0;
6227 : } else {
6228 4173975 : this->InletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
6229 4173975 : this->FanEnergy = this->FanPower * ReportingConstant;
6230 4173975 : this->AirFlowRatio = this->airFlowRateRatio;
6231 4173975 : this->WaterAmountUsed = this->WaterUsage * ReportingConstant;
6232 4173975 : this->BasinHeaterConsumption = this->BasinHeaterPower * ReportingConstant;
6233 : }
6234 8831655 : }
6235 :
6236 8243068 : void CoolingTower::checkMassFlowAndLoad(EnergyPlusData &state, Real64 const MyLoad, bool RunFlag, bool &returnFlagSet)
6237 : {
6238 8243068 : if ((MyLoad > -HVAC::SmallLoad) || !RunFlag) {
6239 : // tower doesn't need to do anything
6240 4602647 : this->OutletWaterTemp = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp;
6241 4602647 : this->FanPower = 0.0;
6242 4602647 : this->airFlowRateRatio = 0.0;
6243 4602647 : this->Qactual = 0.0;
6244 4602647 : this->WaterMassFlowRate = 0.0;
6245 4602647 : PlantUtilities::SetComponentFlowRate(state, this->WaterMassFlowRate, this->WaterInletNodeNum, this->WaterOutletNodeNum, this->plantLoc);
6246 4602647 : CalcBasinHeaterPower(
6247 4602647 : state, this->BasinHeaterPowerFTempDiff, this->basinHeaterSched, this->BasinHeaterSetPointTemp, this->BasinHeaterPower);
6248 4602647 : returnFlagSet = true;
6249 3640421 : } else if (this->WaterMassFlowRate <= DataBranchAirLoopPlant::MassFlowTolerance) {
6250 : // for multiple cells, we assume that it's a common basin
6251 0 : CalcBasinHeaterPower(
6252 0 : state, this->BasinHeaterPowerFTempDiff, this->basinHeaterSched, this->BasinHeaterSetPointTemp, this->BasinHeaterPower);
6253 0 : returnFlagSet = true;
6254 : }
6255 8243068 : }
6256 :
6257 0 : Real64 CoolingTower::getDynamicMaxCapacity(EnergyPlusData &state)
6258 : {
6259 : // TODO: does not include faults object impact
6260 : static constexpr std::string_view routineName("getDynamicMaxCapacity");
6261 0 : Real64 outletWaterTemp = 0.0;
6262 0 : Real64 constexpr designWetBulb = 25.56;
6263 0 : Real64 constexpr airFlowRateRatio = 1.0;
6264 0 : Real64 constexpr waterFlowRateRatio = 1.0;
6265 : Real64 const CpWater =
6266 0 : this->plantLoc.loop->glycol->getSpecificHeat(state, state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp, routineName);
6267 : // use operating max (i.e., at current plant flow rates, could be 0 if plant is off) or max available capacity?
6268 : // Real64 waterMassFlowRate = state.dataLoopNodes->Node(this->WaterInletNodeNum).MassFlowRateMaxAvail;
6269 0 : Real64 waterMassFlowRate = this->DesWaterMassFlowRate;
6270 0 : this->AirWetBulb = (this->OutdoorAirInletNodeNum > 0) ? state.dataLoopNodes->Node(this->OutdoorAirInletNodeNum).OutAirWetBulb
6271 0 : : state.dataEnvrn->OutWetBulbTemp;
6272 :
6273 0 : switch (this->TowerType) {
6274 0 : case DataPlant::PlantEquipmentType::CoolingTower_SingleSpd:
6275 : case DataPlant::PlantEquipmentType::CoolingTower_TwoSpd: {
6276 : // only needed for calculateSimpleTowerOutletTemp
6277 0 : this->AirTemp =
6278 0 : (this->OutdoorAirInletNodeNum > 0) ? state.dataLoopNodes->Node(this->OutdoorAirInletNodeNum).Temp : state.dataEnvrn->OutDryBulbTemp;
6279 0 : Real64 WaterMassFlowRatePerCell = waterMassFlowRate / this->NumCell;
6280 0 : Real64 UAdesign = this->HighSpeedTowerUA / this->NumCell; // could save these calculations in e.g., this->DesUAPerCell
6281 0 : Real64 AirFlowRate = this->HighSpeedAirFlowRate / this->NumCell;
6282 0 : outletWaterTemp = this->calculateSimpleTowerOutletTemp(state, WaterMassFlowRatePerCell, AirFlowRate, UAdesign);
6283 0 : } break;
6284 0 : case DataPlant::PlantEquipmentType::CoolingTower_VarSpd: {
6285 : Real64 TrCapped; // range temp passed to VS tower model
6286 : Real64 TaCapped; // approach temp passed to VS tower model
6287 0 : Real64 Twb = this->AirWetBulb;
6288 0 : Real64 TwbCapped = this->AirWetBulb;
6289 : // water temperature setpoint
6290 0 : Real64 TempSetPoint(0.0); // Outlet water temperature setpoint (C)
6291 0 : switch (this->plantLoc.loop->LoopDemandCalcScheme) {
6292 0 : case DataPlant::LoopDemandCalcScheme::SingleSetPoint: {
6293 0 : TempSetPoint = this->plantLoc.side->TempSetPoint;
6294 0 : } break;
6295 0 : case DataPlant::LoopDemandCalcScheme::DualSetPointDeadBand: {
6296 0 : TempSetPoint = this->plantLoc.side->TempSetPointHi;
6297 0 : } break;
6298 0 : default: {
6299 0 : assert(false);
6300 : } break;
6301 : }
6302 0 : Real64 Tr = state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - TempSetPoint;
6303 0 : Real64 Ta = TempSetPoint - this->AirWetBulb;
6304 0 : Real64 waterFlowRateRatioCapped = 0.0; // Water flow rate ratio passed to VS tower model
6305 : // check independent inputs with respect to model boundaries
6306 0 : this->checkModelBounds(state, Twb, Tr, Ta, waterFlowRateRatio, TwbCapped, TrCapped, TaCapped, waterFlowRateRatioCapped);
6307 0 : outletWaterTemp = this->calculateVariableTowerOutletTemp(state, waterFlowRateRatioCapped, airFlowRateRatio, TwbCapped);
6308 0 : } break;
6309 0 : case DataPlant::PlantEquipmentType::CoolingTower_VarSpdMerkel: {
6310 : // only needed for calculateSimpleTowerOutletTemp
6311 0 : this->AirTemp =
6312 0 : (this->OutdoorAirInletNodeNum > 0) ? state.dataLoopNodes->Node(this->OutdoorAirInletNodeNum).Temp : state.dataEnvrn->OutDryBulbTemp;
6313 0 : Real64 const UAdesignPerCell = this->HighSpeedTowerUA / this->NumCell;
6314 0 : Real64 const airFlowRatePerCell = this->HighSpeedAirFlowRate / this->NumCell;
6315 0 : Real64 const waterMassFlowRatePerCell = waterMassFlowRate / this->NumCell;
6316 0 : Real64 const WaterFlowRateRatio = waterMassFlowRatePerCell / this->DesWaterMassFlowRatePerCell; // this should always be 1 ?
6317 0 : Real64 const UAwetbulbAdjFac = Curve::CurveValue(state, this->UAModFuncWetBulbDiffCurvePtr, (designWetBulb - this->AirWetBulb));
6318 0 : Real64 const UAairflowAdjFac = Curve::CurveValue(state, this->UAModFuncAirFlowRatioCurvePtr, airFlowRateRatio);
6319 0 : Real64 const UAwaterflowAdjFac = Curve::CurveValue(state, this->UAModFuncWaterFlowRatioCurvePtr, WaterFlowRateRatio);
6320 0 : Real64 const UAadjustedPerCell = UAdesignPerCell * UAwetbulbAdjFac * UAairflowAdjFac * UAwaterflowAdjFac;
6321 0 : outletWaterTemp = this->calculateSimpleTowerOutletTemp(state, waterMassFlowRatePerCell, airFlowRatePerCell, UAadjustedPerCell);
6322 0 : } break;
6323 0 : default:
6324 0 : assert(false);
6325 : }
6326 0 : return waterMassFlowRate * CpWater * (state.dataLoopNodes->Node(this->WaterInletNodeNum).Temp - outletWaterTemp);
6327 : }
6328 :
6329 : } // namespace CondenserLoopTowers
6330 :
6331 : } // namespace EnergyPlus
|