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