Skip to content

Commit

Permalink
Merge pull request #26764 from loganharbour/enum_cli
Browse files Browse the repository at this point in the history
Add MooseEnum support to CLI params, improve CLI params
  • Loading branch information
loganharbour authored Feb 9, 2024
2 parents a42a67f + 4fbd59f commit 27c9045
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 120 deletions.
89 changes: 65 additions & 24 deletions framework/include/parser/CommandLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// Moose Includes
#include "MooseError.h"
#include "Conversion.h"
#include "MooseEnum.h"
#include "InputParameters.h"

#include "libmesh/parallel.h"

Expand All @@ -31,24 +33,19 @@ class InputParameters;
class CommandLine
{
public:
/// Type of argument for a given option
enum ARGUMENT
{
NONE,
OPTIONAL,
REQUIRED
};
using ArgumentType = InputParameters::CommandLineMetadata::ArgumentType;

struct Option
{
std::string description;
std::vector<std::string> cli_syntax;
bool required;
ARGUMENT argument_type;
ArgumentType argument_type;
/// This gets filled in automagicaly when calling addOption()
std::vector<std::string> cli_switch;
};

CommandLine();
CommandLine(int argc, char * argv[]);
CommandLine(const CommandLine & other);
virtual ~CommandLine();
Expand All @@ -73,7 +70,7 @@ class CommandLine
*/
const std::vector<std::string> & getArguments() { return _argv; }

void addCommandLineOptionsFromParams(InputParameters & params);
void addCommandLineOptionsFromParams(const InputParameters & params);

void populateInputParams(InputParameters & params);

Expand Down Expand Up @@ -144,11 +141,17 @@ class CommandLine

protected:
/**
* Used to set the argument value, allows specialization
* Helper for setting the argument value, allows specialization
*/
template <typename T>
void setArgument(std::stringstream & stream, T & argument);

/**
* Helper for setting the argument value; catches errors so we can provide more context
*/
template <typename T>
void setArgument(std::stringstream & stream, T & argument, const std::string & cli_switch);

/// Command line options
std::map<std::string, Option> _cli_options;

Expand All @@ -172,40 +175,75 @@ CommandLine::setArgument(std::stringstream & stream, T & argument)
stream >> argument;
}

template <typename T>
void
CommandLine::setArgument(std::stringstream & stream, T & argument, const std::string & cli_switch)
{
// Keep track of and change the throw on error characteristics so that
// we can catch parsing errors for the argument
const auto throw_on_error_orig = Moose::_throw_on_error;
Moose::_throw_on_error = true;

const auto raw_value = stream.str();
try
{
setArgument(stream, argument);
}
catch (std::exception & e)
{
Moose::_throw_on_error = throw_on_error_orig;
mooseError("While parsing command line argument '",
cli_switch,
"' with value '",
raw_value,
"':\n\n",
e.what());
}

Moose::_throw_on_error = throw_on_error_orig;
}

// Specialization for std::string
template <>
void CommandLine::setArgument<std::string>(std::stringstream & stream, std::string & argument);
// Specialization for MooseEnum
template <>
void CommandLine::setArgument<MooseEnum>(std::stringstream & stream, MooseEnum & argument);

template <typename T>
bool
CommandLine::search(const std::string & option_name, T & argument)
{
std::map<std::string, Option>::iterator pos = _cli_options.find(option_name);
if (pos != _cli_options.end())
if (auto pos = _cli_options.find(option_name); pos != _cli_options.end())
{
for (unsigned int i = 0; i < pos->second.cli_switch.size(); ++i)
{
for (size_t j = 0; j < _args.size(); j++)
const auto & option = pos->second;
for (const auto & cli_switch : option.cli_switch)
for (const auto arg_i : index_range(_args))
{
auto arg = _args[j];
const auto & arg = _args[arg_i];

if (arg == pos->second.cli_switch[i])
if (arg == cli_switch)
{
// "Flag" CLI options are added as Boolean types, when we see them
// we set the Boolean argument to true
if (pos->second.argument_type == NONE)
if (option.argument_type == ArgumentType::NONE)
argument = true;
else if (j + 1 < _args.size())
else if (arg_i + 1 < _args.size())
{
std::stringstream ss;
ss << _args[j + 1];
ss << _args[arg_i + 1];

setArgument(ss, argument);
setArgument(ss, argument, cli_switch);
}
else if (option.argument_type == ArgumentType::REQUIRED)
{
mooseError("The command line argument '",
cli_switch,
"' requires a value and one was not provided.");
}
return true;
}
}
}

if (pos->second.required)
{
Expand Down Expand Up @@ -235,8 +273,11 @@ CommandLine::search(const std::string & option_name, std::vector<T> & argument)
// "Flag" CLI options added vector of Boolean types may apprear multiple times on the
// command line (like a repeated verbosity flag to increase verbosity), when we see them
// we append a true value to the vector.
if (pos->second.argument_type == NONE)
if (pos->second.argument_type == ArgumentType::NONE)
argument.push_back(T());
else if (pos->second.argument_type == ArgumentType::REQUIRED)
mooseError("Adding vector command line parameters with required arguments is not "
"currently supported");
else
while (j + 1 < _argv.size() && _argv[j + 1][0] != '-' &&
_argv[j + 1].find("=") == std::string::npos)
Expand All @@ -245,7 +286,7 @@ CommandLine::search(const std::string & option_name, std::vector<T> & argument)
ss << _argv[j + 1];

T item;
setArgument(ss, item);
setArgument(ss, item, pos->second.cli_switch[i]);
argument.push_back(item);
++j;
}
Expand Down
84 changes: 62 additions & 22 deletions framework/include/utils/InputParameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class FunctionParserBase
#include <tuple>
#include <unordered_map>
#include <mutex>
#include <optional>

// Forward declarations
class Action;
Expand All @@ -54,6 +55,24 @@ class InputParameters : public Parameters

virtual void clear() override;

/**
* Structure for storing information about a command line parameter
*/
struct CommandLineMetadata
{
enum ArgumentType
{
NONE,
OPTIONAL,
REQUIRED
};

/// The syntax for the parameter (i.e., ["-t", "--timing"])
std::vector<std::string> syntax;
/// The type of argument
ArgumentType argument_type;
};

/**
* This method adds a description of the class that will be displayed
* in the input file syntax dump
Expand Down Expand Up @@ -281,9 +300,19 @@ class InputParameters : public Parameters
void checkConsistentType(const std::string & name) const;

/**
* Get the syntax for a command-line parameter
* @return Whether or not the parameter \p name is a command line parameter
*/
std::vector<std::string> getSyntax(const std::string & name) const;
bool isCommandLineParameter(const std::string & name) const;

/**
* @return The command line syntax for the parameter \p name
*/
const std::vector<std::string> & getCommandLineSyntax(const std::string & name) const;

/**
* @return The command line argument type for the parameter \p name
*/
CommandLineMetadata::ArgumentType getCommandLineArgumentType(const std::string & name) const;

/**
* Get the documentation string for a parameter
Expand Down Expand Up @@ -992,22 +1021,6 @@ class InputParameters : public Parameters
*/
std::string appendFunctorDescription(const std::string & doc_string) const;

/**
* Helper that uses overloading to distinguish adding command-line parameters of
* a scalar and a vector kind. Vector parameters are options that may appear multiple
* times on the command line (like -i).
*/
template <typename T>
void addCommandLineParamHelper(const std::string & name,
const std::string & syntax,
const std::string & doc_string,
T *);
template <typename T>
void addCommandLineParamHelper(const std::string & name,
const std::string & syntax,
const std::string & doc_string,
std::vector<T> *);

/**
* Private method for setting deprecated coupled variable documentation strings
*/
Expand All @@ -1030,7 +1043,8 @@ class InputParameters : public Parameters
std::string _doc_string;
/// The custom type that will be printed in the YAML dump for a parameter if supplied
std::string _custom_type;
std::vector<std::string> _cli_flag_names;
/// The data pertaining to a command line parameter (empty if not a command line param)
std::optional<CommandLineMetadata> _cl_data;
/// The names of the parameters organized into groups
std::string _group;
/// The map of functions used for range checked parameters
Expand Down Expand Up @@ -1108,6 +1122,17 @@ class InputParameters : public Parameters
template <typename T, typename S>
void setParamHelper(const std::string & name, T & l_value, const S & r_value);

/**
* @return The command line metadata for the parameter \p name.
*/
const CommandLineMetadata & getCommandLineMetadata(const std::string & name) const;

/**
* Helper for all of the addCommandLineParam() calls, which sets up _cl_data in the metadata
*/
template <typename T>
void addCommandLineParamHelper(const std::string & name, const std::string & syntax);

/// original location of input block (i.e. filename,linenum) - used for nice error messages.
std::string _block_location;

Expand Down Expand Up @@ -1450,6 +1475,21 @@ InputParameters::setParamHelper(const std::string & /*name*/, T & l_value, const
l_value = r_value;
}

template <typename T>
void
InputParameters::addCommandLineParamHelper(const std::string & name, const std::string & syntax)
{
auto & cl_data = at(name)._cl_data;
cl_data = CommandLineMetadata();
MooseUtils::tokenize(syntax, cl_data->syntax, 1, " \t\n\v\f\r");
if constexpr (std::is_same_v<T, bool>)
cl_data->argument_type = CommandLineMetadata::ArgumentType::NONE;
else if constexpr (std::is_same_v<T, MooseEnum>)
cl_data->argument_type = CommandLineMetadata::ArgumentType::REQUIRED;
else
cl_data->argument_type = CommandLineMetadata::ArgumentType::OPTIONAL;
}

template <typename T>
void
InputParameters::addRequiredRangeCheckedParam(const std::string & name,
Expand Down Expand Up @@ -1559,7 +1599,7 @@ InputParameters::addRequiredCommandLineParam(const std::string & name,
const std::string & doc_string)
{
addRequiredParam<T>(name, doc_string);
MooseUtils::tokenize(syntax, _params[name]._cli_flag_names, 1, " \t\n\v\f\r");
addCommandLineParamHelper<T>(name, syntax);
}

template <typename T>
Expand All @@ -1569,7 +1609,7 @@ InputParameters::addCommandLineParam(const std::string & name,
const std::string & doc_string)
{
addParam<T>(name, doc_string);
MooseUtils::tokenize(syntax, _params[name]._cli_flag_names, 1, " \t\n\v\f\r");
addCommandLineParamHelper<T>(name, syntax);
}

template <typename T>
Expand All @@ -1580,7 +1620,7 @@ InputParameters::addCommandLineParam(const std::string & name,
const std::string & doc_string)
{
addParam<T>(name, value, doc_string);
MooseUtils::tokenize(syntax, _params[name]._cli_flag_names, 1, " \t\n\v\f\r");
addCommandLineParamHelper<T>(name, syntax);
}

template <typename T>
Expand Down
12 changes: 6 additions & 6 deletions framework/src/base/MooseApp.C
Original file line number Diff line number Diff line change
Expand Up @@ -1539,7 +1539,7 @@ MooseApp::showInputs() const
{
if (isParamValid("show_inputs"))
{
auto copy_syntax = _pars.getSyntax("copy_inputs");
auto copy_syntax = _pars.getCommandLineSyntax("copy_inputs");
std::vector<std::string> dirs;
const auto installable_inputs = getInstallableInputs();

Expand Down Expand Up @@ -1590,11 +1590,11 @@ MooseApp::copyInputs() const
if (binname == "")
mooseError("could not locate installed tests to run (unresolved binary/app name)");

auto src_dir =
MooseUtils::installedInputsDir(binname,
dir_to_copy,
"Rerun binary with " + _pars.getSyntax("show_inputs")[0] +
" to get a list of installable directories.");
auto src_dir = MooseUtils::installedInputsDir(binname,
dir_to_copy,
"Rerun binary with " +
_pars.getCommandLineSyntax("show_inputs")[0] +
" to get a list of installable directories.");

// Use the command line here because if we have a symlink to another binary,
// we want to dump into a directory that is named after the symlink not the true binary
Expand Down
Loading

0 comments on commit 27c9045

Please sign in to comment.