From 60bf38a88189b12b87b3a2bd4e0ae9a9a20b3c31 Mon Sep 17 00:00:00 2001 From: William Throwe Date: Tue, 11 Apr 2023 15:31:20 -0400 Subject: [PATCH] Add executable summarizing time stepper properties --- cmake/AddStandaloneTests.cmake | 5 - src/Executables/CMakeLists.txt | 1 + .../TimeStepperSummary/CMakeLists.txt | 21 ++ .../TimeStepperSummary/TimeStepperSummary.cpp | 185 ++++++++++++++++++ src/Time/TimeSteppers/AdamsBashforth.hpp | 1 + tests/Unit/Executables/CMakeLists.txt | 6 + 6 files changed, 214 insertions(+), 5 deletions(-) create mode 100644 src/Executables/TimeStepperSummary/CMakeLists.txt create mode 100644 src/Executables/TimeStepperSummary/TimeStepperSummary.cpp diff --git a/cmake/AddStandaloneTests.cmake b/cmake/AddStandaloneTests.cmake index 58c16087d9bf0..b9929e02a1d86 100644 --- a/cmake/AddStandaloneTests.cmake +++ b/cmake/AddStandaloneTests.cmake @@ -73,11 +73,6 @@ function(add_standalone_test TEST_NAME) ${ARGN}) if(DEFINED ARG_EXECUTABLE) - if(NOT ARG_EXECUTABLE MATCHES "^Test_") - message( - FATAL_ERROR - "Test executable name '${ARG_EXECUTABLE}' must begin with 'Test_'") - endif() set(EXECUTABLE_NAME "${ARG_EXECUTABLE}") else() # Extract last component of test name as executable name diff --git a/src/Executables/CMakeLists.txt b/src/Executables/CMakeLists.txt index 1623c15bfefeb..94dc105eea5b0 100644 --- a/src/Executables/CMakeLists.txt +++ b/src/Executables/CMakeLists.txt @@ -9,3 +9,4 @@ add_subdirectory(ExportCoordinates) add_subdirectory(FindHorizons) add_subdirectory(ParallelInfo) add_subdirectory(ReduceCceWorldtube) +add_subdirectory(TimeStepperSummary) diff --git a/src/Executables/TimeStepperSummary/CMakeLists.txt b/src/Executables/TimeStepperSummary/CMakeLists.txt new file mode 100644 index 0000000000000..1f702edef61da --- /dev/null +++ b/src/Executables/TimeStepperSummary/CMakeLists.txt @@ -0,0 +1,21 @@ +# Distributed under the MIT License. +# See LICENSE.txt for details. + +set(EXECUTABLE TimeStepperSummary) + +add_spectre_executable( + ${EXECUTABLE} + EXCLUDE_FROM_ALL + TimeStepperSummary.cpp + ) + +target_link_libraries( + ${EXECUTABLE} + PRIVATE + Boost::boost + Boost::program_options + ErrorHandling + Parallel + Time + Utilities + ) diff --git a/src/Executables/TimeStepperSummary/TimeStepperSummary.cpp b/src/Executables/TimeStepperSummary/TimeStepperSummary.cpp new file mode 100644 index 0000000000000..36d9296c8b0a3 --- /dev/null +++ b/src/Executables/TimeStepperSummary/TimeStepperSummary.cpp @@ -0,0 +1,185 @@ +// Distributed under the MIT License. +// See LICENSE.txt for details. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Parallel/Printf.hpp" +#include "Time/TimeSteppers/Factory.hpp" +#include "Utilities/Algorithm.hpp" +#include "Utilities/ErrorHandling/Assert.hpp" +#include "Utilities/ErrorHandling/Error.hpp" +#include "Utilities/Literals.hpp" +#include "Utilities/MakeString.hpp" +#include "Utilities/Numeric.hpp" +#include "Utilities/TMPL.hpp" + +class ImexTimeStepper; +class LtsTimeStepper; +namespace TimeSteppers { +class AdamsBashforth; +class AdamsMoultonPc; +} // namespace TimeSteppers + +// Charm looks for this function but since we build without a main function or +// main module we just have it be empty +extern "C" void CkRegisterMainModule(void) {} + +namespace { +using time_steppers_taking_order = tmpl::list; + +const char* const program_help = + "Print various properties about SpECTRE's time steppers. Abbreviations\n" + "used to reduce the output width are:\n" + " stab = stable step size\n" + " subs = substeps\n" + " esubs = substeps with error estimation enabled\n"; + +const std::array columns{"name"s, "order"s, "subs"s, + "esubs"s, "stab"s, "stab/subs"s, + "stab/esubs"s, "IMEX"s, "LTS"s}; +using Row = std::tuple; + +template +Row generate_row(const Stepper& stepper, std::string name) { + return {std::move(name), + stepper.order(), + stepper.number_of_substeps(), + stepper.number_of_substeps_for_error(), + stepper.stable_step(), + stepper.stable_step() / stepper.number_of_substeps(), + stepper.stable_step() / stepper.number_of_substeps_for_error(), + std::is_base_of_v, + std::is_base_of_v}; +} + +std::vector generate_table() { + std::vector table{}; + tmpl::for_each([&](auto stepper_v) { + using Stepper = tmpl::type_from; + if constexpr (tmpl::list_contains_v) { + for (size_t order = Stepper::minimum_order; + order <= Stepper::maximum_order; + ++order) { + table.push_back(generate_row( + Stepper(order), MakeString{} << pretty_type::name() << "[" + << order << "]")); + } + } else { + table.push_back(generate_row(Stepper{}, pretty_type::name())); + } + }); + return table; +} + +std::string bool_yn(const bool& b) { return b ? "Y" : "N"; } +template +const T& bool_yn(const T& t) { + return t; +} + +void print_table(std::vector table, const size_t sort_index) { + tmpl::for_each>( + [&](auto constexpr_sort_index_v) { + constexpr size_t constexpr_sort_index = + tmpl::type_from::value; + if (constexpr_sort_index == sort_index) { + std::stable_sort(table.begin(), table.end(), + [&](const auto& a, const auto& b) { + return get(a) < + get(b); + }); + } + }); + + std::array column_widths{}; + for (size_t i = 0; i < columns.size(); ++i) { + column_widths[i] = columns[i].size(); + } + std::vector> stringified_table; + stringified_table.reserve(table.size()); + + for (const auto& row : table) { + stringified_table.emplace_back(); + tmpl::for_each>([&](auto column_v) { + constexpr size_t column = tmpl::type_from::value; + std::string stringified = + MakeString{} << std::setprecision(3) << bool_yn(get(row)); + column_widths[column] = + std::max(column_widths[column], stringified.size()); + stringified_table.back()[column] = std::move(stringified); + }); + } + + for (size_t i = 0; i < columns.size(); ++i) { + Parallel::printf("%-*s", column_widths[i] + 1, columns[i]); + } + Parallel::printf("\n"); + { + const size_t num_chars = + alg::accumulate(column_widths, 0_st) + columns.size(); + for (size_t i = 0; i < num_chars; ++i) { + Parallel::printf("-"); + } + } + Parallel::printf("\n"); + for (const auto& row : stringified_table) { + for (size_t i = 0; i < columns.size(); ++i) { + Parallel::printf("%-*s", column_widths[i] + 1, row[i]); + } + Parallel::printf("\n"); + } +} +} // namespace + +int main(const int argc, char** const argv) { + namespace bpo = boost::program_options; + try { + bpo::options_description command_line_options; + + const std::string sort_help = + MakeString{} << "Sort column. One of " << columns; + // clang-format off + command_line_options.add_options() + ("help,h", "Describe program options") + ("sort", bpo::value()->default_value(columns.front()), + sort_help.c_str()) + ; + // clang-format on + + bpo::command_line_parser command_line_parser(argc, argv); + command_line_parser.options(command_line_options); + + bpo::variables_map parsed_command_line_options; + bpo::store(command_line_parser.run(), parsed_command_line_options); + bpo::notify(parsed_command_line_options); + + if (parsed_command_line_options.count("help") != 0) { + Parallel::printf("%s\n%s", command_line_options, program_help); + return 0; + } + + const std::string sort_column = + parsed_command_line_options.at("sort").as(); + const auto sort_index = + static_cast(alg::find(columns, sort_column) - columns.begin()); + if (sort_index == columns.size()) { + ERROR_NO_TRACE("Invalid sort column. Must be " << columns); + } + + print_table(generate_table(), sort_index); + } catch (const bpo::error& e) { + ERROR_NO_TRACE(e.what()); + } + return 0; +} diff --git a/src/Time/TimeSteppers/AdamsBashforth.hpp b/src/Time/TimeSteppers/AdamsBashforth.hpp index f485ea84fc90a..e5740a0fd303e 100644 --- a/src/Time/TimeSteppers/AdamsBashforth.hpp +++ b/src/Time/TimeSteppers/AdamsBashforth.hpp @@ -191,6 +191,7 @@ namespace TimeSteppers { */ class AdamsBashforth : public LtsTimeStepper { public: + static constexpr const size_t minimum_order = 1; static constexpr const size_t maximum_order = 8; struct Order { diff --git a/tests/Unit/Executables/CMakeLists.txt b/tests/Unit/Executables/CMakeLists.txt index d34c1ccefca4a..65c61c84ef7d8 100644 --- a/tests/Unit/Executables/CMakeLists.txt +++ b/tests/Unit/Executables/CMakeLists.txt @@ -30,3 +30,9 @@ add_dependencies( test-executables ConvertComposeTable ) + +add_standalone_test( + "Unit.Executables.TimeStepperSummary" + EXECUTABLE "TimeStepperSummary" + REGEX_TO_MATCH "AdamsBashforth\\[2\\][^\\n]*N *Y" + )