Skip to content

Commit

Permalink
Add the ability for multiapps to take CLI params
Browse files Browse the repository at this point in the history
  • Loading branch information
permcody committed May 29, 2015
1 parent b8eca4e commit 21be686
Show file tree
Hide file tree
Showing 21 changed files with 440 additions and 34 deletions.
1 change: 1 addition & 0 deletions framework/include/base/FEProblem.h
Expand Up @@ -501,6 +501,7 @@ class FEProblem :
* Returns whether or not the current simulation has any multiapps
*/
bool hasMultiApps() const { return _has_multiapps; }
bool hasMultiApp(const std::string & name);

/**
* Check existence of the VectorPostprocessor.
Expand Down
29 changes: 26 additions & 3 deletions framework/include/parser/CommandLine.h
Expand Up @@ -48,7 +48,6 @@ class CommandLine
std::vector<std::string> cli_switch;
};


CommandLine(int argc, char * argv[]);
virtual ~CommandLine();

Expand Down Expand Up @@ -90,12 +89,29 @@ class CommandLine
GetPot * getPot() { return _get_pot; }

/**
* Check if we have a variable on the command line
* Check if we have a variable on the command line. Note that a call to this
* method can modify the prefix unless the optional Boolean is set to false.
*
* @param name The name of the variable
* @return True if the variable was defined on the command line
*/
bool haveVariable(const std::string & name);
bool haveVariable(const std::string & name, bool allow_prefix_change=true);

/**
* Sets the prefix for the CommandLine object. This is used for passing
* parameters to Multiapps
*/
void setPrefix(const std::string & name, const std::string & num="");

/**
* Resets the prefix to the value set with the last call to setPrefix.
* Generally you do not need to call this method unless you wish
* to reset the prefix after calling haveVariable before retrieving a
* raw pointer to the GetPot object.
*/
void resetPrefix();

// Dump the contents of the GetPot object
void print(const char * prefix, std::ostream & out_stream, unsigned int skip_count);

protected:
Expand All @@ -105,6 +121,13 @@ class CommandLine
std::map<std::string, Option> _cli_options;
/// This is a set of all "extra" options on the command line
std::set<std::string> _command_line_vars;

/// The base prefix for this CommandLine object
std::string _base_prefix;
/// The number added to the prefix to point it at a specific Multiapp
std::string _prefix_num;
/// Boolean indicating whether we have prefixes set on this CommandLine Object
bool _has_prefix;
};

template <typename T>
Expand Down
11 changes: 11 additions & 0 deletions framework/src/base/FEProblem.C
Expand Up @@ -2717,6 +2717,17 @@ FEProblem::addMultiApp(const std::string & multi_app_name, const std::string & n
// multi_app->init();
}

bool
FEProblem::hasMultiApp(const std::string & multi_app_name)
{
for (unsigned int i = 0; i < Moose::exec_types.size(); i++)
if (_multi_apps(Moose::exec_types[i])[0].hasMultiApp(multi_app_name))
return true;

return false;
}


MultiApp *
FEProblem::getMultiApp(const std::string & multi_app_name)
{
Expand Down
60 changes: 53 additions & 7 deletions framework/src/base/MooseApp.C
Expand Up @@ -24,6 +24,7 @@
#include "Conversion.h"
#include "CommandLine.h"
#include "InfixIterator.h"
#include "MultiApp.h"

// Regular expression includes
#include "pcrecpp.h"
Expand Down Expand Up @@ -297,14 +298,59 @@ MooseApp::runInputFile()
std::vector<std::string> all_vars = _parser.getPotHandle()->get_variable_names();
_parser.checkUnidentifiedParams(all_vars, error_unused, true);

// Only check CLI parameters on the main application
// TODO: Add support for SubApp overrides and checks #4119
if (_name == "main")
// Check the CLI parameters
all_vars = _command_line->getPot()->get_variable_names();
// Remove flags, they aren't "input" parameters
all_vars.erase( std::remove_if(all_vars.begin(), all_vars.end(), isFlag), all_vars.end() );

MooseSharedPointer<FEProblem> fe_problem= _action_warehouse.problem();
if (fe_problem.get())
{
// Check the CLI parameters
all_vars = _command_line->getPot()->get_variable_names();
// Remove flags, they aren't "input" parameters
all_vars.erase( std::remove_if(all_vars.begin(), all_vars.end(), isFlag), all_vars.end() );
// Make sure that multiapp overrides were processed properly
int last = all_vars.size() - 1; // last is allowed to go negative
for (int i = 0; i <= last; /* no increment */) // i is an int because last is an int
{
std::string multi_app, variable;
int app_num;

/**
* Command line parameters that contain a colon are assumed to apply to MultiApps
* (e.g. MultiApp_name[num]:fully_qualified_parameter)
*
* Note: To separate regexs are used since the digit part is optional. Attempting
* to have an optional capture into a non-string type will cause pcrecpp to report
* false. Capturing into a string an converting is more work than just using two
* regexs to begin with.
*/
if (pcrecpp::RE("(.*?)" // Match the MultiApp name
"(\\d+)" // MultiApp number (leave off to apply to all MultiApps with this name)
":" // the colon delimiter
"(.*)" // the variable override that applies to the MultiApp
).FullMatch(all_vars[i], &multi_app, &app_num, &variable) &&
fe_problem->hasMultiApp(multi_app) && // Make sure the MultiApp exists
app_num < fe_problem->getMultiApp(multi_app)->numGlobalApps()) // Finally make sure the number is in range (if provided)

// delete the current item by copying the last item to this position and decrementing the vector end position
all_vars[i] = all_vars[last--];

else if (pcrecpp::RE("(.*?)" // Same as above without the MultiApp number
":"
"(.*)"
).FullMatch(all_vars[i], &multi_app, &variable) &&
fe_problem->hasMultiApp(multi_app)) // Make sure the MultiApp exists but no need to check numbers

// delete (see comment above)
all_vars[i] = all_vars[last--];

else
// only increment if we didn't "delete", otherwise we'll need to revisit the current index due to copy
++i;
}

mooseAssert(last + 1 >= 0, "index \"last\" is negative");

// Remove the deleted items
all_vars.resize(last+1);

_parser.checkUnidentifiedParams(all_vars, error_unused, false);
}
Expand Down
17 changes: 8 additions & 9 deletions framework/src/multiapps/MultiApp.C
Expand Up @@ -108,6 +108,11 @@ MultiApp::MultiApp(const std::string & name, InputParameters parameters):
_move_happened(false),
_has_an_app(true)
{
if (_move_apps.size() != _move_positions.size())
mooseError("The number of apps to move and the positions to move them to must be the same for MultiApp "<<_name);

// Fill in the _positions vector
fillPositions();
}

MultiApp::~MultiApp()
Expand All @@ -126,15 +131,6 @@ MultiApp::~MultiApp()
void
MultiApp::initialSetup()
{
// Fill in the _positions vector
fillPositions();

if (_move_apps.size() != _move_positions.size())
mooseError("The number of apps to move and the positions to move them to must be the same for MultiApp "<<_name);

_total_num_apps = _positions.size();
mooseAssert(_input_files.size() == 1 || _positions.size() == _input_files.size(), "Number of positions and input files are not the same!");

/// Set up our Comm and set the number of apps we're going to be working on
buildComm();

Expand Down Expand Up @@ -191,6 +187,9 @@ MultiApp::fillPositions()
}
else
mooseError("Must supply either 'positions' or 'positions_file' for MultiApp "<<_name);

_total_num_apps = _positions.size();
mooseAssert(_input_files.size() == 1 || _positions.size() == _input_files.size(), "Number of positions and input files are not the same!");
}


Expand Down
51 changes: 48 additions & 3 deletions framework/src/parser/CommandLine.C
Expand Up @@ -20,7 +20,8 @@
#include "InputParameters.h"

CommandLine::CommandLine(int argc, char *argv[]) :
_get_pot(new GetPot(argc, argv))
_get_pot(new GetPot(argc, argv)),
_has_prefix(false)
{
}

Expand Down Expand Up @@ -181,9 +182,53 @@ CommandLine::isVariableOnCommandLine(const std::string &name) const
}

bool
CommandLine::haveVariable(const std::string & name)
CommandLine::haveVariable(const std::string & name, bool allow_prefix_change)
{
return _get_pot->have_variable(name);
// Make sure the CommandLine object is in the right state with the right prefix
resetPrefix();
if (_get_pot->have_variable(name))
return true;

if (allow_prefix_change && _has_prefix)
{
/**
* Try falling back to the base prefix before giving up. Note that this
* will modify the behavior of invocations to GetPot after this method
* completes. This is desired and intended behavior. After allowing
* the prefix to fall back, one should call resetPrefix() to restore
* normal behavior.
*/
_get_pot->set_prefix((_base_prefix + ":").c_str());

return _get_pot->have_variable(name);
}

return false;
}

void
CommandLine::setPrefix(const std::string & name, const std::string & num)
{
_base_prefix = name;
_prefix_num = num;
_has_prefix = true;

/**
* By default we'll append the name and num together and delimit with a colon for the GetPot parser.
* However, we may need to fall back and check only the base prefix if a user wants to apply a parameter
* override to all Multiapps with a given name.
*/
_get_pot->set_prefix((name + num + ":").c_str());
}

void
CommandLine::resetPrefix()
{
// If this CommandLine instance doesn't have a prefix, do nothing
if (!_has_prefix)
return;

_get_pot->set_prefix((_base_prefix + _prefix_num + ":").c_str());
}

void
Expand Down
45 changes: 33 additions & 12 deletions framework/src/parser/Parser.C
Expand Up @@ -44,6 +44,9 @@

#include "MooseTypes.h"

// Regular expression includes
#include "pcrecpp.h"

// libMesh
#include "libmesh/getpot.h"

Expand Down Expand Up @@ -147,6 +150,19 @@ Parser::parse(const std::string &input_filename)
_getpot_initialized = true;
_inactive_strings.clear();

/**
* If this is a Multiapp make sure we set a prefix on the CommandLine object for CLI overrides.
*/
if (_app.name() != "main")
{
std::string name;
std::string num;
if (pcrecpp::RE("(.*?)" // Match the multiapp name
"(\\d+)" // math the multiapp number
).FullMatch(_app.name(), &name, &num))
_app.commandLine()->setPrefix(name, num);
}

// Check for "unidentified nominuses". These can indicate a vector
// input which the user failed to wrap in quotes e.g.: v = 1 2
{
Expand All @@ -161,7 +177,6 @@ Parser::parse(const std::string &input_filename)
}
}


section_names = _getpot_file.get_section_names();
appendAndReorderSectionNames(section_names);

Expand Down Expand Up @@ -320,12 +335,12 @@ void
Parser::appendAndReorderSectionNames(std::vector<std::string> & section_names)
{
/**
* We only want to retrieve CLI overrides for the main application. We'll check the
* We only want to retrieve non-prefixed CLI overrides for the main application. We'll check the
* name of the controlling application to determine whether to use the command line
* here or not.
*/
MooseSharedPointer<CommandLine> cmd_line;
if (_app.name() == "main") // See AppFactory::createApp
// if (_app.name() == "main") // See AppFactory::createApp
cmd_line = _app.commandLine();

if (cmd_line.get())
Expand All @@ -336,11 +351,15 @@ Parser::appendAndReorderSectionNames(std::vector<std::string> & section_names)
std::vector<std::string> cli_variables = get_pot->get_variable_names();
for (unsigned int i=0; i<cli_variables.size(); ++i)
{
std::string::size_type pos = cli_variables[i].find_last_of('/');
if (pos != std::string::npos)
std::string::size_type colon_pos = cli_variables[i].find(':');
std::string::size_type last_slash_pos = cli_variables[i].find_last_of('/');

// Make sure that the variable does not contain a colon. This indicates that the override is for
// a Multiapp parameter which we won't handle here.
if (colon_pos == std::string::npos && last_slash_pos != std::string::npos)
{
// If the user supplies a CLI argument whose section doesn't exist in the input file, we'll append it here
std::string section = cli_variables[i].substr(0, pos+1);
std::string section = cli_variables[i].substr(0, last_slash_pos+1);
if (std::find(section_names.begin(), section_names.end(), section) == section_names.end())
section_names.push_back(section);
}
Expand Down Expand Up @@ -876,7 +895,7 @@ void Parser::setScalarParameter<MooseEnum>(const std::string & full_name, const

// See if this variable was passed on the command line
// if it was then we will retrieve the value from the command line instead of the file
if (_app.name() == "main" && _app.commandLine()->haveVariable(full_name.c_str()))
if (_app.commandLine() && _app.commandLine()->haveVariable(full_name.c_str()))
gp = _app.commandLine()->getPot();
else
gp = &_getpot_file;
Expand All @@ -885,6 +904,7 @@ void Parser::setScalarParameter<MooseEnum>(const std::string & full_name, const
std::string current_name = current_param;

std::string value = gp->get_value_no_default(full_name.c_str(), current_name);

param->set() = value;
if (in_global)
{
Expand All @@ -900,7 +920,7 @@ void Parser::setScalarParameter<MultiMooseEnum>(const std::string & full_name, c

// See if this variable was passed on the command line
// if it was then we will retrieve the value from the command line instead of the file
if (_app.name() == "main" && _app.commandLine()->haveVariable(full_name.c_str()))
if (_app.commandLine() && _app.commandLine()->haveVariable(full_name.c_str()))
gp = _app.commandLine()->getPot();
else
gp = &_getpot_file;
Expand Down Expand Up @@ -932,7 +952,7 @@ void Parser::setScalarParameter<RealTensorValue>(const std::string & full_name,

// See if this variable was passed on the command line
// if it was then we will retrieve the value from the command line instead of the file
if (_app.name() == "main" && _app.commandLine()->haveVariable(full_name.c_str()))
if (_app.commandLine() && _app.commandLine()->haveVariable(full_name.c_str()))
gp = _app.commandLine()->getPot();
else
gp = &_getpot_file;
Expand Down Expand Up @@ -963,12 +983,13 @@ void Parser::setScalarParameter<PostprocessorName>(const std::string & full_name

// See if this variable was passed on the command line
// if it was then we will retrieve the value from the command line instead of the file
if (_app.name() == "main" && _app.commandLine()->haveVariable(full_name.c_str()))
if (_app.commandLine() && _app.commandLine()->haveVariable(full_name.c_str()))
gp = _app.commandLine()->getPot();
else
gp = &_getpot_file;

PostprocessorName pps_name = gp->get_value_no_default(full_name.c_str(), param->get());

// Set the value here
param->set() = pps_name;

Expand Down Expand Up @@ -1004,7 +1025,7 @@ void Parser::setVectorParameter<MooseEnum>(const std::string & full_name, const

// See if this variable was passed on the command line
// if it was then we will retrieve the value from the command line instead of the file
if (_app.name() == "main" && _app.commandLine()->haveVariable(full_name.c_str()))
if (_app.commandLine() && _app.commandLine()->haveVariable(full_name.c_str()))
gp = _app.commandLine()->getPot();
else
gp = &_getpot_file;
Expand Down Expand Up @@ -1042,7 +1063,7 @@ void Parser::setVectorParameter<VariableName>(const std::string & full_name, con

// See if this variable was passed on the command line
// if it was then we will retrieve the value from the command line instead of the file
if (_app.name() == "main" && _app.commandLine()->haveVariable(full_name.c_str()))
if (_app.commandLine() && _app.commandLine()->haveVariable(full_name.c_str()))
gp = _app.commandLine()->getPot();
else
gp = &_getpot_file;
Expand Down

0 comments on commit 21be686

Please sign in to comment.