Line data Source code
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 "SobolReporter.h"
11 : #include "SobolSampler.h"
12 : #include "SobolCalculators.h"
13 : #include "BootstrapCalculators.h"
14 :
15 : // MOOSE includes
16 : #include "MooseVariable.h"
17 : #include "ThreadedElementLoopBase.h"
18 : #include "ThreadedNodeLoop.h"
19 :
20 : #include "libmesh/quadrature.h"
21 :
22 : #include <numeric>
23 :
24 : registerMooseObject("StochasticToolsApp", SobolReporter);
25 :
26 : InputParameters
27 136 : SobolReporter::validParams()
28 : {
29 136 : InputParameters params = GeneralReporter::validParams();
30 136 : params.addClassDescription("Compute SOBOL statistics values of a given VectorPostprocessor or "
31 : "Reporter objects and vectors.");
32 272 : params.addParam<SamplerName>("sampler", "SobolSampler object.");
33 :
34 272 : params.addParam<std::vector<VectorPostprocessorName>>(
35 : "vectorpostprocessors",
36 : "List of VectorPostprocessor(s) to utilized for statistic computations.");
37 :
38 272 : params.addParam<std::vector<ReporterName>>(
39 : "reporters", {}, "List of Reporter values to utilized for statistic computations.");
40 :
41 136 : params.addParam<std::vector<Real>>(
42 : "ci_levels",
43 136 : std::vector<Real>(),
44 : "A vector of confidence levels to consider, values must be in (0, 1).");
45 272 : params.addParam<unsigned int>(
46 : "ci_replicates",
47 272 : 10000,
48 : "The number of replicates to use when computing confidence level intervals.");
49 272 : params.addParam<unsigned int>("ci_seed",
50 272 : 1,
51 : "The random number generator seed used for creating replicates "
52 : "while computing confidence level intervals.");
53 136 : return params;
54 0 : }
55 :
56 68 : SobolReporter::SobolReporter(const InputParameters & parameters)
57 : : GeneralReporter(parameters),
58 68 : _sobol_sampler(getSampler<SobolSampler>("sampler")),
59 136 : _ci_levels(getParam<std::vector<Real>>("ci_levels")),
60 136 : _ci_replicates(getParam<unsigned int>("ci_replicates")),
61 136 : _ci_seed(getParam<unsigned int>("ci_seed")),
62 68 : _initialized(false)
63 : {
64 : // CI levels error checking
65 68 : if (!_ci_levels.empty())
66 : {
67 68 : if (*std::min_element(_ci_levels.begin(), _ci_levels.end()) <= 0)
68 0 : paramError("ci_levels", "The supplied levels must be greater than zero.");
69 68 : else if (*std::max_element(_ci_levels.begin(), _ci_levels.end()) >= 1)
70 0 : paramError("ci_levels", "The supplied levels must be less than 1.0");
71 : }
72 :
73 272 : if ((!isParamValid("reporters") && !isParamValid("vectorpostprocessors")) ||
74 170 : (getParam<std::vector<ReporterName>>("reporters").empty() &&
75 136 : getParam<std::vector<VectorPostprocessorName>>("vectorpostprocessors").empty()))
76 0 : mooseError(
77 : "The 'vectorpostprocessors' and/or 'reporters' parameters must be defined and non-empty.");
78 68 : }
79 :
80 : void
81 68 : SobolReporter::initialize()
82 : {
83 68 : if (_initialized)
84 : return;
85 :
86 : // Stats for Reporters
87 136 : if (isParamValid("reporters"))
88 : {
89 : std::vector<std::string> unsupported_types;
90 136 : const auto & reporter_names = getParam<std::vector<ReporterName>>("reporters");
91 153 : for (const auto & r_name : reporter_names)
92 : {
93 85 : if (hasReporterValueByName<std::vector<Real>>(r_name))
94 68 : declareValueHelper<Real>(r_name);
95 17 : else if (hasReporterValueByName<std::vector<std::vector<Real>>>(r_name))
96 17 : declareValueHelper<std::vector<Real>>(r_name);
97 : else
98 0 : unsupported_types.emplace_back(r_name.getCombinedName());
99 : }
100 :
101 68 : if (!unsupported_types.empty())
102 0 : paramError("reporters",
103 : "The following reporter value(s) do not have a type supported by the "
104 : "StatisticsReporter:\n",
105 0 : MooseUtils::join(unsupported_types, ", "));
106 68 : }
107 :
108 : // Stats for VPP
109 136 : if (isParamValid("vectorpostprocessors"))
110 : {
111 68 : const auto & vpp_names = getParam<std::vector<VectorPostprocessorName>>("vectorpostprocessors");
112 68 : for (const auto & vpp_name : vpp_names)
113 : {
114 : const VectorPostprocessor & vpp_object =
115 34 : _fe_problem.getVectorPostprocessorObjectByName(vpp_name);
116 34 : const std::set<std::string> & vpp_vectors = vpp_object.getVectorNames();
117 68 : for (const auto & vec_name : vpp_vectors)
118 : {
119 34 : ReporterName r_name(vpp_name, vec_name);
120 34 : declareValueHelper<Real>(r_name);
121 : }
122 : }
123 : }
124 :
125 68 : _initialized = true;
126 : }
127 :
128 : void
129 44 : SobolReporter::store(nlohmann::json & json) const
130 : {
131 44 : Reporter::store(json);
132 44 : if (!_ci_levels.empty())
133 44 : json["confidence_intervals"] = {{"method", "percentile"},
134 : {"levels", _ci_levels},
135 : {"replicates", _ci_replicates},
136 572 : {"seed", _ci_seed}};
137 :
138 88 : json["num_params"] = _sobol_sampler.getNumberOfCols();
139 44 : json["indices"].push_back("FIRST_ORDER");
140 44 : json["indices"].push_back("TOTAL");
141 44 : if (_sobol_sampler.resample())
142 66 : json["indices"].push_back("SECOND_ORDER");
143 616 : }
144 :
145 : template <typename T>
146 : void
147 119 : SobolReporter::declareValueHelper(const ReporterName & r_name)
148 : {
149 119 : const auto & mode = _fe_problem.getReporterData().getReporterMode(r_name);
150 119 : const auto & data = getReporterValueByName<std::vector<T>>(r_name);
151 119 : const std::string s_name = r_name.getObjectName() + "_" + r_name.getValueName();
152 119 : if (!_ci_levels.empty())
153 357 : declareValueByName<SobolState<T>, SobolReporterContext<std::vector<T>, T>>(
154 : s_name,
155 : REPORTER_MODE_ROOT,
156 : data,
157 : mode,
158 : _sobol_sampler,
159 357 : MooseEnum("percentile", "percentile"),
160 : _ci_levels,
161 : _ci_replicates,
162 : _ci_seed);
163 : else
164 0 : declareValueByName<SobolState<T>, SobolReporterContext<std::vector<T>, T>>(
165 : s_name, REPORTER_MODE_ROOT, data, mode, _sobol_sampler);
166 119 : }
167 :
168 : template <typename InType, typename OutType>
169 119 : SobolReporterContext<InType, OutType>::SobolReporterContext(
170 : const libMesh::ParallelObject & other,
171 : const MooseObject & producer,
172 : ReporterState<SobolState<OutType>> & state,
173 : const InType & data,
174 : const ReporterProducerEnum & mode,
175 : const SobolSampler & sampler)
176 : : ReporterGeneralContext<SobolState<OutType>>(other, producer, state),
177 119 : _data(data),
178 119 : _data_mode(mode),
179 119 : _sampler(sampler),
180 238 : _calc(other, "SOBOL", _sampler.resample())
181 : {
182 119 : }
183 :
184 : template <typename InType, typename OutType>
185 119 : SobolReporterContext<InType, OutType>::SobolReporterContext(
186 : const libMesh::ParallelObject & other,
187 : const MooseObject & producer,
188 : ReporterState<SobolState<OutType>> & state,
189 : const InType & data,
190 : const ReporterProducerEnum & mode,
191 : const SobolSampler & sampler,
192 : const MooseEnum & ci_method,
193 : const std::vector<Real> & ci_levels,
194 : unsigned int ci_replicates,
195 : unsigned int ci_seed)
196 119 : : SobolReporterContext<InType, OutType>(other, producer, state, data, mode, sampler)
197 : {
198 238 : _ci_calc_ptr =
199 : StochasticTools::makeBootstrapCalculator<std::vector<InType>, std::vector<OutType>>(
200 : ci_method, other, ci_levels, ci_replicates, ci_seed, _calc);
201 119 : }
202 :
203 : template <typename InType, typename OutType>
204 : void
205 119 : SobolReporterContext<InType, OutType>::finalize()
206 : {
207 119 : auto & val = this->_state.value(); // Convenience
208 119 : val.first.clear();
209 119 : val.second.clear();
210 :
211 119 : const bool is_dist = _data_mode == REPORTER_MODE_DISTRIBUTED;
212 119 : if (is_dist || this->processor_id() == 0)
213 : {
214 : const std::size_t ncol =
215 119 : _sampler.resample() ? 2 * _sampler.getNumberOfCols() + 2 : _sampler.getNumberOfCols() + 2;
216 119 : const std::vector<InType> data_reshape =
217 : StochasticTools::reshapeVector(_data, ncol, /*row_major =*/true);
218 :
219 238 : val.first = _calc.compute(data_reshape, is_dist);
220 119 : if (_ci_calc_ptr)
221 238 : val.second = _ci_calc_ptr->compute(data_reshape, is_dist);
222 119 : }
223 :
224 119 : ReporterGeneralContext<SobolState<OutType>>::finalize();
225 119 : }
226 :
227 : template <typename InType, typename OutType>
228 : void
229 77 : SobolReporterContext<InType, OutType>::store(nlohmann::json & json) const
230 : {
231 77 : storeSobol(json, this->_state.value(), _sampler.getNumberOfCols());
232 77 : }
233 :
234 : template <typename InType, typename OutType>
235 : void
236 154 : SobolReporterContext<InType, OutType>::storeSobol(nlohmann::json & json,
237 : const SobolState<OutType> & val,
238 : unsigned int nparam)
239 : {
240 154 : if (val.first.empty())
241 0 : return;
242 :
243 : // Convenience
244 154 : const unsigned int nlevels = val.second.size();
245 :
246 : std::pair<std::vector<OutType>, std::vector<std::vector<OutType>>> first_order;
247 : std::pair<std::vector<OutType>, std::vector<std::vector<OutType>>> total;
248 :
249 154 : first_order.first.assign(val.first.begin(), val.first.begin() + nparam);
250 154 : total.first.assign(val.first.begin() + nparam, val.first.begin() + nparam + nparam);
251 154 : if (nlevels > 0)
252 : {
253 77 : first_order.second.resize(nlevels);
254 77 : total.second.resize(nlevels);
255 308 : for (const auto & l : make_range(nlevels))
256 : {
257 231 : first_order.second[l].assign(val.second[l].begin(), val.second[l].begin() + nparam);
258 231 : total.second[l].assign(val.second[l].begin() + nparam,
259 : val.second[l].begin() + nparam + nparam);
260 : }
261 : }
262 154 : json["FIRST_ORDER"] = first_order;
263 308 : json["TOTAL"] = total;
264 :
265 154 : if (val.first.size() > 2 * nparam)
266 : {
267 : std::pair<std::vector<std::vector<OutType>>, std::vector<std::vector<std::vector<OutType>>>>
268 : second_order;
269 :
270 143 : second_order.first.resize(nparam, std::vector<OutType>(nparam));
271 869 : for (const auto & i : make_range(nparam))
272 726 : second_order.first[i][i] = val.first[i];
273 : unsigned int ind = 2 * nparam;
274 869 : for (const auto & i : make_range(nparam))
275 2277 : for (const auto & j : make_range(i + 1, nparam))
276 : {
277 1551 : second_order.first[i][j] = val.first[ind++];
278 1551 : second_order.first[j][i] = second_order.first[i][j];
279 : }
280 :
281 143 : if (nlevels > 0)
282 : {
283 66 : second_order.second.resize(nlevels, second_order.first);
284 :
285 275 : for (const auto & l : make_range(nlevels))
286 : {
287 1375 : for (const auto & i : make_range(nparam))
288 1166 : second_order.second[l][i][i] = val.second[l][i];
289 : ind = 2 * nparam;
290 1375 : for (const auto & i : make_range(nparam))
291 3905 : for (const auto & j : make_range(i + 1, nparam))
292 : {
293 2739 : second_order.second[l][i][j] = val.second[l][ind++];
294 2739 : second_order.second[l][j][i] = second_order.second[l][i][j];
295 : }
296 : }
297 : }
298 286 : json["SECOND_ORDER"] = second_order;
299 : }
300 : }
301 :
302 : template void SobolReporter::declareValueHelper<Real>(const ReporterName & r_name);
303 : template class SobolReporterContext<std::vector<Real>, Real>;
304 : template void SobolReporter::declareValueHelper<std::vector<Real>>(const ReporterName & r_name);
305 : template class SobolReporterContext<std::vector<std::vector<Real>>, std::vector<Real>>;
|