LCOV - code coverage report
Current view: top level - EnergyPlus - IOFiles.hh (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 209 265 78.9 %
Date: 2023-01-17 19:17:23 Functions: 1616 2428 66.6 %

          Line data    Source code
       1             : // EnergyPlus, Copyright (c) 1996-2023, The Board of Trustees of the University of Illinois,
       2             : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
       3             : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
       4             : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
       5             : // contributors. All rights reserved.
       6             : //
       7             : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
       8             : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
       9             : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
      10             : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
      11             : // derivative works, and perform publicly and display publicly, and to permit others to do so.
      12             : //
      13             : // Redistribution and use in source and binary forms, with or without modification, are permitted
      14             : // provided that the following conditions are met:
      15             : //
      16             : // (1) Redistributions of source code must retain the above copyright notice, this list of
      17             : //     conditions and the following disclaimer.
      18             : //
      19             : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
      20             : //     conditions and the following disclaimer in the documentation and/or other materials
      21             : //     provided with the distribution.
      22             : //
      23             : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
      24             : //     the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
      25             : //     used to endorse or promote products derived from this software without specific prior
      26             : //     written permission.
      27             : //
      28             : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
      29             : //     without changes from the version obtained under this License, or (ii) Licensee makes a
      30             : //     reference solely to the software portion of its product, Licensee must refer to the
      31             : //     software as "EnergyPlus version X" software, where "X" is the version number Licensee
      32             : //     obtained under this License and may not use a different name for the software. Except as
      33             : //     specifically required in this Section (4), Licensee shall not use in a company name, a
      34             : //     product name, in advertising, publicity, or other promotional activities any name, trade
      35             : //     name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
      36             : //     similar designation, without the U.S. Department of Energy's prior written consent.
      37             : //
      38             : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
      39             : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
      40             : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
      41             : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
      42             : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      43             : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      44             : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
      45             : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
      46             : // POSSIBILITY OF SUCH DAMAGE.
      47             : 
      48             : #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    15374398 :     DoubleWrapper(double val) : value(val){};
      79    15405923 :     operator double() const
      80             :     {
      81    15405923 :         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    30811846 : 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    15405923 :         void on_align(align_t)
     105             :         {
     106    15405923 :         }
     107           0 :         void on_sign(sign_t)
     108             :         {
     109           0 :         }
     110     2370832 :         void on_hash()
     111             :         {
     112     2370832 :         }
     113             :     };
     114             : 
     115     2774097 :     static constexpr bool should_be_fixed_output(const double value)
     116             :     {
     117     2774097 :         return (value >= 0.099999999999999995 || value <= -0.099999999999999995) || (value == 0.0) || (value == -0.0);
     118             :     }
     119             : 
     120       35700 :     static constexpr bool fixed_will_fit(const double value, const int places)
     121             :     {
     122       35700 :         if (value < 1.0 && value > -1.0) {
     123        8563 :             return true;
     124             :         } else {
     125       27137 :             return static_cast<int>(std::log10(std::abs(value))) < places;
     126             :         }
     127             :     }
     128             : 
     129      177860 :     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      177860 :         if (str.size() > 3) {
     133      177860 :             if (!std::isdigit(str[str.size() - 3])) {
     134             :                 // wants a 0 inserted
     135      177860 :                 str.insert(str.size() - 2, "0");
     136             :             }
     137             :         }
     138      177860 :         return str;
     139             :     }
     140             : 
     141    15405593 :     std::string_view spec_builder()
     142             :     {
     143    15405593 :         buffer.clear();
     144    15405593 :         buffer.push_back('{');
     145    15405593 :         buffer.push_back(':');
     146             :         //    [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
     147             : 
     148             :         //    [[fill]align]
     149    15405593 :         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    15405593 :         case align_t::none:
     163             :         case align_t::numeric:
     164    15405593 :             break;
     165           0 :         default:
     166           0 :             throw fmt::format_error("Bad alignment");
     167             :         }
     168             : 
     169             :         //    [sign]
     170    15405593 :         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    15405593 :         case sign_t::none:
     181    15405593 :             break;
     182           0 :         default:
     183           0 :             throw fmt::format_error("Bad sign");
     184             :         }
     185             : 
     186             :         //    [alt]
     187    15405593 :         if (specs_.alt) {
     188     2403459 :             buffer.push_back('#');
     189             :         }
     190             : 
     191             :         //    [width]
     192    15405593 :         if (specs_.width >= 0) {
     193    15405593 :             if (specs_.fill[0] == '0') {
     194           0 :                 buffer.push_back('0');
     195             :             }
     196    15405593 :             auto fmt_int = fmt::format_int(specs_.width);
     197    15405593 :             buffer.append(fmt_int.data(), fmt_int.data() + fmt_int.size());
     198             :         }
     199             : 
     200             :         //    [precision]
     201    15405593 :         if (specs_.precision >= 0) {
     202    15405593 :             buffer.push_back('.');
     203             : 
     204    15405593 :             auto fmt_int = fmt::format_int(specs_.precision);
     205    15405593 :             buffer.append(fmt_int.data(), fmt_int.data() + fmt_int.size());
     206             :         }
     207             : 
     208             :         //    [locale]
     209    15405593 :         if (specs_.localized) {
     210           0 :             buffer.push_back('L');
     211             :         }
     212             : 
     213             :         //    [type]
     214    15405593 :         buffer.push_back(specs_.type);
     215             : 
     216    15405593 :         buffer.push_back('}');
     217             : 
     218    15405593 :         return {buffer.data(), buffer.size()};
     219             :     }
     220             : 
     221    15405923 :     template <typename Context> void handle_specs(Context &ctx)
     222             :     {
     223    15405923 :         detail::handle_dynamic_spec<detail::width_checker>(specs_.width, specs_.width_ref, ctx);
     224    15405923 :         detail::handle_dynamic_spec<detail::precision_checker>(specs_.precision, specs_.precision_ref, ctx);
     225    15405923 :     }
     226             : 
     227             : public:
     228    15405923 :     template <typename ParseContext> constexpr auto parse(ParseContext &ctx)
     229             :     {
     230    15405923 :         auto begin = ctx.begin(), end = ctx.end();
     231    15405923 :         format_str_ = begin;
     232    15405923 :         if (begin == end) return begin;
     233             :         using handler_type = fmt::detail::dynamic_specs_handler<ParseContext>;
     234    30811846 :         auto it = fmt::detail::parse_format_specs(begin, end, handler_type(specs_, ctx));
     235    15405923 :         return it;
     236             :     }
     237             : 
     238    15405923 :     template <typename FormatContext> auto format(const DoubleWrapper &doubleWrapper, FormatContext &ctx)
     239             :     {
     240     4317835 :         const auto next_float = [](const double value) {
     241     4317835 :             if (std::signbit(value)) {
     242     1058768 :                 if (value == -0.0) {
     243           0 :                     return value;
     244             :                 } else {
     245     1058768 :                     return std::nextafter(value, std::numeric_limits<decltype(value)>::lowest());
     246             :                 }
     247             :             } else {
     248     3259067 :                 if (value == 0.0) {
     249           0 :                     return value;
     250             :                 } else {
     251     3259067 :                     return std::nextafter(value, std::numeric_limits<decltype(value)>::max());
     252             :                 }
     253             :             }
     254             :         };
     255             : 
     256    15405923 :         double val = doubleWrapper;
     257             : 
     258    15405923 :         handle_specs(ctx);
     259    15405923 :         detail::specs_checker<null_handler> checker(null_handler(), detail::mapped_type_constant<double, FormatContext>::value);
     260    15405923 :         checker.on_align(specs_.align);
     261    15405923 :         if (specs_.sign != sign::none) checker.on_sign(specs_.sign);
     262    15405923 :         if (specs_.alt) checker.on_hash();
     263    15405923 :         if (specs_.precision >= 0) checker.end_precision();
     264             : 
     265             :         // matches Fortran's 'E' format
     266    15405923 :         if (specs_.type == 'Z') {
     267             :             // The Fortran 'G' format insists on a leading 0, even though
     268             :             // that actually means losing data
     269       32627 :             specs_.type = 'E';
     270             : 
     271             :             // 0 pad the end
     272       32627 :             specs_.alt = true;
     273             : 
     274       32627 :             bool initialPrecisionWas1 = false;
     275       32627 :             if (specs_.precision > 1) {
     276             :                 // reduce the precision to get rounding behavior
     277       32627 :                 --specs_.precision;
     278             :             } else {
     279             :                 // We need AT LEAST one in precision so we capture a '.' below
     280           0 :                 initialPrecisionWas1 = true;
     281           0 :                 specs_.precision = 1;
     282           0 :                 ++specs_.width;
     283             :             }
     284             : 
     285             :             // multiply by 10 to get the exponent we want
     286       97881 :             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       32627 :             if (str.front() != ' ') {
     291           0 :                 str.insert(str.begin(), ' ');
     292             :             }
     293             : 
     294       32627 :             auto begin = std::find(std::begin(str), std::end(str), '.');
     295       32627 :             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           0 :                 str.erase(std::next(begin));
     299             :             }
     300             :             // if (begin != std::end(str)) {
     301             :             // ' -1.2345E15'
     302             :             //     ^
     303       32627 :             std::swap(*begin, *std::prev(begin));
     304             :             // ' -.12345E15'
     305             :             //     ^
     306       32627 :             std::advance(begin, -2);
     307             :             // ' -.12345E15'
     308             :             //   ^
     309       32627 :             if (*begin != ' ') {
     310             :                 // found a sign
     311        9765 :                 std::swap(*begin, *std::prev(begin));
     312             :                 // '- .12345E15'
     313             :                 //   ^
     314             :             }
     315             :             // '-0.12345E15'
     316             :             //   ^
     317       32627 :             *begin = '0';
     318       65254 :             return fmt::format_to(ctx.out(), "{}", str);
     319    15373296 :         } 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    15373296 :         } else if (specs_.type == 'N') {
     340             :             // matches Fortran's 'G' format
     341             : 
     342       68528 :             if (specs_.width == 0 && specs_.precision == -1) {
     343             :                 // Need to rerun with double wrapper since 'N' is one of our custom ones
     344         660 :                 return fmt::format_to(ctx.out(), "{:20N}", doubleWrapper);
     345       68198 :             } else if (should_be_fixed_output(val) && fixed_will_fit(val, specs_.width - 5)) {
     346       35693 :                 specs_.type = 'F';
     347             : 
     348             :                 // account for alignment with E formatted
     349       35693 :                 specs_.width -= 4;
     350       35693 :                 if (val == 0.0) {
     351         536 :                     --specs_.precision;
     352       35157 :                 } else if (val < 1.0 && val > -1.0) {
     353             :                     // No adjustment necessary
     354       27130 :                 } else if (specs_.precision == -1) {
     355         330 :                     const auto order_of_magnitude = static_cast<int>(std::log10(std::abs(val)));
     356         330 :                     specs_.precision = specs_.width - (order_of_magnitude + 2);
     357             :                 } else {
     358       26800 :                     const auto order_of_magnitude = static_cast<int>(std::log10(std::abs(val)));
     359       26800 :                     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       35693 :                 if (specs_.precision <= 0) {
     365           2 :                     specs_.width -= 1;
     366           2 :                     specs_.precision = 0;
     367             :                 }
     368             : 
     369      107079 :                 auto str = fmt::format(spec_builder(), val);
     370             : 
     371             :                 // When precision hit 0, add . to match Fortran formatting
     372       35693 :                 if (specs_.precision == 0) {
     373             :                     // write the last 4 chars
     374           4 :                     return fmt::format_to(ctx.out(), "{}.    ", str);
     375             :                 } else {
     376             :                     // write the last 4 chars
     377       71382 :                     return fmt::format_to(ctx.out(), "{}    ", str);
     378             :                 }
     379             :             } else {
     380             :                 // The Fortran 'G' format insists on a leading 0, even though
     381             :                 // that actually means losing data
     382       32505 :                 specs_.type = 'Z';
     383             :                 // Need to rerun with double wrapper since 'Z' is one of our custom ones
     384       97515 :                 return fmt::format_to(ctx.out(), spec_builder(), doubleWrapper);
     385             :             }
     386    15304768 :         } else if (specs_.type == 'R') { // matches RoundSigDigits() behavior
     387             :             // push the value up a tad to get the same rounding behavior as Objexx
     388     1783535 :             const auto fixed_output = should_be_fixed_output(val);
     389             : 
     390     1783535 :             if (fixed_output) {
     391     1678471 :                 specs_.type = 'F';
     392             : 
     393     1678471 :                 if (val > 100000.0) {
     394        3734 :                     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        3734 :                     if (digits10 + specs_.precision >= std::numeric_limits<decltype(val)>::max_digits10) {
     398           2 :                         specs_.precision = 0;
     399           2 :                         spec_builder();
     400             :                         // add '.' to match old RoundSigDigits
     401           2 :                         buffer.push_back('.');
     402           2 :                         std::string_view fmt_buffer(buffer.data(), buffer.size());
     403           4 :                         return fmt::format_to(ctx.out(), fmt_buffer, val);
     404             :                     } else {
     405       11196 :                         return fmt::format_to(ctx.out(), spec_builder(), val);
     406             :                     }
     407             :                 } else {
     408     1674737 :                     if (val == 0.0 || val == -0.0) {
     409      811440 :                         return fmt::format_to(ctx.out(), spec_builder(), 0.0);
     410             :                     } else {
     411             :                         // nudge up to next rounded val
     412     4212771 :                         return fmt::format_to(ctx.out(), spec_builder(), next_float(next_float(next_float(val))));
     413             :                     }
     414             :                 }
     415             :             } else {
     416      105064 :                 specs_.type = 'E';
     417      315192 :                 auto str = fmt::format(spec_builder(), next_float(val));
     418      315192 :                 return fmt::format_to(ctx.out(), "{}", zero_pad_exponent(str));
     419             :             }
     420    13521233 :         } else if (specs_.type == 'T') { // matches TrimSigDigits behavior
     421      922364 :             const auto fixed_output = should_be_fixed_output(val);
     422             : 
     423      922364 :             if (fixed_output) {
     424      849568 :                 const auto magnitude = std::pow(10, specs_.precision);
     425      849568 :                 const auto adjusted = (val * magnitude) + 0.0001;
     426      849568 :                 const auto truncated = std::trunc(adjusted) / magnitude;
     427      849568 :                 specs_.type = 'F';
     428     2548704 :                 return fmt::format_to(ctx.out(), spec_builder(), truncated);
     429             :             } else {
     430       72796 :                 specs_.type = 'E';
     431       72796 :                 specs_.precision += 2;
     432             : 
     433             :                 // write the `E` formatted float to a std::string
     434      218388 :                 auto str = fmt::format(spec_builder(), val);
     435       72796 :                 str = zero_pad_exponent(str);
     436             : 
     437             :                 // Erase last 2 numbers to truncate the value
     438       72796 :                 const auto E_itr = std::find(begin(str), end(str), 'E');
     439       72796 :                 if (E_itr != str.end()) {
     440       72796 :                     str.erase(std::prev(E_itr, 2), E_itr);
     441             :                 }
     442             : 
     443      145592 :                 return fmt::format_to(ctx.out(), "{}", str);
     444             :             }
     445             :         }
     446    37796607 :         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        3589 : class InputFile
     507             : {
     508             : public:
     509     2853528 :     template <typename Type> struct ReadResult
     510             :     {
     511     2821044 :         ReadResult(Type data_, bool eof_, bool good_) : data{std::move(data_)}, eof{eof_}, good{good_}
     512             :         {
     513     2821044 :         }
     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     2202514 :         void update(ReadResult &&other)
     520             :         {
     521     2202514 :             eof = other.eof;
     522     2202514 :             good = other.good;
     523     2202514 :             if (good) {
     524     2202514 :                 data = std::move(other.data);
     525             :             }
     526     2202514 :         }
     527             : 
     528             :         Type data;
     529             :         bool eof;
     530             :         bool good;
     531             :     };
     532             : 
     533             :     void close();
     534             :     bool good() const noexcept;
     535             : 
     536             :     bool is_open() const noexcept;
     537             : 
     538             :     void backspace() noexcept;
     539             : 
     540             :     std::string error_state_to_string() const;
     541             : 
     542             :     // opens the file if it is not currently open and returns
     543             :     // a reference back to itself
     544             :     InputFile &ensure_open(EnergyPlusData &state, const std::string &caller, bool output_to_file = true);
     545             :     std::istream::iostate rdstate() const noexcept;
     546             : 
     547             :     fs::path filePath;
     548             :     void open(bool = false, bool = true);
     549             :     std::fstream::pos_type position() const noexcept;
     550             : 
     551        1191 :     void rewind() noexcept
     552             :     {
     553        1191 :         if (is) {
     554        1191 :             is->clear(); // clear eofbit and potentially failbit
     555        1191 :             is->seekg(0, std::ios::beg);
     556             :         }
     557        1191 :     }
     558             : 
     559             :     ReadResult<std::string> readLine() noexcept;
     560             : 
     561        6111 :     template <typename T> ReadResult<T> read() noexcept
     562             :     {
     563        6111 :         if (is) {
     564             :             T result;
     565        6111 :             *is >> result;
     566        6111 :             return ReadResult<T>{result, is->eof(), is->good()};
     567             :         } else {
     568           0 :             return ReadResult<T>{T{}, true, false};
     569             :         }
     570             :     }
     571             : 
     572             :     std::string readFile();
     573             : 
     574             :     nlohmann::json readJSON();
     575             : 
     576             :     explicit InputFile(fs::path FilePath);
     577             : 
     578             : private:
     579             :     std::uintmax_t file_size{};
     580             :     std::unique_ptr<std::istream> is;
     581             :     friend class IOFiles;
     582             : };
     583             : 
     584      166435 : class InputOutputFile
     585             : {
     586             : public:
     587             :     fs::path filePath;
     588             :     bool defaultToStdOut = false;
     589             : 
     590             :     void close();
     591             :     void del();
     592             :     bool good() const;
     593             : 
     594             :     // opens the file if it is not currently open and returns
     595             :     // a reference back to itself
     596             :     InputOutputFile &ensure_open(EnergyPlusData &state, const std::string &caller, bool output_to_file = true);
     597             : 
     598             :     void open(const bool forAppend = false, bool output_to_file = true);
     599             :     std::fstream::pos_type position() const noexcept;
     600             :     std::vector<std::string> getLines();
     601             :     void open_as_stringstream();
     602             :     std::string get_output();
     603             :     void flush();
     604             :     explicit InputOutputFile(fs::path FilePath, const bool DefaultToStdOut = false);
     605             : 
     606             : private:
     607             :     std::unique_ptr<std::iostream> os;
     608             :     bool print_to_dev_null = false;
     609             :     template <FormatSyntax, typename... Args> friend void print(InputOutputFile &outputFile, std::string_view format_str, Args &&... args);
     610             :     friend class IOFiles;
     611             : };
     612             : 
     613       10023 : template <typename FileType> struct IOFilePath
     614             : {
     615             :     fs::path filePath;
     616        1371 :     FileType open(EnergyPlusData &state, const std::string &caller, bool output_to_file = true)
     617             :     {
     618        1371 :         FileType file{filePath};
     619        1371 :         file.ensure_open(state, caller, output_to_file);
     620        1371 :         return file;
     621             :     }
     622        2808 :     FileType try_open(bool output_to_file = true)
     623             :     {
     624        2808 :         FileType file{filePath};
     625        2808 :         file.open(false, output_to_file);
     626        2808 :         return file;
     627             :     }
     628             : };
     629             : 
     630             : using InputOutputFilePath = IOFilePath<InputOutputFile>;
     631             : using InputFilePath = IOFilePath<InputFile>;
     632             : 
     633        1542 : struct JsonOutputFilePaths
     634             : {
     635             :     fs::path outputJsonFilePath;
     636             :     fs::path outputTSHvacJsonFilePath;
     637             :     fs::path outputTSZoneJsonFilePath;
     638             :     fs::path outputTSJsonFilePath;
     639             :     fs::path outputYRJsonFilePath;
     640             :     fs::path outputMNJsonFilePath;
     641             :     fs::path outputDYJsonFilePath;
     642             :     fs::path outputHRJsonFilePath;
     643             :     fs::path outputSMJsonFilePath;
     644             :     fs::path outputCborFilePath;
     645             :     fs::path outputTSHvacCborFilePath;
     646             :     fs::path outputTSZoneCborFilePath;
     647             :     fs::path outputTSCborFilePath;
     648             :     fs::path outputYRCborFilePath;
     649             :     fs::path outputMNCborFilePath;
     650             :     fs::path outputDYCborFilePath;
     651             :     fs::path outputHRCborFilePath;
     652             :     fs::path outputSMCborFilePath;
     653             :     fs::path outputMsgPackFilePath;
     654             :     fs::path outputTSHvacMsgPackFilePath;
     655             :     fs::path outputTSZoneMsgPackFilePath;
     656             :     fs::path outputTSMsgPackFilePath;
     657             :     fs::path outputYRMsgPackFilePath;
     658             :     fs::path outputMNMsgPackFilePath;
     659             :     fs::path outputDYMsgPackFilePath;
     660             :     fs::path outputHRMsgPackFilePath;
     661             :     fs::path outputSMMsgPackFilePath;
     662             : };
     663             : 
     664        1542 : class IOFiles
     665             : {
     666             : public:
     667             :     struct OutputControl
     668             :     {
     669         771 :         OutputControl() = default;
     670             : 
     671             :         void getInput(EnergyPlusData &state);
     672             : 
     673             :         bool csv = false;
     674             :         bool mtr = true;
     675             :         bool eso = true;
     676             :         bool eio = true;
     677             :         bool audit = true;
     678             :         bool zsz = true;
     679             :         bool ssz = true;
     680             :         bool dxf = true;
     681             :         bool bnd = true;
     682             :         bool rdd = true;
     683             :         bool mdd = true;
     684             :         bool mtd = true;
     685             :         bool end = true;
     686             :         bool shd = true;
     687             :         bool dfs = true;
     688             :         bool glhe = true;
     689             :         bool delightin = true;
     690             :         bool delighteldmp = true;
     691             :         bool delightdfdmp = true;
     692             :         bool edd = true;
     693             :         bool dbg = true;
     694             :         bool perflog = true;
     695             :         bool sln = true;
     696             :         bool sci = true;
     697             :         bool wrl = true;
     698             :         bool screen = true;
     699             :         bool tarcog = true;
     700             :         bool extshd = true;
     701             :         bool json = true;
     702             :         bool tabular = true;
     703             :         bool sqlite = true;
     704             :     };
     705             : 
     706             :     OutputControl outputControl;
     707             : 
     708             :     InputOutputFile audit{"eplusout.audit"};
     709             :     InputOutputFile eio{"eplusout.eio"};
     710             :     InputOutputFile eso{"eplusout.eso"}; // (hourly data only)
     711             : 
     712             :     InputOutputFile zsz{""};
     713             :     fs::path outputZszCsvFilePath{"epluszsz.csv"};
     714             :     fs::path outputZszTabFilePath{"epluszsz.tab"};
     715             :     fs::path outputZszTxtFilePath{"epluszsz.txt"};
     716             : 
     717             :     InputOutputFile ssz{""};
     718             :     fs::path outputSszCsvFilePath{"eplusssz.csv"};
     719             :     fs::path outputSszTabFilePath{"eplusssz.tab"};
     720             :     fs::path outputSszTxtFilePath{"eplusssz.txt"};
     721             : 
     722             :     InputOutputFile map{""};
     723             :     fs::path outputMapCsvFilePath{"eplusmap.csv"};
     724             :     fs::path outputMapTabFilePath{"eplusmap.tab"};
     725             :     fs::path outputMapTxtFilePath{"eplusmap.txt"};
     726             : 
     727             :     InputOutputFile mtr{"eplusout.mtr"};
     728             :     InputOutputFile bnd{"eplusout.bnd"};
     729             :     InputOutputFile rdd{"eplusout.rdd"};
     730             :     InputOutputFile mdd{"eplusout.mdd"};
     731             : 
     732             :     InputOutputFile debug{"eplusout.dbg"};
     733             : 
     734             :     InputOutputFile dfs{"eplusout.dfs"};
     735             : 
     736             :     InputOutputFilePath sln{"eplusout.sln"};
     737             :     InputOutputFilePath dxf{"eplusout.dxf"};
     738             :     InputOutputFilePath sci{"eplusout.sci"};
     739             :     InputOutputFilePath wrl{"eplusout.wrl"};
     740             : 
     741             :     InputOutputFilePath delightIn{"eplusout.delightin"};
     742             : 
     743             :     InputOutputFile mtd{"eplusout.mtd"};
     744             :     InputOutputFile edd{"eplusout.edd", true}; // write to stdout if no file never opened
     745             :     InputOutputFile shade{"eplusshading.csv"};
     746             : 
     747             :     InputOutputFile csv{"eplusout.csv"};
     748             :     InputOutputFile mtr_csv{"eplusmtr.csv"};
     749             : 
     750             :     InputOutputFilePath screenCsv{"eplusscreen.csv"};
     751             :     InputOutputFilePath endFile{"eplusout.end"};
     752             : 
     753             :     InputFilePath iniFile{"EnergyPlus.ini"};
     754             : 
     755             :     InputFilePath outputDelightEldmpFilePath{"eplusout.delighteldmp"};
     756             :     InputFilePath outputDelightDfdmpFilePath{"eplusout.delightdfdmp"};
     757             : 
     758             :     // for transient uses of weather files
     759             :     // also, keeper of the currently set input weather file name
     760             :     InputFilePath inputWeatherFilePath{""};
     761             : 
     762             :     // for the persistent weather simulation, using the EPW
     763             :     // uses the file name set in `inputWeatherFilePath`
     764             :     InputFile inputWeatherFile{""};
     765             : 
     766             :     InputFilePath TempFullFilePath{""};
     767             :     InputFilePath inStatFilePath{""};
     768             : 
     769             :     fs::path outputErrFilePath{"eplusout.err"};
     770             :     std::unique_ptr<std::ostream> err_stream;
     771             : 
     772             :     JsonOutputFilePaths json; // Internal streams used for json outputs
     773             : 
     774             :     void flushAll(); // For RunningEnergyPlusViaAPI only
     775             : };
     776             : 
     777        2352 : class SharedFileHandle
     778             : {
     779             :     std::shared_ptr<InputOutputFile> file;
     780        3040 :     InputOutputFile *ptr()
     781             :     {
     782        3040 :         if (!file) {
     783           7 :             file = std::make_shared<InputOutputFile>("");
     784             :         }
     785             : 
     786        3040 :         return file.get();
     787             :     }
     788             : 
     789             : public:
     790        3019 :     InputOutputFile &operator*()
     791             :     {
     792        3019 :         return *ptr();
     793             :     }
     794             : 
     795          21 :     InputOutputFile *operator->()
     796             :     {
     797          21 :         return ptr();
     798             :     }
     799             : };
     800             : 
     801     6207614 : template <typename... Args> void vprint(std::ostream &os, std::string_view format_str, const Args &... args)
     802             : {
     803             :     //    assert(os.good());
     804    12415228 :     auto buffer = fmt::memory_buffer();
     805             :     try {
     806    12415228 :         fmt::format_to(std::back_inserter(buffer), format_str, args...);
     807           0 :     } catch (const fmt::format_error &) {
     808           0 :         throw EnergyPlus::FatalError(fmt::format("Error with format, '{}', passed {} args", format_str, sizeof...(Args)));
     809             :     }
     810     6207614 :     os.write(buffer.data(), buffer.size());
     811     6207614 : }
     812             : 
     813    20225710 : template <typename... Args> std::string vprint(std::string_view format_str, const Args &... args)
     814             : {
     815    40451420 :     auto buffer = fmt::memory_buffer();
     816             :     try {
     817    40451420 :         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    40451420 :     return fmt::to_string(buffer);
     822             : }
     823             : 
     824             : // Uses lib {fmt} (which has been accepted for C++20)
     825             : // Formatting syntax guide is here: https://fmt.dev/latest/syntax.html
     826             : // The syntax is similar to printf, but uses {} to indicate parameters to be formatted
     827             : // you must escape any {} that you want with {}, like `{{}}`
     828             : //
     829             : // Defines a custom formatting type 'R' (round_ which chooses between `E` and `G` depending
     830             : // on the value being printed.
     831             : // This is necessary for parity with the old "RoundSigDigits" utility function
     832             : //
     833             : // Defines a custom formatting type 'S' that behaves like Fortran's G type, but stripped of whitespace
     834             : // 'S' was chosen for "Stripped". It is implemented in terms of 'N'
     835             : //
     836             : // Defines a custom formatting type 'N' that behaves like Fortran's G type.
     837             : // 'N' was chosen for "Number"
     838             : //
     839             : // Defines a custom formatting type 'Z' that behaves like Fortran's E type.
     840             : // 'Z' was chosen because Fortran's 'E' format always starts with a Zero
     841             : //
     842             : // Defines a custom formatting type 'T' that that truncates the value
     843             : // to match the behavior of TrimSigDigits utility function
     844             : //
     845             : 
     846             : namespace {
     847     6207614 :     template <typename... Args> void print_fortran_syntax(std::ostream &os, std::string_view format_str, const Args &... args)
     848             :     {
     849     6207614 :         EnergyPlus::vprint<std::conditional_t<std::is_same_v<double, Args>, DoubleWrapper, Args>...>(os, format_str, args...);
     850     6207614 :     }
     851             : 
     852    20225710 :     template <typename... Args> std::string format_fortran_syntax(std::string_view format_str, const Args &... args)
     853             :     {
     854    20225710 :         return EnergyPlus::vprint<std::conditional_t<std::is_same_v<double, Args>, DoubleWrapper, Args>...>(format_str, args...);
     855             :     }
     856             : } // namespace
     857             : 
     858             : template <FormatSyntax formatSyntax = FormatSyntax::Fortran, typename... Args>
     859             : void print(std::ostream &os, std::string_view format_str, Args &&... args)
     860             : {
     861             :     if constexpr (formatSyntax == FormatSyntax::Fortran) {
     862             :         print_fortran_syntax(os, format_str, args...);
     863             :     } else if constexpr (formatSyntax == FormatSyntax::FMT) {
     864             :         fmt::print(os, format_str, std::forward<Args>(args)...);
     865             :     } else {
     866             :         static_assert(!(formatSyntax == FormatSyntax::Fortran || formatSyntax == FormatSyntax::FMT), "Invalid FormatSyntax selection");
     867             :     }
     868             : }
     869             : 
     870    16298286 : template <FormatSyntax formatSyntax, typename... Args> void print(InputOutputFile &outputFile, std::string_view format_str, Args &&... args)
     871             : {
     872    32596572 :     auto *outputStream = [&]() -> std::ostream * {
     873    16298286 :         if (outputFile.os) {
     874    16298286 :             return outputFile.os.get();
     875             :         } else {
     876           0 :             if (outputFile.defaultToStdOut) {
     877           0 :                 return &std::cout;
     878             :             } else {
     879           0 :                 assert(outputFile.os);
     880           0 :                 return nullptr;
     881             :             }
     882             :         }
     883    32596572 :     }();
     884             :     if constexpr (formatSyntax == FormatSyntax::Fortran) {
     885     6207614 :         print_fortran_syntax(*outputStream, format_str, args...);
     886             :     } else if constexpr (formatSyntax == FormatSyntax::FMT) {
     887    10090672 :         fmt::print(*outputStream, format_str, std::forward<Args>(args)...);
     888             :     } else {
     889             :         static_assert(!(formatSyntax == FormatSyntax::Fortran || formatSyntax == FormatSyntax::FMT), "Invalid FormatSyntax selection");
     890             :     }
     891    16298286 : }
     892             : 
     893    22055964 : template <FormatSyntax formatSyntax = FormatSyntax::Fortran, typename... Args> std::string format(std::string_view format_str, Args &&... args)
     894             : {
     895             :     if constexpr (formatSyntax == FormatSyntax::Fortran) {
     896    20225710 :         return format_fortran_syntax(format_str, args...);
     897             :     } else if constexpr (formatSyntax == FormatSyntax::FMT) {
     898     3660508 :         return fmt::format(format_str, std::forward<Args>(args)...);
     899             :     } else if constexpr (formatSyntax == FormatSyntax::Printf) {
     900             :         return fmt::sprintf(format_str, std::forward<Args>(args)...);
     901             :     }
     902             : }
     903             : 
     904             : } // namespace EnergyPlus
     905             : 
     906             : // extern template the most commonly used format function calls
     907             : // to save on compilation time. They will be explicitly instantiated
     908             : // in IOFiles.cc
     909             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int>(std::string_view, int &&);
     910             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const char *const &>(std::string_view, const char *const &);
     911             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, std::string &>(std::string_view, int &, std::string &);
     912             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, std::string &, std::string &, double &>(
     913             :     std::string_view, std::string &, std::string &, std::string &, double &);
     914             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const std::string_view &>(std::string_view,
     915             :                                                                                                             const std::string_view &);
     916             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const std::string_view &, std::string &>(std::string_view,
     917             :                                                                                                                            const std::string_view &,
     918             :                                                                                                                            std::string &);
     919             : extern template std::string
     920             : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, double &, double &>(std::string_view, std::string &, double &, double &);
     921             : extern template std::string
     922             : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, std::string &, int &>(std::string_view, std::string &, std::string &, int &);
     923             : extern template std::string
     924             : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &, double &, double &>(std::string_view, double &, double &, double &);
     925             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &, std::string &>(std::string_view, double &, std::string &);
     926             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &>(std::string_view, std::string &);
     927             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const int &, int &>(std::string_view, const int &, int &);
     928             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double>(std::string_view, double &&);
     929             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, int &>(std::string_view, int &, int &);
     930             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const double &>(std::string_view, const double &);
     931             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, int &>(std::string_view, std::string &, int &);
     932             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, std::string &, double &>(std::string_view,
     933             :                                                                                                                           std::string &,
     934             :                                                                                                                           std::string &,
     935             :                                                                                                                           double &);
     936             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, double &, std::string &, double &>(
     937             :     std::string_view, std::string &, double &, std::string &, double &);
     938             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const int &>(std::string_view, const int &);
     939             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, const std::string &, std::string &>(std::string_view,
     940             :                                                                                                                              int &,
     941             :                                                                                                                              const std::string &,
     942             :                                                                                                                              std::string &);
     943             : extern template std::string
     944             : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, int &, const std::string &>(std::string_view, int &, int &, const std::string &);
     945             : extern template std::string
     946             : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, int &, std::string_view &>(std::string_view, int &, int &, std::string_view &);
     947             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, std::string_view &, std::string &>(std::string_view,
     948             :                                                                                                                             int &,
     949             :                                                                                                                             std::string_view &,
     950             :                                                                                                                             std::string &);
     951             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &, double &>(std::string_view, double &, double &);
     952             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &>(std::string_view, int &);
     953             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, double &>(std::string_view, std::string &, double &);
     954             : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &>(std::string_view, double &);
     955             : 
     956             : #endif

Generated by: LCOV version 1.13