https://mooseframework.inl.gov
MorrisReporter.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 "MorrisReporter.h"
11 
12 #include "MorrisSampler.h"
13 
14 registerMooseObject("StochasticToolsApp", MorrisReporter);
15 
18 {
20  params.addClassDescription("Compute global sensitivities using the Morris method.");
21 
22  params.addRequiredParam<SamplerName>("sampler", "Morris sampler used to generate samples.");
23  params.addParam<std::vector<VectorPostprocessorName>>(
24  "vectorpostprocessors",
25  "List of VectorPostprocessor(s) to utilized for statistic computations.");
26  params.addParam<std::vector<ReporterName>>(
27  "reporters", {}, "List of Reporter values to utilized for statistic computations.");
28 
29  // Confidence Levels
30  params.addParam<std::vector<Real>>(
31  "ci_levels",
32  std::vector<Real>(),
33  "A vector of confidence levels to consider, values must be in (0, 1).");
34  params.addParam<unsigned int>("ci_replicates",
35  10000,
36  "The number of replicates to use when computing confidence level "
37  "intervals. This is basically the number of times the statistics "
38  "are recomputed with a random selection of indices.");
39  params.addParam<unsigned int>("ci_seed",
40  1,
41  "The random number generator seed used for creating replicates "
42  "while computing confidence level intervals.");
43  return params;
44 }
45 
47  : GeneralReporter(parameters),
48  _sampler(getSampler("sampler")),
49  _ci_levels(getParam<std::vector<Real>>("ci_levels")),
50  _ci_replicates(getParam<unsigned int>("ci_replicates")),
51  _ci_seed(getParam<unsigned int>("ci_seed")),
52  _initialized(false)
53 {
54  if (!dynamic_cast<MorrisSampler *>(&_sampler))
55  paramError("sampler", "Computing Morris sensitivities requires the use of a Morris sampler.");
56 
57  // CI levels error checking
58  if (!_ci_levels.empty())
59  {
60  if (*std::min_element(_ci_levels.begin(), _ci_levels.end()) <= 0)
61  paramError("ci_levels", "The supplied levels must be greater than zero.");
62  else if (*std::max_element(_ci_levels.begin(), _ci_levels.end()) >= 1)
63  paramError("ci_levels", "The supplied levels must be less than 1.0");
64  }
65 
66  if ((!isParamValid("reporters") && !isParamValid("vectorpostprocessors")) ||
67  (getParam<std::vector<ReporterName>>("reporters").empty() &&
68  getParam<std::vector<VectorPostprocessorName>>("vectorpostprocessors").empty()))
69  mooseError(
70  "The 'vectorpostprocessors' and/or 'reporters' parameters must be defined and non-empty.");
71 }
72 
73 void
75 {
76  if (_initialized)
77  return;
78 
79  // Stats for Reporters
80  if (isParamValid("reporters"))
81  {
82  std::vector<std::string> unsupported_types;
83  for (const auto & r_name : getParam<std::vector<ReporterName>>("reporters"))
84  {
85  if (hasReporterValueByName<std::vector<Real>>(r_name))
86  declareValueHelper<Real>(r_name);
87  else if (hasReporterValueByName<std::vector<std::vector<Real>>>(r_name))
88  declareValueHelper<std::vector<Real>>(r_name);
89  else
90  unsupported_types.push_back(r_name.getCombinedName());
91  }
92 
93  if (!unsupported_types.empty())
94  paramError("reporters",
95  "The following reporter value(s) do not have a type supported by the "
96  "MorrisReporter:\n",
97  MooseUtils::join(unsupported_types, ", "));
98  }
99 
100  // Stats for VPP
101  if (isParamValid("vectorpostprocessors"))
102  for (const auto & vpp_name :
103  getParam<std::vector<VectorPostprocessorName>>("vectorpostprocessors"))
104  for (const auto & vec_name :
106  declareValueHelper<Real>(ReporterName(vpp_name, vec_name));
107  _initialized = true;
108 }
109 
110 void
111 MorrisReporter::store(nlohmann::json & json) const
112 {
113  Reporter::store(json);
114  if (!_ci_levels.empty())
115  json["confidence_intervals"] = {{"method", "percentile"},
116  {"levels", _ci_levels},
117  {"replicates", _ci_replicates},
118  {"seed", _ci_seed}};
119  json["num_params"] = _sampler.getNumberOfCols();
120  json["num_trajectories"] = _sampler.getNumberOfRows() / (_sampler.getNumberOfCols() + 1);
121  if (_sampler.parameters().isParamValid("levels"))
122  json["levels"] = _sampler.parameters().get<unsigned int>("levels");
123 }
124 
125 template <typename DataType>
126 void
128 {
129  const auto & data = getReporterValueByName<std::vector<DataType>>(r_name);
130  const std::string s_name = r_name.getObjectName() + "_" + r_name.getValueName();
131  if (!_ci_levels.empty())
133  s_name,
135  _sampler,
136  data,
137  MooseEnum("percentile", "percentile"),
138  _ci_levels,
140  _ci_seed);
141  else
142  declareValueByName<MorrisState<DataType>, MorrisReporterContext<DataType>>(
143  s_name, REPORTER_MODE_ROOT, _sampler, data);
144 }
145 
146 template <typename DataType>
148  const MooseObject & producer,
150  Sampler & sampler,
151  const std::vector<DataType> & data)
152  : ReporterGeneralContext<MorrisState<DataType>>(other, producer, state),
153  _sampler(sampler),
154  _data(data)
155 {
156  MultiMooseEnum items("mean meanabs stddev", "mean meanabs stddev", true);
157  _mu_calc = StochasticTools::makeCalculator<std::vector<DataType>, DataType>(items[0], other);
158  _mustar_calc = StochasticTools::makeCalculator<std::vector<DataType>, DataType>(items[1], other);
159  _sig_calc = StochasticTools::makeCalculator<std::vector<DataType>, DataType>(items[2], other);
160 
161  // Initialize state
162  auto & mu = this->_state.value()["mu"].first;
163  mu.resize(_sampler.getNumberOfCols());
164  auto & mu_star = this->_state.value()["mu_star"].first;
165  mu_star.resize(_sampler.getNumberOfCols());
166  auto & sig = this->_state.value()["sigma"].first;
167  sig.resize(_sampler.getNumberOfCols());
168 }
169 
170 template <typename DataType>
172  const MooseObject & producer,
174  Sampler & sampler,
175  const std::vector<DataType> & data,
176  const MooseEnum & ci_method,
177  const std::vector<Real> & ci_levels,
178  unsigned int ci_replicates,
179  unsigned int ci_seed)
180  : MorrisReporterContext<DataType>(other, producer, state, sampler, data)
181 {
182  _ci_mu_calc = StochasticTools::makeBootstrapCalculator<std::vector<DataType>, DataType>(
183  ci_method, other, ci_levels, ci_replicates, ci_seed, *_mu_calc);
184  _ci_mustar_calc = StochasticTools::makeBootstrapCalculator<std::vector<DataType>, DataType>(
185  ci_method, other, ci_levels, ci_replicates, ci_seed, *_mustar_calc);
186  _ci_sig_calc = StochasticTools::makeBootstrapCalculator<std::vector<DataType>, DataType>(
187  ci_method, other, ci_levels, ci_replicates, ci_seed, *_sig_calc);
188 
189  // Initialize state
190  auto & mu = this->_state.value()["mu"].second;
191  mu.resize(_sampler.getNumberOfCols());
192  auto & mu_star = this->_state.value()["mu_star"].second;
193  mu_star.resize(_sampler.getNumberOfCols());
194  auto & sig = this->_state.value()["sigma"].second;
195  sig.resize(_sampler.getNumberOfCols());
196 }
197 
198 template <typename DataType>
199 void
201 {
202  dof_id_type offset;
203  if (_data.size() == _sampler.getNumberOfRows())
204  offset = _sampler.getLocalRowBegin();
205  else if (_data.size() == _sampler.getNumberOfLocalRows())
206  offset = 0;
207  else
208  mooseError("Data size inconsistency. Expected data vector to have ",
209  _sampler.getNumberOfLocalRows(),
210  " (local) or ",
211  _sampler.getNumberOfRows(),
212  " (global) elements, but actually has ",
213  _data.size(),
214  " elements. Are you using the same sampler?");
215 
216  const dof_id_type nc = _sampler.getNumberOfCols(); // convenience
217  if (_sampler.getNumberOfLocalRows() % (nc + 1) > 0)
218  mooseError(
219  "Sampler does not have an appropriate number of rows. Are you using a Morris sampler?");
220 
221  std::vector<std::vector<DataType>> elem_effects(
222  nc, std::vector<DataType>(_sampler.getNumberOfLocalRows() / (nc + 1)));
223  RealEigenMatrix x(nc + 1, nc);
224  std::vector<DataType> y(nc + 1);
225 
226  for (dof_id_type p = 0; p < _sampler.getNumberOfLocalRows(); ++p)
227  {
228  dof_id_type traj = p / (nc + 1);
229  dof_id_type tind = p % (nc + 1);
230  const std::vector<Real> row = _sampler.getNextLocalRow();
231  for (unsigned int k = 0; k < nc; ++k)
232  x(tind, k) = row[k];
233  y[tind] = _data[p + offset];
234 
235  if (tind == nc)
236  {
237  const std::vector<DataType> ee = computeElementaryEffects(x, y);
238  for (unsigned int k = 0; k < nc; ++k)
239  elem_effects[k][traj] = ee[k];
240  }
241  }
242 
243  auto & mu = this->_state.value()["mu"];
244  auto & mustar = this->_state.value()["mu_star"];
245  auto & sig = this->_state.value()["sigma"];
246  for (unsigned int k = 0; k < nc; ++k)
247  {
248  mu.first[k] = _mu_calc->compute(elem_effects[k], true);
249  mustar.first[k] = _mustar_calc->compute(elem_effects[k], true);
250  sig.first[k] = _sig_calc->compute(elem_effects[k], true);
251 
252  if (_ci_mu_calc)
253  mu.second[k] = _ci_mu_calc->compute(elem_effects[k], true);
254  if (_ci_mustar_calc)
255  mustar.second[k] = _ci_mustar_calc->compute(elem_effects[k], true);
256  if (_ci_sig_calc)
257  sig.second[k] = _ci_sig_calc->compute(elem_effects[k], true);
258  }
259 }
260 
261 template <>
262 std::vector<Real>
264  const std::vector<Real> & y) const
265 {
266  const auto k = y.size() - 1;
267  const RealEigenMatrix dx = x.bottomRows(k) - x.topRows(k);
268  RealEigenVector dy(k);
269  for (unsigned int j = 0; j < k; ++j)
270  dy(j) = y[j + 1] - y[j];
271 
272  const RealEigenVector u = dx.fullPivLu().solve(dy);
273  return std::vector<Real>(u.data(), u.data() + u.size());
274 }
275 
276 template <>
277 std::vector<std::vector<Real>>
278 MorrisReporterContext<std::vector<Real>>::computeElementaryEffects(
279  const RealEigenMatrix & x, const std::vector<std::vector<Real>> & y) const
280 {
281  const auto k = y.size() - 1;
282  const RealEigenMatrix dx = x.bottomRows(k) - x.topRows(k);
283  const auto solver = dx.fullPivLu();
284  RealEigenVector dy(k);
285  std::vector<std::vector<Real>> ee(k, std::vector<Real>(y[0].size()));
286  for (unsigned int i = 0; i < y[0].size(); ++i)
287  {
288  for (unsigned int j = 0; j < k; ++j)
289  dy(j) = y[j + 1][i] - y[j][i];
290  const RealEigenVector u = solver.solve(dy);
291  for (unsigned int j = 0; j < k; ++j)
292  ee[j][i] = u(j);
293  }
294  return ee;
295 }
296 
297 template void MorrisReporter::declareValueHelper<Real>(const ReporterName & r_name);
298 template class MorrisReporterContext<Real>;
299 template void MorrisReporter::declareValueHelper<std::vector<Real>>(const ReporterName & r_name);
std::string join(Iterator begin, Iterator end, const std::string &delimiter)
T & declareValueByName(const ReporterValueName &value_name, Args &&... args)
virtual void finalize() override
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void mooseError(Args &&... args)
std::vector< std::pair< R1, R2 > > get(const std::string &param1, const std::string &param2) const
const ReporterMode REPORTER_MODE_ROOT
static InputParameters validParams()
const unsigned int & _ci_seed
Random seed for producing CI replicates.
ReporterState< MorrisState< DataType > > & _state
std::unique_ptr< StochasticTools::BootstrapCalculator< std::vector< DataType >, DataType > > _ci_mustar_calc
void declareValueHelper(const ReporterName &r_name)
Helper for adding Morris reporter values.
const std::vector< double > y
static InputParameters validParams()
const std::vector< Real > & _ci_levels
CI levels to be computed.
std::vector< DataType > computeElementaryEffects(const RealEigenMatrix &x, const std::vector< DataType > &y) const
Function for computing elementary effects for a single set of trajectories This is meant to be specia...
void addRequiredParam(const std::string &name, const std::string &doc_string)
virtual void store(nlohmann::json &json) const
bool isParamValid(const std::string &name) const
const unsigned int & _ci_replicates
Number of CI replicates to use in Bootstrap methods.
const std::vector< double > x
virtual void initialize() override final
Sampler & _sampler
Morris sampler (don&#39;t need any specific functions, but should be this type)
static const std::string mu
Definition: NS.h:123
std::map< std::string, std::pair< std::vector< DataType >, std::vector< std::vector< DataType > >> > MorrisState
const T & getParam(const std::string &name) const
const VectorPostprocessor & getVectorPostprocessorObjectByName(const std::string &object_name, const THREAD_ID tid=0) const
void paramError(const std::string &param, Args... args) const
Eigen::Matrix< Real, Eigen::Dynamic, Eigen::Dynamic > RealEigenMatrix
const std::string & getObjectName() const
dof_id_type getNumberOfRows() const
MorrisReporterContext(const libMesh::ParallelObject &other, const MooseObject &producer, ReporterState< MorrisState< DataType >> &state, Sampler &sampler, const std::vector< DataType > &data)
std::unique_ptr< StochasticTools::Calculator< std::vector< DataType >, DataType > > _mu_calc
Storage for the Calculator object for the desired stat, this is created in constructor.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
bool _initialized
Whether or not initialize() has been called for reporter value declaration.
FEProblemBase & _fe_problem
void mooseError(Args &&... args) const
registerMooseObject("StochasticToolsApp", MorrisReporter)
void addClassDescription(const std::string &doc_string)
const InputParameters & parameters() const
Sampler & _sampler
Morris sampler (don&#39;t need any specific functions, but should be this type)
std::unique_ptr< StochasticTools::Calculator< std::vector< DataType >, DataType > > _sig_calc
T & value(const std::size_t time_index=0)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
Eigen::Matrix< Real, Eigen::Dynamic, 1 > RealEigenVector
MorrisReporter(const InputParameters &parameters)
const std::string & getValueName() const
static const std::string k
Definition: NS.h:130
const std::set< std::string > & getVectorNames() const
void ErrorVector unsigned int
dof_id_type getNumberOfCols() const
virtual void store(nlohmann::json &json) const override
bool hasReporterValueByName(const ReporterName &reporter_name) const
uint8_t dof_id_type
std::unique_ptr< StochasticTools::BootstrapCalculator< std::vector< DataType >, DataType > > _ci_sig_calc
std::unique_ptr< StochasticTools::BootstrapCalculator< std::vector< DataType >, DataType > > _ci_mu_calc
Storage for the BootstrapCalculator for the desired confidence interval calculations (optional) ...
std::unique_ptr< StochasticTools::Calculator< std::vector< DataType >, DataType > > _mustar_calc
bool isParamValid(const std::string &name) const