diff --git a/framework/doc/content/source/outputs/ProgressOutput.md b/framework/doc/content/source/outputs/ProgressOutput.md new file mode 100644 index 000000000000..95d2a7263eda --- /dev/null +++ b/framework/doc/content/source/outputs/ProgressOutput.md @@ -0,0 +1,19 @@ +# Progress + +!syntax description /Outputs/Progress + +## Overview + +The Progress output displays an ASCII art progress bar at the end of each timestep, visualizing the amount of simulation time that has passed vs. the total simulation time. It requires the use of a transient executioner along with predetermined start and end times. The width of the bar widget can be specified using the [!param](/Outputs/Progress/progress_bar_width) parameter. If omitted the value of the `MOOSE_PPS_WIDTH` environment variable is queried. If that variable is not set the terminal window width is queried (with a fallback value of 132 chars). + +``` ++-Progress (full.i)--------------------------------+ +|#########################.........................| ++--------------------------------------------------+ +``` + +!syntax parameters /Outputs/Progress + +!syntax inputs /Outputs/Progress + +!syntax children /Outputs/Progress diff --git a/framework/include/executioners/Transient.h b/framework/include/executioners/Transient.h index bea46de0d3e3..f14bee2bdb05 100644 --- a/framework/include/executioners/Transient.h +++ b/framework/include/executioners/Transient.h @@ -92,7 +92,7 @@ class Transient : public Executioner /** * Get the current time. */ - virtual Real getTime() { return _time; }; + virtual Real getTime() const { return _time; }; /** * Get the current target time @@ -120,6 +120,7 @@ class Transient : public Executioner * @return Pointer to the time stepper for this Executioner */ TimeStepper * getTimeStepper() { return _time_stepper; } + const TimeStepper * getTimeStepper() const { return _time_stepper; } /** * Set the timestepper to use. @@ -143,7 +144,7 @@ class Transient : public Executioner * Get the time scheme used * @return MooseEnum with the time scheme */ - Moose::TimeIntegratorType getTimeScheme() { return _time_scheme; } + Moose::TimeIntegratorType getTimeScheme() const { return _time_scheme; } /** * Get the set of sync times @@ -167,12 +168,18 @@ class Transient : public Executioner * Return the start time * @return The start time */ - Real getStartTime() { return _start_time; } + Real getStartTime() const { return _start_time; } /** * Get the end time * @return The end time */ + Real getEndTime() const { return _end_time; } + + /** + * Get a modifiable reference to the end time + * @return The end time + */ Real & endTime() { return _end_time; } /** diff --git a/framework/include/outputs/ProgressOutput.h b/framework/include/outputs/ProgressOutput.h new file mode 100644 index 000000000000..dd874ef38cbb --- /dev/null +++ b/framework/include/outputs/ProgressOutput.h @@ -0,0 +1,36 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once + +#include "Output.h" + +class Transient; + +/** + * Output a simulation time progress bar on the console + */ +class ProgressOutput : public Output +{ +public: + static InputParameters validParams(); + + ProgressOutput(const InputParameters & parameters); + +protected: + void output() override; + + const Transient * const _transient_executioner; + + /// display input file name in the progress bar title + const bool _use_filename; + + /// total length of the progress bar + const unsigned int _length; +}; diff --git a/framework/include/utils/FormattedTable.h b/framework/include/utils/FormattedTable.h index ee8c87ea6820..7be093928a52 100644 --- a/framework/include/utils/FormattedTable.h +++ b/framework/include/utils/FormattedTable.h @@ -249,11 +249,6 @@ class FormattedTable std::vector::iterator & col_begin, std::vector::iterator & col_end) const; - /** - * Returns the width of the terminal using sys/ioctl - */ - unsigned short getTermWidth(bool use_environment) const; - /** * Data structure for the console table: * The first part of the pair tracks the independent variable (normally time) and is associated diff --git a/framework/include/utils/MooseUtils.h b/framework/include/utils/MooseUtils.h index eb730eec3680..c1f50e7601d3 100644 --- a/framework/include/utils/MooseUtils.h +++ b/framework/include/utils/MooseUtils.h @@ -286,6 +286,11 @@ std::string baseName(const std::string & name); */ std::string hostname(); +/** + * Returns the width of the terminal using sys/ioctl + */ +unsigned short getTermWidth(bool use_environment); + /** * @returns A cleaner representation of the c++ type \p cpp_type. */ diff --git a/framework/src/actions/CommonOutputAction.C b/framework/src/actions/CommonOutputAction.C index a4740404c51d..0eb7044aafe0 100644 --- a/framework/src/actions/CommonOutputAction.C +++ b/framework/src/actions/CommonOutputAction.C @@ -69,6 +69,7 @@ CommonOutputAction::validParams() "Output the scalar and postprocessor results using the default settings for GNUPlot output"); params.addParam( "solution_history", false, "Print a solution history file (.slh) using the default settings"); + params.addParam("progress", false, "Print a progress bar"); params.addParam("dofmap", false, "Create the dof map .json output file"); params.addParam("controls", false, "Enable the screen output of Control systems."); @@ -220,6 +221,9 @@ CommonOutputAction::act() if (getParam("solution_history")) create("SolutionHistory"); + if (getParam("progress")) + create("Progress"); + if (getParam("dofmap")) create("DOFMap"); diff --git a/framework/src/outputs/ProgressOutput.C b/framework/src/outputs/ProgressOutput.C new file mode 100644 index 000000000000..e0537a4aa18c --- /dev/null +++ b/framework/src/outputs/ProgressOutput.C @@ -0,0 +1,68 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#include "ProgressOutput.h" +#include "Transient.h" + +registerMooseObjectAliased("MooseApp", ProgressOutput, "Progress"); + +InputParameters +ProgressOutput::validParams() +{ + auto params = Output::validParams(); + params.addClassDescription("Output a simulation time progress bar on the console."); + params.set("execute_on") = {EXEC_TIMESTEP_END}; + params.addParam( + "use_filename", true, "Put the input filename into the title of the progress bar"); + params.addParam( + "progress_bar_width", + "Explicitly specify the bar width. If omitted the MOOSE_PPS_WIDTH environment variable or, " + "if not set, the terminal width is queried."); + return params; +} + +ProgressOutput::ProgressOutput(const InputParameters & parameters) + : Output(parameters), + _transient_executioner(dynamic_cast(_app.getExecutioner())), + _use_filename(getParam("use_filename")), + _length(isParamValid("progress_bar_width") ? getParam("progress_bar_width") + : MooseUtils::getTermWidth(true) - 2) +{ +} + +void +ProgressOutput::output() +{ + if (_transient_executioner == nullptr || _current_execute_flag != EXEC_TIMESTEP_END) + return; + + const auto passed = _transient_executioner->getTime() - _transient_executioner->getStartTime(); + const auto total = _transient_executioner->getEndTime() - _transient_executioner->getStartTime(); + if (total == 0) + return; + + // length of filled portion + const auto progress = std::round((passed * _length) / total); + + // title string + std::string title = name(); + if (_use_filename) + title += " (" + getMooseApp().getFileName() + ')'; + if (title.length() >= _length - 1) + title = title.substr(0, _length - 4) + "..."; + + // top line + Moose::out << "+-" << title << std::string(_length - 1 - title.length(), '-') << "+\n"; + + // bar + Moose::out << '|' << std::string(progress, '#') << std::string(_length - progress, '.') << "|\n"; + + // bottom line + Moose::out << '+' << std::string(_length, '-') << "+\n"; +} diff --git a/framework/src/utils/FormattedTable.C b/framework/src/utils/FormattedTable.C index bc7d6bf39a68..0e91013f324f 100644 --- a/framework/src/utils/FormattedTable.C +++ b/framework/src/utils/FormattedTable.C @@ -16,12 +16,6 @@ #include #include -// Used for terminal width -#ifndef __WIN32__ -#include -#endif -#include - const unsigned short FormattedTable::_column_width = 15; const unsigned short FormattedTable::_min_pps_width = 40; @@ -258,9 +252,9 @@ FormattedTable::printTable(std::ostream & out, unsigned short term_width; if (suggested_term_width == "ENVIRONMENT") - term_width = getTermWidth(true); + term_width = MooseUtils::getTermWidth(true); else if (suggested_term_width == "AUTO") - term_width = getTermWidth(false); + term_width = MooseUtils::getTermWidth(false); else term_width = MooseUtils::stringToInteger(suggested_term_width); @@ -583,54 +577,6 @@ FormattedTable::fillEmptyValues() std::dynamic_pointer_cast(std::make_shared>('0')); } -unsigned short -FormattedTable::getTermWidth(bool use_environment) const -{ -#ifndef __WIN32__ - struct winsize w; -#else - struct - { - unsigned short ws_col; - } w; -#endif - /** - * Initialize the value we intend to populate just in case - * the system call fails - */ - w.ws_col = std::numeric_limits::max(); - - if (use_environment) - { - char * pps_width = std::getenv("MOOSE_PPS_WIDTH"); - if (pps_width != NULL) - { - std::stringstream ss(pps_width); - ss >> w.ws_col; - } - } - // Default to AUTO if no environment variable was set - if (w.ws_col == std::numeric_limits::max()) - { -#ifndef __WIN32__ - try - { - ioctl(0, TIOCGWINSZ, &w); - } - catch (...) -#endif - { - } - } - - // Something bad happened, make sure we have a sane value - // 132 seems good for medium sized screens, and is available as a GNOME preset - if (w.ws_col == std::numeric_limits::max()) - w.ws_col = 132; - - return w.ws_col; -} - MooseEnum FormattedTable::getWidthModes() { diff --git a/framework/src/utils/MooseUtils.C b/framework/src/utils/MooseUtils.C index 1782f31a1ff4..55d8beb026d9 100644 --- a/framework/src/utils/MooseUtils.C +++ b/framework/src/utils/MooseUtils.C @@ -34,6 +34,7 @@ #include #include #include +#include // System includes #include @@ -46,6 +47,8 @@ #include #include #include +#else +#include #endif namespace MooseUtils @@ -649,6 +652,54 @@ hostname() return hostname; } +unsigned short +getTermWidth(bool use_environment) +{ +#ifndef __WIN32__ + struct winsize w; +#else + struct + { + unsigned short ws_col; + } w; +#endif + /** + * Initialize the value we intend to populate just in case + * the system call fails + */ + w.ws_col = std::numeric_limits::max(); + + if (use_environment) + { + char * pps_width = std::getenv("MOOSE_PPS_WIDTH"); + if (pps_width != NULL) + { + std::stringstream ss(pps_width); + ss >> w.ws_col; + } + } + // Default to AUTO if no environment variable was set + if (w.ws_col == std::numeric_limits::max()) + { +#ifndef __WIN32__ + try + { + ioctl(0, TIOCGWINSZ, &w); + } + catch (...) +#endif + { + } + } + + // Something bad happened, make sure we have a sane value + // 132 seems good for medium sized screens, and is available as a GNOME preset + if (w.ws_col == std::numeric_limits::max()) + w.ws_col = 132; + + return w.ws_col; +} + void MaterialPropertyStorageDump( const HashMap> & props) diff --git a/test/tests/outputs/progress/common.i b/test/tests/outputs/progress/common.i new file mode 100644 index 000000000000..48198c10b91d --- /dev/null +++ b/test/tests/outputs/progress/common.i @@ -0,0 +1,27 @@ +[Mesh] + [gen] + type = GeneratedMeshGenerator + dim = 1 + [] +[] + +[Variables] + [u] + [] +[] + +[Problem] + solve = false + kernel_coverage_check = false +[] + +[Executioner] + type = Transient + start_time = 10 + end_time = 20 + dt = 5 +[] + +[Outputs] + progress = true +[] diff --git a/test/tests/outputs/progress/full.i b/test/tests/outputs/progress/full.i new file mode 100644 index 000000000000..a293b3e8e585 --- /dev/null +++ b/test/tests/outputs/progress/full.i @@ -0,0 +1,30 @@ +[Mesh] + [gen] + type = GeneratedMeshGenerator + dim = 1 + [] +[] + +[Variables] + [u] + [] +[] + +[Problem] + solve = false + kernel_coverage_check = false +[] + +[Executioner] + type = Transient + start_time = 10 + end_time = 20 + dt = 5 +[] + +[Outputs] + [Progress] + type = Progress + progress_bar_width = 50 + [] +[] diff --git a/test/tests/outputs/progress/tests b/test/tests/outputs/progress/tests new file mode 100644 index 000000000000..9ae5719ff582 --- /dev/null +++ b/test/tests/outputs/progress/tests @@ -0,0 +1,17 @@ +[Tests] + issues = '#22906' + design = ProgressOutput.md + [full] + type = RunApp + input = 'full.i' + expect_out = "\+-Progress \(.*\)-*\+\n\|#########################.........................\|\n\+--------------------------------------------------\+" + requirement = "The system shall support printing a progress bar that indicates the fraction of total simulation time passed." + [] + [common] + type = RunApp + input = 'common.i' + expect_out = '\+-progress \(.*\)-*\+' + requirement = "The system shall support using a shortcut to set up a progress bar output." + [] +[] +