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 <cmath>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Array.functions.hh>
53 :
54 : // EnergyPlus Headers
55 : #include <EnergyPlus/BranchNodeConnections.hh>
56 : #include <EnergyPlus/Construction.hh>
57 : #include <EnergyPlus/Data/EnergyPlusData.hh>
58 : #include <EnergyPlus/DataConversions.hh>
59 : #include <EnergyPlus/DataEnvironment.hh>
60 : #include <EnergyPlus/DataHVACGlobals.hh>
61 : #include <EnergyPlus/DataHeatBalFanSys.hh>
62 : #include <EnergyPlus/DataHeatBalSurface.hh>
63 : #include <EnergyPlus/DataHeatBalance.hh>
64 : #include <EnergyPlus/DataLoopNode.hh>
65 : #include <EnergyPlus/DataSizing.hh>
66 : #include <EnergyPlus/DataSurfaceLists.hh>
67 : #include <EnergyPlus/DataSurfaces.hh>
68 : #include <EnergyPlus/FluidProperties.hh>
69 : #include <EnergyPlus/General.hh>
70 : #include <EnergyPlus/GeneralRoutines.hh>
71 : #include <EnergyPlus/HeatBalanceSurfaceManager.hh>
72 : #include <EnergyPlus/InputProcessing/InputProcessor.hh>
73 : #include <EnergyPlus/NodeInputManager.hh>
74 : #include <EnergyPlus/OutputProcessor.hh>
75 : #include <EnergyPlus/Plant/DataPlant.hh>
76 : #include <EnergyPlus/Plant/PlantLocation.hh>
77 : #include <EnergyPlus/PlantUtilities.hh>
78 : #include <EnergyPlus/Psychrometrics.hh>
79 : #include <EnergyPlus/ScheduleManager.hh>
80 : #include <EnergyPlus/SwimmingPool.hh>
81 : #include <EnergyPlus/UtilityRoutines.hh>
82 : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
83 :
84 : namespace EnergyPlus::SwimmingPool {
85 :
86 : // MODULE INFORMATION:
87 : // AUTHOR Rick Strand, Ho-Sung Kim
88 : // DATE WRITTEN June 2012 (F90) and October 2014 (C++)
89 :
90 : // PURPOSE OF THIS MODULE:
91 : // The purpose of this module is to encapsulate the data and algorithms required
92 : // to manage the SwimmingPool System Component.
93 :
94 : // METHODOLOGY EMPLOYED:
95 : // The swimming pool acts as a surface within the heat balance and then connects
96 : // to the plant via a water loop.
97 :
98 : // REFERENCES:
99 : // 1. ASHRAE (2011). 2011 ASHRAE Handbook - HVAC Applications. Atlanta: American Society of Heating,
100 : // Refrigerating and Air-Conditioning Engineers, Inc., p.5.6-5.9.
101 : // 2. Janis, R. and W. Tao (2005). Mechanical and Electrical Systems in Buildings. 3rd ed. Upper
102 : // Saddle River, NJ: Pearson Education, Inc., p.246.
103 : // 3. Kittler, R. (1989). Indoor Natatorium Design and Energy Recycling. ASHRAE Transactions 95(1), p.521-526.
104 : // 4. Smith, C., R. Jones, and G. Lof (1993). Energy Requirements and Potential Savings for Heated
105 : // Indoor Swimming Pools. ASHRAE Transactions 99(2), p.864-874.
106 :
107 4 : SwimmingPoolData *SwimmingPoolData::factory(EnergyPlusData &state, std::string const &objectName)
108 : {
109 4 : if (state.dataSwimmingPools->getSwimmingPoolInput) {
110 0 : GetSwimmingPool(state);
111 0 : state.dataSwimmingPools->getSwimmingPoolInput = false;
112 : }
113 : // Now look for this particular swimming pool in the list
114 10 : for (auto &pool : state.dataSwimmingPools->Pool) {
115 10 : if (pool.Name == objectName) {
116 4 : return &pool;
117 : }
118 : }
119 : // If we didn't find it, fatal
120 0 : ShowFatalError(state,
121 : format("LocalSwimmingPoolFactory: Error getting inputs or index for swimming pool named: {}", objectName)); // LCOV_EXCL_LINE
122 : // Shut up the compiler
123 : return nullptr; // LCOV_EXCL_LINE
124 : }
125 :
126 0 : void SwimmingPoolData::simulate(EnergyPlusData &state,
127 : [[maybe_unused]] const PlantLocation &calledFromLocation,
128 : bool FirstHVACIteration,
129 : [[maybe_unused]] Real64 &CurLoad,
130 : [[maybe_unused]] bool RunFlag)
131 : {
132 0 : state.dataHeatBalFanSys->SumConvPool(this->ZonePtr) = 0.0;
133 0 : state.dataHeatBalFanSys->SumLatentPool(this->ZonePtr) = 0.0;
134 :
135 0 : CurLoad = 0.0;
136 0 : RunFlag = true;
137 :
138 0 : this->initialize(state, FirstHVACIteration);
139 :
140 0 : this->calculate(state);
141 :
142 0 : this->update(state);
143 :
144 0 : if (state.dataSwimmingPools->NumSwimmingPools > 0) HeatBalanceSurfaceManager::CalcHeatBalanceInsideSurf(state);
145 :
146 0 : this->report(state);
147 0 : }
148 :
149 0 : void GetSwimmingPool(EnergyPlusData &state)
150 : {
151 : // SUBROUTINE INFORMATION:
152 : // AUTHOR Rick Strand, Ho-Sung Kim
153 : // DATE WRITTEN October 2014
154 :
155 : // PURPOSE OF THIS SUBROUTINE:
156 : // This subroutine reads the input for all swimming pools present in
157 : // the user input file. This will contain all of the information needed
158 : // to simulate a swimming pool.
159 :
160 : // SUBROUTINE PARAMETER DEFINITIONS:
161 : static constexpr std::string_view RoutineName("GetSwimmingPool: "); // include trailing blank space
162 : static constexpr std::string_view routineName = "GetSwimmingPool";
163 :
164 0 : Real64 constexpr MinCoverFactor(0.0); // minimum value for cover factors
165 0 : Real64 constexpr MaxCoverFactor(1.0); // maximum value for cover factors
166 0 : Real64 constexpr MinDepth(0.05); // minimum average pool depth (to avoid obvious input errors)
167 0 : Real64 constexpr MaxDepth(10.0); // maximum average pool depth (to avoid obvious input errors)
168 0 : Real64 constexpr MinPowerFactor(0.0); // minimum power factor for miscellaneous equipment
169 :
170 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
171 0 : bool ErrorsFound(false); // Set to true if something goes wrong
172 0 : std::string CurrentModuleObject; // for ease in getting objects
173 0 : Array1D_string Alphas; // Alpha items for object
174 0 : Array1D_string cAlphaFields; // Alpha field names
175 0 : Array1D_string cNumericFields; // Numeric field names
176 0 : int IOStatus = 0; // Used in GetObjectItem
177 0 : Array1D<Real64> Numbers; // Numeric items for object
178 0 : int NumAlphas = 0; // Number of Alphas for each GetObjectItem call
179 0 : int NumArgs = 0; // Unused variable that is part of a subroutine call
180 0 : int NumNumbers = 0; // Number of Numbers for each GetObjectItem call
181 0 : Array1D_bool lAlphaBlanks; // Logical array, alpha field input BLANK = .TRUE.
182 0 : Array1D_bool lNumericBlanks; // Logical array, numeric field input BLANK = .TRUE.
183 :
184 : // Initializations and allocations
185 0 : int MaxAlphas = 0; // Maximum number of alphas for these input keywords
186 0 : int MaxNumbers = 0; // Maximum number of numbers for these input keywords
187 :
188 0 : state.dataInputProcessing->inputProcessor->getObjectDefMaxArgs(state, "SwimmingPool:Indoor", NumArgs, NumAlphas, NumNumbers);
189 0 : MaxAlphas = max(MaxAlphas, NumAlphas);
190 0 : MaxNumbers = max(MaxNumbers, NumNumbers);
191 :
192 0 : Alphas.allocate(MaxAlphas);
193 0 : Alphas = "";
194 0 : Numbers.allocate(MaxNumbers);
195 0 : Numbers = 0.0;
196 0 : cAlphaFields.allocate(MaxAlphas);
197 0 : cAlphaFields = "";
198 0 : cNumericFields.allocate(MaxNumbers);
199 0 : cNumericFields = "";
200 0 : lAlphaBlanks.allocate(MaxAlphas);
201 0 : lAlphaBlanks = true;
202 0 : lNumericBlanks.allocate(MaxNumbers);
203 0 : lNumericBlanks = true;
204 :
205 0 : state.dataSwimmingPools->NumSwimmingPools = state.dataInputProcessing->inputProcessor->getNumObjectsFound(state, "SwimmingPool:Indoor");
206 0 : state.dataSwimmingPools->CheckEquipName.allocate(state.dataSwimmingPools->NumSwimmingPools);
207 0 : state.dataSwimmingPools->CheckEquipName = true;
208 :
209 0 : state.dataSwimmingPools->Pool.allocate(state.dataSwimmingPools->NumSwimmingPools);
210 :
211 : // Obtain all of the user data related to indoor swimming pools...
212 0 : CurrentModuleObject = "SwimmingPool:Indoor";
213 0 : for (int Item = 1; Item <= state.dataSwimmingPools->NumSwimmingPools; ++Item) {
214 :
215 0 : state.dataInputProcessing->inputProcessor->getObjectItem(state,
216 : CurrentModuleObject,
217 : Item,
218 : Alphas,
219 : NumAlphas,
220 : Numbers,
221 : NumNumbers,
222 : IOStatus,
223 : lNumericBlanks,
224 : lAlphaBlanks,
225 : cAlphaFields,
226 : cNumericFields);
227 :
228 0 : ErrorObjectHeader eoh{routineName, CurrentModuleObject, Alphas(1)};
229 :
230 0 : Util::IsNameEmpty(state, Alphas(1), CurrentModuleObject, ErrorsFound);
231 0 : state.dataSwimmingPools->Pool(Item).Name = Alphas(1);
232 :
233 0 : state.dataSwimmingPools->Pool(Item).SurfaceName = Alphas(2);
234 0 : state.dataSwimmingPools->Pool(Item).SurfacePtr = 0;
235 :
236 0 : state.dataSwimmingPools->Pool(Item).glycol = Fluid::GetWater(state);
237 :
238 0 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
239 0 : if (Util::SameString(state.dataSurface->Surface(SurfNum).Name, state.dataSwimmingPools->Pool(Item).SurfaceName)) {
240 0 : state.dataSwimmingPools->Pool(Item).SurfacePtr = SurfNum;
241 0 : break;
242 : }
243 : }
244 :
245 0 : state.dataSwimmingPools->Pool(Item).ErrorCheckSetupPoolSurface(state, Alphas(1), Alphas(2), cAlphaFields(2), ErrorsFound);
246 :
247 0 : state.dataSwimmingPools->Pool(Item).AvgDepth = Numbers(1);
248 0 : if (state.dataSwimmingPools->Pool(Item).AvgDepth < MinDepth) {
249 0 : ShowWarningError(state, format("{}{}=\"{} has an average depth that is too small.", RoutineName, CurrentModuleObject, Alphas(1)));
250 0 : ShowContinueError(state, "The pool average depth has been reset to the minimum allowed depth.");
251 0 : } else if (state.dataSwimmingPools->Pool(Item).AvgDepth > MaxDepth) {
252 0 : ShowSevereError(state, format("{}{}=\"{} has an average depth that is too large.", RoutineName, CurrentModuleObject, Alphas(1)));
253 0 : ShowContinueError(state, "The pool depth must be less than the maximum average depth of 10 meters.");
254 0 : ErrorsFound = true;
255 : }
256 :
257 0 : if (lAlphaBlanks(3)) {
258 0 : } else if ((state.dataSwimmingPools->Pool(Item).activityFactorSched = Sched::GetSchedule(state, Alphas(3))) == nullptr) {
259 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(3), Alphas(3));
260 0 : ErrorsFound = true;
261 : }
262 :
263 0 : if (lAlphaBlanks(4)) {
264 0 : } else if ((state.dataSwimmingPools->Pool(Item).makeupWaterSupplySched = Sched::GetSchedule(state, Alphas(4))) == nullptr) {
265 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(4), Alphas(4));
266 0 : ErrorsFound = true;
267 : }
268 :
269 0 : if (lAlphaBlanks(5)) {
270 0 : } else if ((state.dataSwimmingPools->Pool(Item).coverSched = Sched::GetSchedule(state, Alphas(5))) == nullptr) {
271 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(5), Alphas(5));
272 0 : ErrorsFound = true;
273 0 : } else if (!state.dataSwimmingPools->Pool(Item).coverSched->checkMinMaxVals(state, Clusive::In, 0.0, Clusive::In, 1.0)) {
274 0 : Sched::ShowSevereBadMinMax(state, eoh, cAlphaFields(5), Alphas(5), Clusive::In, 0.0, Clusive::In, 1.0);
275 0 : ErrorsFound = true;
276 : }
277 :
278 0 : state.dataSwimmingPools->Pool(Item).CoverEvapFactor = Numbers(2);
279 0 : if (state.dataSwimmingPools->Pool(Item).CoverEvapFactor < MinCoverFactor) {
280 0 : ShowWarningError(state, format("{}{}=\"{} has an evaporation cover factor less than zero.", RoutineName, CurrentModuleObject, Alphas(1)));
281 0 : ShowContinueError(state, "The evaporation cover factor has been reset to zero.");
282 0 : state.dataSwimmingPools->Pool(Item).CoverEvapFactor = MinCoverFactor;
283 0 : } else if (state.dataSwimmingPools->Pool(Item).CoverEvapFactor > MaxCoverFactor) {
284 0 : ShowWarningError(state,
285 0 : format("{}{}=\"{} has an evaporation cover factor greater than one.", RoutineName, CurrentModuleObject, Alphas(1)));
286 0 : ShowContinueError(state, "The evaporation cover factor has been reset to one.");
287 0 : state.dataSwimmingPools->Pool(Item).CoverEvapFactor = MaxCoverFactor;
288 : }
289 :
290 0 : state.dataSwimmingPools->Pool(Item).CoverConvFactor = Numbers(3);
291 0 : if (state.dataSwimmingPools->Pool(Item).CoverConvFactor < MinCoverFactor) {
292 0 : ShowWarningError(state, format("{}{}=\"{} has a convection cover factor less than zero.", RoutineName, CurrentModuleObject, Alphas(1)));
293 0 : ShowContinueError(state, "The convection cover factor has been reset to zero.");
294 0 : state.dataSwimmingPools->Pool(Item).CoverConvFactor = MinCoverFactor;
295 0 : } else if (state.dataSwimmingPools->Pool(Item).CoverConvFactor > MaxCoverFactor) {
296 0 : ShowWarningError(state, format("{}{}=\"{} has a convection cover factor greater than one.", RoutineName, CurrentModuleObject, Alphas(1)));
297 0 : ShowContinueError(state, "The convection cover factor has been reset to one.");
298 0 : state.dataSwimmingPools->Pool(Item).CoverConvFactor = MaxCoverFactor;
299 : }
300 :
301 0 : state.dataSwimmingPools->Pool(Item).CoverSWRadFactor = Numbers(4);
302 0 : if (state.dataSwimmingPools->Pool(Item).CoverSWRadFactor < MinCoverFactor) {
303 0 : ShowWarningError(
304 : state,
305 0 : format("{}{}=\"{} has a short-wavelength radiation cover factor less than zero.", RoutineName, CurrentModuleObject, Alphas(1)));
306 0 : ShowContinueError(state, "The short-wavelength radiation cover factor has been reset to zero.");
307 0 : state.dataSwimmingPools->Pool(Item).CoverSWRadFactor = MinCoverFactor;
308 0 : } else if (state.dataSwimmingPools->Pool(Item).CoverSWRadFactor > MaxCoverFactor) {
309 0 : ShowWarningError(
310 : state,
311 0 : format("{}{}=\"{} has a short-wavelength radiation cover factor greater than one.", RoutineName, CurrentModuleObject, Alphas(1)));
312 0 : ShowContinueError(state, "The short-wavelength radiation cover factor has been reset to one.");
313 0 : state.dataSwimmingPools->Pool(Item).CoverSWRadFactor = MaxCoverFactor;
314 : }
315 :
316 0 : state.dataSwimmingPools->Pool(Item).CoverLWRadFactor = Numbers(5);
317 0 : if (state.dataSwimmingPools->Pool(Item).CoverLWRadFactor < MinCoverFactor) {
318 0 : ShowWarningError(
319 0 : state, format("{}{}=\"{} has a long-wavelength radiation cover factor less than zero.", RoutineName, CurrentModuleObject, Alphas(1)));
320 0 : ShowContinueError(state, "The long-wavelength radiation cover factor has been reset to zero.");
321 0 : state.dataSwimmingPools->Pool(Item).CoverLWRadFactor = MinCoverFactor;
322 0 : } else if (state.dataSwimmingPools->Pool(Item).CoverLWRadFactor > MaxCoverFactor) {
323 0 : ShowWarningError(
324 : state,
325 0 : format("{}{}=\"{} has a long-wavelength radiation cover factor greater than one.", RoutineName, CurrentModuleObject, Alphas(1)));
326 0 : ShowContinueError(state, "The long-wavelength radiation cover factor has been reset to one.");
327 0 : state.dataSwimmingPools->Pool(Item).CoverLWRadFactor = MaxCoverFactor;
328 : }
329 :
330 0 : state.dataSwimmingPools->Pool(Item).WaterInletNodeName = Alphas(6);
331 0 : state.dataSwimmingPools->Pool(Item).WaterOutletNodeName = Alphas(7);
332 0 : state.dataSwimmingPools->Pool(Item).WaterInletNode =
333 0 : NodeInputManager::GetOnlySingleNode(state,
334 0 : Alphas(6),
335 : ErrorsFound,
336 : DataLoopNode::ConnectionObjectType::SwimmingPoolIndoor,
337 0 : Alphas(1),
338 : DataLoopNode::NodeFluidType::Water,
339 : DataLoopNode::ConnectionType::Inlet,
340 : NodeInputManager::CompFluidStream::Primary,
341 : DataLoopNode::ObjectIsNotParent);
342 0 : state.dataSwimmingPools->Pool(Item).WaterOutletNode =
343 0 : NodeInputManager::GetOnlySingleNode(state,
344 0 : Alphas(7),
345 : ErrorsFound,
346 : DataLoopNode::ConnectionObjectType::SwimmingPoolIndoor,
347 0 : Alphas(1),
348 : DataLoopNode::NodeFluidType::Water,
349 : DataLoopNode::ConnectionType::Outlet,
350 : NodeInputManager::CompFluidStream::Primary,
351 : DataLoopNode::ObjectIsNotParent);
352 0 : if ((!lAlphaBlanks(6)) || (!lAlphaBlanks(7))) {
353 0 : BranchNodeConnections::TestCompSet(state, CurrentModuleObject, Alphas(1), Alphas(6), Alphas(7), "Hot Water Nodes");
354 : }
355 0 : state.dataSwimmingPools->Pool(Item).WaterVolFlowMax = Numbers(6);
356 0 : state.dataSwimmingPools->Pool(Item).MiscPowerFactor = Numbers(7);
357 0 : if (state.dataSwimmingPools->Pool(Item).MiscPowerFactor < MinPowerFactor) {
358 0 : ShowWarningError(state,
359 0 : format("{}{}=\"{} has a miscellaneous power factor less than zero.", RoutineName, CurrentModuleObject, Alphas(1)));
360 0 : ShowContinueError(state, "The miscellaneous power factor has been reset to zero.");
361 0 : state.dataSwimmingPools->Pool(Item).MiscPowerFactor = MinPowerFactor;
362 : }
363 :
364 0 : if (lAlphaBlanks(8)) {
365 0 : ShowSevereEmptyField(state, eoh, cAlphaFields(8));
366 0 : ErrorsFound = true;
367 0 : } else if ((state.dataSwimmingPools->Pool(Item).setPtTempSched = Sched::GetSchedule(state, Alphas(8))) == nullptr) {
368 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(8), Alphas(8));
369 0 : ErrorsFound = true;
370 : }
371 :
372 0 : state.dataSwimmingPools->Pool(Item).MaxNumOfPeople = Numbers(8);
373 0 : if (state.dataSwimmingPools->Pool(Item).MaxNumOfPeople < 0.0) {
374 0 : ShowWarningError(
375 0 : state, format("{}{}=\"{} was entered with negative people. This is not allowed.", RoutineName, CurrentModuleObject, Alphas(1)));
376 0 : ShowContinueError(state, "The number of people has been reset to zero.");
377 0 : state.dataSwimmingPools->Pool(Item).MaxNumOfPeople = 0.0;
378 : }
379 :
380 0 : if (lAlphaBlanks(9)) {
381 0 : } else if ((state.dataSwimmingPools->Pool(Item).peopleSched = Sched::GetSchedule(state, Alphas(9))) == nullptr) {
382 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(9), Alphas(9));
383 0 : ErrorsFound = true;
384 : }
385 :
386 0 : if (lAlphaBlanks(10)) {
387 0 : } else if ((state.dataSwimmingPools->Pool(Item).peopleHeatGainSched = Sched::GetSchedule(state, Alphas(10))) == nullptr) {
388 0 : ShowSevereItemNotFound(state, eoh, cAlphaFields(10), Alphas(10));
389 0 : ErrorsFound = true;
390 : }
391 : }
392 :
393 0 : Alphas.deallocate();
394 0 : Numbers.deallocate();
395 0 : cAlphaFields.deallocate();
396 0 : cNumericFields.deallocate();
397 0 : lAlphaBlanks.deallocate();
398 0 : lNumericBlanks.deallocate();
399 :
400 0 : if (ErrorsFound) {
401 0 : ShowFatalError(state, format("{}Errors found in swimming pool input. Preceding conditions cause termination.", RoutineName));
402 : }
403 0 : }
404 :
405 8 : void SwimmingPoolData::ErrorCheckSetupPoolSurface(
406 : EnergyPlusData &state, std::string_view Alpha1, std::string_view Alpha2, std::string_view cAlphaField2, bool &ErrorsFound)
407 : {
408 :
409 : static constexpr std::string_view RoutineName("ErrorCheckSetupPoolSurface: "); // include trailing blank space
410 : static constexpr std::string_view CurrentModuleObject("SwimmingPool:Indoor");
411 :
412 8 : if (this->SurfacePtr <= 0) {
413 1 : ShowSevereError(state, format("{}Invalid {} = {}", RoutineName, cAlphaField2, Alpha2));
414 1 : ShowContinueError(state, format("Occurs in {} = {}", CurrentModuleObject, Alpha1));
415 1 : ErrorsFound = true;
416 7 : } else if (state.dataSurface->SurfIsRadSurfOrVentSlabOrPool(this->SurfacePtr)) {
417 1 : ShowSevereError(state, format("{}{}=\"{}\", Invalid Surface", RoutineName, CurrentModuleObject, Alpha1));
418 1 : ShowContinueError(state, format("{}=\"{}\" has been used in another radiant system, ventilated slab, or pool.", cAlphaField2, Alpha2));
419 2 : ShowContinueError(state,
420 : "A single surface can only be a radiant system, a ventilated slab, or a pool. It CANNOT be more than one of these.");
421 1 : ErrorsFound = true;
422 : // Something present that is not allowed for a swimming pool (non-CTF algorithm, movable insulation, or radiant source/sink
423 6 : } else if (state.dataSurface->Surface(this->SurfacePtr).HeatTransferAlgorithm != DataSurfaces::HeatTransferModel::CTF) {
424 2 : ShowSevereError(state,
425 2 : format("{} is a pool and is attempting to use a non-CTF solution algorithm. This is not allowed. Use the CTF solution "
426 : "algorithm for this surface.",
427 1 : state.dataSurface->Surface(this->SurfacePtr).Name));
428 1 : ErrorsFound = true;
429 :
430 5 : } else if (state.dataSurface->Surface(this->SurfacePtr).Class == DataSurfaces::SurfaceClass::Window) {
431 2 : ShowSevereError(state,
432 2 : format("{} is a pool and is defined as a window. This is not allowed. A pool must be a floor that is NOT a window.",
433 1 : state.dataSurface->Surface(this->SurfacePtr).Name));
434 1 : ErrorsFound = true;
435 4 : } else if (state.dataSurface->intMovInsuls(this->SurfacePtr).matNum > 0) {
436 6 : ShowSevereError(state,
437 6 : format("{} is a pool and has movable insulation. This is not allowed. Remove the movable insulation for this surface.",
438 3 : state.dataSurface->Surface(this->SurfacePtr).Name));
439 3 : ErrorsFound = true;
440 1 : } else if (state.dataConstruction->Construct(state.dataSurface->Surface(this->SurfacePtr).Construction).SourceSinkPresent) {
441 0 : ShowSevereError(
442 : state,
443 0 : format("{} is a pool and uses a construction with a source/sink. This is not allowed. Use a standard construction for this surface.",
444 0 : state.dataSurface->Surface(this->SurfacePtr).Name));
445 0 : ErrorsFound = true;
446 : } else { // ( Pool( Item ).SurfacePtr > 0 )
447 1 : state.dataSurface->SurfIsRadSurfOrVentSlabOrPool(this->SurfacePtr) = true;
448 1 : state.dataSurface->SurfIsPool(this->SurfacePtr) = true;
449 1 : this->ZonePtr = state.dataSurface->Surface(this->SurfacePtr).Zone;
450 : // Check to make sure pool surface is a floor
451 1 : if (state.dataSurface->Surface(this->SurfacePtr).Class != DataSurfaces::SurfaceClass::Floor) {
452 0 : ShowSevereError(state, format("{}{}=\"{} contains a surface name that is NOT a floor.", RoutineName, CurrentModuleObject, Alpha1));
453 0 : ShowContinueError(
454 : state, "A swimming pool must be associated with a surface that is a FLOOR. Association with other surface types is not permitted.");
455 0 : ErrorsFound = true;
456 : }
457 : }
458 8 : }
459 :
460 0 : void SwimmingPoolData::initialize(EnergyPlusData &state, bool const FirstHVACIteration // true during the first HVAC iteration
461 : )
462 : {
463 : // SUBROUTINE INFORMATION:
464 : // AUTHOR Rick Strand, Ho-Sung Kim
465 : // DATE WRITTEN October 2014
466 :
467 : // PURPOSE OF THIS SUBROUTINE:
468 : // This subroutine initializes variables relating to indoor swimming pools.
469 :
470 : // SUBROUTINE PARAMETER DEFINITIONS:
471 : static constexpr std::string_view RoutineName("InitSwimmingPool");
472 0 : Real64 constexpr MinActivityFactor = 0.0; // Minimum value for activity factor
473 0 : Real64 constexpr MaxActivityFactor = 10.0; // Maximum value for activity factor (realistically)
474 :
475 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
476 0 : Real64 HeatGainPerPerson = this->peopleHeatGainSched->getCurrentVal();
477 0 : Real64 PeopleModifier = this->peopleSched->getCurrentVal();
478 :
479 0 : if (this->MyOneTimeFlag) {
480 0 : this->setupOutputVars(state); // Set up the output variables once here
481 0 : this->MyOneTimeFlag = false;
482 : }
483 :
484 0 : SwimmingPoolData::initSwimmingPoolPlantLoopIndex(state);
485 :
486 0 : if (state.dataGlobal->BeginEnvrnFlag && this->MyEnvrnFlagGeneral) {
487 0 : this->ZeroPoolSourceSumHATsurf = 0.0;
488 0 : this->QPoolSrcAvg = 0.0;
489 0 : this->HeatTransCoefsAvg = 0.0;
490 0 : this->LastQPoolSrc = 0.0;
491 0 : this->LastHeatTransCoefs = 0.0;
492 0 : this->LastSysTimeElapsed = 0.0;
493 0 : this->LastTimeStepSys = 0.0;
494 0 : this->MyEnvrnFlagGeneral = false;
495 : }
496 :
497 0 : if (!state.dataGlobal->BeginEnvrnFlag) this->MyEnvrnFlagGeneral = true;
498 :
499 0 : if (state.dataGlobal->BeginEnvrnFlag) {
500 0 : this->PoolWaterTemp = 23.0;
501 0 : this->HeatPower = 0.0;
502 0 : this->HeatEnergy = 0.0;
503 0 : this->MiscEquipPower = 0.0;
504 0 : this->MiscEquipEnergy = 0.0;
505 0 : this->WaterInletTemp = 0.0;
506 0 : this->WaterOutletTemp = 0.0;
507 0 : this->WaterMassFlowRate = 0.0;
508 0 : this->PeopleHeatGain = 0.0;
509 0 : Real64 Density = this->glycol->getDensity(state, this->PoolWaterTemp, RoutineName);
510 0 : this->WaterMass = state.dataSurface->Surface(this->SurfacePtr).Area * this->AvgDepth * Density;
511 0 : this->WaterMassFlowRateMax = this->WaterVolFlowMax * Density;
512 0 : this->initSwimmingPoolPlantNodeFlow(state);
513 : }
514 :
515 0 : if (state.dataGlobal->BeginTimeStepFlag && FirstHVACIteration) { // This is the first pass through in a particular time step
516 :
517 0 : int ZoneNum = this->ZonePtr;
518 0 : this->ZeroPoolSourceSumHATsurf =
519 0 : state.dataHeatBal->Zone(ZoneNum).sumHATsurf(state); // Set this to figure what the impact of the swimming pool on all zone surfaces
520 0 : this->QPoolSrcAvg = 0.0; // Initialize this variable to zero (pool parameters "off")
521 0 : this->HeatTransCoefsAvg = 0.0; // Initialize this variable to zero (pool parameters "off")
522 0 : this->LastQPoolSrc = 0.0; // At the start of a time step, reset to zero so average calculation can begin again
523 0 : this->LastSysTimeElapsed = 0.0; // At the start of a time step, reset to zero so average calculation can begin again
524 0 : this->LastTimeStepSys = 0.0; // At the start of a time step, reset to zero so average calculation can begin again
525 : }
526 :
527 : // initialize the flow rate for the component on the plant side (this follows standard procedure for other components like low temperature
528 : // radiant systems)
529 0 : Real64 mdot = 0.0;
530 0 : PlantUtilities::SetComponentFlowRate(state, mdot, this->WaterInletNode, this->WaterOutletNode, this->HWplantLoc);
531 0 : this->WaterInletTemp = state.dataLoopNodes->Node(this->WaterInletNode).Temp;
532 :
533 : // get the schedule values for different scheduled parameters
534 0 : if (this->activityFactorSched != nullptr) {
535 0 : this->CurActivityFactor = this->activityFactorSched->getCurrentVal();
536 0 : if (this->CurActivityFactor < MinActivityFactor) {
537 0 : this->CurActivityFactor = MinActivityFactor;
538 0 : ShowWarningError(state,
539 0 : format("{}: Swimming Pool =\"{} Activity Factor Schedule =\"{} has a negative value. This is not allowed.",
540 : RoutineName,
541 0 : this->Name,
542 0 : this->activityFactorSched->Name));
543 0 : ShowContinueError(state, "The activity factor has been reset to zero.");
544 : }
545 0 : if (this->CurActivityFactor > MaxActivityFactor) {
546 0 : this->CurActivityFactor = 1.0;
547 0 : ShowWarningError(state,
548 0 : format("{}: Swimming Pool =\"{} Activity Factor Schedule =\"{} has a value larger than 10. This is not allowed.",
549 : RoutineName,
550 0 : this->Name,
551 0 : this->activityFactorSched->Name));
552 0 : ShowContinueError(state, "The activity factor has been reset to unity.");
553 : }
554 : } else {
555 : // default is activity factor of 1.0
556 0 : this->CurActivityFactor = 1.0;
557 : }
558 :
559 0 : this->CurSetPtTemp = this->setPtTempSched->getCurrentVal();
560 :
561 0 : if (this->makeupWaterSupplySched != nullptr) {
562 0 : this->CurMakeupWaterTemp = this->makeupWaterSupplySched->getCurrentVal();
563 : } else {
564 : // use water main temperaure if no schedule present in input
565 0 : this->CurMakeupWaterTemp = state.dataEnvrn->WaterMainsTemp;
566 : }
567 :
568 : // determine the current heat gain from people
569 0 : if (this->peopleHeatGainSched != nullptr) {
570 0 : if (HeatGainPerPerson < 0.0) {
571 0 : ShowWarningError(state,
572 0 : format("{}: Swimming Pool =\"{} Heat Gain Schedule =\"{} has a negative value. This is not allowed.",
573 : RoutineName,
574 0 : this->Name,
575 0 : this->peopleHeatGainSched->Name));
576 0 : ShowContinueError(state, "The heat gain per person has been reset to zero.");
577 0 : HeatGainPerPerson = 0.0;
578 : }
579 0 : if (this->peopleSched != nullptr) {
580 0 : if (PeopleModifier < 0.0) {
581 0 : ShowWarningError(state,
582 0 : format("{}: Swimming Pool =\"{} People Schedule =\"{} has a negative value. This is not allowed.",
583 : RoutineName,
584 0 : this->Name,
585 0 : this->peopleSched->Name));
586 0 : ShowContinueError(state, "The number of people has been reset to zero.");
587 0 : PeopleModifier = 0.0;
588 : }
589 : } else { // no people schedule entered--assume that full number always present
590 0 : PeopleModifier = 1.0;
591 : }
592 : } else { // no heat gain schedule added--assume a zero value for Heat Gain per Person and no people present
593 0 : HeatGainPerPerson = 0.0;
594 0 : PeopleModifier = 0.0;
595 : }
596 0 : this->PeopleHeatGain = PeopleModifier * HeatGainPerPerson * this->MaxNumOfPeople;
597 :
598 : // once cover schedule value is established, define the current values of the cover heat transfer factors
599 0 : if (this->coverSched != nullptr) {
600 0 : this->CurCoverSchedVal = this->coverSched->getCurrentVal();
601 : // Why is this checking done here as opposed to where the schedule is first retrieved?
602 0 : if (this->CurCoverSchedVal > 1.0) {
603 0 : ShowWarningError(state,
604 0 : format("{}: Swimming Pool =\"{} Cover Schedule =\"{} has a value greater than 1.0 (100%). This is not allowed.",
605 : RoutineName,
606 0 : this->Name,
607 0 : this->coverSched->Name));
608 0 : ShowContinueError(state, "The cover has been reset to one or fully covered.");
609 0 : this->CurCoverSchedVal = 1.0;
610 0 : } else if (this->CurCoverSchedVal < 0.0) {
611 0 : ShowWarningError(state,
612 0 : format("{}: Swimming Pool =\"{} Cover Schedule =\"{} has a negative value. This is not allowed.",
613 : RoutineName,
614 0 : this->Name,
615 0 : this->coverSched->Name));
616 0 : ShowContinueError(state, "The cover has been reset to zero or uncovered.");
617 0 : this->CurCoverSchedVal = 0.0;
618 : }
619 : } else {
620 : // default is NO pool cover
621 0 : this->CurCoverSchedVal = 0.0;
622 : }
623 : // for the current cover factors, a value of 1.0 means that the pool is open (not covered)
624 : // the user input values determine the amount the pool cover degrades one of the factors
625 : // for example, if the cover reduces convection by 50% and the pool is half covered, then
626 : // the reduction factor for convection is 25% or 75% of the normal value. this establishes
627 : // the following relationships and how they are used in other parts of the code.
628 : // note that for the radiation factors, the reduction in absorption of radiation caused by
629 : // the cover will result in a net imbalance if this energy which is no longer accounted for
630 : // in the surface heat balance is not accounted for elsewhere. thus, these terms will dump
631 : // any reduced radiation into the air heat balance as an additional convective gain to avoid
632 : // any loss of energy in the overall heat balance.
633 0 : this->CurCoverEvapFac = 1.0 - (this->CurCoverSchedVal * this->CoverEvapFactor);
634 0 : this->CurCoverConvFac = 1.0 - (this->CurCoverSchedVal * this->CoverConvFactor);
635 0 : this->CurCoverSWRadFac = 1.0 - (this->CurCoverSchedVal * this->CoverSWRadFactor);
636 0 : this->CurCoverLWRadFac = 1.0 - (this->CurCoverSchedVal * this->CoverLWRadFactor);
637 0 : }
638 :
639 0 : void SwimmingPoolData::setupOutputVars(EnergyPlusData &state)
640 : {
641 0 : SetupOutputVariable(state,
642 : "Indoor Pool Makeup Water Rate",
643 : Constant::Units::m3_s,
644 0 : this->MakeUpWaterVolFlowRate,
645 : OutputProcessor::TimeStepType::System,
646 : OutputProcessor::StoreType::Average,
647 0 : this->Name);
648 0 : SetupOutputVariable(state,
649 : "Indoor Pool Makeup Water Volume",
650 : Constant::Units::m3,
651 0 : this->MakeUpWaterVol,
652 : OutputProcessor::TimeStepType::System,
653 : OutputProcessor::StoreType::Sum,
654 0 : this->Name,
655 : Constant::eResource::MainsWater,
656 : OutputProcessor::Group::HVAC,
657 : OutputProcessor::EndUseCat::Heating);
658 0 : SetupOutputVariable(state,
659 : "Indoor Pool Makeup Water Temperature",
660 : Constant::Units::C,
661 0 : this->CurMakeupWaterTemp,
662 : OutputProcessor::TimeStepType::System,
663 : OutputProcessor::StoreType::Average,
664 0 : this->Name);
665 0 : SetupOutputVariable(state,
666 : "Indoor Pool Water Temperature",
667 : Constant::Units::C,
668 0 : this->PoolWaterTemp,
669 : OutputProcessor::TimeStepType::System,
670 : OutputProcessor::StoreType::Average,
671 0 : this->Name);
672 0 : SetupOutputVariable(state,
673 : "Indoor Pool Inlet Water Temperature",
674 : Constant::Units::C,
675 0 : this->WaterInletTemp,
676 : OutputProcessor::TimeStepType::System,
677 : OutputProcessor::StoreType::Average,
678 0 : this->Name);
679 0 : SetupOutputVariable(state,
680 : "Indoor Pool Inlet Water Mass Flow Rate",
681 : Constant::Units::kg_s,
682 0 : this->WaterMassFlowRate,
683 : OutputProcessor::TimeStepType::System,
684 : OutputProcessor::StoreType::Average,
685 0 : this->Name);
686 0 : SetupOutputVariable(state,
687 : "Indoor Pool Miscellaneous Equipment Power",
688 : Constant::Units::W,
689 0 : this->MiscEquipPower,
690 : OutputProcessor::TimeStepType::System,
691 : OutputProcessor::StoreType::Average,
692 0 : this->Name);
693 0 : SetupOutputVariable(state,
694 : "Indoor Pool Miscellaneous Equipment Energy",
695 : Constant::Units::J,
696 0 : this->MiscEquipEnergy,
697 : OutputProcessor::TimeStepType::System,
698 : OutputProcessor::StoreType::Sum,
699 0 : this->Name);
700 0 : SetupOutputVariable(state,
701 : "Indoor Pool Water Heating Rate",
702 : Constant::Units::W,
703 0 : this->HeatPower,
704 : OutputProcessor::TimeStepType::System,
705 : OutputProcessor::StoreType::Average,
706 0 : this->Name);
707 0 : SetupOutputVariable(state,
708 : "Indoor Pool Water Heating Energy",
709 : Constant::Units::J,
710 0 : this->HeatEnergy,
711 : OutputProcessor::TimeStepType::System,
712 : OutputProcessor::StoreType::Sum,
713 0 : this->Name,
714 : Constant::eResource::EnergyTransfer,
715 : OutputProcessor::Group::HVAC,
716 : OutputProcessor::EndUseCat::HeatingCoils);
717 0 : SetupOutputVariable(state,
718 : "Indoor Pool Radiant to Convection by Cover",
719 : Constant::Units::W,
720 0 : this->RadConvertToConvect,
721 : OutputProcessor::TimeStepType::System,
722 : OutputProcessor::StoreType::Average,
723 0 : this->Name);
724 0 : SetupOutputVariable(state,
725 : "Indoor Pool People Heat Gain",
726 : Constant::Units::W,
727 0 : this->PeopleHeatGain,
728 : OutputProcessor::TimeStepType::System,
729 : OutputProcessor::StoreType::Average,
730 0 : this->Name);
731 0 : SetupOutputVariable(state,
732 : "Indoor Pool Current Activity Factor",
733 : Constant::Units::None,
734 0 : this->CurActivityFactor,
735 : OutputProcessor::TimeStepType::System,
736 : OutputProcessor::StoreType::Average,
737 0 : this->Name);
738 0 : SetupOutputVariable(state,
739 : "Indoor Pool Current Cover Factor",
740 : Constant::Units::None,
741 0 : this->CurCoverSchedVal,
742 : OutputProcessor::TimeStepType::System,
743 : OutputProcessor::StoreType::Average,
744 0 : this->Name);
745 0 : SetupOutputVariable(state,
746 : "Indoor Pool Evaporative Heat Loss Rate",
747 : Constant::Units::W,
748 0 : this->EvapHeatLossRate,
749 : OutputProcessor::TimeStepType::System,
750 : OutputProcessor::StoreType::Average,
751 0 : this->Name);
752 0 : SetupOutputVariable(state,
753 : "Indoor Pool Evaporative Heat Loss Energy",
754 : Constant::Units::J,
755 0 : this->EvapEnergyLoss,
756 : OutputProcessor::TimeStepType::System,
757 : OutputProcessor::StoreType::Sum,
758 0 : this->Name);
759 0 : SetupOutputVariable(state,
760 : "Indoor Pool Saturation Pressure at Pool Temperature",
761 : Constant::Units::Pa,
762 0 : this->SatPressPoolWaterTemp,
763 : OutputProcessor::TimeStepType::System,
764 : OutputProcessor::StoreType::Average,
765 0 : this->Name);
766 0 : SetupOutputVariable(state,
767 : "Indoor Pool Partial Pressure of Water Vapor in Air",
768 : Constant::Units::Pa,
769 0 : this->PartPressZoneAirTemp,
770 : OutputProcessor::TimeStepType::System,
771 : OutputProcessor::StoreType::Average,
772 0 : this->Name);
773 0 : SetupOutputVariable(state,
774 : "Indoor Pool Current Cover Evaporation Factor",
775 : Constant::Units::None,
776 0 : this->CurCoverEvapFac,
777 : OutputProcessor::TimeStepType::System,
778 : OutputProcessor::StoreType::Average,
779 0 : this->Name);
780 0 : SetupOutputVariable(state,
781 : "Indoor Pool Current Cover Convective Factor",
782 : Constant::Units::None,
783 0 : this->CurCoverConvFac,
784 : OutputProcessor::TimeStepType::System,
785 : OutputProcessor::StoreType::Average,
786 0 : this->Name);
787 0 : SetupOutputVariable(state,
788 : "Indoor Pool Current Cover SW Radiation Factor",
789 : Constant::Units::None,
790 0 : this->CurCoverSWRadFac,
791 : OutputProcessor::TimeStepType::System,
792 : OutputProcessor::StoreType::Average,
793 0 : this->Name);
794 0 : SetupOutputVariable(state,
795 : "Indoor Pool Current Cover LW Radiation Factor",
796 : Constant::Units::None,
797 0 : this->CurCoverLWRadFac,
798 : OutputProcessor::TimeStepType::System,
799 : OutputProcessor::StoreType::Average,
800 0 : this->Name);
801 0 : }
802 :
803 2 : void SwimmingPoolData::initSwimmingPoolPlantLoopIndex(EnergyPlusData &state)
804 : {
805 : // SUBROUTINE INFORMATION:
806 : // AUTHOR Rick Strand
807 : // DATE WRITTEN June 2017
808 :
809 : static constexpr std::string_view RoutineName("InitSwimmingPoolPlantLoopIndex");
810 :
811 2 : if (this->MyPlantScanFlagPool && allocated(state.dataPlnt->PlantLoop)) {
812 2 : bool errFlag = false;
813 2 : if (this->WaterInletNode > 0) {
814 6 : PlantUtilities::ScanPlantLoopsForObject(
815 4 : state, this->Name, DataPlant::PlantEquipmentType::SwimmingPool_Indoor, this->HWplantLoc, errFlag, _, _, _, this->WaterInletNode, _);
816 2 : if (errFlag) {
817 0 : ShowFatalError(state, format("{}: Program terminated due to previous condition(s).", RoutineName));
818 : }
819 : }
820 2 : this->MyPlantScanFlagPool = false;
821 0 : } else if (this->MyPlantScanFlagPool && !state.dataGlobal->AnyPlantInModel) {
822 0 : this->MyPlantScanFlagPool = false;
823 : }
824 2 : }
825 :
826 2 : void SwimmingPoolData::initSwimmingPoolPlantNodeFlow(EnergyPlusData &state) const
827 : {
828 :
829 2 : if (!this->MyPlantScanFlagPool) {
830 2 : if (this->WaterInletNode > 0) {
831 2 : PlantUtilities::InitComponentNodes(state, 0.0, this->WaterMassFlowRateMax, this->WaterInletNode, this->WaterOutletNode);
832 2 : PlantUtilities::RegisterPlantCompDesignFlow(state, this->WaterInletNode, this->WaterVolFlowMax);
833 : }
834 : }
835 2 : }
836 :
837 0 : void SwimmingPoolData::calculate(EnergyPlusData &state)
838 : {
839 : // SUBROUTINE INFORMATION:
840 : // AUTHOR Rick Strand, Ho-Sung Kim
841 : // DATE WRITTEN October 2014
842 :
843 : // PURPOSE OF THIS SUBROUTINE:
844 : // This subroutine simulates the components making up the Indoor Swimming Pool model.
845 :
846 : // METHODOLOGY EMPLOYED:
847 : // The swimming pool is modeled as a SURFACE to get access to all of the existing
848 : // surface related algorithms. This subroutine mainly models the components of the
849 : // swimming pool so that information can be used in a standard surface heat balance.
850 : // The pool is assumed to be located at the inside surface face with a possible cover
851 : // affecting the heat balance. The pool model takes the form of an equation solving
852 : // for the inside surface temperature which is assumed to be the same as the pool
853 : // water temperature.
854 : // Standard Heat Balance Equation:
855 : // SurfTempInTmp( SurfNum ) = ( SurfCTFConstInPart( SurfNum ) + SurfQRadThermInAbs( SurfNum ) + SurfOpaqQRadSWInAbs( SurfNum ) + HConvIn(
856 : // SurfNum
857 : //)
858 : //* RefAirTemp( SurfNum ) + SurfNetLWRadToSurf( SurfNum ) + Construct( ConstrNum ).CTFSourceIn( 0 ) * SurfQsrcHist( 1, SurfNum ) +
859 : // SurfQdotRadHVACInPerArea( SurfNum ) + IterDampConst * SurfTempInsOld(
860 : // SurfNum ) + Construct( ConstrNum ).CTFCross( 0 ) * TH11 ) / ( Construct( ConstrNum ).CTFInside( 0 ) + HConvIn( SurfNum ) + IterDampConst );
861 : //// Constant part of conduction eq (history terms) | LW radiation from internal sources | SW radiation from internal sources | Convection
862 : // from surface to zone air | Net radiant exchange with other zone surfaces | Heat source/sink term for radiant systems | (if there is one
863 : // present) | Radiant flux from high temp radiant heater | Radiant flux from a hot water baseboard heater | Radiant flux from a steam
864 : // baseboard heater | Radiant flux from an electric baseboard heater | Iterative damping term (for stability) | Current conduction from | the
865 : // outside surface | Coefficient for conduction (current time) | Convection and damping term
866 : // That equation is modified to include pool specific terms and removes the IterDampConst
867 : // term which is for iterations within the inside surface heat balance. Then, the resulting
868 : // equation is solved for the plant loop mass flow rate. It also assigns the appropriate
869 : // terms for use in the actual heat balance routine.
870 :
871 : // REFERENCES:
872 : // 1. ASHRAE (2011). 2011 ASHRAE Handbook - HVAC Applications. Atlanta: American Society of Heating,
873 : // Refrigerating and Air-Conditioning Engineers, Inc., p.5.6-5.9.
874 : // 2. Janis, R. and W. Tao (2005). Mechanical and Electrical Systems in Buildings. 3rd ed. Upper
875 : // Saddle River, NJ: Pearson Education, Inc., p.246.
876 : // 3. Kittler, R. (1989). Indoor Natatorium Design and Energy Recycling. ASHRAE Transactions 95(1), p.521-526.
877 : // 4. Smith, C., R. Jones, and G. Lof (1993). Energy Requirements and Potential Savings for Heated
878 : // Indoor Swimming Pools. ASHRAE Transactions 99(2), p.864-874.
879 :
880 : // SUBROUTINE PARAMETER DEFINITIONS:
881 : static constexpr std::string_view RoutineName("CalcSwimmingPool");
882 :
883 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
884 0 : Real64 EvapRate = 0.0; // evaporation rate for pool in kg/s
885 :
886 : // initialize local variables
887 0 : int SurfNum = this->SurfacePtr; // surface number of floor that is the pool
888 0 : int ZoneNum = state.dataSurface->Surface(SurfNum).Zone; // index to zone array
889 0 : auto &thisZoneHB = state.dataZoneTempPredictorCorrector->zoneHeatBalance(ZoneNum);
890 :
891 : // Convection coefficient calculation
892 : Real64 HConvIn =
893 0 : 0.22 * std::pow(std::abs(this->PoolWaterTemp - thisZoneHB.MAT), 1.0 / 3.0) * this->CurCoverConvFac; // convection coefficient for pool
894 0 : calcSwimmingPoolEvap(state, EvapRate, SurfNum, thisZoneHB.MAT, thisZoneHB.airHumRat);
895 0 : this->MakeUpWaterMassFlowRate = EvapRate;
896 0 : Real64 EvapEnergyLossPerArea = -EvapRate *
897 0 : Psychrometrics::PsyHfgAirFnWTdb(thisZoneHB.airHumRat,
898 : thisZoneHB.MAT) /
899 0 : state.dataSurface->Surface(SurfNum).Area; // energy effect of evaporation rate per unit area in W/m2
900 0 : this->EvapHeatLossRate = EvapEnergyLossPerArea * state.dataSurface->Surface(SurfNum).Area;
901 : // LW and SW radiation term modification: any "excess" radiation blocked by the cover gets convected
902 : // to the air directly and added to the zone air heat balance
903 0 : Real64 LWsum = (state.dataHeatBal->SurfQdotRadIntGainsInPerArea(SurfNum) + state.dataHeatBalSurf->SurfQdotRadNetLWInPerArea(SurfNum) +
904 0 : state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(SurfNum)); // summation of all long-wavelenth radiation going to surface
905 0 : Real64 LWtotal = this->CurCoverLWRadFac * LWsum; // total flux from long-wavelength radiation to surface
906 : Real64 SWtotal =
907 0 : this->CurCoverSWRadFac * state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(SurfNum); // total flux from short-wavelength radiation to surface
908 0 : this->RadConvertToConvect =
909 0 : ((1.0 - this->CurCoverLWRadFac) * LWsum) + ((1.0 - this->CurCoverSWRadFac) * state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(SurfNum));
910 :
911 : // Heat gain from people (assumed to be all convective to pool water)
912 : Real64 PeopleGain =
913 0 : this->PeopleHeatGain / state.dataSurface->Surface(SurfNum).Area; // heat gain from people in pool (assumed to be all convective)
914 :
915 : // Get an estimate of the pool water specific heat
916 0 : Real64 Cp = this->glycol->getSpecificHeat(state, this->PoolWaterTemp, RoutineName); // specific heat of pool water
917 :
918 0 : Real64 TH22 = state.dataHeatBalSurf->SurfInsideTempHist(2)(
919 0 : SurfNum); // inside surface temperature at the previous time step equals the old pool water temperature
920 0 : Real64 Tmuw = this->CurMakeupWaterTemp; // Inlet makeup water temperature
921 0 : Real64 TLoopInletTemp = state.dataLoopNodes->Node(this->WaterInletNode).Temp; // Inlet water temperature from the plant loop
922 0 : this->WaterInletTemp = TLoopInletTemp;
923 :
924 : Real64 MassFlowRate;
925 0 : this->calcMassFlowRate(state, MassFlowRate, TH22, TLoopInletTemp);
926 :
927 0 : PlantUtilities::SetComponentFlowRate(state, MassFlowRate, this->WaterInletNode, this->WaterOutletNode, this->HWplantLoc);
928 0 : this->WaterMassFlowRate = MassFlowRate;
929 :
930 : // We now have a flow rate so we can assemble the terms needed for the surface heat balance that is solved for the inside face temperature
931 0 : state.dataHeatBalFanSys->QPoolSurfNumerator(SurfNum) =
932 0 : SWtotal + LWtotal + PeopleGain + EvapEnergyLossPerArea + HConvIn * thisZoneHB.MAT +
933 0 : (EvapRate * Tmuw + MassFlowRate * TLoopInletTemp + (this->WaterMass * TH22 / state.dataGlobal->TimeStepZoneSec)) * Cp /
934 0 : state.dataSurface->Surface(SurfNum).Area;
935 0 : state.dataHeatBalFanSys->PoolHeatTransCoefs(SurfNum) =
936 0 : HConvIn + (EvapRate + MassFlowRate + (this->WaterMass / state.dataGlobal->TimeStepZoneSec)) * Cp / state.dataSurface->Surface(SurfNum).Area;
937 :
938 : // Finally take care of the latent and convective gains resulting from the pool
939 0 : state.dataHeatBalFanSys->SumConvPool(ZoneNum) += this->RadConvertToConvect;
940 0 : state.dataHeatBalFanSys->SumLatentPool(ZoneNum) += EvapRate * Psychrometrics::PsyHfgAirFnWTdb(thisZoneHB.airHumRat, thisZoneHB.MAT);
941 0 : }
942 :
943 6 : void SwimmingPoolData::calcMassFlowRate(EnergyPlusData &state, Real64 &massFlowRate, Real64 TH22, Real64 TLoopInletTemp)
944 : {
945 : // Calculate the mass flow rate to achieve the proper setpoint temperature
946 6 : if (TLoopInletTemp != this->CurSetPtTemp) {
947 4 : massFlowRate = this->WaterMass / state.dataHVACGlobal->TimeStepSysSec * (this->CurSetPtTemp - TH22) / (TLoopInletTemp - this->CurSetPtTemp);
948 : } else { // avoid the divide by zero, reset later if necessary
949 2 : massFlowRate = 0.0;
950 : }
951 6 : if (massFlowRate > this->WaterMassFlowRateMax) {
952 1 : massFlowRate = this->WaterMassFlowRateMax;
953 5 : } else if (massFlowRate <= 0.0) {
954 : // trap case where loop temperature is lower than the setpoint but could still do heating Defect 10317
955 4 : if (TLoopInletTemp > TH22 && TLoopInletTemp <= this->CurSetPtTemp) {
956 2 : massFlowRate = this->WaterMassFlowRateMax;
957 : } else {
958 2 : massFlowRate = 0.0;
959 : }
960 : }
961 6 : }
962 :
963 2 : void SwimmingPoolData::calcSwimmingPoolEvap(EnergyPlusData &state,
964 : Real64 &EvapRate, // evaporation rate of pool
965 : int const SurfNum, // surface index
966 : Real64 const MAT, // mean air temperature
967 : Real64 const HumRat // zone air humidity ratio
968 : )
969 : {
970 : static constexpr std::string_view RoutineName("CalcSwimmingPoolEvap");
971 2 : Real64 constexpr CFinHg(0.00029613); // Multiple pressure in Pa by this constant to get inches of Hg
972 :
973 : // Evaporation calculation:
974 : // Evaporation Rate (lb/h) = 0.1 * Area (ft2) * Activity Factor * (Psat,pool - Ppar,air) (in Hg)
975 : // So evaporation rate, area, and pressures have to be converted to standard E+ units (kg/s, m2, and Pa, respectively)
976 : // Evaporation Rate per Area = Evaporation Rate * Heat of Vaporization / Area of Surface
977 :
978 2 : Real64 PSatPool = Psychrometrics::PsyPsatFnTemp(state, this->PoolWaterTemp, RoutineName);
979 : Real64 PParAir =
980 2 : Psychrometrics::PsyPsatFnTemp(state, MAT, RoutineName) * Psychrometrics::PsyRhFnTdbWPb(state, MAT, HumRat, state.dataEnvrn->OutBaroPress);
981 2 : if (PSatPool < PParAir) PSatPool = PParAir;
982 2 : this->SatPressPoolWaterTemp = PSatPool;
983 2 : this->PartPressZoneAirTemp = PParAir;
984 2 : EvapRate = (0.1 * (state.dataSurface->Surface(SurfNum).Area / DataConversions::CFA) * this->CurActivityFactor * ((PSatPool - PParAir) * CFinHg)) *
985 2 : DataConversions::CFMF * this->CurCoverEvapFac;
986 2 : }
987 :
988 0 : void SwimmingPoolData::update(EnergyPlusData &state)
989 : {
990 : // SUBROUTINE INFORMATION:
991 : // AUTHOR Rick Strand, Ho-Sung Kim
992 : // DATE WRITTEN October 2014
993 :
994 : // PURPOSE OF THIS SUBROUTINE:
995 : // This subroutine does any updating that needs to be done for the swimming pool model.
996 :
997 0 : int SurfNum = this->SurfacePtr; // surface number/pointer
998 :
999 0 : if (this->LastSysTimeElapsed == state.dataHVACGlobal->SysTimeElapsed) {
1000 : // Still iterating or reducing system time step, so subtract old values which were
1001 : // not valid
1002 0 : this->QPoolSrcAvg -= this->LastQPoolSrc * this->LastTimeStepSys / state.dataGlobal->TimeStepZone;
1003 0 : this->HeatTransCoefsAvg -= this->LastHeatTransCoefs * this->LastTimeStepSys / state.dataGlobal->TimeStepZone;
1004 : }
1005 :
1006 : // Update the running average and the "last" values with the current values of the appropriate variables
1007 0 : this->QPoolSrcAvg += state.dataHeatBalFanSys->QPoolSurfNumerator(SurfNum) * state.dataHVACGlobal->TimeStepSys / state.dataGlobal->TimeStepZone;
1008 0 : this->HeatTransCoefsAvg +=
1009 0 : state.dataHeatBalFanSys->PoolHeatTransCoefs(SurfNum) * state.dataHVACGlobal->TimeStepSys / state.dataGlobal->TimeStepZone;
1010 :
1011 0 : this->LastQPoolSrc = state.dataHeatBalFanSys->QPoolSurfNumerator(SurfNum);
1012 0 : this->LastHeatTransCoefs = state.dataHeatBalFanSys->PoolHeatTransCoefs(SurfNum);
1013 0 : this->LastSysTimeElapsed = state.dataHVACGlobal->SysTimeElapsed;
1014 0 : this->LastTimeStepSys = state.dataHVACGlobal->TimeStepSys;
1015 :
1016 0 : PlantUtilities::SafeCopyPlantNode(state, this->WaterInletNode, this->WaterOutletNode);
1017 :
1018 0 : Real64 WaterMassFlow = state.dataLoopNodes->Node(this->WaterInletNode).MassFlowRate; // water mass flow rate
1019 0 : if (WaterMassFlow > 0.0) state.dataLoopNodes->Node(this->WaterOutletNode).Temp = this->PoolWaterTemp;
1020 0 : }
1021 :
1022 0 : void SwimmingPoolData::oneTimeInit_new([[maybe_unused]] EnergyPlusData &state)
1023 : {
1024 0 : }
1025 :
1026 0 : void SwimmingPoolData::oneTimeInit([[maybe_unused]] EnergyPlusData &state)
1027 : {
1028 0 : }
1029 :
1030 1 : void SwimmingPoolData::report(EnergyPlusData &state)
1031 : {
1032 : // SUBROUTINE INFORMATION:
1033 : // AUTHOR Rick Strand, Ho-Sung Kim
1034 : // DATE WRITTEN October 2014
1035 :
1036 : // PURPOSE OF THIS SUBROUTINE:
1037 : // This subroutine simply produces output for the swimming pool model.
1038 :
1039 : // SUBROUTINE PARAMETER DEFINITIONS:
1040 : static constexpr std::string_view RoutineName("SwimmingPoolData::report");
1041 1 : Real64 constexpr MinDensity = 1.0; // to avoid a divide by zero
1042 :
1043 1 : int SurfNum = this->SurfacePtr; // surface number index
1044 :
1045 : // First transfer the surface inside temperature data to the current pool water temperature
1046 1 : this->PoolWaterTemp = state.dataHeatBalSurf->SurfInsideTempHist(1)(SurfNum);
1047 :
1048 : // Next calculate the amount of heating done by the plant loop
1049 1 : Real64 Cp = this->glycol->getSpecificHeat(state, this->PoolWaterTemp, RoutineName); // specific heat of water
1050 1 : this->HeatPower = this->WaterMassFlowRate * Cp * (this->WaterInletTemp - this->PoolWaterTemp);
1051 :
1052 : // Now the power consumption of miscellaneous equipment
1053 1 : Real64 Density = this->glycol->getDensity(state, this->PoolWaterTemp, RoutineName); // density of water
1054 1 : if (Density > MinDensity) {
1055 1 : this->MiscEquipPower = this->MiscPowerFactor * this->WaterMassFlowRate / Density;
1056 : } else {
1057 0 : this->MiscEquipPower = 0.0;
1058 : }
1059 :
1060 : // Also the radiant exchange converted to convection by the pool cover
1061 1 : this->RadConvertToConvectRep = this->RadConvertToConvect * state.dataSurface->Surface(SurfNum).Area;
1062 :
1063 : // Finally calculate the summed up report variables
1064 1 : Real64 thisTimeStepSysSec = state.dataHVACGlobal->TimeStepSysSec;
1065 1 : this->MiscEquipEnergy = this->MiscEquipPower * thisTimeStepSysSec;
1066 1 : this->HeatEnergy = this->HeatPower * thisTimeStepSysSec;
1067 1 : this->MakeUpWaterMass = this->MakeUpWaterMassFlowRate * thisTimeStepSysSec;
1068 1 : this->EvapEnergyLoss = this->EvapHeatLossRate * thisTimeStepSysSec;
1069 :
1070 1 : this->MakeUpWaterVolFlowRate = MakeUpWaterVolFlowFunct(this->MakeUpWaterMassFlowRate, Density);
1071 1 : this->MakeUpWaterVol = MakeUpWaterVolFunct(this->MakeUpWaterMass, Density);
1072 1 : }
1073 :
1074 249949 : void UpdatePoolSourceValAvg(EnergyPlusData &state, bool &SwimmingPoolOn) // .TRUE. if the swimming pool "runs" this zone time step
1075 : {
1076 : // SUBROUTINE INFORMATION:
1077 : // AUTHOR Rick Strand
1078 : // DATE WRITTEN October 2014
1079 :
1080 : // PURPOSE OF THIS SUBROUTINE:
1081 : // To transfer the average value of the pool heat balance term over the entire zone time step back to the heat balance routines so that the
1082 : // heat balance algorithms can simulate one last time with the average source to maintain some reasonable amount of continuity and energy
1083 : // balance in the temperature and flux histories.
1084 :
1085 : // METHODOLOGY EMPLOYED:
1086 : // All of the record keeping for the average term is done in the Update routine so the only other thing that this subroutine does is check to
1087 : // see if the system was even on. If any average term is non-zero, then one or more of the swimming pools was running. Method borrowed from
1088 : // radiant systems.
1089 :
1090 : // SUBROUTINE PARAMETER DEFINITIONS:
1091 249949 : Real64 constexpr CloseEnough(0.01); // Some arbitrarily small value to avoid zeros and numbers that are almost the same
1092 :
1093 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
1094 249949 : SwimmingPoolOn = false;
1095 :
1096 : // If there are no pools, then just RETURN
1097 :
1098 249949 : if (state.dataSwimmingPools->NumSwimmingPools == 0) return;
1099 :
1100 12 : for (int PoolNum = 1; PoolNum <= state.dataSwimmingPools->NumSwimmingPools; ++PoolNum) {
1101 8 : auto const &thisPool = state.dataSwimmingPools->Pool(PoolNum);
1102 8 : if (thisPool.QPoolSrcAvg != 0.0) SwimmingPoolOn = true;
1103 8 : int SurfNum = thisPool.SurfacePtr; // surface number index
1104 8 : state.dataHeatBalFanSys->QPoolSurfNumerator(SurfNum) = thisPool.QPoolSrcAvg;
1105 8 : state.dataHeatBalFanSys->PoolHeatTransCoefs(SurfNum) = thisPool.HeatTransCoefsAvg;
1106 : }
1107 :
1108 : // For interzone surfaces, QPoolSrcAvg was only updated for the "active" side. The active side
1109 : // would have a non-zero value at this point. If the numbers differ, then we have to manually update.
1110 12 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
1111 8 : if (state.dataSurface->Surface(SurfNum).ExtBoundCond > 0 && state.dataSurface->Surface(SurfNum).ExtBoundCond != SurfNum) {
1112 0 : if (std::abs(state.dataHeatBalFanSys->QPoolSurfNumerator(SurfNum) -
1113 0 : state.dataHeatBalFanSys->QPoolSurfNumerator(state.dataSurface->Surface(SurfNum).ExtBoundCond)) >
1114 : CloseEnough) { // numbers differ
1115 0 : if (std::abs(state.dataHeatBalFanSys->QPoolSurfNumerator(SurfNum)) >
1116 0 : std::abs(state.dataHeatBalFanSys->QPoolSurfNumerator(state.dataSurface->Surface(SurfNum).ExtBoundCond))) {
1117 0 : state.dataHeatBalFanSys->QPoolSurfNumerator(state.dataSurface->Surface(SurfNum).ExtBoundCond) =
1118 0 : state.dataHeatBalFanSys->QPoolSurfNumerator(SurfNum);
1119 : } else {
1120 0 : state.dataHeatBalFanSys->QPoolSurfNumerator(SurfNum) =
1121 0 : state.dataHeatBalFanSys->QPoolSurfNumerator(state.dataSurface->Surface(SurfNum).ExtBoundCond);
1122 : }
1123 : }
1124 : }
1125 : }
1126 : // For interzone surfaces, PoolHeatTransCoefs was only updated for the "active" side. The active side
1127 : // would have a non-zero value at this point. If the numbers differ, then we have to manually update.
1128 12 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; ++SurfNum) {
1129 8 : if (state.dataSurface->Surface(SurfNum).ExtBoundCond > 0 && state.dataSurface->Surface(SurfNum).ExtBoundCond != SurfNum) {
1130 0 : if (std::abs(state.dataHeatBalFanSys->PoolHeatTransCoefs(SurfNum) -
1131 0 : state.dataHeatBalFanSys->PoolHeatTransCoefs(state.dataSurface->Surface(SurfNum).ExtBoundCond)) >
1132 : CloseEnough) { // numbers differ
1133 0 : if (std::abs(state.dataHeatBalFanSys->PoolHeatTransCoefs(SurfNum)) >
1134 0 : std::abs(state.dataHeatBalFanSys->PoolHeatTransCoefs(state.dataSurface->Surface(SurfNum).ExtBoundCond))) {
1135 0 : state.dataHeatBalFanSys->PoolHeatTransCoefs(state.dataSurface->Surface(SurfNum).ExtBoundCond) =
1136 0 : state.dataHeatBalFanSys->PoolHeatTransCoefs(SurfNum);
1137 : } else {
1138 0 : state.dataHeatBalFanSys->PoolHeatTransCoefs(SurfNum) =
1139 0 : state.dataHeatBalFanSys->PoolHeatTransCoefs(state.dataSurface->Surface(SurfNum).ExtBoundCond);
1140 : }
1141 : }
1142 : }
1143 : }
1144 : }
1145 :
1146 5 : Real64 MakeUpWaterVolFlowFunct(Real64 MakeUpWaterMassFlowRate, Real64 Density)
1147 : {
1148 5 : return MakeUpWaterMassFlowRate / Density;
1149 : }
1150 :
1151 5 : Real64 MakeUpWaterVolFunct(Real64 MakeUpWaterMass, Real64 Density)
1152 : {
1153 5 : return MakeUpWaterMass / Density;
1154 : }
1155 :
1156 : } // namespace EnergyPlus::SwimmingPool
|