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 : // clang-format off
79 16862367 : DoubleWrapper(double val) : value(val) {};
80 : // clang-format on
81 16862367 : operator double() const
82 : {
83 16862367 : return value;
84 : };
85 : DoubleWrapper &operator=(const double &other)
86 : {
87 : value = other;
88 : return *this;
89 : }
90 :
91 : private:
92 : double value;
93 : };
94 : } // namespace
95 :
96 : namespace fmt {
97 : template <> struct formatter<DoubleWrapper>
98 : {
99 : private:
100 : fmt::detail::dynamic_format_specs<char> specs_;
101 : const char *format_str_;
102 : fmt::memory_buffer buffer = fmt::memory_buffer();
103 :
104 : struct null_handler : detail::error_handler
105 : {
106 16862367 : void on_align(align_t)
107 : {
108 16862367 : }
109 0 : void on_sign(sign_t)
110 : {
111 0 : }
112 2506894 : void on_hash()
113 : {
114 2506894 : }
115 : };
116 :
117 2999985 : static constexpr bool should_be_fixed_output(const double value)
118 : {
119 2999985 : return (value >= 0.099999999999999995 || value <= -0.099999999999999995) || (value == 0.0) || (value == -0.0);
120 : }
121 :
122 : static constexpr bool fixed_will_fit(const double value, const int places)
123 : {
124 : if (value < 1.0 && value > -1.0) {
125 : return true;
126 : } else {
127 : return static_cast<int>(std::log10(std::abs(value))) < places;
128 : }
129 : }
130 :
131 202263 : static std::string &zero_pad_exponent(std::string &str)
132 : {
133 : // if necessary, pad the exponent with a 0 to match the old formatting from Objexx
134 202263 : if (str.size() > 3) {
135 202263 : if (!std::isdigit(str[str.size() - 3])) {
136 : // wants a 0 inserted
137 202263 : str.insert(str.size() - 2, "0");
138 : }
139 : }
140 202263 : return str;
141 : }
142 :
143 16862367 : std::string_view spec_builder()
144 : {
145 16862367 : buffer.clear();
146 16862367 : buffer.push_back('{');
147 16862367 : buffer.push_back(':');
148 : // [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
149 :
150 : // [[fill]align]
151 16862367 : switch (specs_.align) {
152 0 : case align_t::left:
153 0 : if (specs_.fill.size()) {
154 0 : buffer.append(specs_.fill);
155 : }
156 0 : buffer.push_back('<');
157 0 : break;
158 0 : case align_t::right:
159 0 : if (specs_.fill.size()) {
160 0 : buffer.append(specs_.fill);
161 : }
162 0 : buffer.push_back('>');
163 0 : break;
164 0 : case align_t::center:
165 0 : if (specs_.fill.size()) {
166 0 : buffer.append(specs_.fill);
167 : }
168 0 : buffer.push_back('^');
169 0 : break;
170 16862367 : case align_t::none:
171 : case align_t::numeric:
172 16862367 : break;
173 0 : default:
174 0 : throw fmt::format_error("Bad alignment");
175 : }
176 :
177 : // [sign]
178 16862367 : switch (specs_.sign) {
179 0 : case sign_t::plus:
180 0 : buffer.push_back('+');
181 0 : break;
182 0 : case sign_t::minus:
183 0 : buffer.push_back('-');
184 0 : break;
185 0 : case sign_t::space:
186 0 : buffer.push_back(' ');
187 0 : break;
188 16862367 : case sign_t::none:
189 16862367 : break;
190 0 : default:
191 0 : throw fmt::format_error("Bad sign");
192 : }
193 :
194 : // [alt]
195 16862367 : if (specs_.alt) {
196 2506894 : buffer.push_back('#');
197 : }
198 :
199 : // [width]
200 16862367 : if (specs_.width >= 0) {
201 16862367 : if (specs_.fill[0] == '0') {
202 0 : buffer.push_back('0');
203 : }
204 16862367 : auto fmt_int = fmt::format_int(specs_.width);
205 16862367 : buffer.append(fmt_int.data(), fmt_int.data() + fmt_int.size());
206 : }
207 :
208 : // [precision]
209 16862367 : if (specs_.precision >= 0) {
210 16861414 : buffer.push_back('.');
211 :
212 16861414 : auto fmt_int = fmt::format_int(specs_.precision);
213 16861414 : buffer.append(fmt_int.data(), fmt_int.data() + fmt_int.size());
214 : }
215 :
216 : // [locale]
217 16862367 : if (specs_.localized) {
218 0 : buffer.push_back('L');
219 : }
220 :
221 : // [type]
222 16862367 : buffer.push_back(specs_.type);
223 :
224 16862367 : buffer.push_back('}');
225 :
226 16862367 : return {buffer.data(), buffer.size()};
227 : }
228 :
229 16862367 : template <typename Context> void handle_specs(Context &ctx)
230 : {
231 16862367 : detail::handle_dynamic_spec<detail::width_checker>(specs_.width, specs_.width_ref, ctx);
232 16862367 : detail::handle_dynamic_spec<detail::precision_checker>(specs_.precision, specs_.precision_ref, ctx);
233 16862367 : }
234 :
235 : public:
236 16862367 : template <typename ParseContext> constexpr auto parse(ParseContext &ctx)
237 : {
238 16862367 : auto begin = ctx.begin(), end = ctx.end();
239 16862367 : format_str_ = begin;
240 16862367 : if (begin == end) {
241 621 : return begin;
242 : }
243 : using handler_type = fmt::detail::dynamic_specs_handler<ParseContext>;
244 16861746 : auto it = fmt::detail::parse_format_specs(begin, end, handler_type(specs_, ctx));
245 16861746 : return it;
246 : }
247 :
248 16862367 : template <typename FormatContext> auto format(const DoubleWrapper &doubleWrapper, FormatContext &ctx)
249 : {
250 3794148 : const auto next_float = [](const double value) {
251 3794148 : if (std::signbit(value)) {
252 85414 : if (value == -0.0) {
253 0 : return value;
254 : } else {
255 85414 : return std::nextafter(value, std::numeric_limits<decltype(value)>::lowest());
256 : }
257 : } else {
258 3708734 : if (value == 0.0) {
259 0 : return value;
260 : } else {
261 3708734 : return std::nextafter(value, std::numeric_limits<decltype(value)>::max());
262 : }
263 : }
264 : };
265 :
266 16862367 : double val = doubleWrapper;
267 :
268 16862367 : handle_specs(ctx);
269 16862367 : detail::specs_checker<null_handler> checker(null_handler(), detail::mapped_type_constant<double, FormatContext>::value);
270 16862367 : checker.on_align(specs_.align);
271 16862367 : if (specs_.sign != sign::none) {
272 0 : checker.on_sign(specs_.sign);
273 : }
274 16862367 : if (specs_.alt) {
275 2506894 : checker.on_hash();
276 : }
277 16862367 : if (specs_.precision >= 0) {
278 16861414 : checker.end_precision();
279 : }
280 :
281 16862367 : if (specs_.type == 'R') { // matches RoundSigDigits() behavior
282 : // push the value up a tad to get the same rounding behavior as Objexx
283 1686608 : const auto fixed_output = should_be_fixed_output(val);
284 :
285 1686608 : if (fixed_output) {
286 1573700 : specs_.type = 'F';
287 :
288 1573700 : if (val > 100000.0) {
289 3969 : const auto digits10 = static_cast<int>(std::log10(val));
290 : // we cannot represent this val to the required precision, truncate the floating
291 : // point portion
292 3969 : if (digits10 + specs_.precision >= std::numeric_limits<decltype(val)>::max_digits10) {
293 3 : specs_.precision = 0;
294 3 : spec_builder();
295 : // add '.' to match old RoundSigDigits
296 3 : buffer.push_back('.');
297 3 : std::string_view fmt_buffer(buffer.data(), buffer.size());
298 6 : return fmt::format_to(ctx.out(), fmt_buffer, val);
299 : } else {
300 11898 : return fmt::format_to(ctx.out(), spec_builder(), val);
301 : }
302 : } else {
303 1569731 : if (val == 0.0 || val == -0.0) {
304 1027953 : return fmt::format_to(ctx.out(), spec_builder(), 0.0);
305 : } else {
306 : // nudge up to next rounded val
307 3681240 : return fmt::format_to(ctx.out(), spec_builder(), next_float(next_float(next_float(val))));
308 : }
309 : }
310 : } else {
311 112908 : specs_.type = 'E';
312 112908 : auto str = fmt::format(spec_builder(), next_float(val));
313 225816 : return fmt::format_to(ctx.out(), "{}", zero_pad_exponent(str));
314 112908 : }
315 15175759 : } else if (specs_.type == 'T') { // matches TrimSigDigits behavior
316 1313377 : const auto fixed_output = should_be_fixed_output(val);
317 :
318 1313377 : if (fixed_output) {
319 1224022 : const auto magnitude = std::pow(10, specs_.precision);
320 1224022 : const auto adjusted = (val * magnitude) + 0.0001;
321 1224022 : const auto truncated = std::trunc(adjusted) / magnitude;
322 1224022 : specs_.type = 'F';
323 3672066 : return fmt::format_to(ctx.out(), spec_builder(), truncated);
324 : } else {
325 89355 : specs_.type = 'E';
326 89355 : specs_.precision += 2;
327 :
328 : // write the `E` formatted float to a std::string
329 89355 : auto str = fmt::format(spec_builder(), val);
330 89355 : str = zero_pad_exponent(str);
331 :
332 : // Erase last 2 numbers to truncate the value
333 178710 : const auto E_itr = std::find(begin(str), end(str), 'E');
334 89355 : if (E_itr != str.end()) {
335 178710 : str.erase(std::prev(E_itr, 2), E_itr);
336 : }
337 :
338 178710 : return fmt::format_to(ctx.out(), "{}", str);
339 89355 : }
340 : }
341 41587146 : return fmt::format_to(ctx.out(), spec_builder(), val);
342 : }
343 : };
344 : } // namespace fmt
345 :
346 : namespace EnergyPlus {
347 :
348 : // Forward declarations
349 : struct EnergyPlusData;
350 :
351 : enum class FormatSyntax
352 : {
353 : Invalid = -1,
354 : Fortran,
355 : FMT,
356 : Printf,
357 : Num
358 : };
359 :
360 : inline constexpr bool is_fortran_syntax(const std::string_view format_str)
361 : {
362 : bool within_fmt_str = false;
363 : for (auto const c : format_str) {
364 : switch (c) {
365 : case '{':
366 : within_fmt_str = true;
367 : break;
368 : case '}':
369 : within_fmt_str = false;
370 : break;
371 : case 'R':
372 : case 'T':
373 : if (within_fmt_str) {
374 : return true;
375 : } else {
376 : break;
377 : }
378 : default:
379 : break;
380 : }
381 : }
382 : return false;
383 : }
384 :
385 : class InputOutputFile;
386 : template <FormatSyntax formatSyntax = FormatSyntax::Fortran, typename... Args>
387 : void print(InputOutputFile &outputFile, std::string_view format_str, Args &&...args);
388 :
389 : inline constexpr FormatSyntax check_syntax(const std::string_view format_str)
390 : {
391 : if (is_fortran_syntax(format_str)) {
392 : return FormatSyntax::Fortran;
393 : } else {
394 : return FormatSyntax::FMT;
395 : }
396 : }
397 :
398 : class InputFile
399 : {
400 : public:
401 : template <typename Type> struct ReadResult
402 : {
403 2906139 : ReadResult(Type data_, bool eof_, bool good_) : data{std::move(data_)}, eof{eof_}, good{good_}
404 : {
405 2906139 : }
406 :
407 : // Update the current eof/good state from the incoming value
408 : // but only update the `data` member if the state is good
409 : // The idea is to keep consistency with the operator>> that was used
410 : // from gio
411 2227012 : void update(ReadResult &&other)
412 : {
413 2227012 : eof = other.eof;
414 2227012 : good = other.good;
415 2227012 : if (good) {
416 2227012 : data = std::move(other.data);
417 : }
418 2227012 : }
419 :
420 : Type data;
421 : bool eof;
422 : bool good;
423 : };
424 :
425 : void close();
426 :
427 : // 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)
428 : // this operate like `operator bool(istream& is)` <=> `!is.bad() && !is.fail()`
429 : bool good() const noexcept;
430 :
431 : bool is_open() const noexcept;
432 :
433 : void backspace() noexcept;
434 :
435 : std::string error_state_to_string() const;
436 :
437 : // opens the file if it is not currently open and returns
438 : // a reference back to itself
439 : InputFile &ensure_open(EnergyPlusData &state, const std::string &caller, bool output_to_file = true);
440 : std::istream::iostate rdstate() const noexcept;
441 :
442 : fs::path filePath;
443 : void open(bool = false, bool = true);
444 : std::fstream::pos_type position() const noexcept;
445 :
446 1209 : void rewind() noexcept
447 : {
448 1209 : if (is) {
449 1209 : is->clear(); // clear potentially failbit and badbit (seekg would only clear eofbit)
450 1209 : is->seekg(0, std::ios::beg);
451 : }
452 1209 : }
453 :
454 : ReadResult<std::string> readLine() noexcept;
455 :
456 6111 : template <typename T> ReadResult<T> read() noexcept
457 : {
458 6111 : if (is) {
459 : T result;
460 6111 : *is >> result;
461 : // Use operator bool, see ReadResult::good() docstring
462 6111 : return ReadResult<T>{result, is->eof(), bool(is)};
463 : } else {
464 0 : return ReadResult<T>{T{}, true, false};
465 : }
466 : }
467 :
468 : std::string readFile();
469 :
470 : nlohmann::json readJSON();
471 :
472 : explicit InputFile(fs::path FilePath);
473 :
474 : private:
475 : std::uintmax_t file_size{};
476 : std::unique_ptr<std::istream> is;
477 : friend class IOFiles;
478 : };
479 :
480 : class InputOutputFile
481 : {
482 : public:
483 : fs::path filePath;
484 : bool defaultToStdOut = false;
485 :
486 : void close();
487 : void del();
488 : bool good() const;
489 :
490 : // opens the file if it is not currently open and returns
491 : // a reference back to itself
492 : InputOutputFile &ensure_open(EnergyPlusData &state, const std::string &caller, bool output_to_file = true);
493 :
494 : void open(const bool forAppend = false, bool output_to_file = true);
495 : std::fstream::pos_type position() const noexcept;
496 : std::vector<std::string> getLines();
497 : void open_as_stringstream();
498 : std::string get_output();
499 : void flush();
500 : explicit InputOutputFile(fs::path FilePath, const bool DefaultToStdOut = false);
501 :
502 : private:
503 : std::unique_ptr<std::iostream> os;
504 : bool print_to_dev_null = false;
505 : template <FormatSyntax, typename... Args> friend void print(InputOutputFile &outputFile, std::string_view format_str, Args &&...args);
506 : friend class IOFiles;
507 : };
508 :
509 : template <typename FileType> struct IOFilePath
510 : {
511 : fs::path filePath;
512 1408 : FileType open(EnergyPlusData &state, const std::string &caller, bool output_to_file = true)
513 : {
514 1408 : FileType file{filePath};
515 1408 : file.ensure_open(state, caller, output_to_file);
516 1408 : return file;
517 0 : }
518 2840 : FileType try_open(bool output_to_file = true)
519 : {
520 2840 : FileType file{filePath};
521 2840 : file.open(false, output_to_file);
522 2840 : return file;
523 0 : }
524 : };
525 :
526 : using InputOutputFilePath = IOFilePath<InputOutputFile>;
527 : using InputFilePath = IOFilePath<InputFile>;
528 :
529 : struct JsonOutputFilePaths
530 : {
531 : fs::path outputJsonFilePath;
532 : fs::path outputTSHvacJsonFilePath;
533 : fs::path outputTSZoneJsonFilePath;
534 : fs::path outputTSJsonFilePath;
535 : fs::path outputYRJsonFilePath;
536 : fs::path outputMNJsonFilePath;
537 : fs::path outputDYJsonFilePath;
538 : fs::path outputHRJsonFilePath;
539 : fs::path outputSMJsonFilePath;
540 : fs::path outputCborFilePath;
541 : fs::path outputTSHvacCborFilePath;
542 : fs::path outputTSZoneCborFilePath;
543 : fs::path outputTSCborFilePath;
544 : fs::path outputYRCborFilePath;
545 : fs::path outputMNCborFilePath;
546 : fs::path outputDYCborFilePath;
547 : fs::path outputHRCborFilePath;
548 : fs::path outputSMCborFilePath;
549 : fs::path outputMsgPackFilePath;
550 : fs::path outputTSHvacMsgPackFilePath;
551 : fs::path outputTSZoneMsgPackFilePath;
552 : fs::path outputTSMsgPackFilePath;
553 : fs::path outputYRMsgPackFilePath;
554 : fs::path outputMNMsgPackFilePath;
555 : fs::path outputDYMsgPackFilePath;
556 : fs::path outputHRMsgPackFilePath;
557 : fs::path outputSMMsgPackFilePath;
558 : };
559 :
560 : class IOFiles
561 : {
562 : public:
563 : struct OutputControl
564 : {
565 803 : OutputControl() = default;
566 :
567 : void getInput(EnergyPlusData &state);
568 : bool writeTabular(EnergyPlusData &state);
569 :
570 : bool csv = false;
571 : bool mtr = true;
572 : bool eso = true;
573 : bool eio = true;
574 : bool audit = true;
575 : bool spsz = true;
576 : bool zsz = true;
577 : bool ssz = true;
578 : bool dxf = true;
579 : bool bnd = true;
580 : bool rdd = true;
581 : bool mdd = true;
582 : bool mtd = true;
583 : bool end = true;
584 : bool shd = true;
585 : bool dfs = true;
586 : bool glhe = true;
587 : bool delightin = true;
588 : bool delighteldmp = true;
589 : bool delightdfdmp = true;
590 : bool edd = true;
591 : bool dbg = true;
592 : bool perflog = true;
593 : bool sln = true;
594 : bool sci = true;
595 : bool wrl = true;
596 : bool screen = true;
597 : bool tarcog = true;
598 : bool extshd = true;
599 : bool json = true;
600 : bool tabular = true;
601 : bool sqlite = true;
602 : };
603 :
604 : OutputControl outputControl;
605 :
606 : InputOutputFile audit{"eplusout.audit"};
607 : InputOutputFile eio{"eplusout.eio"};
608 : InputOutputFile eso{"eplusout.eso"}; // (hourly data only)
609 :
610 : InputOutputFile zsz{""};
611 : fs::path outputZszCsvFilePath{"epluszsz.csv"};
612 : fs::path outputZszTabFilePath{"epluszsz.tab"};
613 : fs::path outputZszTxtFilePath{"epluszsz.txt"};
614 :
615 : InputOutputFile spsz{""};
616 : fs::path outputSpszCsvFilePath{"eplusspsz.csv"};
617 : fs::path outputSpszTabFilePath{"eplusspsz.tab"};
618 : fs::path outputSpszTxtFilePath{"eplusspsz.txt"};
619 :
620 : InputOutputFile ssz{""};
621 : fs::path outputSszCsvFilePath{"eplusssz.csv"};
622 : fs::path outputSszTabFilePath{"eplusssz.tab"};
623 : fs::path outputSszTxtFilePath{"eplusssz.txt"};
624 :
625 : InputOutputFile map{""};
626 : fs::path outputMapCsvFilePath{"eplusmap.csv"};
627 : fs::path outputMapTabFilePath{"eplusmap.tab"};
628 : fs::path outputMapTxtFilePath{"eplusmap.txt"};
629 :
630 : InputOutputFile mtr{"eplusout.mtr"};
631 : InputOutputFile bnd{"eplusout.bnd"};
632 : InputOutputFile rdd{"eplusout.rdd"};
633 : InputOutputFile mdd{"eplusout.mdd"};
634 :
635 : InputOutputFile debug{"eplusout.dbg"};
636 :
637 : InputOutputFile dfs{"eplusout.dfs"};
638 :
639 : InputOutputFilePath sln{"eplusout.sln"};
640 : InputOutputFilePath dxf{"eplusout.dxf"};
641 : InputOutputFilePath sci{"eplusout.sci"};
642 : InputOutputFilePath wrl{"eplusout.wrl"};
643 :
644 : InputOutputFilePath delightIn{"eplusout.delightin"};
645 :
646 : InputOutputFile mtd{"eplusout.mtd"};
647 : InputOutputFile edd{"eplusout.edd", true}; // write to stdout if no file never opened
648 : InputOutputFile shade{"eplusshading.csv"};
649 :
650 : InputOutputFile csv{"eplusout.csv"};
651 : InputOutputFile mtr_csv{"eplusmtr.csv"};
652 :
653 : InputOutputFilePath screenCsv{"eplusscreen.csv"};
654 : InputOutputFilePath endFile{"eplusout.end"};
655 :
656 : InputFilePath iniFile{"EnergyPlus.ini"};
657 :
658 : InputFilePath outputDelightEldmpFilePath{"eplusout.delighteldmp"};
659 : InputFilePath outputDelightDfdmpFilePath{"eplusout.delightdfdmp"};
660 :
661 : // for transient uses of weather files
662 : // also, keeper of the currently set input weather file name
663 : InputFilePath inputWeatherFilePath{""};
664 :
665 : // for the persistent weather simulation, using the EPW
666 : // uses the file name set in `inputWeatherFilePath`
667 : InputFile inputWeatherFile{""};
668 :
669 : InputFilePath TempFullFilePath{""};
670 : InputFilePath inStatFilePath{""};
671 :
672 : fs::path outputErrFilePath{"eplusout.err"};
673 : std::unique_ptr<std::ostream> err_stream;
674 :
675 : JsonOutputFilePaths json; // Internal streams used for json outputs
676 :
677 : void flushAll(); // For RunningEnergyPlusViaAPI only
678 : };
679 :
680 : class SharedFileHandle
681 : {
682 : std::shared_ptr<InputOutputFile> file;
683 3322 : InputOutputFile *ptr()
684 : {
685 3322 : if (!file) {
686 9 : file = std::make_shared<InputOutputFile>("");
687 : }
688 :
689 3322 : return file.get();
690 : }
691 :
692 : public:
693 3295 : InputOutputFile &operator*()
694 : {
695 3295 : return *ptr();
696 : }
697 :
698 27 : InputOutputFile *operator->()
699 : {
700 27 : return ptr();
701 : }
702 : };
703 :
704 6829067 : template <typename... Args> void vprint(std::ostream &os, std::string_view format_str, const Args &...args)
705 : {
706 : // assert(os.good());
707 13658134 : auto buffer = fmt::memory_buffer();
708 : try {
709 6829067 : fmt::format_to(std::back_inserter(buffer), format_str, args...);
710 0 : } catch (const fmt::format_error &) {
711 0 : throw EnergyPlus::FatalError(fmt::format("Error with format, '{}', passed {} args", format_str, sizeof...(Args)));
712 : }
713 6829067 : os.write(buffer.data(), buffer.size());
714 6829067 : }
715 :
716 21553015 : template <typename... Args> std::string vprint(std::string_view format_str, const Args &...args)
717 : {
718 43106030 : auto buffer = fmt::memory_buffer();
719 : try {
720 21553036 : fmt::format_to(std::back_inserter(buffer), format_str, args...);
721 42 : } catch (const fmt::format_error &) {
722 42 : throw EnergyPlus::FatalError(fmt::format("Error with format, '{}', passed {} args", format_str, sizeof...(Args)));
723 : }
724 43105988 : return fmt::to_string(buffer);
725 21553015 : }
726 :
727 : // Uses lib {fmt} (which has been accepted for C++20)
728 : // Formatting syntax guide is here: https://fmt.dev/latest/syntax.html
729 : // The syntax is similar to printf, but uses {} to indicate parameters to be formatted
730 : // you must escape any {} that you want with {}, like `{{}}`
731 : //
732 : // Defines a custom formatting type 'R' (round_ which chooses between `E` and `G` depending
733 : // on the value being printed.
734 : // This is necessary for parity with the old "RoundSigDigits" utility function
735 : //
736 : // Defines a custom formatting type 'T' that that truncates the value
737 : // to match the behavior of TrimSigDigits utility function
738 : //
739 :
740 : namespace {
741 6829067 : template <typename... Args> void print_fortran_syntax(std::ostream &os, std::string_view format_str, const Args &...args)
742 : {
743 6829067 : EnergyPlus::vprint<std::conditional_t<std::is_same_v<double, Args>, DoubleWrapper, Args>...>(os, format_str, args...);
744 6829067 : }
745 :
746 21553015 : template <typename... Args> std::string format_fortran_syntax(std::string_view format_str, const Args &...args)
747 : {
748 21553015 : return EnergyPlus::vprint<std::conditional_t<std::is_same_v<double, Args>, DoubleWrapper, Args>...>(format_str, args...);
749 : }
750 : } // namespace
751 :
752 : template <FormatSyntax formatSyntax = FormatSyntax::Fortran, typename... Args>
753 : void print(std::ostream &os, std::string_view format_str, Args &&...args)
754 : {
755 : if constexpr (formatSyntax == FormatSyntax::Fortran) {
756 : print_fortran_syntax(os, format_str, args...);
757 : } else if constexpr (formatSyntax == FormatSyntax::FMT) {
758 : fmt::print(os, format_str, std::forward<Args>(args)...);
759 : } else {
760 : static_assert(!(formatSyntax == FormatSyntax::Fortran || formatSyntax == FormatSyntax::FMT), "Invalid FormatSyntax selection");
761 : }
762 : }
763 :
764 23752300 : template <FormatSyntax formatSyntax, typename... Args> void print(InputOutputFile &outputFile, std::string_view format_str, Args &&...args)
765 : {
766 71256900 : auto *outputStream = [&]() -> std::ostream * {
767 23752300 : if (outputFile.os) {
768 23752300 : return outputFile.os.get();
769 : } else {
770 0 : if (outputFile.defaultToStdOut) {
771 0 : return &std::cout;
772 : } else {
773 0 : assert(outputFile.os);
774 0 : return nullptr;
775 : }
776 : }
777 23752300 : }();
778 : if constexpr (formatSyntax == FormatSyntax::Fortran) {
779 6829067 : print_fortran_syntax(*outputStream, format_str, args...);
780 : } else if constexpr (formatSyntax == FormatSyntax::FMT) {
781 16923233 : fmt::print(*outputStream, format_str, std::forward<Args>(args)...);
782 : } else {
783 : static_assert(!(formatSyntax == FormatSyntax::Fortran || formatSyntax == FormatSyntax::FMT), "Invalid FormatSyntax selection");
784 : }
785 23752300 : }
786 :
787 23534042 : template <FormatSyntax formatSyntax = FormatSyntax::Fortran, typename... Args> std::string format(std::string_view format_str, Args &&...args)
788 : {
789 : if constexpr (formatSyntax == FormatSyntax::Fortran) {
790 21553015 : return format_fortran_syntax(format_str, args...);
791 : } else if constexpr (formatSyntax == FormatSyntax::FMT) {
792 3962054 : return fmt::format(format_str, std::forward<Args>(args)...);
793 : } else if constexpr (formatSyntax == FormatSyntax::Printf) {
794 : return fmt::sprintf(format_str, std::forward<Args>(args)...);
795 : }
796 : }
797 :
798 : } // namespace EnergyPlus
799 :
800 : // extern template the most commonly used format function calls
801 : // to save on compilation time. They will be explicitly instantiated
802 : // in IOFiles.cc
803 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int>(std::string_view, int &&);
804 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const char *const &>(std::string_view, const char *const &);
805 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, std::string &>(std::string_view, int &, std::string &);
806 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, std::string &, std::string &, double &>(
807 : std::string_view, std::string &, std::string &, std::string &, double &);
808 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const std::string_view &>(std::string_view,
809 : const std::string_view &);
810 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const std::string_view &, std::string &>(std::string_view,
811 : const std::string_view &,
812 : std::string &);
813 : extern template std::string
814 : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, double &, double &>(std::string_view, std::string &, double &, double &);
815 : extern template std::string
816 : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, std::string &, int &>(std::string_view, std::string &, std::string &, int &);
817 : extern template std::string
818 : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &, double &, double &>(std::string_view, double &, double &, double &);
819 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &, std::string &>(std::string_view, double &, std::string &);
820 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &>(std::string_view, std::string &);
821 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const int &, int &>(std::string_view, const int &, int &);
822 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double>(std::string_view, double &&);
823 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, int &>(std::string_view, int &, int &);
824 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const double &>(std::string_view, const double &);
825 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, int &>(std::string_view, std::string &, int &);
826 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, std::string &, double &>(std::string_view,
827 : std::string &,
828 : std::string &,
829 : double &);
830 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, double &, std::string &, double &>(
831 : std::string_view, std::string &, double &, std::string &, double &);
832 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, const int &>(std::string_view, const int &);
833 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, const std::string &, std::string &>(std::string_view,
834 : int &,
835 : const std::string &,
836 : std::string &);
837 : extern template std::string
838 : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, int &, const std::string &>(std::string_view, int &, int &, const std::string &);
839 : extern template std::string
840 : EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, int &, std::string_view &>(std::string_view, int &, int &, std::string_view &);
841 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &, std::string_view &, std::string &>(std::string_view,
842 : int &,
843 : std::string_view &,
844 : std::string &);
845 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &, double &>(std::string_view, double &, double &);
846 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, int &>(std::string_view, int &);
847 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, std::string &, double &>(std::string_view, std::string &, double &);
848 : extern template std::string EnergyPlus::format<EnergyPlus::FormatSyntax::Fortran, double &>(std::string_view, double &);
849 :
850 : #endif
|