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>("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),
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
85void
91
92void
93SamplerFullSolveMultiApp::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
129bool
130SamplerFullSolveMultiApp::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
149bool
150SamplerFullSolveMultiApp::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
189 for (dof_id_type i = _rank_config.first_local_sim_index;
191 ++i)
192 {
194
195 bool run = true;
196 if (_should_run)
197 {
198 if (isRootProcessor())
199 run = (*_should_run)[_local_batch_app_index];
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
264void
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
309void
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
323 dof_id_type last_row = _rank_config.is_first_local_rank
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
344std::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
359std::vector<std::string>
361{
364
365 std::vector<std::string> args;
366
367 // With multiple processors per app, there are no local rows for non-root processors
368 if (isRootProcessor())
369 {
370 // Since we only store param_names in cli_args, we need to find the values for each param from
371 // sampler data and combine them to get full command line option strings.
374
376 }
377
379 return args;
380}
381
382void
384{
385 if (!isRootProcessor())
386 return;
387
388 mooseAssert(local_index < _sampler.getNumberOfLocalRows(),
389 "Local index must be less than number of local rows.");
390
391 if (_row_data.empty() ||
392 (_local_row_index == _sampler.getNumberOfLocalRows() - 1 && local_index == 0))
393 {
394 mooseAssert(local_index == 0,
395 "The first time calling updateRowData must have a local index of 0.");
398 }
399 else if (local_index - _local_row_index == 1)
400 {
403 }
404
405 mooseAssert(local_index == _local_row_index,
406 "Local index must be equal or one greater than the index previously called.");
407}
408
409std::vector<std::string>
411 const std::vector<std::string> & full_args_name)
412{
413 std::vector<std::string> args;
414
415 // Find parameters that are meant to be assigned by sampler values
416 std::vector<std::string> cli_args_name;
417 for (const auto & fan : full_args_name)
418 {
419 // If it has an '=', then it is not meant to be modified
420 if (fan.find("=") == std::string::npos)
421 cli_args_name.push_back(fan);
422 else
423 args.push_back(fan);
424 }
425
426 // Make sure the parameters either all have brackets, or none of them do
427 bool has_brackets = false;
428 if (cli_args_name.size())
429 {
430 has_brackets = cli_args_name[0].find("[") != std::string::npos;
431 for (unsigned int i = 1; i < cli_args_name.size(); ++i)
432 if (has_brackets != (cli_args_name[i].find("[") != std::string::npos))
433 ::mooseError("If the bracket is used, it must be provided to every parameter.");
434 }
435 if (!has_brackets && cli_args_name.size() && cli_args_name.size() != row.size())
436 ::mooseError("Number of command line arguments does not match number of sampler columns.");
437
438 for (unsigned int i = 0; i < cli_args_name.size(); ++i)
439 {
440 // Assign bracketed parameters
441 if (has_brackets)
442 {
443 // Split param name and vector assignment: "param[0,(3.14),1]" -> {"param", "0,(3.14),1]"}
444 const std::vector<std::string> & vector_param = MooseUtils::split(cli_args_name[i], "[");
445 // Get indices of vector: "0,(3.14),1]" -> {"0", "(3.14)", "1"}
446 const std::vector<std::string> & index_string =
447 MooseUtils::split(vector_param[1].substr(0, vector_param[1].find("]")), ",");
448
449 // Loop through indices and assign parameter: param='row[0] 3.14 row[1]'
450 std::vector<std::string> values;
451 for (const auto & istr : index_string)
452 {
453 Real value;
454
455 // If the value is enclosed in parentheses, then it isn't an index, it's a value
456 if (istr.find("(") != std::string::npos)
457 value = std::stod(istr.substr(istr.find("(") + 1));
458 // Assign the value from row if it is an index
459 else
460 {
461 unsigned int index = MooseUtils::stringToInteger(istr);
462 if (index >= row.size())
463 ::mooseError("The provided global column index (",
464 index,
465 ") for ",
466 vector_param[0],
467 " is out of bound.");
468 value = row[index];
469 }
470
471 values.push_back(Moose::stringifyExact(value));
472 }
473
474 // If there is only one value, do not put quotes around it
475 if (values.size() == 1)
476 args.push_back(vector_param[0] + "=" + values[0]);
477 else
478 args.push_back(vector_param[0] + "='" + MooseUtils::stringJoin(values) + "'");
479 }
480 // Assign scalar parameters
481 else
482 args.push_back(cli_args_name[i] + "=" + Moose::stringifyExact(row[i]));
483 }
484
485 return args;
486}
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
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
static std::string getMultiAppName(const std::string &base_name, dof_id_type index, dof_id_type total)
virtual void backup()
void init(unsigned int num_apps, bool batch_mode=false)
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.
void updateRowData(dof_id_type local_index)
Helper function for updating _row_data and _local_row_index.
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.
std::vector< Real > _row_data
Current row of data updated by updateRowData. Used by transfers and setting command line args.
static InputParameters validParams()
dof_id_type _local_row_index
Current local index representing _row_data.
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()
std::vector< Real > getNextLocalRow()
dof_id_type getNumberOfLocalRows() 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