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