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