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
87 {
90 }
91 
92 void
93 SamplerFullSolveMultiApp::preTransfer(Real /*dt*/, Real /*target_time*/)
94 {
95  // Logic for calling initial setup again:
96  // 1) If and only if not doing batch-reset (solveStepBatch does this at each local row)
97  // 2) If the number of rows have changed since the communicator is re-split.
98  // 3) If we have already solved and doing "normal" execution, effectively resetting the apps
99  bool initial_setup_required = false;
100 
101  // Reinitialize MultiApp size
102  const auto num_rows = _sampler.getNumberOfRows();
103  if (num_rows != _number_of_sampler_rows)
104  {
105  init(num_rows,
108  _number_of_sampler_rows = num_rows;
109  _row_data.clear();
110  initial_setup_required = _mode != StochasticTools::MultiAppMode::BATCH_RESET;
111  }
112  else if (_solved_once)
113  initial_setup_required = _mode == StochasticTools::MultiAppMode::NORMAL;
114 
115  // Call initial setup based on the logic above
116  if (initial_setup_required)
117  {
118  initialSetup();
119  _solved_once = false;
120  }
121  // Otherwise we need to restore for batch-restore
123  restore();
124 
125  if (isParamValid("should_run_reporter"))
126  _should_run = &getReporterValue<std::vector<bool>>("should_run_reporter");
127 }
128 
129 bool
130 SamplerFullSolveMultiApp::solveStep(Real dt, Real target_time, bool auto_advance)
131 {
132  TIME_SECTION("solveStep", 3, "Solving SamplerFullSolveMultiApp");
133 
135 
136  bool last_solve_converged = true;
137 
140  last_solve_converged = solveStepBatch(dt, target_time, auto_advance);
141  else
142  last_solve_converged = FullSolveMultiApp::solveStep(dt, target_time, auto_advance);
143 
144  _solved_once = true;
145 
146  return last_solve_converged;
147 }
148 
149 bool
150 SamplerFullSolveMultiApp::solveStepBatch(Real dt, Real target_time, bool auto_advance)
151 {
152  TIME_SECTION("solveStepBatch", 3, "Solving Step Batch For SamplerFullSolveMultiApp");
153 
155  paramError("should_run_reporter",
156  "Reporter deteriming multiapp run must be of size greater than or equal to the "
157  "number of local rows in the sampler, ",
158  _should_run->size(),
159  " < ",
161  ".");
162 
163  // Value to return
164  bool last_solve_converged = true;
165 
166  // List of active relevant Transfer objects
167  std::vector<std::shared_ptr<StochasticToolsTransfer>> to_transfers =
169  std::vector<std::shared_ptr<StochasticToolsTransfer>> from_transfers =
171 
172  // Initialize to/from transfers
173  for (auto transfer : to_transfers)
174  {
175  transfer->setGlobalMultiAppIndex(_rank_config.first_local_app_index);
176  transfer->initializeToMultiapp();
177  }
178  for (auto transfer : from_transfers)
179  {
180  transfer->setGlobalMultiAppIndex(_rank_config.first_local_app_index);
181  transfer->initializeFromMultiapp();
182  }
183 
186 
187  // Perform batch MultiApp solves
191  ++i)
192  {
194 
195  bool run = true;
196  if (_should_run)
197  {
198  if (isRootProcessor())
199  run = (*_should_run)[_local_batch_app_index];
200  _my_communicator.broadcast(run, 0);
201  }
202  if (!run)
203  {
205  continue;
206  }
207 
208  // Given that we don't initialize in preTransfer for batch-reset mode, we need
209  // a different logic for resetting the apps for every sample:
210  // - batch-restore: after (re-)initializing the problem, we only need to restore
211  // starting from the second sample
213  {
215  restore();
216  }
217  // - batch-reset: we don't need to initialize for the first sample in the first
218  // solve. After that, we initialize every time. This is mainly to avoid unnecessary
219  // initializations for cases when the multiapp does not need to be executed (conditional runs)
220  else
221  {
223  initialSetup();
224  }
225 
226  execBatchTransfers(to_transfers,
227  i,
228  _row_data,
231  _console);
232 
233  // Set the file base based on the current row
234  for (unsigned int ai = 0; ai < _my_num_apps; ++ai)
235  {
236  const std::string mname = getMultiAppName(name(), i, _number_of_sampler_rows);
237  _apps[ai]->setOutputFileBase(_app.getOutputFileBase() + "_" + mname);
238  }
239 
240  const bool curr_last_solve_converged =
241  FullSolveMultiApp::solveStep(dt, target_time, auto_advance);
242  last_solve_converged = last_solve_converged && curr_last_solve_converged;
243 
244  execBatchTransfers(from_transfers,
245  i,
246  _row_data,
249  _console);
250 
252  }
254 
255  // Finalize to/from transfers
256  for (auto transfer : to_transfers)
257  transfer->finalizeToMultiapp();
258  for (auto transfer : from_transfers)
259  transfer->finalizeFromMultiapp();
260 
261  return last_solve_converged;
262 }
263 
264 void
266  const std::vector<std::shared_ptr<StochasticToolsTransfer>> & transfers,
267  dof_id_type global_row_index,
268  const std::vector<Real> & row_data,
269  Transfer::DIRECTION direction,
270  bool verbose,
271  const ConsoleStream & console)
272 {
273  if (verbose && transfers.size())
274  {
275  console << COLOR_CYAN << "\nBatch transfers for row " << global_row_index;
276  if (direction == MultiAppTransfer::TO_MULTIAPP)
277  console << " To ";
278  else if (direction == MultiAppTransfer::FROM_MULTIAPP)
279  console << " From ";
280  console << "MultiApps" << COLOR_DEFAULT << ":" << std::endl;
281 
282  console << "Sampler row " << global_row_index << " data: [" << Moose::stringify(row_data) << "]"
283  << std::endl;
284 
285  // Build Table of Transfer Info
287  {"Name", "Type", "From", "To"});
288  for (const auto & transfer : transfers)
289  table.addRow(
290  transfer->name(), transfer->type(), transfer->getFromName(), transfer->getToName());
291  table.print(console);
292  }
293 
294  for (auto & transfer : transfers)
295  {
296  transfer->setGlobalRowIndex(global_row_index);
297  transfer->setCurrentRow(row_data);
298  if (direction == MultiAppTransfer::TO_MULTIAPP)
299  transfer->executeToMultiapp();
300  else if (direction == MultiAppTransfer::FROM_MULTIAPP)
301  transfer->executeFromMultiapp();
302  }
303 
304  if (verbose && transfers.size())
305  console << COLOR_CYAN << "Batch transfers for row " << global_row_index << " Are Finished\n"
306  << COLOR_DEFAULT << std::endl;
307 }
308 
309 void
311 {
312  // Local row is the app index if in normal mode, otherwise it's _local_batch_app_index
313  const dof_id_type local_row =
315  // If the local row is less than the number of local sims, we aren't finished yet
316  if (local_row < _rank_config.num_local_sims - 1)
317  return;
318 
319  // Loop through processors to communicate completeness
320  for (const auto & pid : make_range(n_processors()))
321  {
322  // This is what is being sent to trigger completeness
325  : 0;
326  // Cannot send/receive to the same processor, so avoid if root
327  if (pid > 0)
328  {
329  // Send data to root
330  if (pid == processor_id())
331  _communicator.send(0, last_row);
332  // Receive data from source
333  else if (processor_id() == 0)
334  _communicator.receive(pid, last_row);
335  }
336 
337  // Output the samples that are complete if it's the main processor for the batch
338  if (last_row)
339  _console << COLOR_CYAN << type() << " [" << name() << "] " << last_row << "/"
340  << _number_of_sampler_rows << " samples complete!" << std::endl;
341  }
342 }
343 
344 std::vector<std::shared_ptr<StochasticToolsTransfer>>
346 {
347  std::vector<std::shared_ptr<StochasticToolsTransfer>> output;
348  const ExecuteMooseObjectWarehouse<Transfer> & warehouse =
350  for (std::shared_ptr<Transfer> transfer : warehouse.getActiveObjects())
351  {
352  auto ptr = std::dynamic_pointer_cast<StochasticToolsTransfer>(transfer);
353  if (ptr && ptr->getMultiApp().get() == this)
354  output.push_back(ptr);
355  }
356  return output;
357 }
358 
359 std::vector<std::string>
360 SamplerFullSolveMultiApp::getCommandLineArgs(const unsigned int local_app)
361 {
362  std::vector<std::string> args;
363 
364  // With multiple processors per app, there are no local rows for non-root processors
365  if (isRootProcessor())
366  {
367  // Since we only store param_names in cli_args, we need to find the values for each param from
368  // sampler data and combine them to get full command line option strings.
371 
373  }
374 
376  return args;
377 }
378 
379 void
381 {
382  if (!isRootProcessor())
383  return;
384 
385  mooseAssert(local_index < _sampler.getNumberOfLocalRows(),
386  "Local index must be less than number of local rows.");
387 
388  if (_row_data.empty() ||
389  (_local_row_index == _sampler.getNumberOfLocalRows() - 1 && local_index == 0))
390  {
391  mooseAssert(local_index == 0,
392  "The first time calling updateRowData must have a local index of 0.");
393  _local_row_index = 0;
395  }
396  else if (local_index - _local_row_index == 1)
397  {
400  }
401 
402  mooseAssert(local_index == _local_row_index,
403  "Local index must be equal or one greater than the index previously called.");
404 }
405 
406 std::vector<std::string>
408  const std::vector<std::string> & full_args_name)
409 {
410  std::vector<std::string> args;
411 
412  // Find parameters that are meant to be assigned by sampler values
413  std::vector<std::string> cli_args_name;
414  for (const auto & fan : full_args_name)
415  {
416  // If it has an '=', then it is not meant to be modified
417  if (fan.find("=") == std::string::npos)
418  cli_args_name.push_back(fan);
419  else
420  args.push_back(fan);
421  }
422 
423  // Make sure the parameters either all have brackets, or none of them do
424  bool has_brackets = false;
425  if (cli_args_name.size())
426  {
427  has_brackets = cli_args_name[0].find("[") != std::string::npos;
428  for (unsigned int i = 1; i < cli_args_name.size(); ++i)
429  if (has_brackets != (cli_args_name[i].find("[") != std::string::npos))
430  ::mooseError("If the bracket is used, it must be provided to every parameter.");
431  }
432  if (!has_brackets && cli_args_name.size() && cli_args_name.size() != row.size())
433  ::mooseError("Number of command line arguments does not match number of sampler columns.");
434 
435  for (unsigned int i = 0; i < cli_args_name.size(); ++i)
436  {
437  // Assign bracketed parameters
438  if (has_brackets)
439  {
440  // Split param name and vector assignment: "param[0,(3.14),1]" -> {"param", "0,(3.14),1]"}
441  const std::vector<std::string> & vector_param = MooseUtils::split(cli_args_name[i], "[");
442  // Get indices of vector: "0,(3.14),1]" -> {"0", "(3.14)", "1"}
443  const std::vector<std::string> & index_string =
444  MooseUtils::split(vector_param[1].substr(0, vector_param[1].find("]")), ",");
445 
446  // Loop through indices and assign parameter: param='row[0] 3.14 row[1]'
447  std::vector<std::string> values;
448  for (const auto & istr : index_string)
449  {
450  Real value;
451 
452  // If the value is enclosed in parentheses, then it isn't an index, it's a value
453  if (istr.find("(") != std::string::npos)
454  value = std::stod(istr.substr(istr.find("(") + 1));
455  // Assign the value from row if it is an index
456  else
457  {
458  unsigned int index = MooseUtils::stringToInteger(istr);
459  if (index >= row.size())
460  ::mooseError("The provided global column index (",
461  index,
462  ") for ",
463  vector_param[0],
464  " is out of bound.");
465  value = row[index];
466  }
467 
468  values.push_back(Moose::stringifyExact(value));
469  }
470 
471  args.push_back(vector_param[0] + "='" + MooseUtils::stringJoin(values) + "'");
472  }
473  // Assign scalar parameters
474  else
475  args.push_back(cli_args_name[i] + "=" + Moose::stringifyExact(row[i]));
476  }
477 
478  return args;
479 }
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.
KOKKOS_INLINE_FUNCTION const T * find(const T &target, const T *const begin, const T *const end)
virtual void initialSetup() override
static InputParameters validParams()
void paramError(const std::string &param, Args... args) const
const T & getParam(const std::string &name) const
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)
virtual void backup() override
This method is overridden so that we only store the initial state and not on any other timestep when ...
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)
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...
void suppressParameter(const std::string &name)
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
const std::string & name() 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 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
std::string stringJoin(const std::vector< std::string > &values, const std::string &separator)
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
dof_id_type first_local_sim_index
bool isParamValid(const std::string &name) const
const ConsoleStream _console
void init(unsigned int num_apps, bool batch_mode=false)
std::vector< std::string > split(const std::string &str, const std::string &delimiter, std::size_t max_count)
processor_id_type processor_id() const
virtual bool solveStep(Real dt, Real target_time, bool auto_advance=true) override
int stringToInteger(const std::string &input, bool throw_on_failure)
uint8_t dof_id_type