LCOV - code coverage report
Current view: top level - EnergyPlus - Psychrometrics.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 233 495 47.1 %
Date: 2023-01-17 19:17:23 Functions: 14 21 66.7 %

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

Generated by: LCOV version 1.13