Skip to content

Commit

Permalink
Add executable summarizing time stepper properties
Browse files Browse the repository at this point in the history
  • Loading branch information
wthrowe committed Oct 11, 2023
1 parent b6ed934 commit 60bf38a
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 5 deletions.
5 changes: 0 additions & 5 deletions cmake/AddStandaloneTests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/Executables/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ add_subdirectory(ExportCoordinates)
add_subdirectory(FindHorizons)
add_subdirectory(ParallelInfo)
add_subdirectory(ReduceCceWorldtube)
add_subdirectory(TimeStepperSummary)
21 changes: 21 additions & 0 deletions src/Executables/TimeStepperSummary/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)
185 changes: 185 additions & 0 deletions src/Executables/TimeStepperSummary/TimeStepperSummary.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Distributed under the MIT License.
// See LICENSE.txt for details.

#include <boost/program_options.hpp>

#include <algorithm>
#include <array>
#include <cstddef>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

#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<TimeSteppers::AdamsBashforth,
TimeSteppers::AdamsMoultonPc>;

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<std::string, size_t, size_t, size_t, double, double,
double, bool, bool>;

template <typename Stepper>
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<ImexTimeStepper, Stepper>,
std::is_base_of_v<LtsTimeStepper, Stepper>};
}

std::vector<Row> generate_table() {
std::vector<Row> table{};
tmpl::for_each<TimeSteppers::time_steppers>([&](auto stepper_v) {
using Stepper = tmpl::type_from<decltype(stepper_v)>;
if constexpr (tmpl::list_contains_v<time_steppers_taking_order, Stepper>) {
for (size_t order = Stepper::minimum_order;
order <= Stepper::maximum_order;
++order) {
table.push_back(generate_row(
Stepper(order), MakeString{} << pretty_type::name<Stepper>() << "["
<< order << "]"));
}
} else {
table.push_back(generate_row(Stepper{}, pretty_type::name<Stepper>()));
}
});
return table;
}

std::string bool_yn(const bool& b) { return b ? "Y" : "N"; }
template <typename T>
const T& bool_yn(const T& t) {
return t;
}

void print_table(std::vector<Row> table, const size_t sort_index) {
tmpl::for_each<tmpl::range<size_t, 0, columns.size()>>(
[&](auto constexpr_sort_index_v) {
constexpr size_t constexpr_sort_index =
tmpl::type_from<decltype(constexpr_sort_index_v)>::value;
if (constexpr_sort_index == sort_index) {
std::stable_sort(table.begin(), table.end(),
[&](const auto& a, const auto& b) {
return get<constexpr_sort_index>(a) <
get<constexpr_sort_index>(b);
});
}
});

std::array<size_t, columns.size()> column_widths{};
for (size_t i = 0; i < columns.size(); ++i) {
column_widths[i] = columns[i].size();
}
std::vector<std::array<std::string, columns.size()>> stringified_table;
stringified_table.reserve(table.size());

for (const auto& row : table) {
stringified_table.emplace_back();
tmpl::for_each<tmpl::range<size_t, 0, columns.size()>>([&](auto column_v) {
constexpr size_t column = tmpl::type_from<decltype(column_v)>::value;
std::string stringified =
MakeString{} << std::setprecision(3) << bool_yn(get<column>(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<std::string>()->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<std::string>();
const auto sort_index =
static_cast<size_t>(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;
}
1 change: 1 addition & 0 deletions src/Time/TimeSteppers/AdamsBashforth.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions tests/Unit/Executables/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

0 comments on commit 60bf38a

Please sign in to comment.