https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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
18
21{
26 "Creates a full-solve type sub-application for each row of each Sampler matrix.");
27 params.addRequiredParam<SamplerName>(
28 "sampler",
29 "The Sampler object to utilize for creating the child applications of this MultiApp.");
30
31 // The number of applications is handled by the sampler
32 params.suppressParameter<std::vector<Point>>("positions");
33 params.suppressParameter<bool>("output_in_position");
34 params.suppressParameter<std::vector<FileName>>("positions_file");
35 params.suppressParameter<Real>("move_time");
36 params.suppressParameter<std::vector<Point>>("move_positions");
37 params.suppressParameter<std::vector<unsigned int>>("move_apps");
38 params.set<bool>("use_positions") = false;
39
40 MooseEnum modes("normal=0 batch-reset=1 batch-restore=2", "normal");
41 params.addParam<MooseEnum>(
42 "mode",
43 modes,
44 "The operation mode, 'normal' creates one sub-application for each row in the Sampler and "
45 "'batch-reset' and 'batch-restore' creates N sub-applications, where N is the minimum of "
46 "'num_rows' in the Sampler and floor(number of processes / min_procs_per_app). To run "
47 "the rows in the Sampler, 'batch-reset' will destroy and re-create sub-apps as needed, "
48 "whereas the 'batch-restore' will backup and restore sub-apps to the initial state prior "
49 "to execution, without destruction.");
50 params.addParam<ReporterName>(
51 "should_run_reporter",
52 "Vector reporter value determining whether a certain multiapp should be run with this "
53 "multiapp. This only works in batch-reset or batch-restore mode.");
54 return params;
55}
56
58 : FullSolveMultiApp(parameters),
59 SamplerInterface(this),
61 _sampler(getSampler("sampler")),
62 _mode(getParam<MooseEnum>("mode").getEnum<StochasticTools::MultiAppMode>()),
63 _local_batch_app_index(0),
64 _solved_once(false)
65{
66 if (getParam<unsigned int>("min_procs_per_app") !=
67 _sampler.getParam<unsigned int>("min_procs_per_row") ||
68 getParam<unsigned int>("max_procs_per_app") !=
69 _sampler.getParam<unsigned int>("max_procs_per_row"))
70 paramError("sampler",
71 "Sampler and multiapp communicator configuration inconsistent. Please ensure that "
72 "'MultiApps/",
73 name(),
74 "/min(max)_procs_per_app' and 'Samplers/",
75 _sampler.name(),
76 "/min(max)_procs_per_row' are the same.");
77
82
83 if (isParamValid("should_run_reporter") && _mode == StochasticTools::MultiAppMode::NORMAL)
84 paramError("should_run_reporter",
85 "Conditionally run sampler multiapp only works in batch modes.");
86}
87
88void
94
95void
96SamplerFullSolveMultiApp::preTransfer(Real /*dt*/, Real /*target_time*/)
97{
98 // Logic for calling initial setup again:
99 // 1) If and only if not doing batch-reset (solveStepBatch does this at each local row)
100 // 2) If the number of rows have changed since the communicator is re-split.
101 // 3) If we have already solved and doing "normal" execution, effectively resetting the apps
102 bool initial_setup_required = false;
103
104 // Reinitialize MultiApp size
105 const auto num_rows = _sampler.getNumberOfRows();
106 if (num_rows != _number_of_sampler_rows)
107 {
108 init(num_rows,
111 _number_of_sampler_rows = num_rows;
112 // Batch-reset rebuilds apps in solveStepBatch(), except for the first local row before any
113 // solve (e.g. after recovering), whose app would otherwise stay on the old communicator.
114 initial_setup_required = _mode != StochasticTools::MultiAppMode::BATCH_RESET || !_solved_once;
115 }
116 else if (_solved_once || _app.isRecovering())
117 initial_setup_required = _mode == StochasticTools::MultiAppMode::NORMAL;
118
119 // Call initial setup based on the logic above
120 if (initial_setup_required)
121 {
122 initialSetup();
123 _solved_once = false;
124 }
125 // Otherwise we need to restore for batch-restore
127 restore();
128
129 if (isParamValid("should_run_reporter"))
130 _should_run = &getReporterValue<std::vector<bool>>("should_run_reporter");
131}
132
133bool
134SamplerFullSolveMultiApp::solveStep(Real dt, Real target_time, bool auto_advance)
135{
136 TIME_SECTION("solveStep", 3, "Solving SamplerFullSolveMultiApp");
137
139
140 bool last_solve_converged = true;
141
144 last_solve_converged = solveStepBatch(dt, target_time, auto_advance);
145 else
146 last_solve_converged = FullSolveMultiApp::solveStep(dt, target_time, auto_advance);
147
148 _solved_once = true;
149
150 return last_solve_converged;
151}
152
153bool
154SamplerFullSolveMultiApp::solveStepBatch(Real dt, Real target_time, bool auto_advance)
155{
156 TIME_SECTION("solveStepBatch", 3, "Solving Step Batch For SamplerFullSolveMultiApp");
157
159 paramError("should_run_reporter",
160 "Reporter deteriming multiapp run must be of size greater than or equal to the "
161 "number of local rows in the sampler, ",
162 _should_run->size(),
163 " < ",
165 ".");
166
167 // Value to return
168 bool last_solve_converged = true;
169
170 // List of active relevant Transfer objects
171 std::vector<std::shared_ptr<StochasticToolsTransfer>> to_transfers =
173 std::vector<std::shared_ptr<StochasticToolsTransfer>> from_transfers =
175
176 // Initialize to/from transfers
177 for (auto transfer : to_transfers)
178 {
179 transfer->setGlobalMultiAppIndex(_rank_config.first_local_app_index);
180 transfer->initializeToMultiapp();
181 }
182 for (auto transfer : from_transfers)
183 {
184 transfer->setGlobalMultiAppIndex(_rank_config.first_local_app_index);
185 transfer->initializeFromMultiapp();
186 }
187
190
191 // Perform batch MultiApp solves
193 for (dof_id_type i = _rank_config.first_local_sim_index;
195 ++i)
196 {
197 bool run = true;
198 if (_should_run)
199 {
200 if (isRootProcessor())
203 }
204 if (!run)
205 {
206 // Keep every rank's showStatusMessage() point-to-point send/receive in sync even
207 // when skipping the solve.
210 continue;
211 }
212
213 // Given that we don't initialize in preTransfer for batch-reset mode, we need
214 // a different logic for resetting the apps for every sample:
215 // - batch-restore: after (re-)initializing the problem, we only need to restore
216 // starting from the second sample
218 {
220 restore();
221 }
222 // - batch-reset: we don't need to initialize for the first sample in the first
223 // solve. After that, we initialize every time. This is mainly to avoid unnecessary
224 // initializations for cases when the multiapp does not need to be executed (conditional runs)
225 else
226 {
228 initialSetup();
229 }
230
231 // With multiple processors per app, there are no local rows for non-root processors
232 std::vector<Real> row_data;
233 if (isRootProcessor())
234 row_data = _sampler.getSampleRow(i);
235 execBatchTransfers(to_transfers,
236 i,
237 row_data,
240 _console);
241
242 // Set the file base based on the current row
243 for (unsigned int ai = 0; ai < _my_num_apps; ++ai)
244 {
245 const std::string mname = getMultiAppName(name(), i, _number_of_sampler_rows);
246 _apps[ai]->setOutputFileBase(_app.getOutputFileBase() + "_" + mname);
247 }
248
249 const bool curr_last_solve_converged =
250 FullSolveMultiApp::solveStep(dt, target_time, auto_advance);
251 last_solve_converged = last_solve_converged && curr_last_solve_converged;
252
253 execBatchTransfers(from_transfers,
254 i,
255 row_data,
258 _console);
259
261 }
263
264 // Finalize to/from transfers
265 for (auto transfer : to_transfers)
266 transfer->finalizeToMultiapp();
267 for (auto transfer : from_transfers)
268 transfer->finalizeFromMultiapp();
269
270 return last_solve_converged;
271}
272
273void
275 const std::vector<std::shared_ptr<StochasticToolsTransfer>> & transfers,
276 dof_id_type global_row_index,
277 const std::vector<Real> & row_data,
278 Transfer::DIRECTION direction,
279 bool verbose,
280 const ConsoleStream & console)
281{
282 if (verbose && transfers.size())
283 {
284 console << COLOR_CYAN << "\nBatch transfers for row " << global_row_index;
285 if (direction == MultiAppTransfer::TO_MULTIAPP)
286 console << " To ";
287 else if (direction == MultiAppTransfer::FROM_MULTIAPP)
288 console << " From ";
289 console << "MultiApps" << COLOR_DEFAULT << ":" << std::endl;
290
291 console << "Sampler row " << global_row_index << " data: [" << Moose::stringify(row_data) << "]"
292 << std::endl;
293
294 // Build Table of Transfer Info
296 {"Name", "Type", "From", "To"});
297 for (const auto & transfer : transfers)
298 table.addRow(
299 transfer->name(), transfer->type(), transfer->getFromName(), transfer->getToName());
300 table.print(console);
301 }
302
303 for (auto & transfer : transfers)
304 {
305 transfer->setGlobalRowIndex(global_row_index);
306 transfer->setCurrentRow(row_data);
307 if (direction == MultiAppTransfer::TO_MULTIAPP)
308 transfer->executeToMultiapp();
309 else if (direction == MultiAppTransfer::FROM_MULTIAPP)
310 transfer->executeFromMultiapp();
311 }
312
313 if (verbose && transfers.size())
314 console << COLOR_CYAN << "Batch transfers for row " << global_row_index << " Are Finished\n"
315 << COLOR_DEFAULT << std::endl;
316}
317
318void
320{
321 // Local row is the app index if in normal mode, otherwise it's _local_batch_app_index
322 const dof_id_type local_row =
324 // If the local row is less than the number of local sims, we aren't finished yet
325 if (local_row < _rank_config.num_local_sims - 1)
326 return;
327
328 // Loop through processors to communicate completeness
329 for (const auto & pid : make_range(n_processors()))
330 {
331 // This is what is being sent to trigger completeness
332 dof_id_type last_row = _rank_config.is_first_local_rank
334 : 0;
335 // Cannot send/receive to the same processor, so avoid if root
336 if (pid > 0)
337 {
338 // Send data to root
339 if (pid == processor_id())
340 _communicator.send(0, last_row);
341 // Receive data from source
342 else if (processor_id() == 0)
343 _communicator.receive(pid, last_row);
344 }
345
346 // Output the samples that are complete if it's the main processor for the batch
347 if (last_row)
348 _console << COLOR_CYAN << type() << " [" << name() << "] " << last_row << "/"
349 << _number_of_sampler_rows << " samples complete!" << std::endl;
350 }
351}
352
353std::vector<std::shared_ptr<StochasticToolsTransfer>>
355{
356 std::vector<std::shared_ptr<StochasticToolsTransfer>> output;
357 const ExecuteMooseObjectWarehouse<Transfer> & warehouse =
359 for (std::shared_ptr<Transfer> transfer : warehouse.getActiveObjects())
360 {
361 auto ptr = std::dynamic_pointer_cast<StochasticToolsTransfer>(transfer);
362 if (ptr && ptr->getMultiApp().get() == this)
363 output.push_back(ptr);
364 }
365 return output;
366}
367
368std::vector<std::string>
370{
373
374 std::vector<std::string> args;
375
376 // With multiple processors per app, there are no local rows for non-root processors
377 if (isRootProcessor())
378 {
379 // Since we only store param_names in cli_args, we need to find the values for each param from
380 // sampler data and combine them to get full command line option strings.
381 dof_id_type row_index;
383 row_index = local_app + _first_local_app;
384 else
388 }
389
391 return args;
392}
393
394std::vector<std::string>
396 const std::vector<std::string> & full_args_name)
397{
398 std::vector<std::string> args;
399
400 // Find parameters that are meant to be assigned by sampler values
401 std::vector<std::string> cli_args_name;
402 for (const auto & fan : full_args_name)
403 {
404 // If it has an '=', then it is not meant to be modified
405 if (fan.find("=") == std::string::npos)
406 cli_args_name.push_back(fan);
407 else
408 args.push_back(fan);
409 }
410
411 // Make sure the parameters either all have brackets, or none of them do
412 bool has_brackets = false;
413 if (cli_args_name.size())
414 {
415 has_brackets = cli_args_name[0].find("[") != std::string::npos;
416 for (unsigned int i = 1; i < cli_args_name.size(); ++i)
417 if (has_brackets != (cli_args_name[i].find("[") != std::string::npos))
418 ::mooseError("If the bracket is used, it must be provided to every parameter.");
419 }
420 if (!has_brackets && cli_args_name.size() && cli_args_name.size() != row.size())
421 ::mooseError("Number of command line arguments does not match number of sampler columns.");
422
423 for (unsigned int i = 0; i < cli_args_name.size(); ++i)
424 {
425 // Assign bracketed parameters
426 if (has_brackets)
427 {
428 // Split param name and vector assignment: "param[0,(3.14),1]" -> {"param", "0,(3.14),1]"}
429 const std::vector<std::string> & vector_param = MooseUtils::split(cli_args_name[i], "[");
430 // Get indices of vector: "0,(3.14),1]" -> {"0", "(3.14)", "1"}
431 const std::vector<std::string> & index_string =
432 MooseUtils::split(vector_param[1].substr(0, vector_param[1].find("]")), ",");
433
434 // Loop through indices and assign parameter: param='row[0] 3.14 row[1]'
435 std::vector<std::string> values;
436 for (const auto & istr : index_string)
437 {
438 Real value;
439
440 // If the value is enclosed in parentheses, then it isn't an index, it's a value
441 if (istr.find("(") != std::string::npos)
442 value = std::stod(istr.substr(istr.find("(") + 1));
443 // Assign the value from row if it is an index
444 else
445 {
446 unsigned int index = MooseUtils::stringToInteger(istr);
447 if (index >= row.size())
448 ::mooseError("The provided global column index (",
449 index,
450 ") for ",
451 vector_param[0],
452 " is out of bound.");
453 value = row[index];
454 }
455
456 values.push_back(Moose::stringifyExact(value));
457 }
458
459 // If there is only one value, do not put quotes around it
460 if (values.size() == 1)
461 args.push_back(vector_param[0] + "=" + values[0]);
462 else
463 args.push_back(vector_param[0] + "='" + MooseUtils::stringJoin(values) + "'");
464 }
465 // Assign scalar parameters
466 else
467 args.push_back(cli_args_name[i] + "=" + Moose::stringifyExact(row[i]));
468 }
469
470 return args;
471}
std::array< Real, 2 > values
registerMooseObject("StochasticToolsApp", SamplerFullSolveMultiApp)
const ConsoleStream _console
bool verboseMultiApps() const
const ExecuteMooseObjectWarehouse< Transfer > & getMultiAppTransferWarehouse(Transfer::DIRECTION direction) const
virtual void initialSetup() override
virtual void restore(bool force=true) override
static InputParameters validParams()
virtual bool solveStep(Real dt, Real target_time, bool auto_advance=true) override
void suppressParameter(const std::string &name)
void addRequiredParam(const std::string &name, const std::string &doc_string)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void addClassDescription(const std::string &doc_string)
T & set(const std::string &name, bool quiet_mode=false)
std::string getOutputFileBase(bool for_non_moose_build_output=false) const
bool isRecovering() const
const std::string & type() const
const std::string & name() const
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
const T & getParam(const std::string &name) const
bool isParamValid(const std::string &name) const
const std::vector< std::shared_ptr< T > > & getActiveObjects(THREAD_ID tid=0) const
libMesh::Parallel::Communicator _my_communicator
void init(unsigned int num_apps, const LocalRankConfig &config)
static std::string getMultiAppName(const std::string &base_name, dof_id_type index, dof_id_type total)
unsigned int _first_local_app
virtual void backup()
FEProblemBase & _fe_problem
std::vector< std::shared_ptr< MooseApp > > _apps
virtual std::vector< std::string > getCommandLineArgs(const unsigned int local_app)
LocalRankConfig _rank_config
unsigned int _my_num_apps
static InputParameters validParams()
SamplerFullSolveMultiApp(const InputParameters &parameters)
Sampler & _sampler
Sampler to utilize for creating MultiApps.
bool solveStepBatch(Real dt, Real target_time, bool auto_advance=true)
Helper method for running in mode='batch'.
virtual void showStatusMessage(unsigned int i) const override
Override to avoid 'solve converged' message and print when processors are finished.
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.
virtual void backup() override
This method is overridden so that we only store the initial state and not on any other timestep when ...
virtual void preTransfer(Real dt, Real target_time) override
const std::vector< bool > * _should_run
Reporter value determining whether the sub-app should be run for a certain sample.
virtual bool solveStep(Real dt, Real target_time, bool auto_advance=true) override
std::vector< std::shared_ptr< StochasticToolsTransfer > > getActiveStochasticToolsTransfers(Transfer::DIRECTION direction)
Helper for getting StochasticToolsTransfer objects.
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.
virtual std::vector< std::string > getCommandLineArgs(const unsigned int local_app) override
Override to allow for batch mode to get correct cli_args.
static InputParameters validParams()
dof_id_type _local_batch_app_index
Counter for extracting command line arguments in batch mode.
const StochasticTools::MultiAppMode _mode
The Sup-application solve mode.
static InputParameters validParams()
dof_id_type getNumberOfLocalRows() const
std::vector< Real > getSampleRow(dof_id_type row_index) const
dof_id_type getNumberOfRows() const
const LocalRankConfig & getRankConfig(bool batch_mode) const
Status receive(const unsigned int dest_processor_id, T &buf, const MessageTag &tag=any_tag) const
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
void send(const unsigned int dest_processor_id, const T &buf, const MessageTag &tag=no_tag) const
void addRow(Ts... entries)
const Parallel::Communicator & _communicator
processor_id_type processor_id() const
processor_id_type n_processors() const
int stringToInteger(const std::string &input, bool throw_on_failure)
std::vector< std::string > split(const std::string &str, const std::string &delimiter, std::size_t max_count)
std::string stringJoin(const std::vector< std::string > &values, const std::string &separator=" ")
std::string stringify(const T &t)
std::string stringifyExact(Real)
Enum for batch type in stochastic tools MultiApp.
dof_id_type num_local_sims
dof_id_type first_local_sim_index
bool is_first_local_rank
dof_id_type first_local_app_index