https://mooseframework.inl.gov
SamplerFullSolveMultiApp.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 // StochasticTools includes
12 #include "Sampler.h"
14 #include "Console.h"
15 #include "VariadicTable.h"
16 
17 registerMooseObject("StochasticToolsApp", SamplerFullSolveMultiApp);
18 
21 {
25  params.addClassDescription(
26  "Creates a full-solve type sub-application for each row of each Sampler matrix.");
27  params.addRequiredParam<SamplerName>("sampler",
28  "The Sampler object to utilize for creating MultiApps.");
29  params.suppressParameter<std::vector<Point>>("positions");
30  params.suppressParameter<bool>("output_in_position");
31  params.suppressParameter<std::vector<FileName>>("positions_file");
32  params.suppressParameter<Real>("move_time");
33  params.suppressParameter<std::vector<Point>>("move_positions");
34  params.suppressParameter<std::vector<unsigned int>>("move_apps");
35  params.set<bool>("use_positions") = false;
36 
37  MooseEnum modes("normal=0 batch-reset=1 batch-restore=2", "normal");
38  params.addParam<MooseEnum>(
39  "mode",
40  modes,
41  "The operation mode, 'normal' creates one sub-application for each row in the Sampler and "
42  "'batch-reset' and 'batch-restore' creates N sub-applications, where N is the minimum of "
43  "'num_rows' in the Sampler and floor(number of processes / min_procs_per_app). To run "
44  "the rows in the Sampler, 'batch-reset' will destroy and re-create sub-apps as needed, "
45  "whereas the 'batch-restore' will backup and restore sub-apps to the initial state prior "
46  "to execution, without destruction.");
47  params.addParam<ReporterName>(
48  "should_run_reporter",
49  "Vector reporter value determining whether a certain multiapp should be run with this "
50  "multiapp. This only works in batch-reset or batch-restore mode.");
51  return params;
52 }
53 
55  : FullSolveMultiApp(parameters),
56  SamplerInterface(this),
57  ReporterInterface(this),
58  _sampler(getSampler("sampler")),
59  _mode(getParam<MooseEnum>("mode").getEnum<StochasticTools::MultiAppMode>()),
60  _local_batch_app_index(0),
61  _solved_once(false)
62 {
63  if (getParam<unsigned int>("min_procs_per_app") !=
64  _sampler.getParam<unsigned int>("min_procs_per_row") ||
65  getParam<unsigned int>("max_procs_per_app") !=
66  _sampler.getParam<unsigned int>("max_procs_per_row"))
67  paramError("sampler",
68  "Sampler and multiapp communicator configuration inconsistent. Please ensure that "
69  "'MultiApps/",
70  name(),
71  "/min(max)_procs_per_app' and 'Samplers/",
72  _sampler.name(),
73  "/min(max)_procs_per_row' are the same.");
74 
79 
80  if (isParamValid("should_run_reporter") && _mode == StochasticTools::MultiAppMode::NORMAL)
81  paramError("should_run_reporter",
82  "Conditionally run sampler multiapp only works in batch modes.");
83 }
84 
85 void
86 SamplerFullSolveMultiApp::preTransfer(Real /*dt*/, Real /*target_time*/)
87 {
88  // Reinitialize MultiApp size
89  const auto num_rows = _sampler.getNumberOfRows();
90  if (num_rows != _number_of_sampler_rows)
91  {
92  init(num_rows,
95  _number_of_sampler_rows = num_rows;
96  _row_data.clear();
97  }
98 
99  // Reinitialize app to original state prior to solve, if a solve has occured.
100  // Since the app is reinitialized in the solve step either way, we skip this
101  // for batch-reset mode.
103  initialSetup();
104 
105  if (isParamValid("should_run_reporter"))
106  _should_run = &getReporterValue<std::vector<bool>>("should_run_reporter");
107 }
108 
109 bool
110 SamplerFullSolveMultiApp::solveStep(Real dt, Real target_time, bool auto_advance)
111 {
112  TIME_SECTION("solveStep", 3, "Solving SamplerFullSolveMultiApp");
113 
115 
116  bool last_solve_converged = true;
117 
120  last_solve_converged = solveStepBatch(dt, target_time, auto_advance);
121  else
122  last_solve_converged = FullSolveMultiApp::solveStep(dt, target_time, auto_advance);
123 
124  _solved_once = true;
125 
126  return last_solve_converged;
127 }
128 
129 bool
130 SamplerFullSolveMultiApp::solveStepBatch(Real dt, Real target_time, bool auto_advance)
131 {
132  TIME_SECTION("solveStepBatch", 3, "Solving Step Batch For SamplerFullSolveMultiApp");
133 
135  paramError("should_run_reporter",
136  "Reporter deteriming multiapp run must be of size greater than or equal to the "
137  "number of local rows in the sampler, ",
138  _should_run->size(),
139  " < ",
141  ".");
142 
143  // Value to return
144  bool last_solve_converged = true;
145 
146  // List of active relevant Transfer objects
147  std::vector<std::shared_ptr<StochasticToolsTransfer>> to_transfers =
149  std::vector<std::shared_ptr<StochasticToolsTransfer>> from_transfers =
151 
152  // Initialize to/from transfers
153  for (auto transfer : to_transfers)
154  {
155  transfer->setGlobalMultiAppIndex(_rank_config.first_local_app_index);
156  transfer->initializeToMultiapp();
157  }
158  for (auto transfer : from_transfers)
159  {
160  transfer->setGlobalMultiAppIndex(_rank_config.first_local_app_index);
161  transfer->initializeFromMultiapp();
162  }
163 
165  backup();
166 
167  // Perform batch MultiApp solves
171  ++i)
172  {
174 
175  bool run = true;
176  if (_should_run)
177  {
178  if (isRootProcessor())
179  run = (*_should_run)[_local_batch_app_index];
180  _my_communicator.broadcast(run, 0);
181  }
182  if (!run)
183  {
185  continue;
186  }
187 
188  // Given that we don't initialize in preTransfer for batch-reset mode, we need
189  // a different logic for resetting the apps for every sample:
190  // - batch-restore: after (re-)initializing the problem, we only need to restore
191  // starting from the second sample
193  {
195  restore();
196  }
197  // - batch-reset: we don't need to initialize for the first sample in the first
198  // solve. After that, we initialize every time. This is mainly to avoid unnecessary
199  // initializations for cases when the multiapp does not need to be executed (conditional runs)
200  else
201  {
203  initialSetup();
204  }
205 
206  execBatchTransfers(to_transfers,
207  i,
208  _row_data,
211  _console);
212 
213  // Set the file base based on the current row
214  for (unsigned int ai = 0; ai < _my_num_apps; ++ai)
215  {
216  const std::string mname = getMultiAppName(name(), i, _number_of_sampler_rows);
217  _apps[ai]->setOutputFileBase(_app.getOutputFileBase() + "_" + mname);
218  }
219 
220  const bool curr_last_solve_converged =
221  FullSolveMultiApp::solveStep(dt, target_time, auto_advance);
222  last_solve_converged = last_solve_converged && curr_last_solve_converged;
223 
224  execBatchTransfers(from_transfers,
225  i,
226  _row_data,
229  _console);
230 
232  }
234 
235  // Finalize to/from transfers
236  for (auto transfer : to_transfers)
237  transfer->finalizeToMultiapp();
238  for (auto transfer : from_transfers)
239  transfer->finalizeFromMultiapp();
240 
241  return last_solve_converged;
242 }
243 
244 void
246  const std::vector<std::shared_ptr<StochasticToolsTransfer>> & transfers,
247  dof_id_type global_row_index,
248  const std::vector<Real> & row_data,
249  Transfer::DIRECTION direction,
250  bool verbose,
251  const ConsoleStream & console)
252 {
253  if (verbose && transfers.size())
254  {
255  console << COLOR_CYAN << "\nBatch transfers for row " << global_row_index;
256  if (direction == MultiAppTransfer::TO_MULTIAPP)
257  console << " To ";
258  else if (direction == MultiAppTransfer::FROM_MULTIAPP)
259  console << " From ";
260  console << "MultiApps" << COLOR_DEFAULT << ":" << std::endl;
261 
262  console << "Sampler row " << global_row_index << " data: [" << Moose::stringify(row_data) << "]"
263  << std::endl;
264 
265  // Build Table of Transfer Info
267  {"Name", "Type", "From", "To"});
268  for (const auto & transfer : transfers)
269  table.addRow(
270  transfer->name(), transfer->type(), transfer->getFromName(), transfer->getToName());
271  table.print(console);
272  }
273 
274  for (auto & transfer : transfers)
275  {
276  transfer->setGlobalRowIndex(global_row_index);
277  transfer->setCurrentRow(row_data);
278  if (direction == MultiAppTransfer::TO_MULTIAPP)
279  transfer->executeToMultiapp();
280  else if (direction == MultiAppTransfer::FROM_MULTIAPP)
281  transfer->executeFromMultiapp();
282  }
283 
284  if (verbose && transfers.size())
285  console << COLOR_CYAN << "Batch transfers for row " << global_row_index << " Are Finished\n"
286  << COLOR_DEFAULT << std::endl;
287 }
288 
289 void
291 {
292  // Local row is the app index if in normal mode, otherwise it's _local_batch_app_index
293  const dof_id_type local_row =
295  // If the local row is less than the number of local sims, we aren't finished yet
296  if (local_row < _rank_config.num_local_sims - 1)
297  return;
298 
299  // Loop through processors to communicate completeness
300  for (const auto & pid : make_range(n_processors()))
301  {
302  // This is what is being sent to trigger completeness
305  : 0;
306  // Cannot send/receive to the same processor, so avoid if root
307  if (pid > 0)
308  {
309  // Send data to root
310  if (pid == processor_id())
311  _communicator.send(0, last_row);
312  // Receive data from source
313  else if (processor_id() == 0)
314  _communicator.receive(pid, last_row);
315  }
316 
317  // Output the samples that are complete if it's the main processor for the batch
318  if (last_row)
319  _console << COLOR_CYAN << type() << " [" << name() << "] " << last_row << "/"
320  << _number_of_sampler_rows << " samples complete!" << std::endl;
321  }
322 }
323 
324 std::vector<std::shared_ptr<StochasticToolsTransfer>>
326 {
327  std::vector<std::shared_ptr<StochasticToolsTransfer>> output;
328  const ExecuteMooseObjectWarehouse<Transfer> & warehouse =
330  for (std::shared_ptr<Transfer> transfer : warehouse.getActiveObjects())
331  {
332  auto ptr = std::dynamic_pointer_cast<StochasticToolsTransfer>(transfer);
333  if (ptr && ptr->getMultiApp().get() == this)
334  output.push_back(ptr);
335  }
336  return output;
337 }
338 
339 std::vector<std::string>
340 SamplerFullSolveMultiApp::getCommandLineArgs(const unsigned int local_app)
341 {
342  std::vector<std::string> args;
343 
344  // With multiple processors per app, there are no local rows for non-root processors
345  if (isRootProcessor())
346  {
347  // Since we only store param_names in cli_args, we need to find the values for each param from
348  // sampler data and combine them to get full command line option strings.
351 
353  }
354 
356  return args;
357 }
358 
359 void
361 {
362  if (!isRootProcessor())
363  return;
364 
365  mooseAssert(local_index < _sampler.getNumberOfLocalRows(),
366  "Local index must be less than number of local rows.");
367 
368  if (_row_data.empty() ||
369  (_local_row_index == _sampler.getNumberOfLocalRows() - 1 && local_index == 0))
370  {
371  mooseAssert(local_index == 0,
372  "The first time calling updateRowData must have a local index of 0.");
373  _local_row_index = 0;
375  }
376  else if (local_index - _local_row_index == 1)
377  {
380  }
381 
382  mooseAssert(local_index == _local_row_index,
383  "Local index must be equal or one greater than the index previously called.");
384 }
385 
386 std::vector<std::string>
388  const std::vector<std::string> & full_args_name)
389 {
390  std::vector<std::string> args;
391 
392  // Find parameters that are meant to be assigned by sampler values
393  std::vector<std::string> cli_args_name;
394  for (const auto & fan : full_args_name)
395  {
396  // If it has an '=', then it is not meant to be modified
397  if (fan.find("=") == std::string::npos)
398  cli_args_name.push_back(fan);
399  else
400  args.push_back(fan);
401  }
402 
403  // Make sure the parameters either all have brackets, or none of them do
404  bool has_brackets = false;
405  if (cli_args_name.size())
406  {
407  has_brackets = cli_args_name[0].find("[") != std::string::npos;
408  for (unsigned int i = 1; i < cli_args_name.size(); ++i)
409  if (has_brackets != (cli_args_name[i].find("[") != std::string::npos))
410  ::mooseError("If the bracket is used, it must be provided to every parameter.");
411  }
412  if (!has_brackets && cli_args_name.size() && cli_args_name.size() != row.size())
413  ::mooseError("Number of command line arguments does not match number of sampler columns.");
414 
415  for (unsigned int i = 0; i < cli_args_name.size(); ++i)
416  {
417  // Assign bracketed parameters
418  if (has_brackets)
419  {
420  // Split param name and vector assignment: "param[0,(3.14),1]" -> {"param", "0,(3.14),1]"}
421  const std::vector<std::string> & vector_param = MooseUtils::split(cli_args_name[i], "[");
422  // Get indices of vector: "0,(3.14),1]" -> {"0", "(3.14)", "1"}
423  const std::vector<std::string> & index_string =
424  MooseUtils::split(vector_param[1].substr(0, vector_param[1].find("]")), ",");
425 
426  // Loop through indices and assign parameter: param='row[0] 3.14 row[1]'
427  std::vector<std::string> values;
428  for (const auto & istr : index_string)
429  {
430  Real value;
431 
432  // If the value is enclosed in parentheses, then it isn't an index, it's a value
433  if (istr.find("(") != std::string::npos)
434  value = std::stod(istr.substr(istr.find("(") + 1));
435  // Assign the value from row if it is an index
436  else
437  {
438  unsigned int index = MooseUtils::stringToInteger(istr);
439  if (index >= row.size())
440  ::mooseError("The provided global column index (",
441  index,
442  ") for ",
443  vector_param[0],
444  " is out of bound.");
445  value = row[index];
446  }
447 
448  values.push_back(Moose::stringifyExact(value));
449  }
450 
451  args.push_back(vector_param[0] + "='" + MooseUtils::stringJoin(values) + "'");
452  }
453  // Assign scalar parameters
454  else
455  args.push_back(cli_args_name[i] + "=" + Moose::stringifyExact(row[i]));
456  }
457 
458  return args;
459 }
const StochasticTools::MultiAppMode _mode
The Sup-application solve mode.
registerMooseObject("StochasticToolsApp", SamplerFullSolveMultiApp)
bool solveStepBatch(Real dt, Real target_time, bool auto_advance=true)
Helper method for running in mode=&#39;batch&#39;.
std::vector< std::shared_ptr< StochasticToolsTransfer > > getActiveStochasticToolsTransfers(Transfer::DIRECTION direction)
Helper for getting StochasticToolsTransfer objects.
virtual void initialSetup() override
static InputParameters validParams()
int stringToInteger(const std::string &input, bool throw_on_failure=false)
virtual void backup()
bool verboseMultiApps() const
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
static InputParameters validParams()
LocalRankConfig _rank_config
virtual bool solveStep(Real dt, Real target_time, bool auto_advance=true) override
std::string getOutputFileBase(bool for_non_moose_build_output=false) const
std::vector< std::shared_ptr< MooseApp > > _apps
T & set(const std::string &name, bool quiet_mode=false)
dof_id_type first_local_app_index
std::vector< Real > getNextLocalRow()
SamplerFullSolveMultiApp(const InputParameters &parameters)
dof_id_type _local_batch_app_index
Counter for extracting command line arguments in batch mode.
const Parallel::Communicator & _communicator
FEProblemBase & _fe_problem
dof_id_type getNumberOfLocalRows() const
virtual std::vector< std::string > getCommandLineArgs(const unsigned int local_app)
virtual const std::string & name() const
void addRequiredParam(const std::string &name, const std::string &doc_string)
The class creates an additional API to allow Transfers to work when running the StochasticTools<FullS...
static InputParameters validParams()
std::vector< Real > _row_data
Current row of data updated by updateRowData. Used by transfers and setting command line args...
std::vector< std::string > split(const std::string &str, const std::string &delimiter, std::size_t max_count=std::numeric_limits< std::size_t >::max())
void suppressParameter(const std::string &name)
bool isParamValid(const std::string &name) const
dof_id_type _local_row_index
Current local index representing _row_data.
std::string stringifyExact(Real)
static std::vector< std::string > sampledCommandLineArgs(const std::vector< Real > &row, const std::vector< std::string > &full_args_name)
Helper for inserting row data into commandline arguments Used here and in SamplerTransientMultiApp.
processor_id_type n_processors() const
Status receive(const unsigned int dest_processor_id, T &buf, const MessageTag &tag=any_tag) const
const std::vector< std::shared_ptr< Transfer > > & getActiveObjects(THREAD_ID tid=0) const
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
Enum for batch type in stochastic tools MultiApp.
virtual std::vector< std::string > getCommandLineArgs(const unsigned int local_app) override
Override to allow for batch mode to get correct cli_args.
Sampler & _sampler
Sampler to utilize for creating MultiApps.
const std::string & type() const
virtual void showStatusMessage(unsigned int i) const override
Override to avoid &#39;solve converged&#39; message and print when processors are finished.
const T & getParam(const std::string &name) const
void paramError(const std::string &param, Args... args) const
const LocalRankConfig & getRankConfig(bool batch_mode) const
std::string stringify(const T &t)
void addRow(Ts... entries)
static InputParameters validParams()
dof_id_type num_local_sims
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
unsigned int _my_num_apps
static std::string getMultiAppName(const std::string &base_name, dof_id_type index, dof_id_type total)
const std::vector< bool > * _should_run
Reporter value determining whether the sub-app should be run for a certain sample.
libMesh::Parallel::Communicator _my_communicator
static void execBatchTransfers(const std::vector< std::shared_ptr< StochasticToolsTransfer >> &transfers, dof_id_type global_row_index, const std::vector< Real > &row_data, Transfer::DIRECTION direction, bool verbose, const ConsoleStream &console)
Helper for executing transfers when doing batch stochastic simulations.
dof_id_type getNumberOfRows() const
const ExecuteMooseObjectWarehouse< Transfer > & getMultiAppTransferWarehouse(Transfer::DIRECTION direction) const
virtual void preTransfer(Real dt, Real target_time) override
void updateRowData(dof_id_type local_index)
Helper function for updating _row_data and _local_row_index.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void send(const unsigned int dest_processor_id, const T &buf, const MessageTag &tag=no_tag) const
bool is_first_local_rank
IntRange< T > make_range(T beg, T end)
virtual void restore(bool force=true) override
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
dof_id_type first_local_sim_index
const ConsoleStream _console
void init(unsigned int num_apps, bool batch_mode=false)
processor_id_type processor_id() const
virtual bool solveStep(Real dt, Real target_time, bool auto_advance=true) override
std::string stringJoin(const std::vector< std::string > &values, const std::string &separator=" ")
uint8_t dof_id_type