LCOV - code coverage report
Current view: top level - EnergyPlus - IOFiles.hh (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 77.5 % 262 203
Test Date: 2025-05-22 16:09:37 Functions: 52.1 % 2541 1323

            Line data    Source code
       1              : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
       2              : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3              : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4              : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5              : // contributors. All rights reserved.
       6              : //
       7              : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8              : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9              : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10              : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11              : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12              : //
      13              : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14              : // provided that the following conditions are met:
      15              : //
      16              : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17              : //     conditions and the following disclaimer.
      18              : //
      19              : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20              : //     conditions and the following disclaimer in the documentation and/or other materials
      21              : //     provided with the distribution.
      22              : //
      23              : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24              : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25              : //     used to endorse or promote products derived from this software without specific prior
      26              : //     written permission.
      27              : //
      28              : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29              : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30              : //     reference solely to the software portion of its product, Licensee must refer to the
      31              : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32              : //     obtained under this License and may not use a different name for the software. Except as
      33              : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34              : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35              : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36              : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37              : //
      38              : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39              : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40              : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41              : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42              : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43              : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44              : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45              : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46              : // POSSIBILITY OF SUCH DAMAGE.
      47              : 
      48              : #ifndef IOFiles_hh_INCLUDED
      49              : #define IOFiles_hh_INCLUDED
      50              : 
      51              : // C++ Headers
      52              : #include <array>
      53              : #include <cassert>
      54              : #include <fstream>
      55              : #include <iostream>
      56              : #include <limits>
      57              : #include <ostream>
      58              : #include <vector>
      59              : 
      60              : // EnergyPlus Headers
      61              : #include <EnergyPlus/EnergyPlus.hh>
      62              : #include <EnergyPlus/FileSystem.hh>
      63              : 
      64              : // Third Party Headers
      65              : #include <fmt/compile.h>
      66              : #include <fmt/format.h>
      67              : #include <fmt/os.h>
      68              : #include <fmt/ostream.h>
      69              : #include <fmt/printf.h>
      70              : #include <fmt/ranges.h>
      71              : #include <nlohmann/json.hpp>
      72              : 
      73              : namespace {
      74              : struct DoubleWrapper
      75              : {
      76              :     // this cannot be marked explicit
      77              :     // we need the implicit conversion for it to work
      78       324387 :     DoubleWrapper(double val) : value(val){};
      79       324853 :     operator double() const
      80              :     {
      81       324853 :         return value;
      82              :     };
      83              :     DoubleWrapper &operator=(const double &other)
      84              :     {
      85              :         value = other;
      86              :         return *this;
      87              :     }
      88              : 
      89              : private:
      90              :     double value;
      91              : };
      92              : } // namespace
      93              : 
      94              : namespace fmt {
      95              : template <> struct formatter<DoubleWrapper>
      96              : {
      97              : private:
      98              :     fmt::detail::dynamic_format_specs<char> specs_;
      99              :     const char *format_str_;
     100              :     fmt::memory_buffer buffer = fmt::memory_buffer();
     101              : 
     102              :     struct null_handler : detail::error_handler
     103              :     {
     104       324853 :         void on_align(align_t)
     105              :         {
     106       324853 :         }
     107            0 :         void on_sign(sign_t)
     108              :         {
     109            0 :         }
     110        60437 :         void on_hash()
     111              :         {
     112        60437 :         }
     113              :     };
     114              : 
     115        38791 :     static constexpr bool should_be_fixed_output(const double value)
     116              :     {
     117        38791 :         return (value >= 0.099999999999999995 || value <= -0.099999999999999995) || (value == 0.0) || (value == -0.0);
     118              :     }
     119              : 
     120         1016 :     static constexpr bool fixed_will_fit(const double value, const int places)
     121              :     {
     122         1016 :         if (value < 1.0 && value > -1.0) {
     123          493 :             return true;
     124              :         } else {
     125          523 :             return static_cast<int>(std::log10(std::abs(value))) < places;
     126              :         }
     127              :     }
     128              : 
     129         3808 :     static std::string &zero_pad_exponent(std::string &str)
     130              :     {
     131              :         // if necessary, pad the exponent with a 0 to match the old formatting from Objexx
     132         3808 :         if (str.size() > 3) {
     133         3808 :             if (!std::isdigit(str[str.size() - 3])) {
     134              :                 // wants a 0 inserted
     135         3808 :                 str.insert(str.size() - 2, "0");
     136              :             }
     137              :         }
     138         3808 :         return str;
     139              :     }
     140              : 
     141       324849 :     std::string_view spec_builder()
     142              :     {
     143       324849 :         buffer.clear();
     144       324849 :         buffer.push_back('{');
     145       324849 :         buffer.push_back(':');
     146              :         //    [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
     147              : 
     148              :         //    [[fill]align]
     149       324849 :         switch (specs_.align) {
     150            0 :         case align_t::left:
     151            0 :             if (specs_.fill.size()) buffer.append(specs_.fill);
     152            0 :             buffer.push_back('<');
     153            0 :             break;
     154            0 :         case align_t::right:
     155            0 :             if (specs_.fill.size()) buffer.append(specs_.fill);
     156            0 :             buffer.push_back('>');
     157            0 :             break;
     158            0 :         case align_t::center:
     159            0 :             if (specs_.fill.size()) buffer.append(specs_.fill);
     160            0 :             buffer.push_back('^');
     161            0 :             break;
     162       324849 :         case align_t::none:
     163              :         case align_t::numeric:
     164       324849 :             break;
     165            0 :         default:
     166            0 :             throw fmt::format_error("Bad alignment");
     167              :         }
     168              : 
     169              :         //    [sign]
     170       324849 :         switch (specs_.sign) {
     171            0 :         case sign_t::plus:
     172            0 :             buffer.push_back('+');
     173            0 :             break;
     174            0 :         case sign_t::minus:
     175            0 :             buffer.push_back('-');
     176            0 :             break;
     177            0 :         case sign_t::space:
     178            0 :             buffer.push_back(' ');
     179            0 :             break;
     180       324849 :         case sign_t::none:
     181       324849 :             break;
     182            0 :         default:
     183            0 :             throw fmt::format_error("Bad sign");
     184              :         }
     185              : 
     186              :         //    [alt]
     187       324849 :         if (specs_.alt) {
     188        61798 :             buffer.push_back('#');
     189              :         }
     190              : 
     191              :         //    [width]
     192       324849 :         if (specs_.width >= 0) {
     193       324849 :             if (specs_.fill[0] == '0') {
     194            0 :                 buffer.push_back('0');
     195              :             }
     196       324849 :             auto fmt_int = fmt::format_int(specs_.width);
     197       324849 :             buffer.append(fmt_int.data(), fmt_int.data() + fmt_int.size());
     198              :         }
     199              : 
     200              :         //    [precision]
     201       324849 :         if (specs_.precision >= 0) {
     202       324703 :             buffer.push_back('.');
     203              : 
     204       324703 :             auto fmt_int = fmt::format_int(specs_.precision);
     205       324703 :             buffer.append(fmt_int.data(), fmt_int.data() + fmt_int.size());
     206              :         }
     207              : 
     208              :         //    [locale]
     209       324849 :         if (specs_.localized) {
     210            0 :             buffer.push_back('L');
     211              :         }
     212              : 
     213              :         //    [type]
     214       324849 :         buffer.push_back(specs_.type);
     215              : 
     216       324849 :         buffer.push_back('}');
     217              : 
     218       324849 :         return {buffer.data(), buffer.size()};
     219              :     }
     220              : 
     221       324853 :     template <typename Context> void handle_specs(Context &ctx)
     222              :     {
     223       324853 :         detail::handle_dynamic_spec<detail::width_checker>(specs_.width, specs_.width_ref, ctx);
     224       324853 :         detail::handle_dynamic_spec<detail::precision_checker>(specs_.precision, specs_.precision_ref, ctx);
     225       324853 :     }
     226              : 
     227              : public:
     228       324853 :     template <typename ParseContext> constexpr auto parse(ParseContext &ctx)
     229              :     {
     230       324853 :         auto begin = ctx.begin(), end = ctx.end();
     231       324853 :         format_str_ = begin;
     232       324853 :         if (begin == end) return begin;
     233              :         using handler_type = fmt::detail::dynamic_specs_handler<ParseContext>;
     234       324761 :         auto it = fmt::detail::parse_format_specs(begin, end, handler_type(specs_, ctx));
     235       324761 :         return it;
     236              :     }
     237              : 
     238       324853 :     template <typename FormatContext> auto format(const DoubleWrapper &doubleWrapper, FormatContext &ctx)
     239              :     {
     240        81908 :         const auto next_float = [](const double value) {
     241        81908 :             if (std::signbit(value)) {
     242         4691 :                 if (value == -0.0) {
     243            0 :                     return value;
     244              :                 } else {
     245         4691 :                     return std::nextafter(value, std::numeric_limits<decltype(value)>::lowest());
     246              :                 }
     247              :             } else {
     248        77217 :                 if (value == 0.0) {
     249            0 :                     return value;
     250              :                 } else {
     251        77217 :                     return std::nextafter(value, std::numeric_limits<decltype(value)>::max());
     252              :                 }
     253              :             }
     254              :         };
     255              : 
     256       324853 :         double val = doubleWrapper;
     257              : 
     258       324853 :         handle_specs(ctx);
     259       324853 :         detail::specs_checker<null_handler> checker(null_handler(), detail::mapped_type_constant<double, FormatContext>::value);
     260       324853 :         checker.on_align(specs_.align);
     261       324853 :         if (specs_.sign != sign::none) checker.on_sign(specs_.sign);
     262       324853 :         if (specs_.alt) checker.on_hash();
     263       324853 :         if (specs_.precision >= 0) checker.end_precision();
     264              : 
     265              :         // matches Fortran's 'E' format
     266       324853 :         if (specs_.type == 'Z') {
     267              :             // The Fortran 'G' format insists on a leading 0, even though
     268              :             // that actually means losing data
     269         1361 :             specs_.type = 'E';
     270              : 
     271              :             // 0 pad the end
     272         1361 :             specs_.alt = true;
     273              : 
     274         1361 :             bool initialPrecisionWas1 = false;
     275         1361 :             if (specs_.precision > 1) {
     276              :                 // reduce the precision to get rounding behavior
     277         1357 :                 --specs_.precision;
     278              :             } else {
     279              :                 // We need AT LEAST one in precision so we capture a '.' below
     280            4 :                 initialPrecisionWas1 = true;
     281            4 :                 specs_.precision = 1;
     282            4 :                 ++specs_.width;
     283              :             }
     284              : 
     285              :             // multiply by 10 to get the exponent we want
     286         1361 :             auto str = fmt::format(spec_builder(), val * 10);
     287              :             //      auto str = write_to_string(value * 10);
     288              : 
     289              :             // we need "space" to insert our leading 0
     290         1361 :             if (str.front() != ' ') {
     291          136 :                 str.insert(str.begin(), ' ');
     292              :             }
     293              : 
     294         2722 :             auto begin = std::find(std::begin(str), std::end(str), '.');
     295         1361 :             if (initialPrecisionWas1) {
     296              :                 // 123.45 => 1.2E+03, except we asked for precision = 1. So we delete the thing after the dot
     297              :                 // and this is why we manually increased the specs_.width by one above
     298            4 :                 str.erase(std::next(begin));
     299              :             }
     300              :             // if (begin != std::end(str)) {
     301              :             // ' -1.2345E15'
     302              :             //     ^
     303         1361 :             std::swap(*begin, *std::prev(begin));
     304              :             // ' -.12345E15'
     305              :             //     ^
     306              :             std::advance(begin, -2);
     307              :             // ' -.12345E15'
     308              :             //   ^
     309         1361 :             if (*begin != ' ') {
     310              :                 // found a sign
     311          575 :                 std::swap(*begin, *std::prev(begin));
     312              :                 // '- .12345E15'
     313              :                 //   ^
     314              :             }
     315              :             // '-0.12345E15'
     316              :             //   ^
     317         1361 :             *begin = '0';
     318         2722 :             return fmt::format_to(ctx.out(), "{}", str);
     319       324853 :         } else if (specs_.type == 'S') {
     320              :             // matches Fortran's 'G', but stripped of whitespace
     321            0 :             specs_.type = 'N';
     322              :             // Need to rerun with double wrapper since 'N' is one of our custom ones
     323            0 :             auto str = fmt::format(spec_builder(), doubleWrapper);
     324              : 
     325            0 :             auto strip_whitespace = [](std::string_view const s) -> std::string {
     326            0 :                 if (s.empty()) {
     327            0 :                     return std::string{};
     328              :                 }
     329            0 :                 auto const first = s.find_first_not_of(' ');
     330            0 :                 auto const last = s.find_last_not_of(' ');
     331            0 :                 if ((first == std::string::npos) || (last == std::string::npos)) {
     332            0 :                     return std::string{};
     333              :                 } else {
     334            0 :                     return std::string{s.substr(first, last - first + 1)};
     335              :                 }
     336              :             };
     337              : 
     338            0 :             return fmt::format_to(ctx.out(), "{}", strip_whitespace(str));
     339       323492 :         } else if (specs_.type == 'N') {
     340              :             // matches Fortran's 'G' format
     341              : 
     342         1478 :             if (specs_.width == 0 && specs_.precision == -1) {
     343              :                 // Need to rerun with double wrapper since 'N' is one of our custom ones
     344            8 :                 return fmt::format_to(ctx.out(), "{:20N}", doubleWrapper);
     345         1474 :             } else if (should_be_fixed_output(val) && fixed_will_fit(val, specs_.width - 5)) {
     346         1012 :                 specs_.type = 'F';
     347              : 
     348              :                 // account for alignment with E formatted
     349         1012 :                 specs_.width -= 4;
     350         1012 :                 if (val == 0.0) {
     351          181 :                     --specs_.precision;
     352          831 :                 } else if (val < 1.0 && val > -1.0) {
     353              :                     // No adjustment necessary
     354          519 :                 } else if (specs_.precision == -1) {
     355            2 :                     const auto order_of_magnitude = static_cast<int>(std::log10(std::abs(val)));
     356            2 :                     specs_.precision = specs_.width - (order_of_magnitude + 2);
     357              :                 } else {
     358          517 :                     const auto order_of_magnitude = static_cast<int>(std::log10(std::abs(val)));
     359          517 :                     specs_.precision -= (order_of_magnitude + 1);
     360              :                 }
     361              : 
     362              :                 // if precision adjustment would result in negative, make it 0 to get rounding
     363              :                 // and adjust spacing
     364         1012 :                 if (specs_.precision <= 0) {
     365            3 :                     specs_.width -= 1;
     366            3 :                     specs_.precision = 0;
     367              :                 }
     368              : 
     369         1012 :                 auto str = fmt::format(spec_builder(), val);
     370              : 
     371              :                 // When precision hit 0, add . to match Fortran formatting
     372         1012 :                 if (specs_.precision == 0) {
     373              :                     // write the last 4 chars
     374            6 :                     return fmt::format_to(ctx.out(), "{}.    ", str);
     375              :                 } else {
     376              :                     // write the last 4 chars
     377         2018 :                     return fmt::format_to(ctx.out(), "{}    ", str);
     378              :                 }
     379         1012 :             } else {
     380              :                 // The Fortran 'G' format insists on a leading 0, even though
     381              :                 // that actually means losing data
     382          462 :                 specs_.type = 'Z';
     383              :                 // Need to rerun with double wrapper since 'Z' is one of our custom ones
     384         1386 :                 return fmt::format_to(ctx.out(), spec_builder(), doubleWrapper);
     385              :             }
     386       322014 :         } else if (specs_.type == 'R') { // matches RoundSigDigits() behavior
     387              :             // push the value up a tad to get the same rounding behavior as Objexx
     388        36580 :             const auto fixed_output = should_be_fixed_output(val);
     389              : 
     390        36580 :             if (fixed_output) {
     391        32849 :                 specs_.type = 'F';
     392              : 
     393        32849 :                 if (val > 100000.0) {
     394          139 :                     const auto digits10 = static_cast<int>(std::log10(val));
     395              :                     // we cannot represent this val to the required precision, truncate the floating
     396              :                     // point portion
     397          139 :                     if (digits10 + specs_.precision >= std::numeric_limits<decltype(val)>::max_digits10) {
     398           13 :                         specs_.precision = 0;
     399           13 :                         spec_builder();
     400              :                         // add '.' to match old RoundSigDigits
     401           13 :                         buffer.push_back('.');
     402           13 :                         std::string_view fmt_buffer(buffer.data(), buffer.size());
     403           26 :                         return fmt::format_to(ctx.out(), fmt_buffer, val);
     404              :                     } else {
     405          378 :                         return fmt::format_to(ctx.out(), spec_builder(), val);
     406              :                     }
     407              :                 } else {
     408        32710 :                     if (val == 0.0 || val == -0.0) {
     409        19953 :                         return fmt::format_to(ctx.out(), spec_builder(), 0.0);
     410              :                     } else {
     411              :                         // nudge up to next rounded val
     412        78177 :                         return fmt::format_to(ctx.out(), spec_builder(), next_float(next_float(next_float(val))));
     413              :                     }
     414              :                 }
     415              :             } else {
     416         3731 :                 specs_.type = 'E';
     417         3731 :                 auto str = fmt::format(spec_builder(), next_float(val));
     418         7462 :                 return fmt::format_to(ctx.out(), "{}", zero_pad_exponent(str));
     419         3731 :             }
     420       285434 :         } else if (specs_.type == 'T') { // matches TrimSigDigits behavior
     421          737 :             const auto fixed_output = should_be_fixed_output(val);
     422              : 
     423          737 :             if (fixed_output) {
     424          660 :                 const auto magnitude = std::pow(10, specs_.precision);
     425          660 :                 const auto adjusted = (val * magnitude) + 0.0001;
     426          660 :                 const auto truncated = std::trunc(adjusted) / magnitude;
     427          660 :                 specs_.type = 'F';
     428         1980 :                 return fmt::format_to(ctx.out(), spec_builder(), truncated);
     429              :             } else {
     430           77 :                 specs_.type = 'E';
     431           77 :                 specs_.precision += 2;
     432              : 
     433              :                 // write the `E` formatted float to a std::string
     434           77 :                 auto str = fmt::format(spec_builder(), val);
     435           77 :                 str = zero_pad_exponent(str);
     436              : 
     437              :                 // Erase last 2 numbers to truncate the value
     438          154 :                 const auto E_itr = std::find(begin(str), end(str), 'E');
     439           77 :                 if (E_itr != str.end()) {
     440          154 :                     str.erase(std::prev(E_itr, 2), E_itr);
     441              :                 }
     442              : 
     443          154 :                 return fmt::format_to(ctx.out(), "{}", str);
     444           77 :             }
     445              :         }
     446       854091 :         return fmt::format_to(ctx.out(), spec_builder(), val);
     447              :     }
     448              : };
     449              : } // namespace fmt
     450              : 
     451              : namespace EnergyPlus {
     452              : 
     453              : // Forward declarations
     454              : struct EnergyPlusData;
     455              : 
     456              : enum class FormatSyntax
     457              : {
     458              :     Invalid = -1,
     459              :     Fortran,
     460              :     FMT,
     461              :     Printf,
     462              :     Num
     463              : };
     464              : 
     465              : inline constexpr bool is_fortran_syntax(const std::string_view format_str)
     466              : {
     467              :     bool within_fmt_str = false;
     468              :     for (auto const c : format_str) {
     469              :         switch (c) {
     470              :         case '{':
     471              :             within_fmt_str = true;
     472              :             break;
     473              :         case '}':
     474              :             within_fmt_str = false;
     475              :             break;
     476              :         case 'R':
     477              :         case 'S':
     478              :         case 'N':
     479              :         case 'Z':
     480              :         case 'T':
     481              :             if (within_fmt_str) {
     482              :                 return true;
     483              :             } else {
     484              :                 break;
     485              :             }
     486              :         default:
     487              :             break;
     488              :         }
     489              :     }
     490              :     return false;
     491              : }
     492              : 
     493              : class InputOutputFile;
     494              : template <FormatSyntax formatSyntax = FormatSyntax::Fortran, typename... Args>
     495              : void print(InputOutputFile &outputFile, std::string_view format_str, Args &&... args);
     496              : 
     497              : inline constexpr FormatSyntax check_syntax(const std::string_view format_str)
     498              : {
     499              :     if (is_fortran_syntax(format_str)) {
     500              :         return FormatSyntax::Fortran;
     501              :     } else {
     502              :         return FormatSyntax::FMT;
     503              :     }
     504              : }
     505              : 
     506              : class InputFile
     507              : {
     508              : public:
     509              :     template <typename Type> struct ReadResult
     510              :     {
     511        72555 :         ReadResult(Type data_, bool eof_, bool good_) : data{std::move(data_)}, eof{eof_}, good{good_}
     512              :         {
     513        72555 :         }
     514              : 
     515              :         // Update the current eof/good state from the incoming value
     516              :         // but only update the `data` member if the state is good
     517              :         // The idea is to keep consistency with the operator>> that was used
     518              :         // from gio
     519        16780 :         void update(ReadResult &&other)
     520              :         {
     521        16780 :             eof = other.eof;
     522        16780 :             good = other.good;
     523        16780 :             if (good) {
     524        16779 :                 data = std::move(other.data);
     525              :             }
     526        16780 :         }
     527              : 
     528              :         Type data;
     529              :         bool eof;
     530              :         bool good;
     531              :     };
     532              : 
     533              :     void close();
     534              : 
     535              :     // This is different from istream::good(), which is false if EOF is true while there were no errors (happens when no EOL at end of file)
     536              :     // this operate like `operator bool(istream& is)` <=> `!is.bad() && !is.fail()`
     537              :     bool good() const noexcept;
     538              : 
     539              :     bool is_open() const noexcept;
     540              : 
     541              :     void backspace() noexcept;
     542              : 
     543              :     std::string error_state_to_string() const;
     544              : 
     545              :     // opens the file if it is not currently open and returns
     546              :     // a reference back to itself
     547              :     InputFile &ensure_open(EnergyPlusData &state, const std::string &caller, bool output_to_file = true);
     548              :     std::istream::iostate rdstate() const noexcept;
     549              : 
     550              :     fs::path filePath;
     551              :     void open(bool = false, bool = true);
     552              :     std::fstream::pos_type position() const noexcept;
     553              : 
     554           25 :     void rewind() noexcept
     555              :     {
     556           25 :         if (is) {
     557           25 :             is->clear(); // clear potentially failbit and badbit (seekg would only clear eofbit)
     558           25 :             is->seekg(0, std::ios::beg);
     559              :         }
     560           25 :     }
     561              : 
     562              :     ReadResult<std::string> readLine() noexcept;
     563              : 
     564            0 :     template <typename T> ReadResult<T> read() noexcept
     565              :     {
     566            0 :         if (is) {
     567              :             T result;
     568            0 :             *is >> result;
     569              :             // Use operator bool, see ReadResult::good() docstring
     570            0 :             return ReadResult<T>{result, is->eof(), bool(is)};
     571              :         } else {
     572            0 :             return ReadResult<T>{T{}, true, false};
     573              :         }
     574              :     }
     575              : 
     576              :     std::string readFile();
     577              : 
     578              :     nlohmann::json readJSON();
     579              : 
     580              :     explicit InputFile(fs::path FilePath);
     581              : 
     582              : private:
     583              :     std::uintmax_t file_size{};
     584              :     std::unique_ptr<std::istream> is;
     585              :     friend class IOFiles;
     586              : };
     587              : 
     588              : class InputOutputFile
     589              : {
     590              : public:
     591              :     fs::path filePath;
     592              :     bool defaultToStdOut = false;
     593              : 
     594              :     void close();
     595              :     void del();
     596              :     bool good() const;
     597              : 
     598              :     // opens the file if it is not currently open and returns
     599              :     // a reference back to itself
     600              :     InputOutputFile &ensure_open(EnergyPlusData &state, const std::string &caller, bool output_to_file = true);
     601              : 
     602              :     void open(const bool forAppend = false, bool output_to_file = true);
     603              :     std::fstream::pos_type position() const noexcept;
     604              :     std::vector<std::string> getLines();
     605              :     void open_as_stringstream();
     606              :     std::string get_output();
     607              :     void flush();
     608              :     explicit InputOutputFile(fs::path FilePath, const bool DefaultToStdOut = false);
     609              : 
     610              : private:
     611              :     std::unique_ptr<std::iostream> os;
     612              :     bool print_to_dev_null = false;
     613              :     template <FormatSyntax, typename... Args> friend void print(InputOutputFile &outputFile, std::string_view format_str, Args &&... args);
     614              :     friend class IOFiles;
     615              : };
     616              : 
     617              : template <typename FileType> struct IOFilePath
     618              : {
     619              :     fs::path filePath;
     620           45 :     FileType open(EnergyPlusData &state, const std::string &caller, bool output_to_file = true)
     621              :     {
     622           45 :         FileType file{filePath};
     623           45 :         file.ensure_open(state, caller, output_to_file);
     624           45 :         return file;
     625            0 :     }
     626           29 :     FileType try_open(bool output_to_file = true)
     627              :     {
     628           29 :         FileType file{filePath};
     629           29 :         file.open(false, output_to_file);
     630           29 :         return file;
     631            0 :     }
     632              : };
     633              : 
     634              : using InputOutputFilePath = IOFilePath<InputOutputFile>;
     635              : using InputFilePath = IOFilePath<InputFile>;
     636              : 
     637              : struct JsonOutputFilePaths
     638              : {
     639              :     fs::path outputJsonFilePath;
     640              :     fs::path outputTSHvacJsonFilePath;
     641              :     fs::path outputTSZoneJsonFilePath;
     642              :     fs::path outputTSJsonFilePath;
     643              :     fs::path outputYRJsonFilePath;
     644              :     fs::path outputMNJsonFilePath;
     645              :     fs::path outputDYJsonFilePath;
     646              :     fs::path outputHRJsonFilePath;
     647              :     fs::path outputSMJsonFilePath;
     648              :     fs::path outputCborFilePath;
     649              :     fs::path outputTSHvacCborFilePath;
     650              :     fs::path outputTSZoneCborFilePath;
     651              :     fs::path outputTSCborFilePath;
     652              :     fs::path outputYRCborFilePath;
     653              :     fs::path outputMNCborFilePath;
     654              :     fs::path outputDYCborFilePath;
     655              :     fs::path outputHRCborFilePath;
     656              :     fs::path outputSMCborFilePath;
     657              :     fs::path outputMsgPackFilePath;
     658              :     fs::path outputTSHvacMsgPackFilePath;
     659              :     fs::path outputTSZoneMsgPackFilePath;
     660              :     fs::path outputTSMsgPackFilePath;
     661              :     fs::path outputYRMsgPackFilePath;
     662              :     fs::path outputMNMsgPackFilePath;
     663              :     fs::path outputDYMsgPackFilePath;
     664              :     fs::path outputHRMsgPackFilePath;
     665              :     fs::path outputSMMsgPackFilePath;
     666              : };
     667              : 
     668              : class IOFiles
     669              : {
     670              : public:
     671              :     struct OutputControl
     672              :     {
     673         2129 :         OutputControl() = default;
     674              : 
     675              :         void getInput(EnergyPlusData &state);
     676              :         bool writeTabular(EnergyPlusData &state);
     677              : 
     678              :         bool csv = false;
     679              :         bool mtr = true;
     680              :         bool eso = true;
     681              :         bool eio = true;
     682              :         bool audit = true;
     683              :         bool spsz = true;
     684              :         bool zsz = true;
     685              :         bool ssz = true;
     686              :         bool dxf = true;
     687              :         bool bnd = true;
     688              :         bool rdd = true;
     689              :         bool mdd = true;
     690              :         bool mtd = true;
     691              :         bool end = true;
     692              :         bool shd = true;
     693              :         bool dfs = true;
     694              :         bool glhe = true;
     695              :         bool delightin = true;
     696              :         bool delighteldmp = true;
     697              :         bool delightdfdmp = true;
     698              :         bool edd = true;
     699              :         bool dbg = true;
     700              :         bool perflog = true;
     701              :         bool sln = true;
     702              :         bool sci = true;
     703              :         bool wrl = true;
     704              :         bool screen = true;
     705              :         bool tarcog = true;
     706              :         bool extshd = true;
     707              :         bool json = true;
     708              :         bool tabular = true;
     709              :         bool sqlite = true;
     710              :     };
     711              : 
     712              :     OutputControl outputControl;
     713              : 
     714              :     InputOutputFile audit{"eplusout.audit"};
     715              :     InputOutputFile eio{"eplusout.eio"};
     716              :     InputOutputFile eso{"eplusout.eso"}; // (hourly data only)
     717              : 
     718              :     InputOutputFile zsz{""};
     719              :     fs::path outputZszCsvFilePath{"epluszsz.csv"};
     720              :     fs::path outputZszTabFilePath{"epluszsz.tab"};
     721              :     fs::path outputZszTxtFilePath{"epluszsz.txt"};
     722              : 
     723              :     InputOutputFile spsz{""};
     724              :     fs::path outputSpszCsvFilePath{"eplusspsz.csv"};
     725              :     fs::path outputSpszTabFilePath{"eplusspsz.tab"};
     726              :     fs::path outputSpszTxtFilePath{"eplusspsz.txt"};
     727              : 
     728              :     InputOutputFile ssz{""};
     729              :     fs::path outputSszCsvFilePath{"eplusssz.csv"};
     730              :     fs::path outputSszTabFilePath{"eplusssz.tab"};
     731              :     fs::path outputSszTxtFilePath{"eplusssz.txt"};
     732              : 
     733              :     InputOutputFile map{""};
     734              :     fs::path outputMapCsvFilePath{"eplusmap.csv"};
     735              :     fs::path outputMapTabFilePath{"eplusmap.tab"};
     736              :     fs::path outputMapTxtFilePath{"eplusmap.txt"};
     737              : 
     738              :     InputOutputFile mtr{"eplusout.mtr"};
     739              :     InputOutputFile bnd{"eplusout.bnd"};
     740              :     InputOutputFile rdd{"eplusout.rdd"};
     741              :     InputOutputFile mdd{"eplusout.mdd"};
     742              : 
     743              :     InputOutputFile debug{"eplusout.dbg"};
     744              : 
     745              :     InputOutputFile dfs{"eplusout.dfs"};
     746              : 
     747              :     InputOutputFilePath sln{"eplusout.sln"};
     748              :     InputOutputFilePath dxf{"eplusout.dxf"};
     749              :     InputOutputFilePath sci{"eplusout.sci"};
     750              :     InputOutputFilePath wrl{"eplusout.wrl"};
     751              : 
     752              :     InputOutputFilePath delightIn{"eplusout.delightin"};
     753              : 
     754              :     InputOutputFile mtd{"eplusout.mtd"};
     755              :     InputOutputFile edd{"eplusout.edd", true}; // write to stdout if no file never opened
     756              :     InputOutputFile shade{"eplusshading.csv"};
     757              : 
     758              :     InputOutputFile csv{"eplusout.csv"};
     759              :     InputOutputFile mtr_csv{"eplusmtr.csv"};
     760              : 
     761              :     InputOutputFilePath screenCsv{"eplusscreen.csv"};
     762              :     InputOutputFilePath endFile{"eplusout.end"};
     763              : 
     764              :     InputFilePath iniFile{"EnergyPlus.ini"};
     765              : 
     766              :     InputFilePath outputDelightEldmpFilePath{"eplusout.delighteldmp"};
     767              :     InputFilePath outputDelightDfdmpFilePath{"eplusout.delightdfdmp"};
     768              : 
     769              :     // for transient uses of weather files
     770              :     // also, keeper of the currently set input weather file name
     771              :     InputFilePath inputWeatherFilePath{""};
     772              : 
     773              :     // for the persistent weather simulation, using the EPW
     774              :     // uses the file name set in `inputWeatherFilePath`
     775              :     InputFile inputWeatherFile{""};
     776              : 
     777              :     InputFilePath TempFullFilePath{""};
     778              :     InputFilePath inStatFilePath{""};
     779              : 
     780              :     fs::path outputErrFilePath{"eplusout.err"};
     781              :     std::unique_ptr<std::ostream> err_stream;
     782              : 
     783              :     JsonOutputFilePaths json; // Internal streams used for json outputs
     784              : 
     785              :     void flushAll(); // For RunningEnergyPlusViaAPI only
     786              : };
     787              : 
     788              : class SharedFileHandle
     789              : {
     790              :     std::shared_ptr<InputOutputFile> file;
     791          179 :     InputOutputFile *ptr()
     792              :     {
     793          179 :         if (!file) {
     794            2 :             file = std::make_shared<InputOutputFile>("");
     795              :         }
     796              : 
     797          179 :         return file.get();
     798              :     }
     799              : 
     800              : public:
     801          179 :     InputOutputFile &operator*()
     802              :     {
     803          179 :         return *ptr();
     804              :     }
     805              : 
     806            0 :     InputOutputFile *operator->()
     807              :     {
     808            0 :         return ptr();
     809              :     }
     810              : };
     811              : 
     812       303781 : template <typename... Args> void vprint(std::ostream &os, std::string_view format_str, const Args &... args)
     813              : {
     814              :     //    assert(os.good());
     815       607562 :     auto buffer = fmt::memory_buffer();
     816              :     try {
     817       303781 :         fmt::format_to(std::back_inserter(buffer), format_str, args...);
     818            0 :     } catch (const fmt::format_error &) {
     819            0 :         throw EnergyPlus::FatalError(fmt::format("Error with format, '{}', passed {} args", format_str, sizeof...(Args)));
     820              :     }
     821       303781 :     os.write(buffer.data(), buffer.size());
     822       303781 : }
     823              : 
     824      1214463 : template <typename... Args> std::string vprint(std::string_view format_str, const Args &... args)
     825              : {
     826      2428926 :     auto buffer = fmt::memory_buffer();
     827              :     try {
     828      1214463 :         fmt::format_to(std::back_inserter(buffer), format_str, args...);
     829            0 :     } catch (const fmt::format_error &) {
     830            0 :         throw EnergyPlus::FatalError(fmt::format("Error with format, '{}', passed {} args", format_str, sizeof...(Args)));
     831              :     }
     832      2428926 :     return fmt::to_string(buffer);
     833      1214463 : }
     834              : 
     835              : // Uses lib {fmt} (which has been accepted for C++20)
     836              : // Formatting syntax guide is here: https://fmt.dev/latest/syntax.html
     837              : // The syntax is similar to printf, but uses {} to indicate parameters to be formatted
     838              : // you must escape any {} that you want with {}, like `{{}}`
     839              : //
     840              : // Defines a custom formatting type 'R' (round_ which chooses between `E` and `G` depending
     841              : // on the value being printed.
     842              : // This is necessary for parity with the old "RoundSigDigits" utility function
     843              : //
     844              : // Defines a custom formatting type 'S' that behaves like Fortran's G type, but stripped of whitespace
     845              : // 'S' was chosen for "Stripped". It is implemented in terms of 'N'
     846              : //
     847              : // Defines a custom formatting type 'N' that behaves like Fortran's G type.
     848              : // 'N' was chosen for "Number"
     849              : //
     850              : // Defines a custom formatting type 'Z' that behaves like Fortran's E type.
     851              : // 'Z' was chosen because Fortran's 'E' format always starts with a Zero
     852              : //
     853              : // Defines a custom formatting type 'T' that that truncates the value
     854              : // to match the behavior of TrimSigDigits utility function
     855              : //
     856              : 
     857              : namespace {
     858       303781 :     template <typename... Args> void print_fortran_syntax(std::ostream &os, std::string_view format_str, const Args &... args)
     859              :     {
     860       303781 :         EnergyPlus::vprint<std::conditional_t<std::is_same_v<double, Args>, DoubleWrapper, Args>...>(os, format_str, args...);
     861       303781 :     }
     862              : 
     863      1214463 :     template <typename... Args> std::string format_fortran_syntax(std::string_view format_str, const Args &... args)
     864              :     {
     865      1214463 :         return EnergyPlus::vprint<std::conditional_t<std::is_same_v<double, Args>, DoubleWrapper, Args>...>(format_str, args...);
     866              :     }
     867              : } // namespace
     868              : 
     869              : template <FormatSyntax formatSyntax = FormatSyntax::Fortran, typename... Args>
     870              : void print(std::ostream &os, std::string_view format_str, Args &&... args)
     871              : {
     872              :     if constexpr (formatSyntax == FormatSyntax::Fortran) {
     873              :         print_fortran_syntax(os, format_str, args...);
     874              :     } else if constexpr (formatSyntax == FormatSyntax::FMT) {
     875              :         fmt::print(os, format_str, std::forward<Args>(args)...);
     876              :     } else {
     877              :         static_assert(!(formatSyntax == FormatSyntax::Fortran || formatSyntax == FormatSyntax::FMT), "Invalid FormatSyntax selection");
     878              :     }
     879              : }
     880              : 
     881       340680 : template <FormatSyntax formatSyntax, typename... Args> void print(InputOutputFile &outputFile, std::string_view format_str, Args &&... args)
     882              : {
     883      1022040 :     auto *outputStream = [&]() -> std::ostream * {
     884       340680 :         if (outputFile.os) {
     885       340680 :             return outputFile.os.get();
     886              :         } else {
     887            0 :             if (outputFile.defaultToStdOut) {
     888            0 :                 return &std::cout;
     889              :             } else {
     890            0 :                 assert(outputFile.os);
     891            0 :                 return nullptr;
     892              :             }
     893              :         }
     894       340680 :     }();
     895              :     if constexpr (formatSyntax == FormatSyntax::Fortran) {
     896       303781 :         print_fortran_syntax(*outputStream, format_str, args...);
     897              :     } else if constexpr (formatSyntax == FormatSyntax::FMT) {
     898        36899 :         fmt::print(*outputStream, format_str, std::forward<Args>(args)...);
     899              :     } else {
     900              :         static_assert(!(formatSyntax == FormatSyntax::Fortran || formatSyntax == FormatSyntax::FMT), "Invalid FormatSyntax selection");
     901              :     }
     902       340680 : }
     903              : 
     904      1305468 : template <FormatSyntax formatSyntax = FormatSyntax::Fortran, typename... Args> std::string format(std::string_view format_str, Args &&... args)
     905              : {
     906              :     if constexpr (formatSyntax == FormatSyntax::Fortran) {
     907      1214463 :         return format_fortran_syntax(format_str, args...);
     908              :     } else if constexpr (formatSyntax == FormatSyntax::FMT) {
     909       182010 :         return fmt::format(format_str, std::forward<Args>(args)...);
     910              :     } else if constexpr (formatSyntax == FormatSyntax::Printf) {
     911              :         return fmt::sprintf(format_str, std::forward<Args>(args)...);
     912              :     }
     913              : }
     914              : 
     915              : } // namespace EnergyPlus
     916              : 
     917              : // extern template the most commonly used format function calls
     918              : // to save on compilation time. They will be explicitly instantiated
     919              : // in IOFiles.cc
     920              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int>(std::string_view, int &&);
     921              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const char *const &>(std::string_view, const char *const &);
     922              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, std::string &>(std::string_view, int &, std::string &);
     923              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, std::string &, std::string &, double &>(
     924              :     std::string_view, std::string &, std::string &, std::string &, double &);
     925              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const std::string_view &>(std::string_view,
     926              :                                                                                                             const std::string_view &);
     927              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const std::string_view &, std::string &>(std::string_view,
     928              :                                                                                                                            const std::string_view &,
     929              :                                                                                                                            std::string &);
     930              : extern template std::string
     931              : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, double &, double &>(std::string_view, std::string &, double &, double &);
     932              : extern template std::string
     933              : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, std::string &, int &>(std::string_view, std::string &, std::string &, int &);
     934              : extern template std::string
     935              : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &, double &, double &>(std::string_view, double &, double &, double &);
     936              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &, std::string &>(std::string_view, double &, std::string &);
     937              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &>(std::string_view, std::string &);
     938              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const int &, int &>(std::string_view, const int &, int &);
     939              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double>(std::string_view, double &&);
     940              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, int &>(std::string_view, int &, int &);
     941              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const double &>(std::string_view, const double &);
     942              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, int &>(std::string_view, std::string &, int &);
     943              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, std::string &, double &>(std::string_view,
     944              :                                                                                                                           std::string &,
     945              :                                                                                                                           std::string &,
     946              :                                                                                                                           double &);
     947              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, double &, std::string &, double &>(
     948              :     std::string_view, std::string &, double &, std::string &, double &);
     949              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const int &>(std::string_view, const int &);
     950              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, const std::string &, std::string &>(std::string_view,
     951              :                                                                                                                              int &,
     952              :                                                                                                                              const std::string &,
     953              :                                                                                                                              std::string &);
     954              : extern template std::string
     955              : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, int &, const std::string &>(std::string_view, int &, int &, const std::string &);
     956              : extern template std::string
     957              : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, int &, std::string_view &>(std::string_view, int &, int &, std::string_view &);
     958              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, std::string_view &, std::string &>(std::string_view,
     959              :                                                                                                                             int &,
     960              :                                                                                                                             std::string_view &,
     961              :                                                                                                                             std::string &);
     962              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &, double &>(std::string_view, double &, double &);
     963              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &>(std::string_view, int &);
     964              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, double &>(std::string_view, std::string &, double &);
     965              : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &>(std::string_view, double &);
     966              : 
     967              : #endif
        

Generated by: LCOV version 2.0-1