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  {
452  params.set<bool>("keep_solution_during_restore") = true;
453  // Conceptually all these runs are not 'sequential in time' (moving along a transient)
454  // but rather simply restarted from the same solution
455  params.set<bool>("update_old_solution_when_keeping_solution_during_restore") = false;
456  }
457  // batch-no-restore
458  else if (_multiapp_mode == 4)
459  params.set<bool>("no_restore") = true;
460  }
461 
462  // Set the minimum number of procs
463  params.set<unsigned int>("min_procs_per_app") = getParam<unsigned int>("min_procs_per_sample");
464 
465  // Setting execute_on to make sure things happen in the correct order
466  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_BEGIN};
467 
468  // Add the multiapp
469  _problem->addMultiApp("SamplerFullSolveMultiApp", multiappName(), params);
470  if (_show_objects)
471  showObject("SamplerFullSolveMultiApp", multiappName(), params);
472  }
473  else if (_current_task == "add_transfer")
474  {
475  // Add the parameter transfer if we are doing 'batch-restore'
476  if (_multiapp_mode >= 2)
477  {
478  auto params = _factory.getValidParams("SamplerParameterTransfer");
479  params.set<MultiAppName>("to_multi_app") = multiappName();
480  params.set<SamplerName>("sampler") = samplerName();
481  params.set<std::vector<std::string>>("parameters") = _parameters;
482  _problem->addTransfer("SamplerParameterTransfer", parameterTransferName(), params);
483  if (_show_objects)
484  showObject("SamplerParameterTransfer", parameterTransferName(), params);
485  }
486 
487  // Add reporter transfer if QoIs have been specified
488  if (isParamValid("quantities_of_interest"))
489  {
490  auto params = _factory.getValidParams("SamplerReporterTransfer");
491  params.set<MultiAppName>("from_multi_app") = multiappName();
492  params.set<SamplerName>("sampler") = samplerName();
493  params.set<std::string>("stochastic_reporter") = stochasticReporterName();
494  params.set<std::vector<ReporterName>>("from_reporter") =
495  getParam<std::vector<ReporterName>>("quantities_of_interest");
496  params.set<std::string>("prefix") = "";
497  _problem->addTransfer("SamplerReporterTransfer", reporterTransferName(), params);
498  if (_show_objects)
499  showObject("SamplerReporterTransfer", reporterTransferName(), params);
500  }
501  }
502  else if (_current_task == "add_output")
503  {
504  const auto & output = getParam<MultiMooseEnum>("output_type");
505 
506  // Add csv output
507  if (output.isValueSet("csv"))
508  {
509  auto params = _factory.getValidParams("CSV");
510  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_END};
511  _problem->addOutput("CSV", outputName("csv"), params);
512  if (_show_objects)
513  showObject("CSV", outputName("csv"), params);
514  }
515 
516  // Add json output
517  if (output.isValueSet("json") || _compute_stats)
518  {
519  auto params = _factory.getValidParams("JSON");
520  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_END};
521  _problem->addOutput("JSON", outputName("json"), params);
522  if (_show_objects)
523  showObject("JSON", outputName("json"), params);
524  }
525  }
526  else if (_current_task == "add_reporter")
527  {
528  // Add stochastic reporter object
529  auto params = _factory.getValidParams("StochasticMatrix");
530 
531  // Ideally this would be based on the number of samples since gathering
532  // data onto a single processor can be memory and run-time expensive,
533  // but most people want everything in one output file
534  params.set<MooseEnum>("parallel_type") = "ROOT";
535 
536  // Supply the sampler for output
537  params.set<SamplerName>("sampler") = samplerName();
538 
539  // Set the column names if supplied or identifiable with "parameters"
540  auto & names = params.set<std::vector<ReporterValueName>>("sampler_column_names");
541  if (isParamValid("sampler_column_names"))
542  names = getParam<std::vector<ReporterValueName>>("sampler_column_names");
543  else
544  {
545  // There isn't a guaranteed mapping if using brackets
546  bool has_bracket = false;
547  for (const auto & param : _parameters)
548  if (param.find("[") != std::string::npos)
549  has_bracket = true;
550 
551  // If no brackets, then there is mapping, so use parameter names
552  if (!has_bracket)
553  for (auto param : _parameters)
554  {
555  // Reporters don't like '/' in the name, so replace those with '_'
556  std::replace(param.begin(), param.end(), '/', '_');
557  names.push_back(param);
558  }
559  }
560 
561  // Specify output objects
562  const auto & output_type = getParam<MultiMooseEnum>("output_type");
563  auto & outputs = params.set<std::vector<OutputName>>("outputs");
564  if (output_type.isValueSet("csv"))
565  outputs.push_back(outputName("csv"));
566  if (output_type.isValueSet("json"))
567  outputs.push_back(outputName("json"));
568  if (output_type.isValueSet("none"))
569  outputs = {"none"};
570 
571  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_END};
572  _problem->addReporter("StochasticMatrix", stochasticReporterName(), params);
573  if (_show_objects)
574  showObject("StochasticReporter", stochasticReporterName(), params);
575 
576  // Add statistics object
577  if (_compute_stats)
578  {
579  auto params = _factory.getValidParams("StatisticsReporter");
580  auto & reps = params.set<std::vector<ReporterName>>("reporters");
581  for (const auto & qoi : getParam<std::vector<ReporterName>>("quantities_of_interest"))
582  reps.push_back(quantityOfInterestName(qoi));
583  params.set<MultiMooseEnum>("compute") = getParam<MultiMooseEnum>("statistics");
584  params.set<MooseEnum>("ci_method") = "percentile";
585  params.set<std::vector<Real>>("ci_levels") = getParam<std::vector<Real>>("ci_levels");
586  params.set<unsigned int>("ci_replicates") = getParam<unsigned int>("ci_replicates");
587  params.set<ExecFlagEnum>("execute_on") = {EXEC_TIMESTEP_END};
588  params.set<std::vector<OutputName>>("outputs") = {outputName("json")};
589  _problem->addReporter("StatisticsReporter", statisticsName(), params);
590  if (_show_objects)
591  showObject("StatisticsReporter", statisticsName(), params);
592  }
593  }
594  else if (_current_task == "add_control")
595  {
596  // Add command-line control if the multiapp mode warrants it
597  if (_multiapp_mode <= 1)
598  {
599  auto params = _factory.getValidParams("MultiAppSamplerControl");
600  params.set<MultiAppName>("multi_app") = multiappName();
601  params.set<SamplerName>("sampler") = samplerName();
602  params.set<std::vector<std::string>>("param_names") = _parameters;
603  auto control =
604  _factory.create<Control>("MultiAppSamplerControl", multiappControlName(), params);
605  _problem->getControlWarehouse().addObject(control);
606  if (_show_objects)
607  showObject("MultiAppSamplerControl", multiappControlName(), params);
608  }
609  }
610 }
611 
612 DistributionName
613 ParameterStudyAction::distributionName(unsigned int count) const
614 {
615  return "study_distribution_" + std::to_string(count);
616 }
617 
618 std::vector<DistributionName>
619 ParameterStudyAction::distributionNames(unsigned int full_count) const
620 {
621  std::vector<DistributionName> dist_names;
622  for (const auto & i : make_range(full_count))
623  dist_names.push_back(distributionName(i));
624  return dist_names;
625 }
626 
629 {
630  return ReporterName(stochasticReporterName(), qoi.getObjectName() + ":" + qoi.getValueName());
631 }
632 
633 void
635  std::string name,
636  const InputParameters & params) const
637 {
638  // Output basic information
639  std::string base_type = params.hasBase() ? params.getBase() : "Unknown";
640  _console << "[ParameterStudy] "
641  << "Base Type: " << COLOR_YELLOW << base_type << COLOR_DEFAULT << "\n"
642  << " Type: " << COLOR_YELLOW << type << COLOR_DEFAULT << "\n"
643  << " Name: " << COLOR_YELLOW << name << COLOR_DEFAULT;
644 
645  // Gather parameters and their values if:
646  // - It is not a private parameter
647  // - Doesn't start with '_' (which usually indicates private)
648  // - Parameter is set by this action
649  // - Is not the "type" parameter
650  std::map<std::string, std::string> param_map;
651  for (const auto & it : params)
652  if (!params.isPrivate(it.first) && it.first[0] != '_' && params.isParamSetByUser(it.first) &&
653  it.first != "type")
654  {
655  std::stringstream ss;
656  it.second->print(ss);
657  param_map[it.first] = ss.str();
658  }
659 
660  // Print the gathered parameters
661  if (!param_map.empty())
662  _console << "\n Parameters: ";
663  bool first = true;
664  for (const auto & it : param_map)
665  {
666  if (!first)
667  _console << "\n" << std::string(29, ' ');
668  _console << COLOR_YELLOW << std::setw(24) << it.first << COLOR_DEFAULT << " : " << COLOR_MAGENTA
669  << it.second << COLOR_DEFAULT;
670  first = false;
671  }
672 
673  _console << std::endl;
674 }
675 
676 std::vector<std::map<std::string, bool>>
678 {
679  // monte-carlo, lhs, cartesian-product, csv, input-matrix
680  return {{{"num_samples", true}, {"distributions", true}},
681  {{"num_samples", true}, {"distributions", true}},
682  {{"linear_space_items", true}},
683  {{"csv_samples_file", true}, {"csv_column_indices", false}, {"csv_column_names", false}},
684  {{"input_matrix", true}}};
685 }
686 
687 std::vector<std::vector<std::string>>
689 {
690  // normal, uniform, weibull, lognormal, tnormal
691  return {
692  {"normal_mean", "normal_standard_deviation"},
693  {"uniform_lower_bound", "uniform_upper_bound"},
694  {"weibull_location", "weibull_scale", "weibull_shape"},
695  {"lognormal_location", "lognormal_scale"},
696  {"tnormal_mean", "tnormal_standard_deviation", "tnormal_lower_bound", "tnormal_upper_bound"}};
697 }
698 
699 std::set<std::string>
701 {
702  return {"statistics", "ci_levels", "ci_replicates"};
703 }
704 
705 unsigned int
707 {
708  if (isParamValid("multiapp_mode"))
709  return getParam<MooseEnum>("multiapp_mode");
710 
711  const unsigned int default_mode = 1;
712 
713  // First obvious thing is if it is a parsed parameter, indicated by the lack of '/'
714  for (const auto & param : _parameters)
715  if (param.find("/") == std::string::npos)
716  return default_mode;
717  // Next we'll see if there is a GlobalParam
718  for (const auto & param : _parameters)
719  if (param.find("GlobalParams") != std::string::npos)
720  return default_mode;
721 
722  // Now for the difficult check
723  // Parse input file and create root hit node
724  const auto input_filename = MooseUtils::realpath(getParam<FileName>("input"));
725  std::ifstream f(input_filename);
726  std::string input((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
727  std::unique_ptr<hit::Node> root(hit::parse(input_filename, input));
728 
729  // Walk through the input and see if every param is controllable
731  root->walk(&control_walker, hit::NodeType::Section);
732  if (!control_walker.areControllable())
733  return default_mode;
734 
735  // Walk through the input and determine how the problem is being executed
736  ExecutionTypeWalker exec_walker;
737  root->walk(&exec_walker, hit::NodeType::Section);
738  // If it is steady-state, then we don't need to restore
739  if (exec_walker.getExecutionType() == 1)
740  return 4;
741  // If it is pseudo-transeint, then we can keep the solution
742  else if (exec_walker.getExecutionType() == 2)
743  return 3;
744  // If it's transient or unknown, don't keep the solution and restore
745  else
746  return 2;
747 }
748 
750  const std::vector<std::string> & parameters, MooseApp & app)
751  : _app(app), _is_controllable(parameters.size(), false)
752 {
753  // Seperate the object from the parameter into a list of pairs
754  for (const auto & param : parameters)
755  {
756  const auto pos = param.rfind("/");
757  _pars.emplace_back(param.substr(0, pos), param.substr(pos + 1));
758  }
759 }
760 
761 void
762 AreParametersControllableWalker::walk(const std::string & fullpath,
763  const std::string & /*nodename*/,
764  hit::Node * n)
765 {
766  for (const auto & i : index_range(_pars))
767  {
768  const std::string obj = _pars[i].first;
769  const std::string par = _pars[i].second;
770  if (obj == fullpath)
771  {
772  const auto typeit = n->find("type");
773  if (typeit && typeit != n && typeit->type() == hit::NodeType::Field)
774  {
775  const std::string obj_type = n->param<std::string>("type");
776  const auto params = _app.getFactory().getValidParams(obj_type);
777  _is_controllable[i] = params.isControllable(par);
778  }
779  }
780  }
781 }
782 
783 bool
785 {
786  for (const auto & ic : _is_controllable)
787  if (!ic)
788  return false;
789  return true;
790 }
791 
792 void
793 ExecutionTypeWalker::walk(const std::string & fullpath,
794  const std::string & /*nodename*/,
795  hit::Node * n)
796 {
797  if (fullpath == "Executioner")
798  {
799  // This should not be hit since there shouldn't be two Executioner blocks
800  // But if it does happen, then go back to not knowing
801  if (_found_exec)
802  _exec_type = 0;
803  else
804  {
805  // Get the type of executioner
806  std::string executioner_type = "Unknown";
807  const auto typeit = n->find("type");
808  if (typeit && typeit != n && typeit->type() == hit::NodeType::Field)
809  executioner_type = n->param<std::string>("type");
810 
811  // If it's Steady or Eigenvalue, then it's a steady-state problem
812  if (executioner_type == "Steady" || executioner_type == "Eigenvalue")
813  _exec_type = 1;
814  // If it's Transient
815  else if (executioner_type == "Transient")
816  {
817  // Now we'll see if it's a pseudo transient
818  const auto it = n->find("steady_state_detection");
819  if (it && it != n && it->type() == hit::NodeType::Field &&
820  n->param<bool>("steady_state_detection"))
821  _exec_type = 2;
822  else
823  _exec_type = 3;
824  }
825  }
826 
827  _found_exec = true;
828  }
829 }
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.
const std::string & _name
std::string statisticsName() const
The perscribed name of the statistics object created in this action.
ActionWarehouse & _awh
void paramError(const std::string &param, Args... args) const
const T & getParam(const std::string &name) const
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)
Factory & _factory
const InputParameters & parameters() const
Helper for performing common tasks for stochastic simulations.
ActionFactory & _action_factory
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)
bool hasBase() const
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.
const std::string & getBase() 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.
const MultiMooseEnum _distributions
The distributions.
std::vector< DistributionName > distributionNames(unsigned int full_count) const
const std::string & name() 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:21
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 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)
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.
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 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
bool isParamValid(const std::string &name) 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