From 200e4ca5ed00a5901d02fc23f18f74aaf7b85c9b Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 17 Feb 2026 14:24:19 +0000 Subject: [PATCH 1/3] Re-enable compile-time format checks Mostly involves using `fmt::vformat` with `fmt::make_format_args` in functions/ctors that act like `fmt::format`. Note that `fmt::make_format_args` requires lvalues, so although we take `Args&&` we _must not_ call `std::forward`. We also have to introduce a new gettext macro `_f` to allow compile-time format arg checking _and_ runtime i18n substitution. --- include/bout/boutexception.hxx | 25 +-- include/bout/msg_stack.hxx | 21 ++- include/bout/options.hxx | 46 ++++- include/bout/optionsreader.hxx | 17 +- include/bout/output.hxx | 48 +++--- include/bout/sys/expressionparser.hxx | 14 +- include/bout/sys/gettext.hxx | 36 +++- src/bout++.cxx | 176 +++++++++++--------- src/mesh/coordinates.cxx | 4 +- src/mesh/impls/bout/boutmesh.cxx | 108 ++++++------ src/mesh/mesh.cxx | 57 +++++-- src/solver/impls/petsc/petsc.cxx | 2 +- src/solver/impls/snes/snes.cxx | 2 +- src/solver/impls/split-rk/split-rk.cxx | 18 +- src/solver/solver.cxx | 45 +++-- src/sys/expressionparser.cxx | 49 ++++-- src/sys/options.cxx | 110 ++++-------- src/sys/options/options_ini.cxx | 21 ++- src/sys/optionsreader.cxx | 13 +- tests/unit/sys/test_options.cxx | 18 +- tests/unit/sys/test_output.cxx | 2 +- tools/pylib/_boutpp_build/boutcpp.pxd.jinja | 2 +- tools/pylib/_boutpp_build/boutpp.pyx.jinja | 2 +- 23 files changed, 495 insertions(+), 341 deletions(-) diff --git a/include/bout/boutexception.hxx b/include/bout/boutexception.hxx index d525aeb608..735f98c7d6 100644 --- a/include/bout/boutexception.hxx +++ b/include/bout/boutexception.hxx @@ -5,6 +5,7 @@ #include #include +#include "fmt/base.h" #include "fmt/core.h" /// Throw BoutRhsFail with \p message if any one process has non-zero @@ -19,10 +20,10 @@ public: BoutException& operator=(BoutException&&) = delete; BoutException(std::string msg); - template - BoutException(S&& format, Args&&... args) - : BoutException(fmt::format(fmt::runtime(std::forward(format)), - std::forward(args)...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + BoutException(fmt::format_string format, Args&&... args) + : BoutException(fmt::vformat(format, fmt::make_format_args(args...))) {} ~BoutException() override; @@ -44,19 +45,19 @@ private: class BoutRhsFail : public BoutException { public: BoutRhsFail(std::string message) : BoutException(std::move(message)) {} - template - BoutRhsFail(S&& format, Args&&... args) - : BoutRhsFail(fmt::format(fmt::runtime(std::forward(format)), - std::forward(args)...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + BoutRhsFail(fmt::format_string format, Args&&... args) + : BoutRhsFail(fmt::vformat(format, fmt::make_format_args(args...))) {} }; class BoutIterationFail : public BoutException { public: BoutIterationFail(std::string message) : BoutException(std::move(message)) {} - template - BoutIterationFail(S&& format, Args&&... args) - : BoutIterationFail(fmt::format(fmt::runtime(std::forward(format)), - std::forward(args)...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + BoutIterationFail(fmt::format_string format, Args&&... args) + : BoutIterationFail(fmt::vformat(format, fmt::make_format_args(args...))) {} }; #endif diff --git a/include/bout/msg_stack.hxx b/include/bout/msg_stack.hxx index 8771aeab42..89b9f2d8d5 100644 --- a/include/bout/msg_stack.hxx +++ b/include/bout/msg_stack.hxx @@ -31,6 +31,7 @@ class MsgStack; #include "bout/build_defines.hxx" +#include "fmt/base.h" #include "fmt/core.h" #include @@ -60,9 +61,10 @@ public: int push(std::string message); int push() { return push(""); } - template - int push(const S& format, const Args&... args) { - return push(fmt::format(fmt::runtime(format), args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + int push(fmt::format_string format, Args&&... args) { + return push(fmt::vformat(format, fmt::make_format_args(args...))); } void pop(); ///< Remove the last message @@ -74,8 +76,8 @@ public: #else /// Dummy functions which should be optimised out int push(const std::string&) { return 0; } - template - int push(const S&, const Args&...) { + template + int push(fmt::format_string format, const Args&... args) { return 0; } @@ -132,10 +134,13 @@ public: MsgStackItem(const std::string& file, int line, const char* msg) : point(msg_stack.push("{:s} on line {:d} of '{:s}'", msg, line, file)) {} - template - MsgStackItem(const std::string& file, int line, const S& msg, const Args&... args) + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + MsgStackItem(const std::string& file, int line, fmt::format_string msg, + Args&&... args) : point(msg_stack.push("{:s} on line {:d} of '{:s}'", - fmt::format(fmt::runtime(msg), args...), line, file)) {} + fmt::vformat(msg, fmt::make_format_args(args...)), line, + file)) {} ~MsgStackItem() { // If an exception has occurred, don't pop the message if (exception_count == std::uncaught_exceptions()) { diff --git a/include/bout/options.hxx b/include/bout/options.hxx index 1c552cd4f9..f6be64fa83 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -52,6 +52,7 @@ class Options; #include "bout/utils.hxx" #include +#include #include #include @@ -626,8 +627,8 @@ public: // Option not found. Copy the value from the default. this->_set_no_check(def.value, DEFAULT_SOURCE); - output_info << _("\tOption ") << full_name << " = " << def.full_name << " (" - << DEFAULT_SOURCE << ")\n"; + output_info.write("{}{} = {}({})\n", _("\tOption "), full_name, def.full_name, + DEFAULT_SOURCE); } else { // Check if this was previously set as a default option if (bout::utils::variantEqualTo(attributes.at("source"), DEFAULT_SOURCE)) { @@ -911,8 +912,8 @@ private: << ")\n"; } else { throw BoutException( - _("Options: Setting a value from same source ({:s}) to new value " - "'{:s}' - old value was '{:s}'."), + _f("Options: Setting a value from same source ({:s}) to new value " + "'{:s}' - old value was '{:s}'."), source, toString(val), bout::utils::variantToString(value)); } } @@ -1043,7 +1044,40 @@ namespace details { /// so that we can put the function definitions in the .cxx file, /// avoiding lengthy recompilation if we change it struct OptionsFormatterBase { - auto parse(fmt::format_parse_context& ctx) -> fmt::format_parse_context::iterator; + constexpr auto parse(fmt::format_parse_context& ctx) { + const auto* it = ctx.begin(); + const auto *const end = ctx.end(); + + while (it != end and *it != '}') { + switch (*it) { + case 'd': + docstrings = true; + ++it; + break; + case 'i': + inline_section_names = true; + ++it; + break; + case 'k': + key_only = true; + ++it; + break; + case 's': + source = true; + ++it; + break; + case 'u': + unused = true; + ++it; + break; + default: + throw fmt::format_error("invalid format for 'Options'"); + } + } + + return it; + } + auto format(const Options& options, fmt::format_context& ctx) const -> fmt::format_context::iterator; @@ -1060,8 +1094,6 @@ private: bool key_only{false}; /// Include the 'source' attribute, if present bool source{false}; - /// Format string to passed down to subsections - std::string format_string; }; } // namespace details } // namespace bout diff --git a/include/bout/optionsreader.hxx b/include/bout/optionsreader.hxx index 0c9c227916..c5cda6ef1f 100644 --- a/include/bout/optionsreader.hxx +++ b/include/bout/optionsreader.hxx @@ -36,7 +36,8 @@ class OptionsReader; #include "bout/options.hxx" -#include "fmt/core.h" +#include +#include #include #include @@ -69,9 +70,10 @@ public: /// @param[in] file The name of the file. printf style arguments can be used to create the file name. void read(Options* options, const std::string& filename); - template - void read(Options* options, const S& format, const Args&... args) { - return read(options, fmt::format(fmt::runtime(format), args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void read(Options* options, fmt::format_string format, Args&&... args) { + return read(options, fmt::vformat(format, fmt::make_format_args(args...))); } /// Write options to file @@ -80,9 +82,10 @@ public: /// @param[in] file The name of the file to (over)write void write(Options* options, const std::string& filename); - template - void write(Options* options, const S& format, const Args&... args) { - return write(options, fmt::format(fmt::runtime(format), args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void write(Options* options, fmt::format_string format, Args&&... args) { + return write(options, fmt::vformat(format, fmt::make_format_args(args...))); } /// Parse options from the command line diff --git a/include/bout/output.hxx b/include/bout/output.hxx index 4eb1d615ca..9416ab411b 100644 --- a/include/bout/output.hxx +++ b/include/bout/output.hxx @@ -39,7 +39,8 @@ class Output; #include "bout/sys/gettext.hxx" // IWYU pragma: keep for gettext _() macro #include "bout/unused.hxx" -#include "fmt/core.h" +#include +#include #include @@ -81,10 +82,10 @@ public: open(filename); } - template - Output(const S& format, Args&&... args) - : Output(fmt::format(fmt::runtime(format), std::forward(args)...)) { - } + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + Output(fmt::format_string format, Args&&... args) + : Output(fmt::vformat(format, fmt::make_format_args(args...))) {} ~Output() override { close(); } @@ -94,9 +95,10 @@ public: /// Open an output log file int open(const std::string& filename); - template - int open(const S& format, Args&&... args) { - return open(fmt::format(fmt::runtime(format), std::forward(args)...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + int open(fmt::format_string format, Args&&... args) { + return open(fmt::vformat(format, fmt::make_format_args(args...))); } /// Close the log file @@ -105,16 +107,18 @@ public: /// Write a string using fmt format virtual void write(const std::string& message); - template - void write(const S& format, Args&&... args) { - write(fmt::format(fmt::runtime(format), std::forward(args)...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void write(fmt::format_string format, Args&&... args) { + write(fmt::vformat(format, fmt::make_format_args(args...))); } /// Same as write, but only to screen virtual void print(const std::string& message); - template - void print(const S& format, Args&&... args) { - print(fmt::format(fmt::runtime(format), std::forward(args)...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void print(fmt::format_string format, Args&&... args) { + print(fmt::vformat(format, fmt::make_format_args(args...))); } /// Add an output stream. All output will be sent to all streams @@ -175,12 +179,12 @@ public: /// This string is then sent to log file and stdout (on processor 0) void write(const std::string& message) override; - template - void write(const S& format, Args&&... args) { + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void write(fmt::format_string format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->write( - fmt::format(fmt::runtime(format), std::forward(args)...)); + base->write(fmt::vformat(format, fmt::make_format_args(args...))); } } @@ -188,12 +192,12 @@ public: /// note: unlike write, this is not also sent to log files void print(const std::string& message) override; - template - void print(const S& format, Args&&... args) { + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void print(fmt::format_string format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->print( - fmt::format(fmt::runtime(format), std::forward(args)...)); + base->print(fmt::vformat(format, fmt::make_format_args(args...))); } } diff --git a/include/bout/sys/expressionparser.hxx b/include/bout/sys/expressionparser.hxx index 78525e5b69..8b2442e843 100644 --- a/include/bout/sys/expressionparser.hxx +++ b/include/bout/sys/expressionparser.hxx @@ -29,7 +29,8 @@ #include "bout/unused.hxx" -#include "fmt/core.h" +#include +#include #include #include @@ -239,11 +240,16 @@ private: class ParseException : public std::exception { public: + ParseException(const ParseException&) = default; + ParseException(ParseException&&) = delete; + ParseException& operator=(const ParseException&) = default; + ParseException& operator=(ParseException&&) = delete; ParseException(const std::string& message_) : message(message_) {} - template - ParseException(const S& format, const Args&... args) - : message(fmt::format(fmt::runtime(format), args...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + ParseException(fmt::format_string format, Args&&... args) + : message(fmt::vformat(format, fmt::make_format_args(args...))) {} ~ParseException() override = default; diff --git a/include/bout/sys/gettext.hxx b/include/bout/sys/gettext.hxx index d67becd54d..d62cd66882 100644 --- a/include/bout/sys/gettext.hxx +++ b/include/bout/sys/gettext.hxx @@ -7,15 +7,47 @@ #if BOUT_HAS_GETTEXT -#include +#include // IWYU pragma: keep + #include #define GETTEXT_PACKAGE "libbout" -#define _(string) dgettext(GETTEXT_PACKAGE, string) +// If we have C++23, we can get fmt to do compile-time checks of our format +// strings, _and_ have gettext do runtime replacement +#if __cpp_if_consteval >= 202106L +constexpr const char* dgettext_wrap(const char* __domainname, const char* __msgid) __THROW + __attribute_format_arg__(2); + +constexpr const char* dgettext_wrap(const char* __domainname, const char* __msgid) { + if consteval { + return __msgid; + } + return dgettext(__domainname, __msgid); +} + +/// Gettext i18n macro for text containing fmt format specifiers +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _f(string) dgettext_wrap(GETTEXT_PACKAGE, string) #else +// We're pre-C++23, so all our i18n text must be fmt runtime formats +#include "fmt/base.h" // IWYU pragma: keep + +/// Gettext i18n macro for text containing fmt format specifiers +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _f(string) fmt::runtime(dgettext(GETTEXT_PACKAGE, string)) +#endif + +/// Gettext i18n macro for plain text that doesn't need formatting +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _(string) dgettext(GETTEXT_PACKAGE, string) + +#else // BOUT_HAS_GETTEXT +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _f(string) string +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _(string) string #endif // BOUT_HAS_GETTEXT diff --git a/src/bout++.cxx b/src/bout++.cxx index 4ca4d352ca..cad3edf6c3 100644 --- a/src/bout++.cxx +++ b/src/bout++.cxx @@ -32,16 +32,23 @@ static constexpr auto DEFAULT_DIR = "data"; #define GLOBALORIGIN #include "bout++-time.hxx" +#include "bout/array.hxx" #include "bout/boundary_factory.hxx" +#include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" #include "bout/coordinates_accessor.hxx" +#include "bout/dcomplex.hxx" +#include "bout/globals.hxx" #include "bout/hyprelib.hxx" #include "bout/interpolation_xz.hxx" #include "bout/interpolation_z.hxx" #include "bout/invert/laplacexz.hxx" #include "bout/invert_laplace.hxx" #include "bout/invert_parderiv.hxx" +#include "bout/mask.hxx" +#include "bout/monitor.hxx" #include "bout/mpi_wrapper.hxx" #include "bout/msg_stack.hxx" #include "bout/openmpwrap.hxx" @@ -52,7 +59,9 @@ static constexpr auto DEFAULT_DIR = "data"; #include "bout/rkscheme.hxx" #include "bout/slepclib.hxx" #include "bout/solver.hxx" +#include "bout/sys/gettext.hxx" #include "bout/sys/timer.hxx" +#include "bout/utils.hxx" #include "bout/version.hxx" #define BOUT_NO_USING_NAMESPACE_BOUTGLOBALS @@ -63,11 +72,22 @@ static constexpr auto DEFAULT_DIR = "data"; #include "bout/adios_object.hxx" #endif +#include #include +#include +#include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include #include #include @@ -137,7 +157,7 @@ int BoutInitialise(int& argc, char**& argv) { try { args = parseCommandLineArgs(argc, argv); } catch (const BoutException& e) { - output_error << _("Bad command line arguments:\n") << e.what() << std::endl; + output_error.write("{:s}{:s}\n", _("Bad command line arguments:\n"), e.what()); return 1; } @@ -323,8 +343,8 @@ template // Now we can print all the options used in constructing our // type. Note that this does require all the options are used in the // constructor, and not in a `init` method or similar - std::cout << fmt::format("Input options for {} '{}':\n\n", Factory::type_name, type); - std::cout << fmt::format(fmt::runtime("{:id}\n"), help_options); + fmt::println("Input options for {:s} '{}':\n", Factory::type_name, type); + fmt::println("{:id}", help_options); std::exit(EXIT_SUCCESS); } @@ -344,7 +364,7 @@ void handleFactoryHelp(const std::string& current_arg, int i, int argc, char** a if (current_arg == help_arg) { if (i + 1 >= argc) { - throw BoutException(_("Usage is {} {} \n"), argv[0], help_arg); + throw BoutException(_f("Usage is {} {} \n"), argv[0], help_arg); } printTypeOptions(argv[i + 1]); } @@ -358,9 +378,10 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { if (current_arg == "-h" || current_arg == "--help") { // Print help message -- note this will be displayed once per processor as we've not // started MPI yet. - output.write(_("Usage: {:s} [-d ] [-f ] [restart " - "[append]] [VAR=VALUE]\n"), - argv[0]); + output.write( + _f("Usage: {:s} [-d ] [-f ] [restart " + "[append]] [VAR=VALUE]\n"), + argv[0]); output.write( _("\n" " -d \t\tLook in for input/output files\n" @@ -373,33 +394,33 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { output.write(_(" -c, --color\t\t\tColor output using bout-log-color\n")); #endif output.write( - _(" --print-config\t\tPrint the compile-time configuration\n" - " --list-solvers\t\tList the available time solvers\n" - " --help-solver \tPrint help for the given time solver\n" - " --list-laplacians\t\tList the available Laplacian inversion solvers\n" - " --help-laplacian \tPrint help for the given Laplacian " - "inversion solver\n" - " --list-laplacexz\t\tList the available LaplaceXZ inversion solvers\n" - " --help-laplacexz \tPrint help for the given LaplaceXZ " - "inversion solver\n" - " --list-invertpars\t\tList the available InvertPar solvers\n" - " --help-invertpar \tPrint help for the given InvertPar solver\n" - " --list-rkschemes\t\tList the available Runge-Kutta schemes\n" - " --help-rkscheme \tPrint help for the given Runge-Kutta scheme\n" - " --list-meshes\t\t\tList the available Meshes\n" - " --help-mesh \t\tPrint help for the given Mesh\n" - " --list-xzinterpolations\tList the available XZInterpolations\n" - " --help-xzinterpolation \tPrint help for the given " - "XZInterpolation\n" - " --list-zinterpolations\tList the available ZInterpolations\n" - " --help-zinterpolation \tPrint help for the given " - "ZInterpolation\n" - " -h, --help\t\t\tThis message\n" - " restart [append]\t\tRestart the simulation. If append is specified, " - "append to the existing output files, otherwise overwrite them\n" - " VAR=VALUE\t\t\tSpecify a VALUE for input parameter VAR\n" - "\nFor all possible input parameters, see the user manual and/or the " - "physics model source (e.g. {:s}.cxx)\n"), + _f(" --print-config\t\tPrint the compile-time configuration\n" + " --list-solvers\t\tList the available time solvers\n" + " --help-solver \tPrint help for the given time solver\n" + " --list-laplacians\t\tList the available Laplacian inversion solvers\n" + " --help-laplacian \tPrint help for the given Laplacian " + "inversion solver\n" + " --list-laplacexz\t\tList the available LaplaceXZ inversion solvers\n" + " --help-laplacexz \tPrint help for the given LaplaceXZ " + "inversion solver\n" + " --list-invertpars\t\tList the available InvertPar solvers\n" + " --help-invertpar \tPrint help for the given InvertPar solver\n" + " --list-rkschemes\t\tList the available Runge-Kutta schemes\n" + " --help-rkscheme \tPrint help for the given Runge-Kutta scheme\n" + " --list-meshes\t\t\tList the available Meshes\n" + " --help-mesh \t\tPrint help for the given Mesh\n" + " --list-xzinterpolations\tList the available XZInterpolations\n" + " --help-xzinterpolation \tPrint help for the given " + "XZInterpolation\n" + " --list-zinterpolations\tList the available ZInterpolations\n" + " --help-zinterpolation \tPrint help for the given " + "ZInterpolation\n" + " -h, --help\t\t\tThis message\n" + " restart [append]\t\tRestart the simulation. If append is specified, " + "append to the existing output files, otherwise overwrite them\n" + " VAR=VALUE\t\t\tSpecify a VALUE for input parameter VAR\n" + "\nFor all possible input parameters, see the user manual and/or the " + "physics model source (e.g. {:s}.cxx)\n"), argv[0]); std::exit(EXIT_SUCCESS); @@ -430,7 +451,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { if (string(argv[i]) == "-d") { // Set data directory if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -d \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -d \n"), argv[0]); } args.data_dir = argv[++i]; @@ -439,7 +460,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } else if (string(argv[i]) == "-f") { // Set options file if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -f \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -f \n"), argv[0]); } args.opt_file = argv[++i]; @@ -448,7 +469,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } else if (string(argv[i]) == "-o") { // Set options file if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -o \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -o \n"), argv[0]); } args.set_file = argv[++i]; @@ -457,7 +478,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } else if ((string(argv[i]) == "-l") || (string(argv[i]) == "--log")) { // Set log file if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -l \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -l \n"), argv[0]); } args.log_file = argv[++i]; @@ -495,10 +516,10 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { void checkDataDirectoryIsAccessible(const std::string& data_dir) { if (std::filesystem::exists(data_dir)) { if (!std::filesystem::is_directory(data_dir)) { - throw BoutException(_("DataDir \"{:s}\" is not a directory\n"), data_dir); + throw BoutException(_f("DataDir \"{:s}\" is not a directory\n"), data_dir); } } else { - throw BoutException(_("DataDir \"{:s}\" does not exist or is not accessible\n"), + throw BoutException(_f("DataDir \"{:s}\" does not exist or is not accessible\n"), data_dir); } } @@ -510,7 +531,7 @@ void savePIDtoFile(const std::string& data_dir, int MYPE) { pid_file.open(filename.str(), std::ios::out | std::ios::trunc); if (not pid_file.is_open()) { - throw BoutException(_("Could not create PID file {:s}"), filename.str()); + throw BoutException(_f("Could not create PID file {:s}"), filename.str()); } pid_file << getpid() << "\n"; @@ -518,17 +539,17 @@ void savePIDtoFile(const std::string& data_dir, int MYPE) { } void printStartupHeader(int MYPE, int NPES) { - output_progress.write(_("BOUT++ version {:s}\n"), bout::version::full); - output_progress.write(_("Revision: {:s}\n"), bout::version::revision); + output_progress.write(_f("BOUT++ version {:s}\n"), bout::version::full); + output_progress.write(_f("Revision: {:s}\n"), bout::version::revision); #ifdef MD5SUM output_progress.write("MD5 checksum: {:s}\n", BUILDFLAG(MD5SUM)); #endif - output_progress.write(_("Code compiled on {:s} at {:s}\n\n"), boutcompiledate, + output_progress.write(_f("Code compiled on {:s} at {:s}\n\n"), boutcompiledate, boutcompiletime); output_info.write("B.Dudson (University of York), M.Umansky (LLNL) 2007\n"); output_info.write("Based on BOUT by Xueqiao Xu, 1999\n\n"); - output_info.write(_("Processor number: {:d} of {:d}\n\n"), MYPE, NPES); + output_info.write(_f("Processor number: {:d} of {:d}\n\n"), MYPE, NPES); output_info.write("pid: {:d}\n\n", getpid()); } @@ -538,53 +559,51 @@ void printCompileTimeOptions() { using namespace bout::build; - output_info.write(_("\tRuntime error checking {}"), is_enabled(check_level > 0)); + output_info.write(_f("\tRuntime error checking {}"), is_enabled(check_level > 0)); if (check_level > 0) { - output_info.write(_(", level {}"), check_level); + output_info.write(_f(", level {}"), check_level); } output_info.write("\n"); #ifdef PNCDF - output_info.write(_("\tParallel NetCDF support enabled\n")); + output_info.write(_f("\tParallel NetCDF support enabled\n")); #else - output_info.write(_("\tParallel NetCDF support disabled\n")); + output_info.write(_f("\tParallel NetCDF support disabled\n")); #endif - output_info.write(_("\tMetrics mode is {}\n"), use_metric_3d ? "3D" : "2D"); - output_info.write(_("\tFFT support {}\n"), is_enabled(has_fftw)); - output_info.write(_("\tNatural language support {}\n"), is_enabled(has_gettext)); - output_info.write(_("\tLAPACK support {}\n"), is_enabled(has_lapack)); + output_info.write(_f("\tMetrics mode is {}\n"), use_metric_3d ? "3D" : "2D"); + output_info.write(_f("\tFFT support {}\n"), is_enabled(has_fftw)); + output_info.write(_f("\tNatural language support {}\n"), is_enabled(has_gettext)); + output_info.write(_f("\tLAPACK support {}\n"), is_enabled(has_lapack)); // Horrible nested ternary to set this at compile time constexpr auto netcdf_flavour = has_netcdf ? (has_legacy_netcdf ? " (Legacy)" : " (NetCDF4)") : ""; - output_info.write(_("\tNetCDF support {}{}\n"), is_enabled(has_netcdf), netcdf_flavour); - output_info.write(_("\tADIOS2 support {}\n"), is_enabled(has_adios2)); - output_info.write(_("\tPETSc support {}\n"), is_enabled(has_petsc)); - output_info.write(_("\tPVODE support {}\n"), is_enabled(has_pvode)); - output_info.write(_("\tScore-P support {}\n"), is_enabled(has_scorep)); - output_info.write(_("\tSLEPc support {}\n"), is_enabled(has_slepc)); - output_info.write(_("\tSUNDIALS support {}\n"), is_enabled(has_sundials)); - output_info.write(_("\tBacktrace in exceptions {}\n"), is_enabled(use_backtrace)); - output_info.write(_("\tColour in logs {}\n"), is_enabled(use_color)); - output_info.write(_("\tOpenMP parallelisation {}, using {} threads\n"), + output_info.write(_f("\tNetCDF support {}{}\n"), is_enabled(has_netcdf), + netcdf_flavour); + output_info.write(_f("\tADIOS2 support {}\n"), is_enabled(has_adios2)); + output_info.write(_f("\tPETSc support {}\n"), is_enabled(has_petsc)); + output_info.write(_f("\tPVODE support {}\n"), is_enabled(has_pvode)); + output_info.write(_f("\tScore-P support {}\n"), is_enabled(has_scorep)); + output_info.write(_f("\tSLEPc support {}\n"), is_enabled(has_slepc)); + output_info.write(_f("\tSUNDIALS support {}\n"), is_enabled(has_sundials)); + output_info.write(_f("\tBacktrace in exceptions {}\n"), is_enabled(use_backtrace)); + output_info.write(_f("\tColour in logs {}\n"), is_enabled(use_color)); + output_info.write(_f("\tOpenMP parallelisation {}, using {} threads\n"), is_enabled(use_openmp), omp_get_max_threads()); - output_info.write(_("\tExtra debug output {}\n"), is_enabled(use_output_debug)); - output_info.write(_("\tFloating-point exceptions {}\n"), is_enabled(use_sigfpe)); - output_info.write(_("\tSignal handling support {}\n"), is_enabled(use_signal)); - output_info.write(_("\tField name tracking {}\n"), is_enabled(use_track)); - output_info.write(_("\tMessage stack {}\n"), is_enabled(use_msgstack)); + output_info.write(_f("\tExtra debug output {}\n"), is_enabled(use_output_debug)); + output_info.write(_f("\tFloating-point exceptions {}\n"), is_enabled(use_sigfpe)); + output_info.write(_f("\tSignal handling support {}\n"), is_enabled(use_signal)); + output_info.write(_f("\tField name tracking {}\n"), is_enabled(use_track)); + output_info.write(_f("\tMessage stack {}\n"), is_enabled(use_msgstack)); // The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings // which could cause problems (e.g. terminate strings). - output_info.write(_("\tCompiled with flags : {:s}\n"), STRINGIFY(BOUT_FLAGS_STRING)); + output_info.write(_f("\tCompiled with flags : {:s}\n"), STRINGIFY(BOUT_FLAGS_STRING)); } void printCommandLineArguments(const std::vector& original_argv) { - output_info.write(_("\tCommand line options for this run : ")); - for (auto& arg : original_argv) { - output_info << arg << " "; - } - output_info.write("\n"); + output_info.write("{:s}{}\n", _("\tCommand line options for this run : "), + fmt::join(original_argv, " ")); } bool setupBoutLogColor(bool color_output, int MYPE) { @@ -618,7 +637,8 @@ bool setupBoutLogColor(bool color_output, int MYPE) { } if (!success) { // Failed . Probably not important enough to stop the simulation - std::cerr << _("Could not run bout-log-color. Make sure it is in your PATH\n"); + fmt::print(stderr, "{:s}", + _("Could not run bout-log-color. Make sure it is in your PATH\n")); } return success; } @@ -638,7 +658,7 @@ void setupOutput(const std::string& data_dir, const std::string& log_file, int v /// Open an output file to echo everything to /// On processor 0 anything written to output will go to stdout and the file if (output.open("{:s}/{:s}.{:d}", data_dir, log_file, MYPE)) { - throw BoutException(_("Could not open {:s}/{:s}.{:d} for writing"), data_dir, + throw BoutException(_f("Could not open {:s}/{:s}.{:d} for writing"), data_dir, log_file, MYPE); } } @@ -731,7 +751,7 @@ int BoutFinalise(bool write_settings) { writeSettingsFile(options, data_dir, set_file); } } catch (const BoutException& e) { - output_error << _("Error whilst writing settings") << e.what() << endl; + output_error.write("{} {}\n", _("Error whilst writing settings"), e.what()); } } @@ -888,7 +908,7 @@ int BoutMonitor::call(Solver* solver, BoutReal t, [[maybe_unused]] int iter, int BoutReal t_remain = mpi_start_time + wall_limit - bout::globals::mpi->MPI_Wtime(); if (t_remain < run_data.wtime * 2) { // Less than 2 time-steps left - output_warn.write(_("Only {:e} seconds ({:.2f} steps) left. Quitting\n"), t_remain, + output_warn.write(_f("Only {:e} seconds ({:.2f} steps) left. Quitting\n"), t_remain, t_remain / run_data.wtime); user_requested_exit = true; } else { diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 086fa2e23e..21287f984d 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -1232,7 +1232,7 @@ int Coordinates::calcCovariant(const std::string& region) { a(0, 2) = a(2, 0) = g13[i]; if (const auto det = bout::invert3x3(a); det.has_value()) { - output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:d}\n", + output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:e}\n", i, det.value()); return 1; } @@ -1287,7 +1287,7 @@ int Coordinates::calcContravariant(const std::string& region) { a(0, 2) = a(2, 0) = g_13[i]; if (const auto det = bout::invert3x3(a); det.has_value()) { - output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:d}\n", + output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:e}\n", i, det.value()); return 1; } diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 3ba16b5f2f..8f0171577d 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -26,24 +26,44 @@ #include "boutmesh.hxx" +#include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include +#include +#include +#include #include #include +#include + #include +#include +#include #include +#include +#include +#include #include +#include +#include +#include /// MPI type of BoutReal for communications #define PVEC_REAL_MPI_TYPE MPI_DOUBLE @@ -163,16 +183,14 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, // Check size of Y mesh if we've got multiple processors in Y if (num_local_y_points < num_y_guards and num_y_processors != 1) { return {false, - fmt::format(fmt::runtime(_( - "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n")), + fmt::format(_f("\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n"), ny, num_y_processors, num_local_y_points, num_y_guards)}; } // Check branch cuts if ((jyseps1_1 + 1) % num_local_y_points != 0) { - return {false, - fmt::format(fmt::runtime(_("\t -> Leg region jyseps1_1+1 ({:d}) must be a " - "multiple of MYSUB ({:d})\n")), - jyseps1_1 + 1, num_local_y_points)}; + return {false, fmt::format(_f("\t -> Leg region jyseps1_1+1 ({:d}) must be a " + "multiple of MYSUB ({:d})\n"), + jyseps1_1 + 1, num_local_y_points)}; } if (jyseps2_1 != jyseps1_2) { @@ -181,18 +199,16 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((jyseps2_1 - jyseps1_1) % num_local_y_points != 0) { return { false, - fmt::format(fmt::runtime(_( - "\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + fmt::format(_f("\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps2_1, jyseps1_1, jyseps2_1 - jyseps1_1, num_local_y_points)}; } if ((jyseps2_2 - jyseps1_2) % num_local_y_points != 0) { return { false, - fmt::format(fmt::runtime(_( - "\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + fmt::format(_f("\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps2_2, jyseps1_2, jyseps2_2 - jyseps1_2, num_local_y_points)}; } @@ -200,17 +216,15 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((ny_inner - jyseps2_1 - 1) % num_local_y_points != 0) { return {false, fmt::format( - fmt::runtime( - _("\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + _f("\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), ny_inner, jyseps2_1, ny_inner - jyseps2_1 - 1, num_local_y_points)}; } if ((jyseps1_2 - ny_inner + 1) % num_local_y_points != 0) { return {false, fmt::format( - fmt::runtime( - _("\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + _f("\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps1_2, ny_inner, jyseps1_2 - ny_inner + 1, num_local_y_points)}; } } else { @@ -218,9 +232,8 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((jyseps2_2 - jyseps1_1) % num_local_y_points != 0) { return { false, - fmt::format(fmt::runtime(_( - "\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + fmt::format(_f("\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps2_2, jyseps1_1, jyseps2_2 - jyseps1_1, num_local_y_points)}; } } @@ -228,9 +241,8 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((ny - jyseps2_2 - 1) % num_local_y_points != 0) { return { false, - fmt::format(fmt::runtime(_( - "\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a " - "multiple of MYSUB ({:d})\n")), + fmt::format(_f("\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a " + "multiple of MYSUB ({:d})\n"), ny, jyseps2_2, ny - jyseps2_2 - 1, num_local_y_points)}; } @@ -251,7 +263,7 @@ void BoutMesh::chooseProcessorSplit(Options& options) { .withDefault(1); if ((NPES % NXPE) != 0) { throw BoutException( - _("Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n"), + _f("Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n"), NPES, NXPE); } @@ -264,7 +276,7 @@ void BoutMesh::chooseProcessorSplit(Options& options) { .withDefault(1); if ((NPES % NYPE) != 0) { throw BoutException( - _("Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n"), + _f("Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n"), NPES, NYPE); } @@ -287,14 +299,14 @@ void BoutMesh::findProcessorSplit() { // Results in square domains const BoutReal ideal = sqrt(MX * NPES / static_cast(ny)); - output_info.write(_("Finding value for NXPE (ideal = {:f})\n"), ideal); + output_info.write(_f("Finding value for NXPE (ideal = {:f})\n"), ideal); for (int i = 1; i <= NPES; i++) { // Loop over all possibilities if ((NPES % i == 0) && // Processors divide equally (MX % i == 0) && // Mesh in X divides equally (ny % (NPES / i) == 0)) { // Mesh in Y divides equally - output_info.write(_("\tCandidate value: {:d}\n"), i); + output_info.write(_f("\tCandidate value: {:d}\n"), i); const int nyp = NPES / i; @@ -321,15 +333,15 @@ void BoutMesh::findProcessorSplit() { NYPE = NPES / NXPE; - output_progress.write(_("\tDomain split (NXPE={:d}, NYPE={:d}) into domains " - "(localNx={:d}, localNy={:d})\n"), + output_progress.write(_f("\tDomain split (NXPE={:d}, NYPE={:d}) into domains " + "(localNx={:d}, localNy={:d})\n"), NXPE, NYPE, MX / NXPE, ny / NYPE); } void BoutMesh::setDerivedGridSizes() { // Check that nx is large enough if (nx <= 2 * MXG) { - throw BoutException(_("Error: nx must be greater than 2 times MXG (2 * {:d})"), MXG); + throw BoutException(_f("Error: nx must be greater than 2 times MXG (2 * {:d})"), MXG); } GlobalNx = nx; @@ -354,8 +366,8 @@ void BoutMesh::setDerivedGridSizes() { MX = nx - 2 * MXG; MXSUB = MX / NXPE; if ((MX % NXPE) != 0) { - throw BoutException(_("Cannot split {:d} X points equally between {:d} processors\n"), - MX, NXPE); + throw BoutException( + _f("Cannot split {:d} X points equally between {:d} processors\n"), MX, NXPE); } // NOTE: No grid data reserved for Y boundary cells - copy from neighbours @@ -363,7 +375,7 @@ void BoutMesh::setDerivedGridSizes() { MYSUB = MY / NYPE; if ((MY % NYPE) != 0) { throw BoutException( - _("\tERROR: Cannot split {:d} Y points equally between {:d} processors\n"), MY, + _f("\tERROR: Cannot split {:d} Y points equally between {:d} processors\n"), MY, NYPE); } @@ -371,7 +383,7 @@ void BoutMesh::setDerivedGridSizes() { MZSUB = MZ / NZPE; if ((MZ % NZPE) != 0) { throw BoutException( - _("\tERROR: Cannot split {:d} Z points equally between {:d} processors\n"), MZ, + _f("\tERROR: Cannot split {:d} Z points equally between {:d} processors\n"), MZ, NZPE); } @@ -472,8 +484,8 @@ int BoutMesh::load() { if (!is_pow2(nz)) { // Should be a power of 2 for efficient FFTs output_warn.write( - _("WARNING: Number of toroidal points should be 2^n for efficient " - "FFT performance -- consider changing MZ ({:d}) if using FFTs\n"), + _f("WARNING: Number of toroidal points should be 2^n for efficient " + "FFT performance -- consider changing MZ ({:d}) if using FFTs\n"), nz); } } else { @@ -1549,7 +1561,7 @@ bool BoutMesh::lastX() const { return PE_XIND == NXPE - 1; } int BoutMesh::sendXOut(BoutReal* buffer, int size, int tag) { Timer timer("comms"); - int proc {-1}; + int proc{-1}; if (PE_XIND == NXPE - 1) { if (periodicX) { // Wrap around to first processor in X @@ -1561,8 +1573,7 @@ int BoutMesh::sendXOut(BoutReal* buffer, int size, int tag) { proc = PROC_NUM(PE_XIND + 1, PE_YIND); } - mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, - BoutComm::get()); + mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get()); return 0; } @@ -1570,7 +1581,7 @@ int BoutMesh::sendXOut(BoutReal* buffer, int size, int tag) { int BoutMesh::sendXIn(BoutReal* buffer, int size, int tag) { Timer timer("comms"); - int proc {-1}; + int proc{-1}; if (PE_XIND == 0) { if (periodicX) { // Wrap around to last processor in X @@ -1582,8 +1593,7 @@ int BoutMesh::sendXIn(BoutReal* buffer, int size, int tag) { proc = PROC_NUM(PE_XIND - 1, PE_YIND); } - mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, - BoutComm::get()); + mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get()); return 0; } @@ -1591,7 +1601,7 @@ int BoutMesh::sendXIn(BoutReal* buffer, int size, int tag) { comm_handle BoutMesh::irecvXOut(BoutReal* buffer, int size, int tag) { Timer timer("comms"); - int proc {-1}; + int proc{-1}; if (PE_XIND == NXPE - 1) { if (periodicX) { // Wrap around to first processor in X @@ -1606,8 +1616,8 @@ comm_handle BoutMesh::irecvXOut(BoutReal* buffer, int size, int tag) { // Get a communications handle. Not fussy about size of arrays CommHandle* ch = get_handle(0, 0); - mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, - BoutComm::get(), ch->request); + mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get(), + ch->request); ch->in_progress = true; @@ -1617,7 +1627,7 @@ comm_handle BoutMesh::irecvXOut(BoutReal* buffer, int size, int tag) { comm_handle BoutMesh::irecvXIn(BoutReal* buffer, int size, int tag) { Timer timer("comms"); - int proc {-1}; + int proc{-1}; if (PE_XIND == 0) { if (periodicX) { // Wrap around to last processor in X @@ -1632,8 +1642,8 @@ comm_handle BoutMesh::irecvXIn(BoutReal* buffer, int size, int tag) { // Get a communications handle. Not fussy about size of arrays CommHandle* ch = get_handle(0, 0); - mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, - BoutComm::get(), ch->request); + mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get(), + ch->request); ch->in_progress = true; diff --git a/src/mesh/mesh.cxx b/src/mesh/mesh.cxx index 8964a6b797..6278f2741a 100644 --- a/src/mesh/mesh.cxx +++ b/src/mesh/mesh.cxx @@ -1,14 +1,38 @@ +#include +#include #include +#include +#include #include #include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include #include +#include +#include +#include +#include #include - -#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include #include "impls/bout/boutmesh.hxx" @@ -509,11 +533,11 @@ const std::vector Mesh::readInts(const std::string& name, int n) { if (source->hasVar(name)) { if (!source->get(this, result, name, n, 0)) { // Error reading - throw BoutException(_("Could not read integer array '{:s}'\n"), name.c_str()); + throw BoutException(_f("Could not read integer array '{:s}'\n"), name.c_str()); } } else { // Not found - throw BoutException(_("Missing integer array {:s}\n"), name.c_str()); + throw BoutException(_f("Missing integer array {:s}\n"), name.c_str()); } return result; @@ -537,7 +561,7 @@ Mesh::createDefaultCoordinates(const CELL_LOC location, const Region<>& Mesh::getRegion3D(const std::string& region_name) const { const auto found = regionMap3D.find(region_name); if (found == end(regionMap3D)) { - throw BoutException(_("Couldn't find region {:s} in regionMap3D"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMap3D"), region_name); } return region3D[found->second]; } @@ -545,7 +569,7 @@ const Region<>& Mesh::getRegion3D(const std::string& region_name) const { size_t Mesh::getRegionID(const std::string& region_name) const { const auto found = regionMap3D.find(region_name); if (found == end(regionMap3D)) { - throw BoutException(_("Couldn't find region {:s} in regionMap3D"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMap3D"), region_name); } return found->second; } @@ -553,7 +577,7 @@ size_t Mesh::getRegionID(const std::string& region_name) const { const Region& Mesh::getRegion2D(const std::string& region_name) const { const auto found = regionMap2D.find(region_name); if (found == end(regionMap2D)) { - throw BoutException(_("Couldn't find region {:s} in regionMap2D"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMap2D"), region_name); } return found->second; } @@ -561,7 +585,7 @@ const Region& Mesh::getRegion2D(const std::string& region_name) const { const Region& Mesh::getRegionPerp(const std::string& region_name) const { const auto found = regionMapPerp.find(region_name); if (found == end(regionMapPerp)) { - throw BoutException(_("Couldn't find region {:s} in regionMapPerp"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMapPerp"), region_name); } return found->second; } @@ -580,8 +604,8 @@ bool Mesh::hasRegionPerp(const std::string& region_name) const { void Mesh::addRegion3D(const std::string& region_name, const Region<>& region) { if (regionMap3D.count(region_name)) { - throw BoutException(_("Trying to add an already existing region {:s} to regionMap3D"), - region_name); + throw BoutException( + _f("Trying to add an already existing region {:s} to regionMap3D"), region_name); } std::optional id; @@ -598,27 +622,28 @@ void Mesh::addRegion3D(const std::string& region_name, const Region<>& region) { regionMap3D[region_name] = id.value(); - output_verbose.write(_("Registered region 3D {:s}"), region_name); + output_verbose.write(_f("Registered region 3D {:s}"), region_name); output_verbose << "\n:\t" << region.getStats() << "\n"; } void Mesh::addRegion2D(const std::string& region_name, const Region& region) { if (regionMap2D.count(region_name)) { - throw BoutException(_("Trying to add an already existing region {:s} to regionMap2D"), - region_name); + throw BoutException( + _f("Trying to add an already existing region {:s} to regionMap2D"), region_name); } regionMap2D[region_name] = region; - output_verbose.write(_("Registered region 2D {:s}"), region_name); + output_verbose.write(_f("Registered region 2D {:s}"), region_name); output_verbose << "\n:\t" << region.getStats() << "\n"; } void Mesh::addRegionPerp(const std::string& region_name, const Region& region) { if (regionMapPerp.count(region_name)) { throw BoutException( - _("Trying to add an already existing region {:s} to regionMapPerp"), region_name); + _f("Trying to add an already existing region {:s} to regionMapPerp"), + region_name); } regionMapPerp[region_name] = region; - output_verbose.write(_("Registered region Perp {:s}"), region_name); + output_verbose.write(_f("Registered region Perp {:s}"), region_name); output_verbose << "\n:\t" << region.getStats() << "\n"; } diff --git a/src/solver/impls/petsc/petsc.cxx b/src/solver/impls/petsc/petsc.cxx index 13e4876048..1e1e05596b 100644 --- a/src/solver/impls/petsc/petsc.cxx +++ b/src/solver/impls/petsc/petsc.cxx @@ -807,7 +807,7 @@ int PetscSolver::init() { if (ierr != 0) { output.write("ERROR: {} {} : ({}, {}) -> ({}, {}) : {} -> {}\n", - row, x, y, xi, yi, ind2, ind2 + n3d - 1); + row, col, x, y, xi, yi, ind2, ind2 + n3d - 1); } CHKERRQ(ierr); } diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index da66092e84..56be566f7e 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -345,7 +345,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { if (ierr != PETSC_SUCCESS) { output.write("ERROR: {} {} : ({}, {}) -> ({}, {}) : {} -> {}\n", row, - x, y, xi, yi, ind2, ind2 + n3d - 1); + col, x, y, xi, yi, ind2, ind2 + n3d - 1); } CHKERRQ(ierr); } diff --git a/src/solver/impls/split-rk/split-rk.cxx b/src/solver/impls/split-rk/split-rk.cxx index fd9a99a6c5..ac4d62ebe2 100644 --- a/src/solver/impls/split-rk/split-rk.cxx +++ b/src/solver/impls/split-rk/split-rk.cxx @@ -1,5 +1,21 @@ #include "split-rk.hxx" +#include "bout/array.hxx" +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" +#include "bout/field2d.hxx" +#include "bout/globals.hxx" +#include "bout/openmpwrap.hxx" +#include "bout/options.hxx" +#include "bout/output.hxx" +#include "bout/solver.hxx" +#include "bout/sys/gettext.hxx" +#include "bout/utils.hxx" + +#include +#include + SplitRK::SplitRK(Options* opts) : Solver(opts), nstages((*options)["nstages"] .doc("Number of stages in RKL step. Must be > 1") @@ -72,7 +88,7 @@ int SplitRK::init() { ASSERT0(ninternal_steps > 0); timestep = getOutputTimestep() / ninternal_steps; - output.write(_("\tUsing a timestep {:e}\n"), timestep); + output.write(_f("\tUsing a timestep {:e}\n"), timestep); return 0; } diff --git a/src/solver/solver.cxx b/src/solver/solver.cxx index ac3d632419..c6e9b464fe 100644 --- a/src/solver/solver.cxx +++ b/src/solver/solver.cxx @@ -24,23 +24,38 @@ #include "bout/array.hxx" #include "bout/assert.hxx" +#include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" #include "bout/field_factory.hxx" +#include "bout/globals.hxx" #include "bout/initialprofiles.hxx" #include "bout/interpolation.hxx" +#include "bout/monitor.hxx" #include "bout/msg_stack.hxx" +#include "bout/options.hxx" #include "bout/output.hxx" #include "bout/region.hxx" #include "bout/solver.hxx" +#include "bout/sys/gettext.hxx" #include "bout/sys/timer.hxx" #include "bout/sys/uuid.h" +#include "bout/unused.hxx" +#include "bout/utils.hxx" +#include "bout/vector2d.hxx" +#include "bout/vector3d.hxx" + +#include #include -#include #include +#include #include #include +#include +#include // Implementations: #include "impls/adams_bashforth/adams_bashforth.hxx" @@ -507,11 +522,11 @@ int Solver::solve(int nout, BoutReal timestep) { finaliseMonitorPeriods(nout, timestep); output_progress.write( - _("Solver running for {:d} outputs with output timestep of {:e}\n"), nout, + _f("Solver running for {:d} outputs with output timestep of {:e}\n"), nout, timestep); if (default_monitor_period > 1) { output_progress.write( - _("Solver running for {:d} outputs with monitor timestep of {:e}\n"), + _f("Solver running for {:d} outputs with monitor timestep of {:e}\n"), nout / default_monitor_period, timestep * default_monitor_period); } @@ -537,7 +552,7 @@ int Solver::solve(int nout, BoutReal timestep) { } time_t start_time = time(nullptr); - output_progress.write(_("\nRun started at : {:s}\n"), toString(start_time)); + output_progress.write(_f("\nRun started at : {:s}\n"), toString(start_time)); Timer timer("run"); // Start timer @@ -583,7 +598,7 @@ int Solver::solve(int nout, BoutReal timestep) { status = run(); time_t end_time = time(nullptr); - output_progress.write(_("\nRun finished at : {:s}\n"), toString(end_time)); + output_progress.write(_f("\nRun finished at : {:s}\n"), toString(end_time)); output_progress.write(_("Run time : ")); int dt = end_time - start_time; @@ -766,7 +781,7 @@ BoutReal Solver::adjustMonitorPeriods(Monitor* new_monitor) { } if (!isMultiple(internal_timestep, new_monitor->timestep)) { - throw BoutException(_("Couldn't add Monitor: {:g} is not a multiple of {:g}!"), + throw BoutException(_f("Couldn't add Monitor: {:g} is not a multiple of {:g}!"), internal_timestep, new_monitor->timestep); } @@ -782,8 +797,8 @@ BoutReal Solver::adjustMonitorPeriods(Monitor* new_monitor) { if (initialised) { throw BoutException( - _("Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) " - "after init is called!"), + _f("Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) " + "after init is called!"), internal_timestep, new_monitor->timestep); } @@ -883,7 +898,7 @@ int Solver::call_monitors(BoutReal simtime, int iter, int NOUT) { monitor.monitor->call(this, simtime, iter / monitor.monitor->period, NOUT / monitor.monitor->period); if (ret != 0) { - throw BoutException(_("Monitor signalled to quit (return code {})"), ret); + throw BoutException(_f("Monitor signalled to quit (return code {})"), ret); } // Write the monitor's diagnostics to the main output file Options monitor_dump; @@ -905,7 +920,7 @@ int Solver::call_monitors(BoutReal simtime, int iter, int NOUT) { for (const auto& monitor : monitors) { monitor.monitor->cleanup(); } - output_error.write(_("Monitor signalled to quit (exception {})\n"), e.what()); + output_error.write(_f("Monitor signalled to quit (exception {})\n"), e.what()); throw; } @@ -1234,13 +1249,13 @@ void Solver::load_derivs(BoutReal* udata) { void Solver::save_vars(BoutReal* udata) { for (const auto& f : f2d) { if (!f.var->isAllocated()) { - throw BoutException(_("Variable '{:s}' not initialised"), f.name); + throw BoutException(_f("Variable '{:s}' not initialised"), f.name); } } for (const auto& f : f3d) { if (!f.var->isAllocated()) { - throw BoutException(_("Variable '{:s}' not initialised"), f.name); + throw BoutException(_f("Variable '{:s}' not initialised"), f.name); } } @@ -1283,8 +1298,8 @@ void Solver::save_derivs(BoutReal* dudata) { // Make sure 3D fields are at the correct cell location for (const auto& f : f3d) { if (f.var->getLocation() != (f.F_var)->getLocation()) { - throw BoutException(_("Time derivative at wrong location - Field is at {:s}, " - "derivative is at {:s} for field '{:s}'\n"), + throw BoutException(_f("Time derivative at wrong location - Field is at {:s}, " + "derivative is at {:s} for field '{:s}'\n"), toString(f.var->getLocation()), toString(f.F_var->getLocation()), f.name); } @@ -1488,7 +1503,7 @@ void Solver::post_rhs(BoutReal UNUSED(t)) { #if CHECK > 0 for (const auto& f : f3d) { if (!f.F_var->isAllocated()) { - throw BoutException(_("Time derivative for variable '{:s}' not set"), f.name); + throw BoutException(_f("Time derivative for variable '{:s}' not set"), f.name); } } #endif diff --git a/src/sys/expressionparser.cxx b/src/sys/expressionparser.cxx index 1c381b26df..5573846c4f 100644 --- a/src/sys/expressionparser.cxx +++ b/src/sys/expressionparser.cxx @@ -22,12 +22,29 @@ * **************************************************************************/ -#include - +#include "bout/sys/expressionparser.hxx" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include - -#include "bout/sys/gettext.hxx" -#include "bout/utils.hxx" +#include using std::list; using std::string; @@ -183,7 +200,7 @@ FieldGeneratorPtr FieldBinary::clone(const list args) { bool toBool(BoutReal rval) { int ival = ROUND(rval); if ((fabs(rval - static_cast(ival)) > 1e-3) or (ival < 0) or (ival > 1)) { - throw BoutException(_("Boolean operator argument {:e} is not a bool"), rval); + throw BoutException(_f("Boolean operator argument {:e} is not a bool"), rval); } return ival == 1; } @@ -302,11 +319,6 @@ ExpressionParser::fuzzyFind(const std::string& name, FieldGeneratorPtr ExpressionParser::parseIdentifierExpr(LexInfo& lex) const { // Make a nice error message if we couldn't find the identifier const auto generatorNotFoundErrorMessage = [&](const std::string& name) -> std::string { - const std::string message_template = _( - R"(Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so you -may need to change your input file. -{})"); - // Start position of the current identifier: by this point, we've either // moved one character past the token, or we're still at the start const auto start = @@ -326,16 +338,19 @@ may need to change your input file. [](const auto& match) -> bool { return match.distance == 0; }); // No matches, just point out the error + std::string error_message = fmt::format( + _f( + R"(Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so you +may need to change your input file. +{})"), + name, problem_bit); if (possible_matches.empty()) { - return fmt::format(fmt::runtime(message_template), name, problem_bit); + return error_message; } // Give the first suggestion as a possible alternative - std::string error_message = - fmt::format(fmt::runtime(message_template), name, problem_bit); - error_message += - fmt::format(fmt::runtime(_("\n {1: ^{2}}{0}\n Did you mean '{0}'?")), - possible_matches.begin()->name, "", start); + error_message += fmt::format(_f("\n {1: ^{2}}{0}\n Did you mean '{0}'?"), + possible_matches.begin()->name, "", start); return error_message; }; diff --git a/src/sys/options.cxx b/src/sys/options.cxx index fb90868081..d409a2e1b1 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -107,10 +107,10 @@ Options::Options(InitializerList values, Options* parent_instance, Options& Options::operator[](const std::string& name) { if (isValue()) { - throw BoutException(_("Trying to index Option '{0}' with '{1}', but '{0}' is a " - "value, not a section.\n" - "This is likely the result of clashing input options, and you " - "may have to rename one of them.\n"), + throw BoutException(_f("Trying to index Option '{0}' with '{1}', but '{0}' is a " + "value, not a section.\n" + "This is likely the result of clashing input options, and you " + "may have to rename one of them.\n"), full_name, name); } @@ -144,10 +144,10 @@ Options& Options::operator[](const std::string& name) { const Options& Options::operator[](const std::string& name) const { if (isValue()) { - throw BoutException(_("Trying to index Option '{0}' with '{1}', but '{0}' is a " - "value, not a section.\n" - "This is likely the result of clashing input options, and you " - "may have to rename one of them.\n"), + throw BoutException(_f("Trying to index Option '{0}' with '{1}', but '{0}' is a " + "value, not a section.\n" + "This is likely the result of clashing input options, and you " + "may have to rename one of them.\n"), full_name, name); } @@ -165,7 +165,7 @@ const Options& Options::operator[](const std::string& name) const { auto child = children.find(name); if (child == children.end()) { // Doesn't exist - throw BoutException(_("Option {:s}:{:s} does not exist"), full_name, name); + throw BoutException(_f("Option {:s}:{:s} does not exist"), full_name, name); } return child->second; @@ -369,7 +369,7 @@ double parseExpression(const Options::ValueType& value, const Options* options, return gen->generate({}); } catch (ParseException& error) { // Convert any exceptions to something a bit more useful - throw BoutException(_("Couldn't get {} from option {:s} = '{:s}': {}"), type, + throw BoutException(_f("Couldn't get {} from option {:s} = '{:s}': {}"), type, full_name, bout::utils::variantToString(value), error.what()); } } @@ -377,7 +377,7 @@ double parseExpression(const Options::ValueType& value, const Options* options, /// Helper function to print `key = value` with optional source template void printNameValueSourceLine(const Options& option, const T& value) { - output_info.write(_("\tOption {} = {}"), option.str(), value); + output_info.write(_f("\tOption {} = {}"), option.str(), value); if (option.hasAttribute("source")) { // Specify the source of the setting output_info.write(" ({})", @@ -390,7 +390,7 @@ void printNameValueSourceLine(const Options& option, const T& value) { template <> std::string Options::as(const std::string& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } // Mark this option as used @@ -406,7 +406,7 @@ std::string Options::as(const std::string& UNUSED(similar_to)) cons template <> int Options::as(const int& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } int result = 0; @@ -426,7 +426,7 @@ int Options::as(const int& UNUSED(similar_to)) const { } else { // Another type which can't be converted - throw BoutException(_("Value for option {:s} is not an integer"), full_name); + throw BoutException(_f("Value for option {:s} is not an integer"), full_name); } // Convert to int by rounding @@ -434,7 +434,7 @@ int Options::as(const int& UNUSED(similar_to)) const { // Check that the value is close to an integer if (fabs(rval - static_cast(result)) > 1e-3) { - throw BoutException(_("Value for option {:s} = {:e} is not an integer"), full_name, + throw BoutException(_f("Value for option {:s} = {:e} is not an integer"), full_name, rval); } } @@ -449,7 +449,7 @@ int Options::as(const int& UNUSED(similar_to)) const { template <> BoutReal Options::as(const BoutReal& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } BoutReal result = BoutNaN; @@ -464,7 +464,7 @@ BoutReal Options::as(const BoutReal& UNUSED(similar_to)) const { result = parseExpression(value, this, "BoutReal", full_name); } else { - throw BoutException(_("Value for option {:s} cannot be converted to a BoutReal"), + throw BoutException(_f("Value for option {:s} cannot be converted to a BoutReal"), full_name); } @@ -479,7 +479,7 @@ BoutReal Options::as(const BoutReal& UNUSED(similar_to)) const { template <> bool Options::as(const bool& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } bool result = false; @@ -494,12 +494,12 @@ bool Options::as(const bool& UNUSED(similar_to)) const { // Check that the result is either close to 1 (true) or close to 0 (false) const int ival = ROUND(rval); if ((fabs(rval - static_cast(ival)) > 1e-3) or (ival < 0) or (ival > 1)) { - throw BoutException(_("Value for option {:s} = {:e} is not a bool"), full_name, + throw BoutException(_f("Value for option {:s} = {:e} is not a bool"), full_name, rval); } result = ival == 1; } else { - throw BoutException(_("Value for option {:s} cannot be converted to a bool"), + throw BoutException(_f("Value for option {:s} cannot be converted to a bool"), full_name); } @@ -579,7 +579,7 @@ Field3D Options::as(const Field3D& similar_to) const { localmesh->LocalNz); } - throw BoutException(_("Value for option {:s} cannot be converted to a Field3D"), + throw BoutException(_f("Value for option {:s} cannot be converted to a Field3D"), full_name); } @@ -631,7 +631,7 @@ Field2D Options::as(const Field2D& similar_to) const { } } - throw BoutException(_("Value for option {:s} cannot be converted to a Field2D"), + throw BoutException(_f("Value for option {:s} cannot be converted to a Field2D"), full_name); } @@ -713,7 +713,7 @@ FieldPerp Options::as(const FieldPerp& similar_to) const { // to select a region from it using Mesh e.g. if this // is from the input grid file. } - throw BoutException(_("Value for option {:s} cannot be converted to a FieldPerp"), + throw BoutException(_f("Value for option {:s} cannot be converted to a FieldPerp"), full_name); } @@ -755,14 +755,13 @@ namespace { template T as_amt(const Options& self, const T& similar_to) { if (self.isSection()) { - throw BoutException(_("Option {:s} has no value"), self.str()); + throw BoutException(_f("Option {:s} has no value"), self.str()); } const T result = bout::utils::visit( ConvertContainer{ - fmt::format( - fmt::runtime(_("Value for option {:s} cannot be converted to an {}")), - self.str(), bout::utils::typeName()), + fmt::format(_f("Value for option {:s} cannot be converted to an {}"), + self.str(), bout::utils::typeName()), similar_to}, self.value); @@ -984,43 +983,6 @@ std::vector Options::getShape() const { return lazy_shape; } -fmt::format_parse_context::iterator -bout::details::OptionsFormatterBase::parse(fmt::format_parse_context& ctx) { - - const auto* closing_brace = std::find(ctx.begin(), ctx.end(), '}'); - std::for_each(ctx.begin(), closing_brace, [&](auto ctx_opt) { - switch (ctx_opt) { - case 'd': - docstrings = true; - break; - case 'i': - inline_section_names = true; - break; - case 'k': - key_only = true; - break; - case 's': - source = true; - break; - case 'u': - unused = true; - break; - default: - throw fmt::format_error("invalid format for 'Options'"); - } - }); - - // Keep a copy of the format string (without the last '}') so we can - // pass it down to the subsections. - const auto size = std::distance(ctx.begin(), closing_brace); - format_string.reserve(size + 3); - format_string.assign("{:"); - format_string.append(ctx.begin(), closing_brace); - format_string.push_back('}'); - - return closing_brace; -} - fmt::format_context::iterator bout::details::OptionsFormatterBase::format(const Options& options, fmt::format_context& ctx) const { @@ -1099,22 +1061,20 @@ bout::details::OptionsFormatterBase::format(const Options& options, // Get all the child values first for (const auto& child : children) { if (child.second.isValue()) { - fmt::format_to(ctx.out(), fmt::runtime(format_string), child.second); + format(child.second, ctx); fmt::format_to(ctx.out(), "\n"); } } // Now descend the tree, accumulating subsections for (const auto& subsection : options.subsections()) { - fmt::format_to(ctx.out(), fmt::runtime(format_string), *subsection.second); + format(*subsection.second, ctx); } return ctx.out(); } -std::string toString(const Options& value) { - return fmt::format(fmt::runtime("{}"), value); -} +std::string toString(const Options& value) { return fmt::format("{}", value); } namespace bout { void checkForUnusedOptions() { @@ -1160,7 +1120,7 @@ void checkForUnusedOptions(const Options& options, const std::string& data_dir, } possible_misspellings += fmt::format("\nUnused option '{}', did you mean:\n", key); for (const auto& match : fuzzy_matches) { - possible_misspellings += fmt::format(fmt::runtime("\t{:idk}\n"), match.match); + possible_misspellings += fmt::format("\t{:idk}\n", match.match); } } @@ -1170,10 +1130,7 @@ void checkForUnusedOptions(const Options& options, const std::string& data_dir, ? "" : fmt::format("Suggested alternatives:\n{}", possible_misspellings); - // Raw string to help with the formatting of the message, and a - // separate variable so clang-format doesn't barf on the - // exception - const std::string unused_message = _(R"( + throw BoutException(_f(R"( There were unused input options: ----- {:i} @@ -1196,9 +1153,8 @@ turn off this check for unused options. You can always set 'input:validate=true' to check inputs without running the full simulation. -{})"); - - throw BoutException(unused_message, unused, data_dir, option_file, +{})"), + unused, data_dir, option_file, unused.getChildren().begin()->first, additional_info); } } diff --git a/src/sys/options/options_ini.cxx b/src/sys/options/options_ini.cxx index b51af8fdcf..b5f813d168 100644 --- a/src/sys/options/options_ini.cxx +++ b/src/sys/options/options_ini.cxx @@ -52,8 +52,17 @@ #include "options_ini.hxx" #include +#include +#include #include +#include +#include +#include +#include + +#include "fmt/format.h" + using namespace std; /************************************************************************** @@ -65,7 +74,7 @@ void OptionINI::read(Options* options, const string& filename) { fin.open(filename.c_str()); if (!fin.good()) { - throw BoutException(_("\tOptions file '{:s}' not found\n"), filename); + throw BoutException(_f("\tOptions file '{:s}' not found\n"), filename); } Options* section = options; // Current section @@ -142,7 +151,7 @@ void OptionINI::read(Options* options, const string& filename) { // Add this to the current section section->set(key, value, filename); } // section test - } // buffer.empty + } // buffer.empty } while (!fin.eof()); fin.close(); @@ -153,11 +162,11 @@ void OptionINI::write(Options* options, const std::string& filename) { fout.open(filename, ios::out | ios::trunc); if (!fout.good()) { - throw BoutException(_("Could not open output file '{:s}'\n"), filename); + throw BoutException(_f("Could not open output file '{:s}'\n"), filename); } // Call recursive function to write to file - fout << fmt::format(fmt::runtime("{:uds}"), *options); + fout << fmt::format("{:uds}", *options); fout.close(); } @@ -193,10 +202,10 @@ void OptionINI::parse(const string& buffer, string& key, string& value) { value = trim(buffer.substr(startpos + 1), " \t\r\n\""); if (key.empty()) { - throw BoutException(_("\tEmpty key\n\tLine: {:s}"), buffer); + throw BoutException(_f("\tEmpty key\n\tLine: {:s}"), buffer); } if (key.find(':') != std::string::npos) { - throw BoutException(_("\tKey must not contain ':' character\n\tLine: {:s}"), buffer); + throw BoutException(_f("\tKey must not contain ':' character\n\tLine: {:s}"), buffer); } } diff --git a/src/sys/optionsreader.cxx b/src/sys/optionsreader.cxx index e16df31c97..3ce668a421 100644 --- a/src/sys/optionsreader.cxx +++ b/src/sys/optionsreader.cxx @@ -1,6 +1,9 @@ #include #include +#include #include +#include +#include #include // Interface for option file parsers @@ -9,7 +12,9 @@ // Individual parsers #include "options/options_ini.hxx" -#include +#include +#include +#include OptionsReader* OptionsReader::instance = nullptr; @@ -36,7 +41,7 @@ void OptionsReader::write(Options* options, const std::string& filename) { throw BoutException("OptionsReader::write passed empty filename\n"); } - output_info.write(_("Writing options to file {:s}\n"), filename); + output_info.write(_f("Writing options to file {:s}\n"), filename); OptionINI{}.write(options, filename); } @@ -105,7 +110,7 @@ void OptionsReader::parseCommandLine(Options* options, size_t endpos = buffer.find_last_of('='); if (startpos != endpos) { - throw BoutException(_("\tMultiple '=' in command-line argument '{:s}'\n"), + throw BoutException(_f("\tMultiple '=' in command-line argument '{:s}'\n"), buffer); } @@ -121,7 +126,7 @@ void OptionsReader::parseCommandLine(Options* options, } if (key.empty() || value.empty()) { - throw BoutException(_("\tEmpty key or value in command line '{:s}'\n"), buffer); + throw BoutException(_f("\tEmpty key or value in command line '{:s}'\n"), buffer); } options->set(key, value, _("Command line")); diff --git a/tests/unit/sys/test_options.cxx b/tests/unit/sys/test_options.cxx index f0256b6aa7..fb3e6a62c5 100644 --- a/tests/unit/sys/test_options.cxx +++ b/tests/unit/sys/test_options.cxx @@ -1112,7 +1112,7 @@ TEST_F(OptionsTest, FormatValue) { const std::string expected = "value1 = 4 # type: int, doc: This is a value, source: some test"; - EXPECT_EQ(expected, fmt::format(fmt::runtime("{:ds}"), options["value1"])); + EXPECT_EQ(expected, fmt::format("{:ds}", options["value1"])); } TEST_F(OptionsTest, FormatDefault) { @@ -1140,7 +1140,7 @@ value4 = 3.2 value6 = 12 )"; - EXPECT_EQ(fmt::format(fmt::runtime("{}"), option), expected); + EXPECT_EQ(fmt::format("{}", option), expected); } TEST_F(OptionsTest, FormatDocstrings) { @@ -1171,7 +1171,7 @@ value4 = 3.2 value6 = 12 )"; - EXPECT_EQ(fmt::format(fmt::runtime("{:d}"), option), expected); + EXPECT_EQ(fmt::format("{:d}", option), expected); } TEST_F(OptionsTest, FormatDocstringsAndInline) { @@ -1194,9 +1194,9 @@ section2:subsection1:value4 = 3.2 section3:subsection2:value6 = 12 )"; - EXPECT_EQ(fmt::format(fmt::runtime("{:di}"), option), expected); + EXPECT_EQ(fmt::format("{:di}", option), expected); // Order of format spec shouldn't matter - EXPECT_EQ(fmt::format(fmt::runtime("{:id}"), option), expected); + EXPECT_EQ(fmt::format("{:id}", option), expected); } TEST_F(OptionsTest, FormatDocstringsAndInlineKeysOnly) { @@ -1221,16 +1221,16 @@ section2:subsection1:value4 section3:subsection2:value6 # source: a test )"; - EXPECT_EQ(fmt::format(fmt::runtime("{:ksdi}"), option), expected); + EXPECT_EQ(fmt::format("{:ksdi}", option), expected); // Order of format spec shouldn't matter - EXPECT_EQ(fmt::format(fmt::runtime("{:idsk}"), option), expected); + EXPECT_EQ(fmt::format("{:idsk}", option), expected); } TEST_F(OptionsTest, FormatUnused) { Options option{{"section1", {{"value1", 42}}}}; std::string expected = "section1:value1\t\t# unused value (NOT marked conditionally used)\n"; - EXPECT_EQ(fmt::format(fmt::runtime("{:iku}"), option), expected); + EXPECT_EQ(fmt::format("{:iku}", option), expected); } TEST_F(OptionsTest, FormatConditionallyUsed) { @@ -1238,7 +1238,7 @@ TEST_F(OptionsTest, FormatConditionallyUsed) { option.setConditionallyUsed(); std::string expected = "section1:value1\t\t# unused value (marked conditionally used)\n"; - EXPECT_EQ(fmt::format(fmt::runtime("{:iku}"), option), expected); + EXPECT_EQ(fmt::format("{:iku}", option), expected); } TEST_F(OptionsTest, GetUnused) { diff --git a/tests/unit/sys/test_output.cxx b/tests/unit/sys/test_output.cxx index 2f13df9cb7..5beb12c0c2 100644 --- a/tests/unit/sys/test_output.cxx +++ b/tests/unit/sys/test_output.cxx @@ -332,7 +332,7 @@ TEST_F(OutputTest, FormatInd3DInvalid) { Ind3D ind(11, 2, 3); Output local_output; - EXPECT_THROW(local_output.write("{:b}", ind), fmt::format_error); + EXPECT_THROW(local_output.write(fmt::runtime("{:b}"), ind), fmt::format_error); } TEST_F(OutputTest, FormatInd2Ddefault) { diff --git a/tools/pylib/_boutpp_build/boutcpp.pxd.jinja b/tools/pylib/_boutpp_build/boutcpp.pxd.jinja index 8f838b864c..40aecb923e 100644 --- a/tools/pylib/_boutpp_build/boutcpp.pxd.jinja +++ b/tools/pylib/_boutpp_build/boutcpp.pxd.jinja @@ -162,7 +162,7 @@ cdef extern from "helper.h": cdef extern from "bout/output.hxx": cppclass ConditionalOutput: - void write(const char * str, const char * str) + void write(const char * str) ConditionalOutput output_info cdef extern from "bout/vecops.hxx": diff --git a/tools/pylib/_boutpp_build/boutpp.pyx.jinja b/tools/pylib/_boutpp_build/boutpp.pyx.jinja index d6f0601f1c..2710c9f21f 100644 --- a/tools/pylib/_boutpp_build/boutpp.pyx.jinja +++ b/tools/pylib/_boutpp_build/boutpp.pyx.jinja @@ -1709,7 +1709,7 @@ def print(*args, sep=" ", end="\n"): cdef _print(value): cdef c.string str_ = value.encode() - c.output_info.write("{:s}", str_.c_str()) + c.output_info.write(str_.c_str()) {% include "helper.py" %} From 421e60ee1015878325bd8b62a65ed0943293ce31 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:26:23 +0000 Subject: [PATCH 2/3] [bot] Apply format changes --- include/bout/options.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/options.hxx b/include/bout/options.hxx index f6be64fa83..775b65ba1c 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -1046,7 +1046,7 @@ namespace details { struct OptionsFormatterBase { constexpr auto parse(fmt::format_parse_context& ctx) { const auto* it = ctx.begin(); - const auto *const end = ctx.end(); + const auto* const end = ctx.end(); while (it != end and *it != '}') { switch (*it) { From ee106f740903562bfa8dbe41d1881d7862f6ee2e Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:26:25 +0000 Subject: [PATCH 3/3] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 5e90d36c14..bd2fb7689e 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -3,3 +3,4 @@ d8f14fdddb5ca0fbb32d8e2bf5ac2960d6ac5ce6 ed2117e6d6826a98b6988e2f18c0c34e408563b6 # Added by the bot 4b010b7634aee1045743be80c268d4644522cd29 +421e60ee1015878325bd8b62a65ed0943293ce31