Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2024, 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 <cstdlib>
50 :
51 : // ObjexxFCL Headers
52 : #include <ObjexxFCL/Fmath.hh>
53 :
54 : // EnergyPlus Headers
55 : #include <EnergyPlus/CommandLineInterface.hh>
56 : #include <EnergyPlus/Data/EnergyPlusData.hh>
57 : #include <EnergyPlus/DataEnvironment.hh>
58 : #include <EnergyPlus/DataGlobalConstants.hh>
59 : #include <EnergyPlus/General.hh>
60 : #include <EnergyPlus/Psychrometrics.hh>
61 : #include <EnergyPlus/UtilityRoutines.hh>
62 :
63 : namespace EnergyPlus {
64 :
65 : #ifdef EP_nocache_Psychrometrics
66 : #undef EP_cache_PsyTwbFnTdbWPb
67 : #undef EP_cache_PsyPsatFnTemp
68 : #undef EP_cache_PsyTsatFnPb
69 : #undef EP_cache_PsyTsatFnHPb
70 : #else
71 : #define EP_cache_PsyTwbFnTdbWPb
72 : #define EP_cache_PsyPsatFnTemp
73 : #define EP_cache_PsyTsatFnPb
74 : #define EP_cache_PsyTsatFnHPb
75 : #endif
76 :
77 : namespace Psychrometrics {
78 : // Module containing the Psychometric simulation routines
79 :
80 : // MODULE INFORMATION:
81 : // AUTHOR Linda Lawrie
82 : // DATE WRITTEN December 1998
83 : // MODIFIED February 2010
84 : // RE-ENGINEERED Jan 2004: Rahul Chillar
85 :
86 : // PURPOSE OF THIS MODULE:
87 : // This module provides a repository for the psychrometric routines.
88 :
89 : // METHODOLOGY EMPLOYED:
90 : // na
91 :
92 : // REFERENCES:
93 : // na
94 :
95 : // OTHER NOTES:
96 : // Todo after 2.2 release:
97 : // remove restriction on MAX(W, 1d-5)
98 : // more research on hfg calc
99 :
100 : // Using/Aliasing
101 : #ifdef EP_psych_errors
102 : using namespace DataEnvironment;
103 : #endif
104 :
105 : // Use Statements for other routines
106 : #ifdef EP_psych_errors
107 :
108 : #endif
109 :
110 796 : void InitializePsychRoutines([[maybe_unused]] EnergyPlusData &state)
111 : {
112 :
113 : // SUBROUTINE INFORMATION:
114 : // AUTHOR Linda Lawrie
115 : // DATE WRITTEN March 2013
116 : // MODIFIED na
117 : // RE-ENGINEERED na
118 :
119 : // PURPOSE OF THIS SUBROUTINE:
120 : // Initializes some variables for PsychRoutines
121 :
122 : #ifdef EP_cache_PsyTwbFnTdbWPb
123 796 : state.dataPsychCache->cached_Twb.fill(cached_twb_t());
124 : #endif
125 : #ifdef EP_cache_PsyPsatFnTemp
126 796 : state.dataPsychCache->cached_Psat.fill(cached_psat_t());
127 : #endif
128 : #ifdef EP_cache_PsyTsatFnPb
129 796 : state.dataPsychCache->cached_Tsat.fill(cached_tsat_h_pb());
130 : #endif
131 : #ifdef EP_cache_PsyTsatFnHPb
132 796 : state.dataPsychCache->cached_Tsat_HPb.fill(cached_tsat_h_pb());
133 : #endif
134 796 : }
135 :
136 794 : void ShowPsychrometricSummary([[maybe_unused]] EnergyPlusData &state, [[maybe_unused]] InputOutputFile &auditFile)
137 : {
138 :
139 : // SUBROUTINE INFORMATION:
140 : // AUTHOR Linda Lawrie
141 : // DATE WRITTEN August 2011
142 : // MODIFIED na
143 : // RE-ENGINEERED na
144 :
145 : // PURPOSE OF THIS SUBROUTINE:
146 : // Provides a Psychrometric summary report to the audit file.
147 : // Maybe later to the .eio file.
148 :
149 : // METHODOLOGY EMPLOYED:
150 : // na
151 :
152 : // REFERENCES:
153 : // na
154 :
155 : // Using/Aliasing
156 :
157 : // Locals
158 : // SUBROUTINE ARGUMENT DEFINITIONS:
159 : // na
160 :
161 : // SUBROUTINE PARAMETER DEFINITIONS:
162 :
163 : // INTERFACE BLOCK SPECIFICATIONS:
164 : // na
165 :
166 : // DERIVED TYPE DEFINITIONS:
167 : // na
168 :
169 : // SUBROUTINE LOCAL VARIABLE DECLARATIONS:
170 : #ifdef EP_psych_stats
171 : int Loop;
172 : Real64 AverageIterations;
173 :
174 : if (!auditFile.good()) return;
175 : for (int item : state.dataPsychCache->NumTimesCalled) {
176 : if (item) { // if item is greater than 0
177 : print(auditFile, "RoutineName,#times Called,Avg Iterations\n");
178 : for (Loop = 0; Loop < static_cast<int>(PsychrometricFunction::Num); ++Loop) {
179 : if (!PsyReportIt[Loop]) continue;
180 : const std::string istring = fmt::to_string(state.dataPsychCache->NumTimesCalled[Loop]);
181 : if (state.dataPsychCache->NumIterations[Loop] > 0) {
182 : AverageIterations = double(state.dataPsychCache->NumIterations[Loop]) / double(state.dataPsychCache->NumTimesCalled[Loop]);
183 : print(auditFile, "{},{},{:.2R}\n", PsyRoutineNames[Loop], istring, AverageIterations);
184 : } else {
185 : print(auditFile, "{},{}\n", PsyRoutineNames[Loop], istring);
186 : }
187 : }
188 : }
189 : }
190 : #endif
191 794 : }
192 :
193 : #ifdef EP_psych_errors
194 0 : void PsyRhoAirFnPbTdbW_error(EnergyPlusData &state,
195 : Real64 const pb, // barometric pressure (Pascals)
196 : Real64 const tdb, // dry bulb temperature (Celsius)
197 : Real64 const dw, // humidity ratio (kgWater/kgDryAir)
198 : Real64 const rhoair, // density of air
199 : std::string_view const CalledFrom // routine this function was called from (error messages) !unused1208
200 : )
201 : {
202 : // Using/Aliasing
203 :
204 0 : if (rhoair < 0.0) {
205 0 : ShowSevereError(state, format("PsyRhoAirFnPbTdbW: RhoAir (Density of Air) is calculated <= 0 [{:.5R}].", rhoair));
206 0 : ShowContinueError(state, format("pb =[{:.2R}], tdb=[{:.2R}], w=[{:.7R}].", pb, tdb, dw));
207 0 : if (!CalledFrom.empty()) {
208 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
209 : } else {
210 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
211 : }
212 0 : ShowFatalError(state, "Program terminates due to preceding condition.");
213 : }
214 0 : }
215 : #endif
216 :
217 : #ifdef EP_psych_errors
218 0 : void PsyRhFnTdbRhovLBnd0C_error(EnergyPlusData &state,
219 : Real64 const Tdb, // dry-bulb temperature {C}
220 : Real64 const Rhovapor, // vapor density in air {kg/m3}
221 : Real64 const RHValue, // relative humidity value (0.0-1.0)
222 : std::string_view const CalledFrom // routine this function was called from (error messages)
223 : )
224 : {
225 0 : if (RHValue > 1.01) {
226 0 : if (!state.dataGlobal->WarmupFlag) {
227 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbRhovLBnd0C)] == 0) {
228 0 : state.dataPsychrometrics->String =
229 0 : format(" Dry-Bulb= {:.2T} Rhovapor= {:.3T} Calculated Relative Humidity [%]= {:.2T}", Tdb, Rhovapor, RHValue * 100.0);
230 0 : ShowWarningMessage(state, "Calculated Relative Humidity out of range (PsyRhFnTdbRhovLBnd0C) ");
231 0 : if (!CalledFrom.empty()) {
232 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
233 : } else {
234 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
235 : }
236 0 : ShowContinueError(state, state.dataPsychrometrics->String);
237 0 : ShowContinueError(state, "Relative Humidity being reset to 100.0%");
238 : }
239 0 : ShowRecurringWarningErrorAtEnd(state,
240 : "Calculated Relative Humidity out of range (PsyRhFnTdbRhovLBnd0C)",
241 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbRhovLBnd0C)],
242 0 : RHValue * 100.0,
243 0 : RHValue * 100.0,
244 : _,
245 : "%",
246 : "%");
247 : }
248 0 : } else if (RHValue < -0.05) {
249 0 : if (!state.dataGlobal->WarmupFlag) {
250 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbRhovLBnd0C)] == 0) {
251 0 : state.dataPsychrometrics->String =
252 0 : format(" Dry-Bulb= {:.2T} Rhovapor= {:.3T} Calculated Relative Humidity [%]= {:.2T}", Tdb, Rhovapor, RHValue * 100.0);
253 0 : ShowWarningMessage(state, "Calculated Relative Humidity out of range (PsyRhFnTdbRhovLBnd0C) ");
254 0 : if (!CalledFrom.empty()) {
255 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
256 : } else {
257 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
258 : }
259 0 : ShowContinueError(state, state.dataPsychrometrics->String);
260 0 : ShowContinueError(state, "Relative Humidity being reset to 1%");
261 : }
262 0 : ShowRecurringWarningErrorAtEnd(state,
263 : "Calculated Relative Humidity out of range (PsyRhFnTdbRhovLBnd0C)",
264 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbRhovLBnd0C)],
265 0 : RHValue * 100.0,
266 0 : RHValue * 100.0,
267 : _,
268 : "%",
269 : "%");
270 : }
271 : }
272 0 : }
273 : #endif
274 :
275 : #ifdef EP_cache_PsyTwbFnTdbWPb
276 :
277 211275508 : Real64 PsyTwbFnTdbWPb(EnergyPlusData &state,
278 : Real64 const Tdb, // dry-bulb temperature {C}
279 : Real64 const W, // humidity ratio
280 : Real64 const Pb, // barometric pressure {Pascals}
281 : std::string_view const CalledFrom // routine this function was called from (error messages)
282 : )
283 : {
284 :
285 : // FUNCTION INFORMATION:
286 : // AUTHOR Linda Lawrie/Amir Roth
287 : // DATE WRITTEN August 2011
288 : // MODIFIED na
289 : // RE-ENGINEERED na
290 :
291 : // PURPOSE OF THIS FUNCTION:
292 : // Provide a "cache" of results for the given arguments and wetbulb (twb) output result.
293 :
294 : // METHODOLOGY EMPLOYED:
295 : // Use grid shifting and masking to provide hash into the cache. Use Equivalence to
296 : // make Fortran ignore "types".
297 :
298 : // REFERENCES:
299 : // na
300 :
301 : // USE STATEMENTS:
302 : // na
303 :
304 : // Return value
305 : Real64 Twb_result; // result=> Temperature Wet-Bulb {C}
306 :
307 : // Locals
308 : // FUNCTION ARGUMENT DEFINITIONS:
309 :
310 : // FUNCTION PARAMETER DEFINITIONS:
311 211275508 : std::uint64_t constexpr Grid_Shift = 64 - 12 - twbprecision_bits;
312 :
313 : // INTERFACE BLOCK SPECIFICATIONS:
314 : // na
315 :
316 : // DERIVED TYPE DEFINITIONS:
317 : // na
318 :
319 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
320 :
321 : #ifdef EP_psych_stats
322 : ++state.dataPsychCache->NumTimesCalled[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb_cache)];
323 : #endif
324 :
325 : DISABLE_WARNING_PUSH
326 : DISABLE_WARNING_STRICT_ALIASING
327 : // cppcheck-suppress invalidPointerCast
328 211275508 : std::uint64_t Tdb_tag = *reinterpret_cast<std::uint64_t const *>(&Tdb) >> Grid_Shift;
329 : // cppcheck-suppress invalidPointerCast
330 211275508 : std::uint64_t W_tag = *reinterpret_cast<std::uint64_t const *>(&W) >> Grid_Shift;
331 : // cppcheck-suppress invalidPointerCast
332 211275508 : std::uint64_t Pb_tag = *reinterpret_cast<std::uint64_t const *>(&Pb) >> Grid_Shift;
333 : DISABLE_WARNING_POP
334 :
335 211275508 : std::uint64_t hash = (Tdb_tag ^ (W_tag ^ Pb_tag)) & std::uint64_t(twbcache_size - 1);
336 :
337 211275508 : auto &cached_Twb = state.dataPsychCache->cached_Twb;
338 :
339 211275508 : if (cached_Twb[hash].iTdb != Tdb_tag || cached_Twb[hash].iW != W_tag || cached_Twb[hash].iPb != Pb_tag) {
340 8799787 : cached_Twb[hash].iTdb = Tdb_tag;
341 8799787 : cached_Twb[hash].iW = W_tag;
342 8799787 : cached_Twb[hash].iPb = Pb_tag;
343 :
344 : DISABLE_WARNING_PUSH
345 : DISABLE_WARNING_STRICT_ALIASING
346 8799787 : Tdb_tag <<= Grid_Shift;
347 : // cppcheck-suppress invalidPointerCast
348 8799787 : Real64 Tdb_tag_r = *reinterpret_cast<Real64 const *>(&Tdb_tag);
349 :
350 8799787 : W_tag <<= Grid_Shift;
351 : // cppcheck-suppress invalidPointerCast
352 8799787 : Real64 W_tag_r = *reinterpret_cast<Real64 const *>(&W_tag);
353 :
354 8799787 : Pb_tag <<= Grid_Shift;
355 : // cppcheck-suppress invalidPointerCast
356 8799787 : Real64 Pb_tag_r = *reinterpret_cast<Real64 const *>(&Pb_tag);
357 : DISABLE_WARNING_POP
358 :
359 8799787 : cached_Twb[hash].Twb = PsyTwbFnTdbWPb_raw(state, Tdb_tag_r, W_tag_r, Pb_tag_r, CalledFrom);
360 : }
361 :
362 : // Twbresult_last = cached_Twb(hash)%Twb
363 : // Twb_result = Twbresult_last
364 211275508 : Twb_result = cached_Twb[hash].Twb;
365 :
366 211275508 : return Twb_result;
367 : }
368 :
369 8799787 : Real64 PsyTwbFnTdbWPb_raw(EnergyPlusData &state,
370 : Real64 const TDB, // dry-bulb temperature {C}
371 : Real64 const dW, // humidity ratio
372 : Real64 const Patm, // barometric pressure {Pascals}
373 : std::string_view const CalledFrom // routine this function was called from (error messages)
374 : )
375 :
376 : #else
377 :
378 : Real64 PsyTwbFnTdbWPb(EnergyPlusData &state,
379 : Real64 const TDB, // dry-bulb temperature {C}
380 : Real64 const dW, // humidity ratio
381 : Real64 const Patm, // barometric pressure {Pascals}
382 : std::string_view const CalledFrom // routine this function was called from (error messages)
383 : )
384 : #endif
385 : {
386 :
387 : // FUNCTION INFORMATION:
388 : // AUTHOR George Shih
389 : // DATE WRITTEN May 1976
390 : // MODIFIED na
391 : // RE-ENGINEERED Dec 2003; Rahul Chillar
392 : // 2011; as time saving measure, cache some values.
393 :
394 : // PURPOSE OF THIS FUNCTION:
395 : // This function provides the wet-bulb temperature from dry-bulb temperature,
396 : // humidity ratio and barometric pressure.
397 :
398 : // METHODOLOGY EMPLOYED:
399 : // Uses an Iterative procedure to calculate WetBulbTemperature
400 :
401 : // Using/Aliasing
402 : using General::Iterate;
403 :
404 : // Return value
405 : Real64 TWB; // result=> Temperature Wet-Bulb {C}
406 :
407 : // FUNCTION PARAMETER DEFINITIONS:
408 8799787 : int constexpr itmax(100); // Maximum No of Iterations
409 :
410 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
411 : Real64 tBoil; // Boiling temperature of water at given pressure
412 : Real64 newW; // Humidity ratio calculated with wet bulb guess
413 : Real64 W; // Humidity ratio entered and corrected as necessary
414 : Real64 ResultX; // ResultX is the final Iteration result passed back to the calling routine
415 : Real64 WBT; // Current Value of WetBulbTemperature
416 : Real64 error; // Deviation of dependent variable in iteration
417 : Real64 X1; // Independent variable in ITERATE
418 : Real64 Y1; // Dependent variable in ITERATE
419 : Real64 Wstar; // Humidity ratio as a function of Sat Press of Wet Bulb
420 : Real64 PSatstar; // Saturation pressure at wet bulb temperature
421 : int iter; // Iteration counter
422 : int icvg; // Iteration convergence flag
423 : bool FlagError; // set when errors should be flagged
424 :
425 : #ifdef EP_psych_stats
426 : ++state.dataPsychCache->NumTimesCalled[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb)];
427 : #endif
428 :
429 : // CHECK TDB IN RANGE.
430 8799787 : FlagError = false;
431 : #ifdef EP_psych_errors
432 8799787 : if (TDB <= -100.0 || TDB >= 200.0) {
433 0 : if (!state.dataGlobal->WarmupFlag) {
434 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb)] == 0) {
435 0 : ShowWarningMessage(state, "Temperature out of range [-100. to 200.] (PsyTwbFnTdbWPb)");
436 0 : if (!CalledFrom.empty()) {
437 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
438 : } else {
439 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
440 : }
441 0 : ShowContinueError(state, format(" Input Temperature={:.2T}", TDB));
442 0 : FlagError = true;
443 : }
444 0 : ShowRecurringWarningErrorAtEnd(state,
445 : "Temperature out of range [-100. to 200.] (PsyTwbFnTdbWPb)",
446 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb)],
447 : TDB,
448 : TDB,
449 : _,
450 : "C",
451 : "C");
452 : }
453 : }
454 : #endif
455 :
456 8799787 : W = dW;
457 8799787 : if (W < 0.0) {
458 : #ifdef EP_psych_errors
459 0 : if (W <= -0.0001) {
460 0 : if (!state.dataGlobal->WarmupFlag) {
461 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb2)] == 0) {
462 0 : state.dataPsychrometrics->String = format(" Dry-Bulb= {:.2T} Humidity Ratio= {:.3T} Pressure= {:.2T}", TDB, W, Patm);
463 0 : ShowWarningMessage(state, "Entered Humidity Ratio invalid (PsyTwbFnTdbWPb)");
464 0 : if (!CalledFrom.empty()) {
465 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
466 : } else {
467 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
468 : }
469 0 : ShowContinueError(state, state.dataPsychrometrics->String);
470 0 : state.dataPsychrometrics->String = format("Humidity Ratio= {:.4T}", W);
471 0 : ShowContinueError(state, format("{} ... Humidity Ratio set to .00001", state.dataPsychrometrics->String));
472 : }
473 0 : ShowRecurringWarningErrorAtEnd(state,
474 : "Entered Humidity Ratio invalid (PsyTwbFnTdbWPb)",
475 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb2)],
476 : W,
477 : W,
478 : _,
479 : "[]",
480 : "[]");
481 : }
482 : }
483 : #endif
484 0 : W = 1.0e-5;
485 : }
486 :
487 : // Initial temperature guess at atmospheric pressure
488 8799787 : if (Patm != state.dataPsychrometrics->last_Patm) {
489 : tBoil =
490 107765 : PsyTsatFnPb(state, Patm, (CalledFrom.empty() ? PsyRoutineNames[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb)] : CalledFrom));
491 107765 : state.dataPsychrometrics->last_Patm = Patm;
492 107765 : state.dataPsychrometrics->last_tBoil = tBoil;
493 : } else {
494 8692022 : tBoil = state.dataPsychrometrics->last_tBoil;
495 : }
496 :
497 : // Set initial guess of WetBulbTemp=Entering Dry Bulb Temperature
498 8799787 : WBT = TDB;
499 :
500 : // Initializing value for iter
501 8799787 : iter = 0;
502 :
503 : // Begin iteration loop
504 62816769 : for (iter = 1; iter <= itmax; ++iter) {
505 :
506 : // Assigning a value to WBT
507 62816734 : if (WBT >= (tBoil - 0.09)) {
508 97 : WBT = tBoil - 0.1;
509 : }
510 :
511 : // Determine the saturation pressure for wet bulb temperature
512 : PSatstar =
513 62816734 : PsyPsatFnTemp(state, WBT, (CalledFrom.empty() ? PsyRoutineNames[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb)] : CalledFrom));
514 :
515 : // Determine humidity ratio for given saturation pressure
516 62816734 : Wstar = 0.62198 * PSatstar / (Patm - PSatstar);
517 :
518 : // Calculate new humidity ratio and determine difference from known
519 : // humidity ratio which is wStar calculated earlier
520 62816734 : if (WBT >= 0.0) {
521 62049061 : newW = ((2501.0 - 2.326 * WBT) * Wstar - 1.006 * (TDB - WBT)) / (2501.0 + 1.86 * TDB - 4.186 * WBT);
522 : } else {
523 767673 : newW = ((2830.0 - 0.24 * WBT) * Wstar - 1.006 * (TDB - WBT)) / (2830.0 + 1.86 * TDB - 2.1 * WBT);
524 : }
525 :
526 : // Check error, if not satisfied, calculate new guess and iterate
527 62816734 : error = W - newW;
528 :
529 : // Using Iterative Procedure to Calculate WetBulb
530 62816734 : Iterate(ResultX, state.dataPsychrometrics->iconvTol, WBT, error, X1, Y1, iter, icvg);
531 62816734 : WBT = ResultX;
532 :
533 : // If converged, leave iteration loop.
534 62816734 : if (icvg == 1) {
535 8799752 : break;
536 : }
537 :
538 : } // End of Iteration Loop
539 :
540 : #ifdef EP_psych_stats
541 : state.dataPsychCache->NumIterations[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb)] += iter;
542 : #endif
543 :
544 : // Wet bulb temperature has not converged after maximum specified
545 : // iterations. Print error message, set return error flag, and RETURN
546 : #ifdef EP_psych_errors
547 8799787 : if (iter > itmax) {
548 35 : if (!state.dataGlobal->WarmupFlag) {
549 5 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb3)] == 0) {
550 3 : ShowWarningMessage(state, format("WetBulb not converged after {} iterations(PsyTwbFnTdbWPb)", iter));
551 3 : if (!CalledFrom.empty()) {
552 1 : ShowContinueErrorTimeStamp(state, format(" Routine={},", CalledFrom));
553 : } else {
554 2 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
555 : }
556 3 : ShowContinueError(state, format(" Input Temperature = {:.2T}", TDB));
557 3 : ShowContinueError(state, format(" Input Humidity Ratio= {:.6T}", W));
558 3 : ShowContinueError(state, format(" Input Pressure = {:.2T}", Patm));
559 3 : FlagError = true;
560 : }
561 10 : ShowRecurringWarningErrorAtEnd(state,
562 : "WetBulb not converged after max iterations(PsyTwbFnTdbWPb)",
563 5 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TwbFnTdbWPb3)]);
564 : }
565 : }
566 : #endif
567 :
568 : // Result is Temperature Wet Bulb
569 8799787 : TWB = WBT;
570 :
571 : #ifdef EP_psych_errors
572 8799787 : if (FlagError) {
573 3 : ShowContinueError(state, format(" Resultant Temperature= {:.2T}", WBT));
574 : }
575 : #endif
576 :
577 : // If (TempWetBulb)>(Dry Bulb Temp) , Setting (TempWetBulb)=(DryBulbTemp).
578 8799787 : if (TWB > TDB) {
579 41005 : TWB = TDB;
580 : }
581 :
582 : #ifdef generatetestdata
583 : print(IOFiles::getSingleton().debug, "{}{}{}{}", TDB, dW, Patm, Twb);
584 : #endif
585 :
586 8799787 : return TWB;
587 : }
588 :
589 : #ifdef EP_psych_errors
590 0 : void PsyVFnTdbWPb_error(EnergyPlusData &state,
591 : Real64 const TDB, // dry-bulb temperature {C}
592 : Real64 const w, // humidity ratio
593 : Real64 const PB, // barometric pressure {Pascals}
594 : Real64 const V, // specific volume {m3/kg}
595 : std::string_view const CalledFrom // routine this function was called from (error messages)
596 : )
597 : {
598 0 : if (V <= -0.01) {
599 0 : if (!state.dataGlobal->WarmupFlag) {
600 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::VFnTdbWPb)] == 0) {
601 0 : state.dataPsychrometrics->String = format(" Dry-Bulb= {:.2T} Humidity Ratio= {:.3T} Pressure= {:.2T}", TDB, w, PB);
602 0 : ShowWarningMessage(state, "Calculated Specific Volume out of range (PsyVFnTdbWPb)");
603 0 : if (!CalledFrom.empty()) {
604 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
605 : } else {
606 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
607 : }
608 0 : ShowContinueError(state, state.dataPsychrometrics->String);
609 0 : state.dataPsychrometrics->String = format("Calculated Volume= {:.3T}", V);
610 0 : ShowContinueError(state, format("{} ... Since Calculated Volume < 0.0, it is set to .83", state.dataPsychrometrics->String));
611 : }
612 0 : ShowRecurringWarningErrorAtEnd(state,
613 : "Calculated Specific Volume out of range (PsyVFnTdbWPb)",
614 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::VFnTdbWPb)],
615 : V,
616 : V,
617 : _,
618 : "m3/kg",
619 : "m3/kg");
620 : }
621 : }
622 0 : }
623 : #endif
624 :
625 : #ifdef EP_psych_errors
626 94 : void PsyWFnTdbH_error(EnergyPlusData &state,
627 : Real64 const TDB, // dry-bulb temperature {C}
628 : Real64 const H, // enthalpy {J/kg}
629 : Real64 const W, // humidity ratio
630 : std::string_view const CalledFrom // routine this function was called from (error messages)
631 : )
632 : {
633 94 : if (W < -0.0001) {
634 94 : if (!state.dataGlobal->WarmupFlag) {
635 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdbH)] == 0) {
636 0 : state.dataPsychrometrics->String = format(" Dry-Bulb= {:.2T} Enthalpy= {:.3T}", TDB, H);
637 0 : ShowWarningMessage(state, "Calculated Humidity Ratio invalid (PsyWFnTdbH)");
638 0 : if (!CalledFrom.empty()) {
639 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
640 : } else {
641 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
642 : }
643 0 : ShowContinueError(state, state.dataPsychrometrics->String);
644 0 : state.dataPsychrometrics->String = format("Calculated Humidity Ratio= {:.4T}", W);
645 0 : ShowContinueError(state, format("{} ... Humidity Ratio set to .00001", state.dataPsychrometrics->String));
646 : }
647 0 : ShowRecurringWarningErrorAtEnd(state,
648 : "Calculated Humidity Ratio invalid (PsyWFnTdbH)",
649 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdbH)],
650 : W,
651 : W,
652 : _,
653 : "[]",
654 : "[]");
655 : }
656 : }
657 94 : }
658 : #endif
659 :
660 : #ifdef EP_cache_PsyPsatFnTemp
661 :
662 465601563 : Real64 PsyPsatFnTemp_raw([[maybe_unused]] EnergyPlusData &state,
663 : Real64 const T, // dry-bulb temperature {C}
664 : [[maybe_unused]] std::string_view const CalledFrom // routine this function was called from (error messages)
665 : )
666 :
667 : #else
668 :
669 : Real64 PsyPsatFnTemp(EnergyPlusData &state,
670 : Real64 const T, // dry-bulb temperature {C}
671 : [[maybe_unused]] std::string_view const CalledFrom // routine this function was called from (error messages)
672 : )
673 : #endif
674 : {
675 : // FUNCTION INFORMATION:
676 : // AUTHOR George Shih
677 : // DATE WRITTEN May 1976
678 : // MODIFIED NA
679 : // RE-ENGINEERED Nov 2003; Rahul Chillar
680 :
681 : // PURPOSE OF THIS FUNCTION:
682 : // This function provides the saturation pressure as a function of temperature.
683 :
684 : // METHODOLOGY EMPLOYED:
685 : // Hyland & Wexler Formulation, range -100C to 200C
686 :
687 : // REFERENCES:
688 : // ASHRAE HANDBOOK OF FUNDAMENTALS, 2005, Chap 6 (Psychrometrics), Eqn 5 & 6.
689 : // Compared to Table 3 values (August 2007) with average error of 0.00%, max .30%,
690 : // min -.39%. (Spreadsheet available on request - Lawrie).
691 :
692 : // Note: the ASHRAE Handbook of Fundamentals is being slightly inaccurate in its wording,
693 : // and referring to Eq 5 applying to -100°C to 0°C and Eq 6 applying to 0°C to 200°C
694 : // In fact, it is **not** 0°C, but the triple-point of water, which is 0.01°C. Evaluating the Eq 5 and 6 up to and from the triple-point
695 : // is removing the discontinuity altogether.
696 :
697 : // USE STATEMENTS:
698 :
699 : // Return value
700 : Real64 Pascal; // result=> saturation pressure {Pascals}
701 :
702 : // Locals
703 : // FUNCTION ARGUMENT DEFINITIONS:
704 :
705 : // FUNCTION PARAMETER DEFINITIONS:
706 :
707 : // INTERFACE BLOCK SPECIFICATIONS
708 : // na
709 :
710 : // DERIVED TYPE DEFINITIONS
711 : // na
712 :
713 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
714 :
715 : #ifdef EP_psych_stats
716 : ++state.dataPsychCache->NumTimesCalled[static_cast<int>(PsychrometricFunction::PsatFnTemp)];
717 : #endif
718 :
719 : // CHECK T IN RANGE.
720 : #ifdef EP_psych_errors
721 465601563 : if (!state.dataGlobal->WarmupFlag) {
722 79755385 : if (T <= -100.0 || T >= 200.0) {
723 7 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::PsatFnTemp)] == 0) {
724 3 : ShowWarningMessage(state, "Temperature out of range [-100. to 200.] (PsyPsatFnTemp)");
725 3 : if (!CalledFrom.empty()) {
726 3 : ShowContinueErrorTimeStamp(state, format(" Routine={},", CalledFrom));
727 : } else {
728 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
729 : }
730 3 : ShowContinueError(state, format(" Input Temperature={:.2T}", T));
731 : }
732 14 : ShowRecurringWarningErrorAtEnd(state,
733 : "Temperature out of range [-100. to 200.] (PsyPsatFnTemp)",
734 7 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::PsatFnTemp)],
735 : T,
736 : T,
737 : _,
738 : "C",
739 : "C");
740 : }
741 : }
742 : #endif
743 :
744 : // Convert temperature from Centigrade to Kelvin.
745 465601563 : Real64 const Tkel(T + Constant::Kelvin); // Dry-bulb in REAL(r64) for function passing
746 :
747 : // If below -100C,set value of Pressure corresponding to Saturation Temperature of -100C.
748 465601563 : if (Tkel < 173.15) {
749 609 : Pascal = 0.001405102123874164;
750 :
751 : // If below freezing, calculate saturation pressure over ice.
752 465600954 : } else if (Tkel < Constant::TriplePointOfWaterTempKelvin) { // Tkel >= 173.15, Tkel < 273.16 (0.01°C)
753 25658503 : Real64 constexpr C1(-5674.5359);
754 25658503 : Real64 constexpr C2(6.3925247);
755 25658503 : Real64 constexpr C3(-0.9677843e-2);
756 25658503 : Real64 constexpr C4(0.62215701e-6);
757 25658503 : Real64 constexpr C5(0.20747825e-8);
758 25658503 : Real64 constexpr C6(-0.9484024e-12);
759 25658503 : Real64 constexpr C7(4.1635019);
760 25658503 : Pascal = std::exp(C1 / Tkel + C2 + Tkel * (C3 + Tkel * (C4 + Tkel * (C5 + C6 * Tkel))) + C7 * std::log(Tkel));
761 :
762 : // If above freezing, calculate saturation pressure over liquid water.
763 439942451 : } else if (Tkel <= 473.15) { // Tkel >= 173.15 // Tkel >= TriplePointOfWaterTempKelvin
764 : #ifndef EP_IF97
765 439942091 : Real64 constexpr C8(-5800.2206);
766 439942091 : Real64 constexpr C9(1.3914993);
767 439942091 : Real64 constexpr C10(-0.048640239);
768 439942091 : Real64 constexpr C11(0.41764768e-4);
769 439942091 : Real64 constexpr C12(-0.14452093e-7);
770 439942091 : Real64 constexpr C13(6.5459673);
771 439942091 : Pascal = std::exp(C8 / Tkel + C9 + Tkel * (C10 + Tkel * (C11 + Tkel * C12)) + C13 * std::log(Tkel));
772 :
773 : // If above 200C, set value of Pressure corresponding to Saturation Temperature of 200C.
774 : } else { // Tkel >= 173.15 // Tkel >= TriplePointOfWaterTempKelvin // Tkel > 473.15
775 360 : Pascal = 1555073.745636215;
776 : }
777 : #else
778 : // Table 34 in IF97
779 : Real64 constexpr N1(0.11670521452767e04);
780 : Real64 constexpr N2(-0.72421316703206e06);
781 : Real64 constexpr N3(-0.17073846940092e02);
782 : Real64 constexpr N4(0.12020824702470e05);
783 : Real64 constexpr N5(-0.32325550322333e07);
784 : Real64 constexpr N6(0.14915108613530e02);
785 : Real64 constexpr N7(-0.48232657361591e04);
786 : Real64 constexpr N8(0.40511340542057e06);
787 : Real64 constexpr N9(-0.23855557567849);
788 : Real64 constexpr N10(0.65017534844798e03);
789 : // !IF97 equations
790 : Real64 const phi = Tkel + N9 / (Tkel - N10); // IF97 equation 29b
791 : Real64 const phi2 = phi * phi; // phi squared
792 : Real64 const A = phi2 + N1 * phi + N2;
793 : Real64 const B = N3 * phi2 + N4 * phi + N5;
794 : Real64 const C = N6 * phi2 + N7 * phi + N8;
795 : Pascal = 1000000.0 * pow_4((2.0 * C) / (-B + std::sqrt((B * B) - 4.0 * A * C)));
796 :
797 : // If above 200C, set value of Pressure corresponding to Saturation Temperature of 200C.
798 : } else { // Tkel >= 173.15 // Tkel >= KelvinConv // Tkel > 473.15
799 : Pascal = 1554671.8682698254;
800 : }
801 : #endif
802 465601563 : return Pascal;
803 : }
804 :
805 : #ifdef EP_psych_errors
806 0 : void PsyWFnTdbTwbPb_temperature_error(EnergyPlusData &state,
807 : Real64 const TDB, // dry-bulb temperature {C}
808 : Real64 const TWB, // wet-bulb temperature {C}
809 : Real64 const PB, // barometric pressure {Pascals}
810 : std::string_view const CalledFrom // routine this function was called from (error messages)
811 : )
812 : {
813 0 : if (TWB > (TDB + 0.01)) {
814 0 : if (state.dataPsychrometrics->ReportErrors && !state.dataGlobal->WarmupFlag) {
815 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdbTwbPb)] == 0) {
816 0 : state.dataPsychrometrics->String = format(" Dry-Bulb= {:.2T} Pressure= {:.2T}", TDB, PB);
817 0 : ShowWarningMessage(state, "Given Wet Bulb Temperature invalid (PsyWFnTdbTwbPb)");
818 0 : if (!CalledFrom.empty()) {
819 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
820 : } else {
821 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
822 : }
823 0 : ShowContinueError(state, state.dataPsychrometrics->String);
824 0 : state.dataPsychrometrics->String = format("Calculated Wet-Bulb= {:.2T}", TWB);
825 0 : ShowContinueError(state,
826 0 : format("{} ... Since Dry Bulb < Wet Bulb, Wet Bulb set = to Dry Bulb", state.dataPsychrometrics->String));
827 : }
828 0 : ShowRecurringWarningErrorAtEnd(state,
829 : "Given Wet Bulb Temperature invalid (PsyWFnTdbTwbPb)",
830 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdbTwbPb)],
831 : TWB,
832 : TWB,
833 : _,
834 : "C",
835 : "C");
836 : }
837 : }
838 0 : }
839 : #endif
840 :
841 : #ifdef EP_psych_errors
842 320 : void PsyWFnTdbTwbPb_humidity_error(EnergyPlusData &state,
843 : Real64 const TDB, // dry-bulb temperature {C}
844 : Real64 const TWB, // wet-bulb temperature {C}
845 : Real64 const PB, // barometric pressure {Pascals}
846 : Real64 const W, // humidity ratio
847 : std::string_view const CalledFrom // routine this function was called from (error messages)
848 : )
849 : {
850 :
851 320 : if (W < 0.0) {
852 320 : if (state.dataPsychrometrics->ReportErrors && !state.dataGlobal->WarmupFlag) {
853 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdbTwbPb2) == 0]) {
854 0 : state.dataPsychrometrics->String = format(" Dry-Bulb= {:.2T} Wet-Bulb= {:.2T} Pressure= {:.2T}", TDB, TWB, PB);
855 0 : ShowWarningMessage(state, "Calculated Humidity Ratio Invalid (PsyWFnTdbTwbPb)");
856 0 : if (!CalledFrom.empty()) {
857 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
858 : } else {
859 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
860 : }
861 0 : ShowContinueError(state, state.dataPsychrometrics->String);
862 0 : state.dataPsychrometrics->String = format("Calculated Humidity Ratio= {:.4T}, will recalculate Humidity Ratio", W);
863 0 : ShowContinueError(
864 0 : state, format("{} using Relative Humidity .01% (and Dry-Bulb and Pressure as shown)", state.dataPsychrometrics->String));
865 : }
866 0 : ShowRecurringWarningErrorAtEnd(state,
867 : "Calculated Humidity Ratio Invalid (PsyWFnTdbTwbPb)",
868 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdbTwbPb2)],
869 : W,
870 : W,
871 : _,
872 : "[]",
873 : "[]");
874 : }
875 : }
876 320 : }
877 : #endif
878 :
879 : #ifdef EP_psych_errors
880 0 : void PsyTdpFnTdbTwbPb_error(EnergyPlusData &state,
881 : Real64 const TDB, // dry-bulb temperature {C}
882 : Real64 const TWB, // wet-bulb temperature {C}
883 : Real64 const PB, // barometric pressure (N/M**2) {Pascals}
884 : Real64 const W, // humidity ratio
885 : Real64 const TDP, // dew-point temperature {C}
886 : std::string_view const CalledFrom // routine this function was called from (error messages)
887 : )
888 : {
889 0 : if (TDP > TWB + 0.1) {
890 0 : if (!state.dataGlobal->WarmupFlag) { // Display error message
891 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TdpFnTdbTwbPb)] == 0) {
892 0 : ShowWarningMessage(state, "Calculated Dew Point Temperature being reset (PsyTdpFnTdbTwbPb)");
893 0 : if (!CalledFrom.empty()) {
894 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
895 : } else {
896 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
897 : }
898 0 : state.dataPsychrometrics->String =
899 0 : format(" Dry-bulb={:.2T} Wet-Bulb (WB)= {:.2T} Pressure= {:.2T} Humidity Ratio={:.3T}", TDB, TWB, PB, W);
900 0 : ShowContinueError(state, state.dataPsychrometrics->String);
901 0 : state.dataPsychrometrics->String =
902 0 : format(" Calculated Dew Point Temperature (DPT)= {:.2T}; Since DPT > WB, DPT will be set to WB", TDP);
903 0 : ShowContinueError(state, state.dataPsychrometrics->String);
904 : }
905 0 : ShowRecurringWarningErrorAtEnd(state,
906 : "Calculated Dew Point Temperature being reset (PsyTdpFnTdbTwbPb)",
907 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TdpFnTdbTwbPb)],
908 : TDP,
909 : TDP,
910 : _,
911 : "C",
912 : "C");
913 : }
914 : }
915 0 : }
916 : #endif
917 :
918 : #ifdef EP_cache_PsyTsatFnHPb
919 57462832 : Real64 PsyTsatFnHPb_raw(EnergyPlusData &state,
920 : Real64 const H, // enthalpy {J/kg}
921 : Real64 const PB, // barometric pressure {Pascals}
922 : [[maybe_unused]] std::string_view const CalledFrom // routine this function was called from (error messages)
923 : )
924 : #else
925 : Real64 PsyTsatFnHPb(EnergyPlusData &state,
926 : Real64 const H, // enthalpy {J/kg}
927 : Real64 const PB, // barometric pressure {Pascals}
928 : std::string_view const CalledFrom // routine this function was called from (error messages)
929 : )
930 : #endif
931 : {
932 :
933 : // FUNCTION INFORMATION:
934 : // AUTHOR George Shih
935 : // DATE WRITTEN May 1976
936 : // MODIFIED July 2003; LKL -- peg min/max values (outside range of functions)
937 : // RE-ENGINEERED na
938 :
939 : // PURPOSE OF THIS FUNCTION:
940 : // This function provides the saturation temperature from the enthalpy
941 : // and barometric pressure.
942 :
943 : // REFERENCES:
944 : // ASHRAE HANDBOOK OF FUNDAMENTALS, 1972, P99, EQN 22
945 :
946 : // Return value
947 : Real64 T; // result=> saturation temperature {C}
948 :
949 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
950 : Real64 T1; // APPROXIMATE SATURATION TEMPERATURE (C)
951 : Real64 T2; // APPROXIMATE SATURATION TEMPERATURE (C)
952 : Real64 TN; // NEW ASSUMED SATURATION TEMPERATURE (C)
953 : Real64 H1; // APPROXIMATE ENTHALPY (J/KG)
954 : Real64 H2; // APPROXIMATE ENTHALPY (J/KG)
955 : Real64 Y1; // ERROR IN ENTHALPY
956 : Real64 Y2; // ERROR IN ENTHALPY
957 : int IterCount;
958 : Real64 HH; // temporary enthalpy (calculation) value
959 : bool FlagError; // Set when errors should be flagged
960 : Real64 Hloc; // local value of H
961 :
962 57462832 : HH = H + 1.78637e4;
963 :
964 57462832 : if (H >= 0.0) {
965 57195494 : Hloc = max(0.00001, H);
966 267338 : } else if (H < 0.0) {
967 267338 : Hloc = min(-0.00001, H);
968 : }
969 :
970 : #ifdef EP_psych_stats
971 : ++state.dataPsychCache->NumTimesCalled[static_cast<int>(PsychrometricFunction::TsatFnHPb)];
972 : #endif
973 :
974 57462832 : FlagError = false;
975 : #ifdef EP_psych_errors
976 57462832 : if (HH <= -4.24E4 || HH >= 4.5866E7) {
977 294 : if (!state.dataGlobal->WarmupFlag) {
978 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TsatFnHPb)] == 0) {
979 0 : ShowWarningMessage(state, "Enthalpy out of range (PsyTsatFnHPb)");
980 0 : if (!CalledFrom.empty()) {
981 0 : ShowContinueErrorTimeStamp(state, format(" Routine={},", CalledFrom));
982 : } else {
983 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
984 : }
985 0 : state.dataPsychrometrics->String = format(" Enthalpy={:.5T} Pressure= {:.2T}", HH, PB);
986 0 : ShowContinueError(state, state.dataPsychrometrics->String);
987 0 : FlagError = true;
988 : }
989 0 : ShowRecurringWarningErrorAtEnd(state,
990 : "Enthalpy out of range (PsyTsatFnHPb)",
991 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TsatFnHPb)],
992 : HH,
993 : HH,
994 : _,
995 : "J/kg",
996 : "J/kg");
997 : }
998 : }
999 : #endif
1000 57462832 : std::array<double, 10> CaseRange = {-4.24e4, -2.2138e4, -6.7012e2, 2.7297e4, 7.5222e4, 1.8379e5, 4.7577e5, 1.5445e6, 3.8353e6, 4.5866e7};
1001 57462832 : int CaseIndex = 0;
1002 57462832 : int beg(0), mid, end(9); // 1-based indexing
1003 :
1004 229851328 : while (beg + 1 < end) {
1005 172388496 : mid = ((beg + end) >> 1);
1006 172388496 : (HH > CaseRange[mid] ? beg : end) = mid;
1007 : }
1008 :
1009 57462832 : CaseIndex = beg + 1;
1010 :
1011 57462832 : switch (CaseIndex) {
1012 380 : case 1: // -2.2138e4 > HH > -4.24e4
1013 380 : if (HH < -4.24e4) HH = -4.24e4;
1014 380 : T = F6(HH, -19.44, 8.53675e-4, -5.12637e-9, -9.85546e-14, -1.00102e-18, -4.2705e-24);
1015 380 : break;
1016 40812 : case 2: // -6.7012e2 > HH > -2.2138e4
1017 40812 : T = F6(HH, -1.94224e1, 8.5892e-4, -4.50709e-9, -6.19492e-14, 8.71734e-20, 8.73051e-24);
1018 40812 : break;
1019 775100 : case 3: // 2.7297e4 > HH > -6.7012e2
1020 775100 : T = F6(HH, -1.94224e1, 8.59061e-4, -4.4875e-9, -5.76696e-14, 7.72217e-19, 3.97894e-24);
1021 775100 : break;
1022 52279702 : case 4: // 7.5222e4 > HH > 2.7297e4
1023 52279702 : T = F6(HH, -2.01147e1, 9.04936e-4, -6.83305e-9, 2.3261e-14, 7.27237e-20, -6.31939e-25);
1024 52279702 : break;
1025 4366560 : case 5: // 7.5222e4 > HH > 2.7297e4
1026 4366560 : T = F6(HH, -1.82124e1, 8.31683e-4, -6.16461e-9, 3.06411e-14, -8.60964e-20, 1.03003e-25);
1027 4366560 : break;
1028 278 : case 6:
1029 278 : T = F6(HH, -1.29419, 3.88538e-4, -1.30237e-9, 2.78254e-15, -3.27225e-21, 1.60969e-27);
1030 278 : break;
1031 0 : case 7:
1032 0 : T = F6(HH, 2.39214e1, 1.27519e-4, -1.52089e-10, 1.1043e-16, -4.33919e-23, 7.05296e-30);
1033 0 : break;
1034 0 : case 8:
1035 0 : T = F6(HH, 4.88446e1, 3.85534e-5, -1.78805e-11, 4.87224e-18, -7.15283e-25, 4.36246e-32);
1036 0 : break;
1037 0 : case 9:
1038 0 : if (HH > 4.5866e7) HH = 4.5866e7;
1039 0 : T = F7(HH, 7.60565e11, 5.80534e4, -7.36433e-3, 5.11531e-10, -1.93619e-17, 3.70511e-25, -2.77313e-33);
1040 0 : break;
1041 : }
1042 :
1043 : #ifdef EP_psych_errors
1044 57462832 : if (FlagError) {
1045 0 : ShowContinueError(state, format(" Initial Resultant Temperature= {:.2T}", T));
1046 : }
1047 : #endif
1048 57462832 : if (std::abs(PB - 1.0133e5) / 1.0133e5 > 0.01) {
1049 52777026 : IterCount = 0;
1050 52777026 : T1 = T;
1051 52777026 : H1 = PsyHFnTdbW(T1, PsyWFnTdbTwbPb(state, T1, T1, PB, CalledFrom));
1052 52777026 : Y1 = H1 - Hloc;
1053 52777026 : if (std::abs(Y1 / Hloc) <= 0.1e-4) {
1054 0 : T = T1;
1055 : } else {
1056 52777026 : T2 = T1 * 0.9;
1057 180489290 : while (IterCount <= 30) {
1058 180489191 : ++IterCount;
1059 180489191 : H2 = PsyHFnTdbW(T2, PsyWFnTdbTwbPb(state, T2, T2, PB, CalledFrom));
1060 180489191 : Y2 = H2 - Hloc;
1061 180489191 : if (std::abs(Y2 / Hloc) <= 0.1e-4 || Y2 == Y1) {
1062 52776927 : T = T2;
1063 52776927 : break;
1064 : }
1065 :
1066 127712264 : TN = T2 - Y2 / (Y2 - Y1) * (T2 - T1);
1067 127712264 : T1 = T2;
1068 127712264 : T2 = TN;
1069 127712264 : Y1 = Y2;
1070 : }
1071 : #ifdef EP_psych_errors
1072 52777026 : if (FlagError && IterCount > 30) {
1073 0 : ShowSevereError(state, "Temperature did not converge (PsyTsatFnHPb)");
1074 0 : if (!CalledFrom.empty()) {
1075 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
1076 : } else {
1077 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
1078 : }
1079 0 : state.dataPsychrometrics->String = format(" Enthalpy={:.5T} Pressure= {:.2T}", HH, PB);
1080 0 : ShowContinueError(state, format("{} Last T={:.2T}", state.dataPsychrometrics->String, T));
1081 : }
1082 : #endif
1083 : }
1084 : }
1085 :
1086 57462832 : return T;
1087 : }
1088 :
1089 : #ifdef EP_psych_errors
1090 0 : void PsyRhFnTdbRhov_error(EnergyPlusData &state,
1091 : Real64 const Tdb, // dry-bulb temperature {C}
1092 : Real64 const Rhovapor, // vapor density in air {kg/m3}
1093 : Real64 const RHValue, // relative humidity value (0.0-1.0)
1094 : std::string_view const CalledFrom // routine this function was called from (error messages)
1095 : )
1096 : {
1097 0 : if (RHValue > 1.01) {
1098 0 : if (!state.dataGlobal->WarmupFlag) {
1099 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbRhov)] == 0) {
1100 0 : state.dataPsychrometrics->String =
1101 0 : format(" Dry-Bulb= {:.2T} Rhovapor= {:.3T} Calculated Relative Humidity [%]= {:.2T}", Tdb, Rhovapor, RHValue * 100.0);
1102 0 : ShowWarningMessage(state, "Calculated Relative Humidity out of range (PsyRhFnTdbRhov) ");
1103 0 : if (!CalledFrom.empty()) {
1104 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
1105 : } else {
1106 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
1107 : }
1108 0 : ShowContinueError(state, state.dataPsychrometrics->String);
1109 0 : ShowContinueError(state, "Relative Humidity being reset to 100.0 %");
1110 : }
1111 0 : ShowRecurringWarningErrorAtEnd(state,
1112 : "Calculated Relative Humidity out of range (PsyRhFnTdbRhov)",
1113 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbRhov)],
1114 0 : RHValue * 100.0,
1115 0 : RHValue * 100.0,
1116 : _,
1117 : "%",
1118 : "%");
1119 : }
1120 0 : } else if (RHValue < -0.05) {
1121 0 : if (!state.dataGlobal->WarmupFlag) {
1122 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbRhov)] == 0) {
1123 0 : state.dataPsychrometrics->String =
1124 0 : format(" Dry-Bulb= {:.2T} Rhovapor= {:.3T} Calculated Relative Humidity [%]= {:.2T}", Tdb, Rhovapor, RHValue * 100.0);
1125 0 : ShowWarningMessage(state, "Calculated Relative Humidity out of range (PsyRhFnTdbRhov) ");
1126 0 : if (!CalledFrom.empty()) {
1127 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
1128 : } else {
1129 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
1130 : }
1131 0 : ShowContinueError(state, state.dataPsychrometrics->String);
1132 0 : ShowContinueError(state, "Relative Humidity being reset to 1%");
1133 : }
1134 0 : ShowRecurringWarningErrorAtEnd(state,
1135 : "Calculated Relative Humidity out of range (PsyRhFnTdbRhov)",
1136 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbRhov)],
1137 0 : RHValue * 100.0,
1138 0 : RHValue * 100.0,
1139 : _,
1140 : "%",
1141 : "%");
1142 : }
1143 : }
1144 0 : }
1145 : #endif
1146 :
1147 : #ifdef EP_psych_errors
1148 26 : void PsyRhFnTdbWPb_error(EnergyPlusData &state,
1149 : Real64 const TDB, // dry-bulb temperature {C}
1150 : Real64 const W, // humidity ratio
1151 : Real64 const RHValue, // relative humidity (0.0-1.0)
1152 : std::string_view const CalledFrom // routine this function was called from (error messages)
1153 : )
1154 : {
1155 26 : if (RHValue > 1.01) {
1156 26 : if (!state.dataGlobal->WarmupFlag) {
1157 9 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbWPb)] == 0) {
1158 1 : state.dataPsychrometrics->String =
1159 2 : format(" Dry-Bulb= {:.2T} Humidity Ratio= {:.3T} Calculated Relative Humidity [%]= {:.2T}", TDB, W, RHValue * 100.0);
1160 1 : ShowWarningMessage(state, "Calculated Relative Humidity out of range (PsyRhFnTdbWPb) ");
1161 1 : if (!CalledFrom.empty()) {
1162 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
1163 : } else {
1164 1 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
1165 : }
1166 1 : ShowContinueError(state, state.dataPsychrometrics->String);
1167 1 : ShowContinueError(state, "Relative Humidity being reset to 100.0%");
1168 : }
1169 27 : ShowRecurringWarningErrorAtEnd(state,
1170 : "Calculated Relative Humidity out of range (PsyRhFnTdbWPb)",
1171 9 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbWPb)],
1172 18 : RHValue * 100.0,
1173 18 : RHValue * 100.0,
1174 : _,
1175 : "%",
1176 : "%");
1177 : }
1178 0 : } else if (RHValue < -0.05) {
1179 0 : if (!state.dataGlobal->WarmupFlag) {
1180 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbWPb)] == 0) {
1181 0 : state.dataPsychrometrics->String =
1182 0 : format(" Dry-Bulb= {:.2T} Humidity Ratio= {:.3T} Calculated Relative Humidity [%]= {:.2T}", TDB, W, RHValue * 100.0);
1183 0 : ShowWarningMessage(state, "Calculated Relative Humidity out of range (PsyRhFnTdbWPb) ");
1184 0 : if (!CalledFrom.empty()) {
1185 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
1186 : } else {
1187 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
1188 : }
1189 0 : ShowContinueError(state, state.dataPsychrometrics->String);
1190 0 : ShowContinueError(state, "Relative Humidity being reset to 1%");
1191 : }
1192 0 : ShowRecurringWarningErrorAtEnd(state,
1193 : "Calculated Relative Humidity out of range (PsyRhFnTdbWPb)",
1194 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::RhFnTdbWPb)],
1195 0 : RHValue * 100.0,
1196 0 : RHValue * 100.0,
1197 : _,
1198 : "%",
1199 : "%");
1200 : }
1201 : }
1202 26 : }
1203 : #endif
1204 :
1205 : #ifdef EP_psych_errors
1206 2 : void PsyWFnTdpPb_error(EnergyPlusData &state,
1207 : Real64 const TDP, // dew-point temperature {C}
1208 : Real64 const PB, // barometric pressure {Pascals}
1209 : Real64 const W, // humidity ratio
1210 : Real64 const DeltaT, // Reduced temperature difference of dew point
1211 : std::string_view const CalledFrom // routine this function was called from (error messages)
1212 : )
1213 : {
1214 2 : if (!state.dataGlobal->WarmupFlag) {
1215 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdpPb)] == 0) {
1216 0 : state.dataPsychrometrics->String = format(" Dew-Point= {:.2T} Barometric Pressure= {:.2T}", TDP, PB);
1217 0 : ShowWarningMessage(state,
1218 : "Calculated partial vapor pressure is greater than the barometric pressure, so that calculated humidity ratio is "
1219 : "invalid (PsyWFnTdpPb).");
1220 0 : if (!CalledFrom.empty()) {
1221 0 : ShowContinueErrorTimeStamp(state, format(" Routine={},", CalledFrom));
1222 : } else {
1223 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
1224 : }
1225 0 : ShowContinueError(state, state.dataPsychrometrics->String);
1226 0 : state.dataPsychrometrics->String =
1227 0 : format("Instead, calculated Humidity Ratio at {:.1T} ({} degree less) = {:.4T}", TDP - DeltaT, static_cast<int>(DeltaT), W);
1228 0 : ShowContinueError(state, format("{} will be used. Simulation continues.", state.dataPsychrometrics->String));
1229 : }
1230 0 : ShowRecurringWarningErrorAtEnd(state,
1231 : "Entered Humidity Ratio invalid (PsyWFnTdpPb)",
1232 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdpPb)],
1233 : W,
1234 : W,
1235 : _,
1236 : "[]",
1237 : "[]");
1238 : }
1239 2 : }
1240 : #endif
1241 :
1242 : #ifdef EP_psych_errors
1243 0 : void PsyWFnTdbRhPb_error(EnergyPlusData &state,
1244 : Real64 const TDB, // dry-bulb temperature {C}
1245 : Real64 const RH, // relative humidity value (0.0-1.0)
1246 : Real64 const PB, // barometric pressure {Pascals}
1247 : Real64 const W, // humidity ratio
1248 : std::string_view const CalledFrom // routine this function was called from (error messages)
1249 : )
1250 : {
1251 0 : if (W <= -0.0001) {
1252 0 : if (!state.dataGlobal->WarmupFlag) {
1253 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdbRhPb)] == 0) {
1254 0 : state.dataPsychrometrics->String =
1255 0 : format(" Dry-Bulb= {:.2T} Relative Humidity [%]= {:.2T} Pressure= {:.2T}", TDB, RH * 100.0, PB);
1256 0 : ShowWarningMessage(state, "Calculated Humidity Ratio is invalid (PsyWFnTdbRhPb)");
1257 0 : if (!CalledFrom.empty()) {
1258 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
1259 : } else {
1260 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
1261 : }
1262 0 : ShowContinueError(state, state.dataPsychrometrics->String);
1263 0 : state.dataPsychrometrics->String = format("Calculated Humidity Ratio= {:.4T}", W);
1264 0 : ShowContinueError(state, format("{} ... Humidity Ratio set to .00001", state.dataPsychrometrics->String));
1265 : }
1266 0 : ShowRecurringWarningErrorAtEnd(state,
1267 : "Calculated Humidity Ratio Invalid (PsyWFnTdbTwbPb)",
1268 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::WFnTdbRhPb)],
1269 : W,
1270 : W,
1271 : _,
1272 : "[]",
1273 : "[]");
1274 : }
1275 : }
1276 0 : }
1277 : #endif
1278 :
1279 : #ifdef EP_cache_PsyTsatFnPb
1280 :
1281 14863951 : Real64 PsyTsatFnPb_raw(EnergyPlusData &state,
1282 : Real64 const Press, // barometric pressure {Pascals}
1283 : std::string_view const CalledFrom // routine this function was called from (error messages)
1284 : )
1285 :
1286 : #else
1287 : Real64 PsyTsatFnPb(EnergyPlusData &state,
1288 : Real64 const Press, // barometric pressure {Pascals}
1289 : std::string_view const CalledFrom // routine this function was called from (error messages)
1290 : )
1291 : #endif
1292 : {
1293 :
1294 : // FUNCTION INFORMATION:
1295 : // AUTHOR George Shih
1296 : // DATE WRITTEN May 1976
1297 : // RE-ENGINEERED Dec 2003; Rahul Chillar
1298 :
1299 : // PURPOSE OF THIS FUNCTION:
1300 : // This function provides the saturation temperature from barometric pressure.
1301 :
1302 : // METHODOLOGY EMPLOYED:
1303 : // na
1304 :
1305 : // REFERENCES:
1306 : // 1989 ASHRAE Handbook - Fundamentals
1307 : // Checked against 2005 HOF, Chap 6, Table 3 (using pressure in, temperature out) with
1308 : // good correlation from -60C to 160C
1309 :
1310 : // Using/Aliasing
1311 : using General::Iterate;
1312 :
1313 : // Return value
1314 :
1315 : // Locals
1316 : // FUNCTION ARGUMENT DEFINITIONS:
1317 :
1318 : // FUNCTION PARAMETER DEFINITIONS:
1319 14863951 : int constexpr itmax(50); // Maximum number of iterations
1320 14863951 : Real64 constexpr convTol(0.0001);
1321 :
1322 : // INTERFACE BLOCK SPECIFICATIONS
1323 : // na
1324 :
1325 : // DERIVED TYPE DEFINITIONS
1326 : // na
1327 :
1328 : // FUNCTION LOCAL VARIABLE DECLARATIONS:
1329 : bool FlagError; // set when errors should be flagged
1330 : Real64 tSat; // Water temperature guess
1331 : int iter; // Iteration counter
1332 :
1333 : #ifdef EP_psych_stats
1334 : ++state.dataPsychCache->NumTimesCalled[static_cast<int>(PsychrometricFunction::TsatFnPb)];
1335 : #endif
1336 :
1337 : // Check press in range.
1338 14863951 : FlagError = false;
1339 : #ifdef EP_psych_errors
1340 14863951 : if (!state.dataGlobal->WarmupFlag) {
1341 2539754 : if (Press <= 0.0017 || Press >= 1555000.0) {
1342 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TsatFnPb)] == 0) {
1343 0 : ShowWarningMessage(state, "Pressure out of range (PsyTsatFnPb)");
1344 0 : if (!CalledFrom.empty()) {
1345 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
1346 : } else {
1347 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
1348 : }
1349 0 : ShowContinueError(state, format(" Input Pressure= {:.2T}", Press));
1350 0 : FlagError = true;
1351 : }
1352 0 : ShowRecurringWarningErrorAtEnd(state,
1353 : "Pressure out of range (PsyTsatFnPb)",
1354 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TsatFnPb)],
1355 : Press,
1356 : Press,
1357 : _,
1358 : "Pa",
1359 : "Pa");
1360 : }
1361 : }
1362 : #endif
1363 14863951 : if (Press == state.dataPsychrometrics->Press_Save) {
1364 0 : return state.dataPsychrometrics->tSat_Save;
1365 : }
1366 14863951 : state.dataPsychrometrics->Press_Save = Press;
1367 14863951 : if (state.dataPsychrometrics->useInterpolationPsychTsatFnPb) {
1368 21907 : int n_sample = 1651; // sample bin size = 64 Pa; continous sample size = 1651
1369 : // CSpline interpolation
1370 21907 : tSat = CSplineint(n_sample, Press); // Cubic spline interpolation
1371 21907 : iter = 0;
1372 : } else {
1373 : // Uses an iterative process to determine the saturation temperature at a given
1374 : // pressure by correlating saturated water vapor as a function of temperature.
1375 :
1376 : // Initial guess of boiling temperature
1377 14842044 : tSat = 100.0;
1378 14842044 : iter = 0;
1379 :
1380 : // If above 1555000,set value of Temp corresponding to Saturation Pressure of 1555000 Pascal.
1381 14842044 : if (Press >= 1555000.0) {
1382 0 : tSat = 200.0;
1383 : // If below 0.0017,set value of Temp corresponding to Saturation Pressure of 0.0017 Pascal.
1384 14842044 : } else if (Press <= 0.0017) {
1385 0 : tSat = -100.0;
1386 :
1387 : // Setting Value of PsyTsatFnPb= 0C, due to non-continuous function for Saturation Pressure at 0C.
1388 14842044 : } else if ((Press > 611.000) && (Press < 611.25)) {
1389 2576 : tSat = 0.0;
1390 :
1391 : } else {
1392 : // Iterate to find the saturation temperature
1393 : // of water given the total pressure
1394 :
1395 : // Set iteration loop parameters
1396 : // make sure these are initialized
1397 : Real64 pSat; // Pressure corresponding to temp. guess
1398 : Real64 error; // Deviation of dependent variable in iteration
1399 : Real64 X1; // Previous value of independent variable in ITERATE
1400 : Real64 Y1; // Previous value of dependent variable in ITERATE
1401 : Real64 ResultX; // ResultX is the final Iteration result passed back to the calling routine
1402 14839468 : bool const CalledFrom_empty(CalledFrom.empty());
1403 : int icvg; // Iteration convergence flag
1404 207209829 : for (iter = 1; iter <= itmax; ++iter) {
1405 :
1406 : // Calculate saturation pressure for estimated boiling temperature
1407 207209829 : pSat = PsyPsatFnTemp(
1408 207209829 : state, tSat, (CalledFrom_empty ? PsyRoutineNames[static_cast<int>(PsychrometricFunction::TsatFnPb)] : CalledFrom));
1409 :
1410 : // Compare with specified pressure and update estimate of temperature
1411 207209829 : error = Press - pSat;
1412 207209829 : Iterate(ResultX, convTol, tSat, error, X1, Y1, iter, icvg);
1413 207209829 : tSat = ResultX;
1414 : // If converged leave loop iteration
1415 207209829 : if (icvg == 1) break;
1416 :
1417 : // Water temperature not converged, repeat calculations with new
1418 : // estimate of water temperature
1419 : }
1420 :
1421 : // Saturation temperature has not converged after maximum specified
1422 : // iterations. Print error message, set return error flag, and RETURN
1423 : }
1424 : } // End If for the Pressure Range Checking
1425 :
1426 : #ifdef EP_psych_stats
1427 : state.dataPsychCache->NumIterations[static_cast<int>(PsychrometricFunction::TsatFnPb)] += iter;
1428 : #endif
1429 :
1430 : #ifdef EP_psych_errors
1431 14863951 : if (iter > itmax) {
1432 0 : if (!state.dataGlobal->WarmupFlag) {
1433 0 : if (state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TsatFnPb2)] == 0) {
1434 0 : ShowWarningMessage(state, format("Saturation Temperature not converged after {} iterations (PsyTsatFnPb)", iter));
1435 0 : if (!CalledFrom.empty()) {
1436 0 : ShowContinueErrorTimeStamp(state, format(" Routine={}", CalledFrom));
1437 : } else {
1438 0 : ShowContinueErrorTimeStamp(state, " Routine=Unknown,");
1439 : }
1440 0 : ShowContinueError(state, format(" Input Pressure= {:.2T}", Press));
1441 0 : FlagError = true;
1442 : }
1443 0 : ShowRecurringWarningErrorAtEnd(state,
1444 : "Saturation Temperature not converged after max iterations (PsyTsatFnPb)",
1445 0 : state.dataPsychrometrics->iPsyErrIndex[static_cast<int>(PsychrometricFunction::TsatFnPb2)],
1446 : tSat,
1447 : tSat,
1448 : _,
1449 : "C",
1450 : "C");
1451 : }
1452 : }
1453 : #endif
1454 :
1455 : // Result is SatTemperature
1456 14863951 : Real64 const Temp = state.dataPsychrometrics->tSat_Save = tSat; // result=> saturation temperature {C}
1457 :
1458 : #ifdef EP_psych_errors
1459 14863951 : if (FlagError) {
1460 0 : ShowContinueError(state, format(" Resultant Temperature= {:.2T}", Temp));
1461 : }
1462 : #endif
1463 :
1464 14863951 : return Temp;
1465 : }
1466 21907 : Real64 CSplineint(int const n, // sample data size
1467 : Real64 x) // given value of x
1468 : { // Cubic Spline interpolation
1469 : // Reference: Numerical Recipies in C (pp.97)
1470 : Real64 A, B, y;
1471 : // find location of x in arrays without searching since array bins are equally sized
1472 21907 : int x_int = static_cast<int>(x);
1473 : //********continous sample start
1474 21907 : int j = (x_int >> 6) - 1; // sample bin 64, sample size=1651
1475 21907 : if (j < 0) j = 0;
1476 21907 : if (j > (n - 2)) j = n - 2;
1477 : static constexpr Real64 h(64); // sample bin 64, sample size=1651
1478 : //********continous sample end
1479 21907 : int tsat_fn_pb_x_j1 = 64 * (j + 1); // sample data for pressure
1480 21907 : A = (tsat_fn_pb_x_j1 - x) / h;
1481 21907 : B = 1 - A;
1482 21907 : y = A * tsat_fn_pb_y[j] + B * tsat_fn_pb_y[j + 1] +
1483 21907 : ((A * A * A - A) * (tsat_fn_pb_d2y[j]) + (B * B * B - B) * (tsat_fn_pb_d2y[j + 1])) * (h * h) * 0.1666666667;
1484 21907 : return y;
1485 : }
1486 :
1487 : } // namespace Psychrometrics
1488 :
1489 : } // namespace EnergyPlus
|