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

Generated by: LCOV version 2.0-1