https://mooseframework.inl.gov
ParameterStudyAction.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 #include "ParameterStudyAction.h"
11 
12 #include "StochasticToolsAction.h"
13 #include "FEProblemBase.h"
14 #include "Control.h"
15 #include "Calculators.h"
16 
17 registerMooseAction("StochasticToolsApp", ParameterStudyAction, "meta_action");
18 registerMooseAction("StochasticToolsApp", ParameterStudyAction, "add_distribution");
19 registerMooseAction("StochasticToolsApp", ParameterStudyAction, "add_sampler");
20 registerMooseAction("StochasticToolsApp", ParameterStudyAction, "add_multi_app");
21 registerMooseAction("StochasticToolsApp", ParameterStudyAction, "add_transfer");
22 registerMooseAction("StochasticToolsApp", ParameterStudyAction, "add_output");
23 registerMooseAction("StochasticToolsApp", ParameterStudyAction, "add_reporter");
24 registerMooseAction("StochasticToolsApp", ParameterStudyAction, "add_control");
25 
28 {
30  params.addClassDescription("Builds objects to set up a basic parameter study.");
31 
32  // Parameters to define what we are studying
33  params.addRequiredParam<FileName>(
34  "input", "The input file containing the physics for the parameter study.");
35  params.addRequiredParam<std::vector<std::string>>(
36  "parameters", "List of parameters being perturbed for the study.");
37  params.addParam<std::vector<ReporterName>>(
38  "quantities_of_interest",
39  "List of the reporter names (object_name/value_name) "
40  "that represent the quantities of interest for the study.");
41 
42  // Statistics Parameters
43  params.addParam<bool>(
44  "compute_statistics",
45  true,
46  "Whether or not to compute statistics on the 'quantities_of_interest'. "
47  "The default is to compute mean and standard deviation with 0.01, 0.05, 0.1, 0.9, "
48  "0.95, and 0.99 confidence intervals.");
50  stats = "mean stddev";
51  params.addParam<MultiMooseEnum>(
52  "statistics", stats, "The statistic(s) to compute for the study.");
53  params.addParam<std::vector<Real>>("ci_levels",
54  std::vector<Real>({0.01, 0.05, 0.1, 0.9, 0.95, 0.99}),
55  "A vector of confidence levels to consider for statistics "
56  "confidence intervals, values must be in (0, 1).");
57  params.addParam<unsigned int>(
58  "ci_replicates",
59  1000,
60  "The number of replicates to use when computing confidence level intervals for statistics.");
61 
62  // Parameters for the sampling scheme
64  "sampling_type", samplingTypes(), "The type of sampling to use for the parameter study.");
65  params.addParam<unsigned int>("seed", 0, "Random number generator initial seed");
66 
67  // Parameters for multi app
68  MooseEnum modes(
69  "normal=0 batch-reset=1 batch-restore=2 batch-keep-solution=3 batch-no-restore=4");
70  params.addParam<MooseEnum>(
71  "multiapp_mode",
72  modes,
73  "The operation mode, 'normal' creates one sub-application for each sample."
74  "'batch' creates one sub-app for each processor and re-executes for each local sample. "
75  "'reset' re-initializes the sub-app for every sample in the batch. "
76  "'restore' does not re-initialize and instead restores to first sample's initialization. "
77  "'keep-solution' re-uses the solution obtained from the first sample in the batch. "
78  "'no-restore' does not restore the sub-app."
79  "The default will be inferred based on the study.");
80  params.addParam<unsigned int>(
81  "min_procs_per_sample",
82  1,
83  "Minimum number of processors to give to each sample. Useful for larger, distributed mesh "
84  "solves where there are memory constraints.");
85  params.addParam<bool>("ignore_solve_not_converge",
86  false,
87  "True to continue main app even if a sub app's solve does not converge.");
88 
89  // Samplers ///////////////////////////
90  // Parameters for Monte Carlo and LHS
91  params.addParam<dof_id_type>(
92  "num_samples", "The number of samples to generate for 'monte-carlo' and 'lhs' sampling.");
93  params.addParam<MultiMooseEnum>(
94  "distributions",
96  "The types of distribution to use for 'monte-carlo' and "
97  "'lhs' sampling. The number of entries defines the number of columns in the matrix.");
98  // Parameters for cartesian product
99  params.addParam<std::vector<Real>>(
100  "linear_space_items",
101  "Parameter for defining the 'cartesian-prodcut' sampling scheme. A list of triplets, each "
102  "item should include the min, step size, and number of steps.");
103  // Parameters for CSV sampler
104  params.addParam<FileName>(
105  "csv_samples_file",
106  "Name of the CSV file that contains the sample matrix for 'csv' sampling.");
107  params.addParam<std::vector<dof_id_type>>(
108  "csv_column_indices",
109  "Column indices in the CSV file to be sampled from for 'csv' sampling. Number of indices "
110  "here "
111  "will be the same as the number of columns per matrix.");
112  params.addParam<std::vector<std::string>>(
113  "csv_column_names",
114  "Column names in the CSV file to be sampled from for 'csv' sampling. Number of columns names "
115  "here will be the same as the number of columns per matrix.");
116  // Parameters for input matrix
117  params.addParam<RealEigenMatrix>("input_matrix", "Sampling matrix for 'input-matrix' sampling.");
118 
119  // Distributions ///////////////////////////
120  // Parameters for normal distributions
121  params.addParam<std::vector<Real>>("normal_mean",
122  "Means (or expectations) of the 'normal' distributions.");
123  params.addParam<std::vector<Real>>("normal_standard_deviation",
124  "Standard deviations of the 'normal' distributions.");
125  // Parameters for uniform distributions
126  params.addParam<std::vector<Real>>("uniform_lower_bound",
127  "Lower bounds for 'uniform' distributions.");
128  params.addParam<std::vector<Real>>("uniform_upper_bound",
129  "Upper bounds 'uniform' distributions.");
130  // Parameters for Weibull distributions
131  params.addParam<std::vector<Real>>("weibull_location",
132  "Location parameter (a or low) for 'weibull' distributions.");
133  params.addParam<std::vector<Real>>("weibull_scale",
134  "Scale parameter (b or lambda) for 'weibull' distributions.");
135  params.addParam<std::vector<Real>>("weibull_shape",
136  "Shape parameter (c or k) for 'weibull' distributions.");
137  // Parameters for lognormal distributions
138  params.addParam<std::vector<Real>>(
139  "lognormal_location", "The 'lognormal' distributions' location parameter (m or mu).");
140  params.addParam<std::vector<Real>>(
141  "lognormal_scale", "The 'lognormal' distributions' scale parameter (s or sigma).");
142  // Parameters for truncated normal distributions
143  params.addParam<std::vector<Real>>("tnormal_mean",
144  "Means (or expectations) of the 'tnormal' distributions.");
145  params.addParam<std::vector<Real>>("tnormal_standard_deviation",
146  "Standard deviations of the 'tnormal' distributions.");
147  params.addParam<std::vector<Real>>("tnormal_lower_bound", "'tnormal' distributions' lower bound");
148  params.addParam<std::vector<Real>>("tnormal_upper_bound", "'tnormal' distributions' upper bound");
149 
150  // Outputting parameters
151  MultiMooseEnum out_type("none=0 csv=1 json=2", "json");
152  params.addParam<MultiMooseEnum>(
153  "output_type",
154  out_type,
155  "Method in which to output sampler matrix and quantities of interest. Warning: "
156  "'csv' output will not include vector-type quantities.");
157  params.addParam<std::vector<ReporterValueName>>(
158  "sampler_column_names",
159  "Names of the sampler columns for outputting the sampling matrix. If 'parameters' are not "
160  "bracketed, the default is based on these values. Otherwise, the default is based on the "
161  "sampler name.");
162 
163  // Debug parameters
164  params.addParam<bool>("show_study_objects",
165  false,
166  "Set to true to show all the objects being built by this action.");
167  return params;
168 }
169 
171  : Action(parameters),
172  _parameters(getParam<std::vector<std::string>>("parameters")),
173  _sampling_type(getParam<MooseEnum>("sampling_type")),
174  _distributions(isParamValid("distributions") ? getParam<MultiMooseEnum>("distributions")
175  : MultiMooseEnum("")),
176  _multiapp_mode(inferMultiAppMode()),
177  _compute_stats(isParamValid("quantities_of_interest") && getParam<bool>("compute_statistics")),
178  _show_objects(getParam<bool>("show_study_objects"))
179 {
180  // Check sampler parameters
181  const auto sampler_params = samplerParameters();
182  const auto this_sampler_params = sampler_params[_sampling_type];
183  // Check required sampler parameters
184  for (const auto & param : this_sampler_params)
185  if (param.second && !isParamValid(param.first))
186  paramError("sampling_type",
187  "The ",
188  param.first,
189  " parameter is required to build the requested sampling type.");
190  // Check unused parameters
191  std::string msg = "";
192  for (unsigned int i = 0; i < sampler_params.size(); ++i)
193  for (const auto & param : sampler_params[i])
194  if (this_sampler_params.find(param.first) == this_sampler_params.end() &&
195  parameters.isParamSetByUser(param.first))
196  msg += (msg.empty() ? "" : ", ") + param.first;
197  if (!msg.empty())
198  paramError("sampling_type",
199  "The following parameters are unused for the selected sampling type: ",
200  msg);
201 
202  // Check distribution parameters
203  const auto distribution_params = distributionParameters();
204  std::vector<unsigned int> dist_count(distribution_params.size(), 0);
205  for (const auto & dist : _distributions)
206  dist_count[(unsigned int)dist]++;
207  msg = "";
208  for (unsigned int i = 0; i < distribution_params.size(); ++i)
209  for (const auto & param : distribution_params[i])
210  {
211  // Check if parameter was set
212  if (dist_count[i] > 0 && !isParamValid(param))
213  paramError("distributions",
214  "The ",
215  param,
216  " parameter is required to build the listed distributions.");
217  // Check if parameter has correct size
218  else if (dist_count[i] > 0 && getParam<std::vector<Real>>(param).size() != dist_count[i])
219  paramError("distributions",
220  "The number of entries in ",
221  param,
222  " does not match the number of required entries (",
223  dist_count[i],
224  ") to build the listed distributions.");
225  // Check if parameter was set and unused
226  else if (dist_count[i] == 0 && parameters.isParamSetByUser(param))
227  msg += (msg.empty() ? "" : ", ") + param;
228  }
229  if (!msg.empty())
230  paramError(
231  "distributions", "The following parameters are unused for the listed distributions: ", msg);
232 
233  // Check statistics parameters
234  if (!_compute_stats)
235  {
236  msg = "";
237  for (const auto & param : statisticsParameters())
238  if (parameters.isParamSetByUser(param))
239  msg += (msg.empty() ? "" : ", ") + param;
240  if (!msg.empty())
241  paramError("compute_statistics",
242  "The following parameters are unused since statistics are not being computed: ",
243  msg);
244  }
245 }
246 
247 MooseEnum
249 {
250  return MooseEnum("monte-carlo=0 lhs=1 cartesian-product=2 csv=3 input-matrix=4");
251 }
252 
255 {
256  return MultiMooseEnum("normal=0 uniform=1 weibull=2 lognormal=3 tnormal=4");
257 }
258 
259 void
261 {
262  if (_current_task == "meta_action")
263  {
264  const auto stm_actions = _awh.getActions<StochasticToolsAction>();
265  if (stm_actions.empty())
266  {
267  auto params = _action_factory.getValidParams("StochasticToolsAction");
268  params.set<bool>("_built_by_moose") = true;
269  params.set<std::string>("registered_identifier") = "(AutoBuilt)";
270 
271  std::shared_ptr<Action> action = _action_factory.create(
272  "StochasticToolsAction", _name + "_stochastic_tools_action", params);
273  _awh.addActionBlock(action);
274 
275  if (_show_objects)
276  showObject("StochasticToolsAction", _name + "_stochastic_tools_action", params);
277  }
278  }
279  else if (_current_task == "add_distribution")
280  {
281  // This map is used to keep track of how many of a certain
282  // distribution is being created.
283  std::unordered_map<std::string, unsigned int> dist_count;
284  for (const auto & dt : distributionTypes().getNames())
285  dist_count[dt] = 0;
286 
287  // We will have a single call to addDistribution for each entry
288  // So declare these quantities and set in the if statements
289  std::string distribution_type;
291 
292  // Loop through the inputted distributions
293  unsigned int full_count = 0;
294  for (const auto & dist : _distributions)
295  {
296  // Convenient reference to the current count
297  unsigned int & count = dist_count[dist.name()];
298 
299  // Set the distribution type and parameters
300  if (dist == "normal")
301  {
302  distribution_type = "Normal";
303  params = _factory.getValidParams(distribution_type);
304  params.set<Real>("mean") = getDistributionParam<Real>("normal_mean", count);
305  params.set<Real>("standard_deviation") =
306  getDistributionParam<Real>("normal_standard_deviation", count);
307  }
308  else if (dist == "uniform")
309  {
310  distribution_type = "Uniform";
311  params = _factory.getValidParams(distribution_type);
312  params.set<Real>("lower_bound") = getDistributionParam<Real>("uniform_lower_bound", count);
313  params.set<Real>("upper_bound") = getDistributionParam<Real>("uniform_upper_bound", count);
314  }
315  else if (dist == "weibull")
316  {
317  distribution_type = "Weibull";
318  params = _factory.getValidParams(distribution_type);
319  params.set<Real>("location") = getDistributionParam<Real>("weibull_location", count);
320  params.set<Real>("scale") = getDistributionParam<Real>("weibull_scale", count);
321  params.set<Real>("shape") = getDistributionParam<Real>("weibull_shape", count);
322  }
323  else if (dist == "lognormal")
324  {
325  distribution_type = "Lognormal";
326  params = _factory.getValidParams(distribution_type);
327  params.set<Real>("location") = getDistributionParam<Real>("lognormal_location", count);
328  params.set<Real>("scale") = getDistributionParam<Real>("lognormal_scale", count);
329  }
330  else if (dist == "tnormal")
331  {
332  distribution_type = "TruncatedNormal";
333  params = _factory.getValidParams(distribution_type);
334  params.set<Real>("mean") = getDistributionParam<Real>("tnormal_mean", count);
335  params.set<Real>("standard_deviation") =
336  getDistributionParam<Real>("tnormal_standard_deviation", count);
337  params.set<Real>("lower_bound") = getDistributionParam<Real>("tnormal_lower_bound", count);
338  params.set<Real>("upper_bound") = getDistributionParam<Real>("tnormal_upper_bound", count);
339  }
340  else
341  paramError("distributions", "Unknown distribution type.");
342 
343  // Add the distribution
344  _problem->addDistribution(distribution_type, distributionName(full_count), params);
345  if (_show_objects)
346  showObject(distribution_type, distributionName(full_count), params);
347 
348  // Increment the counts
349  count++;
350  full_count++;
351  }
352  }
353  else if (_current_task == "add_sampler")
354  {
355  // We will have a single call to addSampler
356  // So declare these quantities and set in the if statements
357  std::string sampler_type;
359 
360  // Set the distribution type and parameters
361  // monte-carlo or lhs
362  if (_sampling_type == 0 || _sampling_type == 1)
363  {
364  sampler_type = _sampling_type == 0 ? "MonteCarlo" : "LatinHypercube";
365  params = _factory.getValidParams(sampler_type);
366  params.set<dof_id_type>("num_rows") = getParam<dof_id_type>("num_samples");
367  params.set<std::vector<DistributionName>>("distributions") =
369  }
370  // cartesian-product
371  else if (_sampling_type == 2)
372  {
373  sampler_type = "CartesianProduct";
374  params = _factory.getValidParams(sampler_type);
375  params.set<std::vector<Real>>("linear_space_items") =
376  getParam<std::vector<Real>>("linear_space_items");
377  }
378  // csv
379  else if (_sampling_type == 3)
380  {
381  sampler_type = "CSVSampler";
382  params = _factory.getValidParams(sampler_type);
383  params.set<FileName>("samples_file") = getParam<FileName>("csv_samples_file");
384  if (isParamValid("csv_column_indices") && isParamValid("csv_column_names"))
385  paramError("csv_column_indices",
386  "'csv_column_indices' and 'csv_column_names' cannot both be set.");
387  else if (isParamValid("csv_column_indices"))
388  params.set<std::vector<dof_id_type>>("column_indices") =
389  getParam<std::vector<dof_id_type>>("csv_column_indices");
390  else if (isParamValid("csv_column_names"))
391  params.set<std::vector<std::string>>("column_names") =
392  getParam<std::vector<std::string>>("csv_column_names");
393  }
394  // input-matrix
395  else if (_sampling_type == 4)
396  {
397  sampler_type = "InputMatrix";
398  params = _factory.getValidParams(sampler_type);
399  params.set<RealEigenMatrix>("matrix") = getParam<RealEigenMatrix>("input_matrix");
400  }
401  else
402  paramError("sampling_type", "Unknown sampling type.");
403 
404  // Need to set the right execute_on for command-line control
405  if (_multiapp_mode <= 1)
406  params.set<ExecFlagEnum>("execute_on") = {EXEC_PRE_MULTIAPP_SETUP};
407  else
408  params.set<ExecFlagEnum>("execute_on") = {EXEC_INITIAL};
409 
410  // Set the minimum number of procs
411  params.set<unsigned int>("min_procs_per_row") = getParam<unsigned int>("min_procs_per_sample");
412 
413  // Add the sampler
414  _problem->addSampler(sampler_type, samplerName(), params);
415  if (_show_objects)
416  showObject(sampler_type, samplerName(), params);
417  }
418  else if (_current_task == "add_multi_app")
419  {
420  auto params = _factory.getValidParams("SamplerFullSolveMultiApp");
421 
422  // Dealing with failed solves
423  params.set<bool>("ignore_solve_not_converge") = getParam<bool>("ignore_solve_not_converge");
424 
425  // Set input file
426  params.set<std::vector<FileName>>("input_files") = {getParam<FileName>("input")};
427 
428  // Set the Sampler
429  params.set<SamplerName>("sampler") = samplerName();
430 
431  // Set parameters based on the sampling mode
432  // normal
433  if (_multiapp_mode == 0)
434  params.set<MooseEnum>("mode") = "normal";
435  // batch-reset
436  else if (_multiapp_mode == 1)
437  params.set<MooseEnum>("mode") = "batch-reset";
438  // batch-restore variants
439  else
440  {
441  // Set the mode to 'batch-restore'
442  params.set<MooseEnum>("mode") = "batch-restore";
443 
444  // If we are doing batch-restore, the parameters must be controllable.
445  // So we will add the necessary control to the sub-app using command-line
446  std::string clia = "Controls/" + samplerReceiverName() + "/type=SamplerReceiver";
447  params.set<std::vector<CLIArgString>>("cli_args").push_back(clia);
448 
449  // batch-keep-solution
450  if (_multiapp_mode == 3)
451  params.set<bool>("keep_solution_during_restore") = true;
452  // batch-no-restore
453  else if (_multiapp_mode == 4)
454  params.set<bool>("no_restore") = true;
455  }
456 
457  // Set the minimum number of procs
458  params.set<unsigned int>("min_procs_per_app") = getParam<unsigned int>("min_procs_per_sample");
459 
460  // Setting execute_on to make sure things happen in the correct order
461  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_BEGIN};
462 
463  // Add the multiapp
464  _problem->addMultiApp("SamplerFullSolveMultiApp", multiappName(), params);
465  if (_show_objects)
466  showObject("SamplerFullSolveMultiApp", multiappName(), params);
467  }
468  else if (_current_task == "add_transfer")
469  {
470  // Add the parameter transfer if we are doing 'batch-restore'
471  if (_multiapp_mode >= 2)
472  {
473  auto params = _factory.getValidParams("SamplerParameterTransfer");
474  params.set<MultiAppName>("to_multi_app") = multiappName();
475  params.set<SamplerName>("sampler") = samplerName();
476  params.set<std::vector<std::string>>("parameters") = _parameters;
477  _problem->addTransfer("SamplerParameterTransfer", parameterTransferName(), params);
478  if (_show_objects)
479  showObject("SamplerParameterTransfer", parameterTransferName(), params);
480  }
481 
482  // Add reporter transfer if QoIs have been specified
483  if (isParamValid("quantities_of_interest"))
484  {
485  auto params = _factory.getValidParams("SamplerReporterTransfer");
486  params.set<MultiAppName>("from_multi_app") = multiappName();
487  params.set<SamplerName>("sampler") = samplerName();
488  params.set<std::string>("stochastic_reporter") = stochasticReporterName();
489  params.set<std::vector<ReporterName>>("from_reporter") =
490  getParam<std::vector<ReporterName>>("quantities_of_interest");
491  params.set<std::string>("prefix") = "";
492  _problem->addTransfer("SamplerReporterTransfer", reporterTransferName(), params);
493  if (_show_objects)
494  showObject("SamplerReporterTransfer", reporterTransferName(), params);
495  }
496  }
497  else if (_current_task == "add_output")
498  {
499  const auto & output = getParam<MultiMooseEnum>("output_type");
500 
501  // Add csv output
502  if (output.isValueSet("csv"))
503  {
504  auto params = _factory.getValidParams("CSV");
505  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_END};
506  _problem->addOutput("CSV", outputName("csv"), params);
507  if (_show_objects)
508  showObject("CSV", outputName("csv"), params);
509  }
510 
511  // Add json output
512  if (output.isValueSet("json") || _compute_stats)
513  {
514  auto params = _factory.getValidParams("JSON");
515  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_END};
516  _problem->addOutput("JSON", outputName("json"), params);
517  if (_show_objects)
518  showObject("JSON", outputName("json"), params);
519  }
520  }
521  else if (_current_task == "add_reporter")
522  {
523  // Add stochastic reporter object
524  auto params = _factory.getValidParams("StochasticMatrix");
525 
526  // Ideally this would be based on the number of samples since gathering
527  // data onto a single processor can be memory and run-time expensive,
528  // but most people want everything in one output file
529  params.set<MooseEnum>("parallel_type") = "ROOT";
530 
531  // Supply the sampler for output
532  params.set<SamplerName>("sampler") = samplerName();
533 
534  // Set the column names if supplied or identifiable with "parameters"
535  auto & names = params.set<std::vector<ReporterValueName>>("sampler_column_names");
536  if (isParamValid("sampler_column_names"))
537  names = getParam<std::vector<ReporterValueName>>("sampler_column_names");
538  else
539  {
540  // There isn't a guaranteed mapping if using brackets
541  bool has_bracket = false;
542  for (const auto & param : _parameters)
543  if (param.find("[") != std::string::npos)
544  has_bracket = true;
545 
546  // If no brackets, then there is mapping, so use parameter names
547  if (!has_bracket)
548  for (auto param : _parameters)
549  {
550  // Reporters don't like '/' in the name, so replace those with '_'
551  std::replace(param.begin(), param.end(), '/', '_');
552  names.push_back(param);
553  }
554  }
555 
556  // Specify output objects
557  const auto & output_type = getParam<MultiMooseEnum>("output_type");
558  auto & outputs = params.set<std::vector<OutputName>>("outputs");
559  if (output_type.isValueSet("csv"))
560  outputs.push_back(outputName("csv"));
561  if (output_type.isValueSet("json"))
562  outputs.push_back(outputName("json"));
563  if (output_type.isValueSet("none"))
564  outputs = {"none"};
565 
566  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_END};
567  _problem->addReporter("StochasticMatrix", stochasticReporterName(), params);
568  if (_show_objects)
569  showObject("StochasticReporter", stochasticReporterName(), params);
570 
571  // Add statistics object
572  if (_compute_stats)
573  {
574  auto params = _factory.getValidParams("StatisticsReporter");
575  auto & reps = params.set<std::vector<ReporterName>>("reporters");
576  for (const auto & qoi : getParam<std::vector<ReporterName>>("quantities_of_interest"))
577  reps.push_back(quantityOfInterestName(qoi));
578  params.set<MultiMooseEnum>("compute") = getParam<MultiMooseEnum>("statistics");
579  params.set<MooseEnum>("ci_method") = "percentile";
580  params.set<std::vector<Real>>("ci_levels") = getParam<std::vector<Real>>("ci_levels");
581  params.set<unsigned int>("ci_replicates") = getParam<unsigned int>("ci_replicates");
582  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_END};
583  params.set<std::vector<OutputName>>("outputs") = {outputName("json")};
584  _problem->addReporter("StatisticsReporter", statisticsName(), params);
585  if (_show_objects)
586  showObject("StatisticsReporter", statisticsName(), params);
587  }
588  }
589  else if (_current_task == "add_control")
590  {
591  // Add command-line control if the multiapp mode warrants it
592  if (_multiapp_mode <= 1)
593  {
594  auto params = _factory.getValidParams("MultiAppSamplerControl");
595  params.set<MultiAppName>("multi_app") = multiappName();
596  params.set<SamplerName>("sampler") = samplerName();
597  params.set<std::vector<std::string>>("param_names") = _parameters;
598  auto control =
599  _factory.create<Control>("MultiAppSamplerControl", multiappControlName(), params);
600  _problem->getControlWarehouse().addObject(control);
601  if (_show_objects)
602  showObject("MultiAppSamplerControl", multiappControlName(), params);
603  }
604  }
605 }
606 
607 DistributionName
608 ParameterStudyAction::distributionName(unsigned int count) const
609 {
610  return "study_distribution_" + std::to_string(count);
611 }
612 
613 std::vector<DistributionName>
614 ParameterStudyAction::distributionNames(unsigned int full_count) const
615 {
616  std::vector<DistributionName> dist_names;
617  for (const auto & i : make_range(full_count))
618  dist_names.push_back(distributionName(i));
619  return dist_names;
620 }
621 
624 {
625  return ReporterName(stochasticReporterName(), qoi.getObjectName() + ":" + qoi.getValueName());
626 }
627 
628 void
630  std::string name,
631  const InputParameters & params) const
632 {
633  // Output basic information
634  std::string base_type = params.have_parameter<std::string>("_moose_base")
635  ? params.get<std::string>("_moose_base")
636  : "Unknown";
637  _console << "[ParameterStudy] "
638  << "Base Type: " << COLOR_YELLOW << base_type << COLOR_DEFAULT << "\n"
639  << " Type: " << COLOR_YELLOW << type << COLOR_DEFAULT << "\n"
640  << " Name: " << COLOR_YELLOW << name << COLOR_DEFAULT;
641 
642  // Gather parameters and their values if:
643  // - It is not a private parameter
644  // - Doesn't start with '_' (which usually indicates private)
645  // - Parameter is set by this action
646  // - Is not the "type" parameter
647  std::map<std::string, std::string> param_map;
648  for (const auto & it : params)
649  if (!params.isPrivate(it.first) && it.first[0] != '_' && params.isParamSetByUser(it.first) &&
650  it.first != "type")
651  {
652  std::stringstream ss;
653  it.second->print(ss);
654  param_map[it.first] = ss.str();
655  }
656 
657  // Print the gathered parameters
658  if (!param_map.empty())
659  _console << "\n Parameters: ";
660  bool first = true;
661  for (const auto & it : param_map)
662  {
663  if (!first)
664  _console << "\n" << std::string(29, ' ');
665  _console << COLOR_YELLOW << std::setw(24) << it.first << COLOR_DEFAULT << " : " << COLOR_MAGENTA
666  << it.second << COLOR_DEFAULT;
667  first = false;
668  }
669 
670  _console << std::endl;
671 }
672 
673 std::vector<std::map<std::string, bool>>
675 {
676  // monte-carlo, lhs, cartesian-product, csv, input-matrix
677  return {{{"num_samples", true}, {"distributions", true}},
678  {{"num_samples", true}, {"distributions", true}},
679  {{"linear_space_items", true}},
680  {{"csv_samples_file", true}, {"csv_column_indices", false}, {"csv_column_names", false}},
681  {{"input_matrix", true}}};
682 }
683 
684 std::vector<std::vector<std::string>>
686 {
687  // normal, uniform, weibull, lognormal, tnormal
688  return {
689  {"normal_mean", "normal_standard_deviation"},
690  {"uniform_lower_bound", "uniform_upper_bound"},
691  {"weibull_location", "weibull_scale", "weibull_shape"},
692  {"lognormal_location", "lognormal_scale"},
693  {"tnormal_mean", "tnormal_standard_deviation", "tnormal_lower_bound", "tnormal_upper_bound"}};
694 }
695 
696 std::set<std::string>
698 {
699  return {"statistics", "ci_levels", "ci_replicates"};
700 }
701 
702 unsigned int
704 {
705  if (isParamValid("multiapp_mode"))
706  return getParam<MooseEnum>("multiapp_mode");
707 
708  const unsigned int default_mode = 1;
709 
710  // First obvious thing is if it is a parsed parameter, indicated by the lack of '/'
711  for (const auto & param : _parameters)
712  if (param.find("/") == std::string::npos)
713  return default_mode;
714  // Next we'll see if there is a GlobalParam
715  for (const auto & param : _parameters)
716  if (param.find("GlobalParams") != std::string::npos)
717  return default_mode;
718 
719  // Now for the difficult check
720  // Parse input file and create root hit node
721  const auto input_filename = MooseUtils::realpath(getParam<FileName>("input"));
722  std::ifstream f(input_filename);
723  std::string input((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
724  std::unique_ptr<hit::Node> root(hit::parse(input_filename, input));
725  hit::explode(root.get());
726 
727  // Walk through the input and see if every param is controllable
729  root->walk(&control_walker, hit::NodeType::Section);
730  if (!control_walker.areControllable())
731  return default_mode;
732 
733  // Walk through the input and determine how the problem is being executed
734  ExecutionTypeWalker exec_walker;
735  root->walk(&exec_walker, hit::NodeType::Section);
736  // If it is steady-state, then we don't need to restore
737  if (exec_walker.getExecutionType() == 1)
738  return 4;
739  // If it is pseudo-transeint, then we can keep the solution
740  else if (exec_walker.getExecutionType() == 2)
741  return 3;
742  // If it's transient or unknown, don't keep the solution and restore
743  else
744  return 2;
745 }
746 
748  const std::vector<std::string> & parameters, MooseApp & app)
749  : _app(app), _is_controllable(parameters.size(), false)
750 {
751  // Seperate the object from the parameter into a list of pairs
752  for (const auto & param : parameters)
753  {
754  const auto pos = param.rfind("/");
755  _pars.emplace_back(param.substr(0, pos), param.substr(pos + 1));
756  }
757 }
758 
759 void
760 AreParametersControllableWalker::walk(const std::string & fullpath,
761  const std::string & /*nodename*/,
762  hit::Node * n)
763 {
764  for (const auto & i : index_range(_pars))
765  {
766  const std::string obj = _pars[i].first;
767  const std::string par = _pars[i].second;
768  if (obj == fullpath)
769  {
770  const auto typeit = n->find("type");
771  if (typeit && typeit != n && typeit->type() == hit::NodeType::Field)
772  {
773  const std::string obj_type = n->param<std::string>("type");
774  const auto params = _app.getFactory().getValidParams(obj_type);
775  _is_controllable[i] = params.isControllable(par);
776  }
777  }
778  }
779 }
780 
781 bool
783 {
784  for (const auto & ic : _is_controllable)
785  if (!ic)
786  return false;
787  return true;
788 }
789 
790 void
791 ExecutionTypeWalker::walk(const std::string & fullpath,
792  const std::string & /*nodename*/,
793  hit::Node * n)
794 {
795  if (fullpath == "Executioner")
796  {
797  // This should not be hit since there shouldn't be two Executioner blocks
798  // But if it does happen, then go back to not knowing
799  if (_found_exec)
800  _exec_type = 0;
801  else
802  {
803  // Get the type of executioner
804  std::string executioner_type = "Unknown";
805  const auto typeit = n->find("type");
806  if (typeit && typeit != n && typeit->type() == hit::NodeType::Field)
807  executioner_type = n->param<std::string>("type");
808 
809  // If it's Steady or Eigenvalue, then it's a steady-state problem
810  if (executioner_type == "Steady" || executioner_type == "Eigenvalue")
811  _exec_type = 1;
812  // If it's Transient
813  else if (executioner_type == "Transient")
814  {
815  // Now we'll see if it's a pseudo transient
816  const auto it = n->find("steady_state_detection");
817  if (it && it != n && it->type() == hit::NodeType::Field &&
818  n->param<bool>("steady_state_detection"))
819  _exec_type = 2;
820  else
821  _exec_type = 3;
822  }
823  }
824 
825  _found_exec = true;
826  }
827 }
SamplerName samplerName() const
The perscribed name of the sampler created in this action.
unsigned int getExecutionType() const
DistributionName distributionName(unsigned int count) const
The perscribed name of the distribution.
std::string statisticsName() const
The perscribed name of the statistics object created in this action.
ActionWarehouse & _awh
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
InputParameters getValidParams(const std::string &name)
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
Helper for performing common tasks for stochastic simulations.
MooseApp & _app
T & set(const std::string &name, bool quiet_mode=false)
std::shared_ptr< MooseObject > create(const std::string &obj_name, const std::string &name, const InputParameters &parameters, THREAD_ID tid=0, bool print_deprecated=true)
if(subdm)
InputParameters getValidParams(const std::string &name) const
unsigned int size() const
This class is a hit walker used to see what type of execution the input is doing. ...
const unsigned int _sampling_type
The sampling type.
void addActionBlock(std::shared_ptr< Action > blk)
const ExecFlagType EXEC_TIMESTEP_END
const unsigned int _multiapp_mode
The multiapp mode.
std::string realpath(const std::string &path)
MultiMooseEnum makeCalculatorEnum()
Definition: Calculators.C:16
const bool _show_objects
Switch to show the objects being built on console.
virtual const std::string & name() const
std::string samplerReceiverName() const
The perscribed name of the control given to the sub-app for parameter transfer.
registerMooseAction("StochasticToolsApp", ParameterStudyAction, "meta_action")
void addRequiredParam(const std::string &name, const std::string &doc_string)
std::shared_ptr< Action > create(const std::string &action, const std::string &action_name, InputParameters &parameters)
InputParameters emptyInputParameters()
Factory & getFactory()
virtual void act() override
std::string multiappControlName() const
The perscribed name of the command-line control created in this action.
bool isParamValid(const std::string &name) const
Factory & _factory
const MultiMooseEnum _distributions
The distributions.
std::vector< DistributionName > distributionNames(unsigned int full_count) const
static InputParameters validParams()
std::string stochasticReporterName() const
The perscribed name of the QoI storage object created in this action.
static MooseEnum samplingTypes()
Return an enum of available sampling types for the study.
void walk(const std::string &fullpath, const std::string &nodename, hit::Node *n) override
ParameterStudyAction(const InputParameters &params)
Real f(Real x)
Test function for Brents method.
const ExecFlagType EXEC_TIMESTEP_BEGIN
std::string reporterTransferName() const
The perscribed name of the reporter transfer created in this action.
const ExecFlagType EXEC_PRE_MULTIAPP_SETUP
const std::string name
Definition: Setup.h:20
Real root(std::function< Real(Real)> const &f, Real x1, Real x2, Real tol=1.0e-12)
Finds the root of a function using Brent&#39;s method.
Definition: BrentsMethod.C:66
void walk(const std::string &fullpath, const std::string &nodename, hit::Node *n) override
void showObject(std::string type, std::string name, const InputParameters &params) const
Helper function to show the object being built.
const std::string & type() const
const T & getParam(const std::string &name) const
const std::string & _current_task
OutputName outputName(std::string type) const
The perscribed name of the output objects created in this action.
AreParametersControllableWalker(const std::vector< std::string > &parameters, MooseApp &app)
void paramError(const std::string &param, Args... args) const
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic > RealEigenMatrix
const std::string & getObjectName() const
ReporterName quantityOfInterestName(const ReporterName &qoi) const
The name of the reporter values in the StochasticReporter representing the QoIs.
ActionFactory & _action_factory
const std::string _name
static std::set< std::string > statisticsParameters()
List of parameters that are only associated with computing statistics.
std::vector< std::pair< std::string, std::string > > _pars
bool have_parameter(std::string_view name) const
bool isParamSetByUser(const std::string &name) const
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const std::vector< std::string > & _parameters
The inputted parameter vector.
static std::vector< std::map< std::string, bool > > samplerParameters()
This is a vector associating the sampling type with a list of associated parameters The list includes...
static std::vector< std::vector< std::string > > distributionParameters()
This is a vector associating the distribution type and a list of parameters that are needed...
std::string parameterTransferName() const
The perscribed name of the parameter transfer created in this action.
MultiAppName multiappName() const
The perscribed name of the multiapp created in this action.
IntRange< T > make_range(T beg, T end)
unsigned int inferMultiAppMode()
This function will infer the best way to run the multiapps.
const bool _compute_stats
Whether or not we are computing statistics.
void addClassDescription(const std::string &doc_string)
std::shared_ptr< FEProblemBase > & _problem
const InputParameters & parameters() const
const ConsoleStream _console
const std::string & getValueName() const
std::vector< const T *> getActions()
static InputParameters validParams()
static MultiMooseEnum distributionTypes()
Return an enum of available distributions for the study.
void ErrorVector unsigned int
auto index_range(const T &sizable)
This class is a hit walker used to see if a list of parameters are all controllable.
uint8_t dof_id_type
const ExecFlagType EXEC_INITIAL