LCOV - code coverage report
Current view: top level - EnergyPlus - Psychrometrics.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 230 494 46.6 %
Date: 2024-08-23 23:50:59 Functions: 12 19 63.2 %

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

Generated by: LCOV version 1.14