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