LCOV - code coverage report
Current view: top level - EnergyPlus - Psychrometrics.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 46.2 % 496 229
Test Date: 2025-06-02 07:23:51 Functions: 63.2 % 19 12

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

Generated by: LCOV version 2.0-1