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