LCOV - code coverage report
Current view: top level - src/reporters - MorrisReporter.C (source / functions) Hit Total Coverage
Test: idaholab/moose stochastic_tools: f45d79 Lines: 144 158 91.1 %
Date: 2025-07-25 05:00:46 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

          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 "MorrisReporter.h"
      11             : 
      12             : #include "MorrisSampler.h"
      13             : 
      14             : registerMooseObject("StochasticToolsApp", MorrisReporter);
      15             : 
      16             : InputParameters
      17         128 : MorrisReporter::validParams()
      18             : {
      19         128 :   InputParameters params = GeneralReporter::validParams();
      20         128 :   params.addClassDescription("Compute global sensitivities using the Morris method.");
      21             : 
      22         256 :   params.addRequiredParam<SamplerName>("sampler", "Morris sampler used to generate samples.");
      23         256 :   params.addParam<std::vector<VectorPostprocessorName>>(
      24             :       "vectorpostprocessors",
      25             :       "List of VectorPostprocessor(s) to utilized for statistic computations.");
      26         256 :   params.addParam<std::vector<ReporterName>>(
      27             :       "reporters", {}, "List of Reporter values to utilized for statistic computations.");
      28             : 
      29             :   // Confidence Levels
      30         256 :   params.addParam<std::vector<Real>>(
      31             :       "ci_levels",
      32         128 :       std::vector<Real>(),
      33             :       "A vector of confidence levels to consider, values must be in (0, 1).");
      34         256 :   params.addParam<unsigned int>("ci_replicates",
      35         256 :                                 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         256 :   params.addParam<unsigned int>("ci_seed",
      40         256 :                                 1,
      41             :                                 "The random number generator seed used for creating replicates "
      42             :                                 "while computing confidence level intervals.");
      43         128 :   return params;
      44           0 : }
      45             : 
      46          64 : MorrisReporter::MorrisReporter(const InputParameters & parameters)
      47             :   : GeneralReporter(parameters),
      48          64 :     _sampler(getSampler("sampler")),
      49         128 :     _ci_levels(getParam<std::vector<Real>>("ci_levels")),
      50         128 :     _ci_replicates(getParam<unsigned int>("ci_replicates")),
      51         128 :     _ci_seed(getParam<unsigned int>("ci_seed")),
      52          64 :     _initialized(false)
      53             : {
      54          64 :   if (!dynamic_cast<MorrisSampler *>(&_sampler))
      55           0 :     paramError("sampler", "Computing Morris sensitivities requires the use of a Morris sampler.");
      56             : 
      57             :   // CI levels error checking
      58          64 :   if (!_ci_levels.empty())
      59             :   {
      60          64 :     if (*std::min_element(_ci_levels.begin(), _ci_levels.end()) <= 0)
      61           0 :       paramError("ci_levels", "The supplied levels must be greater than zero.");
      62          64 :     else if (*std::max_element(_ci_levels.begin(), _ci_levels.end()) >= 1)
      63           0 :       paramError("ci_levels", "The supplied levels must be less than 1.0");
      64             :   }
      65             : 
      66         256 :   if ((!isParamValid("reporters") && !isParamValid("vectorpostprocessors")) ||
      67         160 :       (getParam<std::vector<ReporterName>>("reporters").empty() &&
      68         128 :        getParam<std::vector<VectorPostprocessorName>>("vectorpostprocessors").empty()))
      69           0 :     mooseError(
      70             :         "The 'vectorpostprocessors' and/or 'reporters' parameters must be defined and non-empty.");
      71          64 : }
      72             : 
      73             : void
      74          64 : MorrisReporter::initialize()
      75             : {
      76          64 :   if (_initialized)
      77             :     return;
      78             : 
      79             :   // Stats for Reporters
      80         128 :   if (isParamValid("reporters"))
      81             :   {
      82             :     std::vector<std::string> unsupported_types;
      83         224 :     for (const auto & r_name : getParam<std::vector<ReporterName>>("reporters"))
      84             :     {
      85          96 :       if (hasReporterValueByName<std::vector<Real>>(r_name))
      86          64 :         declareValueHelper<Real>(r_name);
      87          32 :       else if (hasReporterValueByName<std::vector<std::vector<Real>>>(r_name))
      88          32 :         declareValueHelper<std::vector<Real>>(r_name);
      89             :       else
      90           0 :         unsupported_types.push_back(r_name.getCombinedName());
      91             :     }
      92             : 
      93          64 :     if (!unsupported_types.empty())
      94           0 :       paramError("reporters",
      95             :                  "The following reporter value(s) do not have a type supported by the "
      96             :                  "MorrisReporter:\n",
      97           0 :                  MooseUtils::join(unsupported_types, ", "));
      98          64 :   }
      99             : 
     100             :   // Stats for VPP
     101         128 :   if (isParamValid("vectorpostprocessors"))
     102          32 :     for (const auto & vpp_name :
     103          96 :          getParam<std::vector<VectorPostprocessorName>>("vectorpostprocessors"))
     104          32 :       for (const auto & vec_name :
     105          96 :            _fe_problem.getVectorPostprocessorObjectByName(vpp_name).getVectorNames())
     106          64 :         declareValueHelper<Real>(ReporterName(vpp_name, vec_name));
     107          64 :   _initialized = true;
     108             : }
     109             : 
     110             : void
     111          40 : MorrisReporter::store(nlohmann::json & json) const
     112             : {
     113          40 :   Reporter::store(json);
     114          40 :   if (!_ci_levels.empty())
     115          40 :     json["confidence_intervals"] = {{"method", "percentile"},
     116             :                                     {"levels", _ci_levels},
     117             :                                     {"replicates", _ci_replicates},
     118         520 :                                     {"seed", _ci_seed}};
     119          80 :   json["num_params"] = _sampler.getNumberOfCols();
     120          40 :   json["num_trajectories"] = _sampler.getNumberOfRows() / (_sampler.getNumberOfCols() + 1);
     121          80 :   if (_sampler.parameters().isParamValid("levels"))
     122         120 :     json["levels"] = _sampler.parameters().get<unsigned int>("levels");
     123         560 : }
     124             : 
     125             : template <typename DataType>
     126             : void
     127         128 : MorrisReporter::declareValueHelper(const ReporterName & r_name)
     128             : {
     129         128 :   const auto & data = getReporterValueByName<std::vector<DataType>>(r_name);
     130         128 :   const std::string s_name = r_name.getObjectName() + "_" + r_name.getValueName();
     131         128 :   if (!_ci_levels.empty())
     132         384 :     declareValueByName<MorrisState<DataType>, MorrisReporterContext<DataType>>(
     133             :         s_name,
     134             :         REPORTER_MODE_ROOT,
     135             :         _sampler,
     136             :         data,
     137         384 :         MooseEnum("percentile", "percentile"),
     138             :         _ci_levels,
     139             :         _ci_replicates,
     140             :         _ci_seed);
     141             :   else
     142           0 :     declareValueByName<MorrisState<DataType>, MorrisReporterContext<DataType>>(
     143             :         s_name, REPORTER_MODE_ROOT, _sampler, data);
     144         128 : }
     145             : 
     146             : template <typename DataType>
     147         128 : MorrisReporterContext<DataType>::MorrisReporterContext(const libMesh::ParallelObject & other,
     148             :                                                        const MooseObject & producer,
     149             :                                                        ReporterState<MorrisState<DataType>> & state,
     150             :                                                        Sampler & sampler,
     151             :                                                        const std::vector<DataType> & data)
     152             :   : ReporterGeneralContext<MorrisState<DataType>>(other, producer, state),
     153         128 :     _sampler(sampler),
     154         128 :     _data(data)
     155             : {
     156         128 :   MultiMooseEnum items("mean meanabs stddev", "mean meanabs stddev", true);
     157         256 :   _mu_calc = StochasticTools::makeCalculator<std::vector<DataType>, DataType>(items[0], other);
     158         256 :   _mustar_calc = StochasticTools::makeCalculator<std::vector<DataType>, DataType>(items[1], other);
     159         256 :   _sig_calc = StochasticTools::makeCalculator<std::vector<DataType>, DataType>(items[2], other);
     160             : 
     161             :   // Initialize state
     162         128 :   auto & mu = this->_state.value()["mu"].first;
     163         128 :   mu.resize(_sampler.getNumberOfCols());
     164         128 :   auto & mu_star = this->_state.value()["mu_star"].first;
     165         128 :   mu_star.resize(_sampler.getNumberOfCols());
     166         128 :   auto & sig = this->_state.value()["sigma"].first;
     167         128 :   sig.resize(_sampler.getNumberOfCols());
     168         128 : }
     169             : 
     170             : template <typename DataType>
     171         128 : MorrisReporterContext<DataType>::MorrisReporterContext(const libMesh::ParallelObject & other,
     172             :                                                        const MooseObject & producer,
     173             :                                                        ReporterState<MorrisState<DataType>> & state,
     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         128 :   : MorrisReporterContext<DataType>(other, producer, state, sampler, data)
     181             : {
     182         128 :   _ci_mu_calc = StochasticTools::makeBootstrapCalculator<std::vector<DataType>, DataType>(
     183             :       ci_method, other, ci_levels, ci_replicates, ci_seed, *_mu_calc);
     184         128 :   _ci_mustar_calc = StochasticTools::makeBootstrapCalculator<std::vector<DataType>, DataType>(
     185             :       ci_method, other, ci_levels, ci_replicates, ci_seed, *_mustar_calc);
     186         128 :   _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         128 :   auto & mu = this->_state.value()["mu"].second;
     191         128 :   mu.resize(_sampler.getNumberOfCols());
     192         128 :   auto & mu_star = this->_state.value()["mu_star"].second;
     193         128 :   mu_star.resize(_sampler.getNumberOfCols());
     194         128 :   auto & sig = this->_state.value()["sigma"].second;
     195         128 :   sig.resize(_sampler.getNumberOfCols());
     196         128 : }
     197             : 
     198             : template <typename DataType>
     199             : void
     200         128 : MorrisReporterContext<DataType>::finalize()
     201             : {
     202             :   dof_id_type offset;
     203         128 :   if (_data.size() == _sampler.getNumberOfRows())
     204          56 :     offset = _sampler.getLocalRowBegin();
     205          72 :   else if (_data.size() == _sampler.getNumberOfLocalRows())
     206             :     offset = 0;
     207             :   else
     208           0 :     mooseError("Data size inconsistency. Expected data vector to have ",
     209           0 :                _sampler.getNumberOfLocalRows(),
     210             :                " (local) or ",
     211           0 :                _sampler.getNumberOfRows(),
     212             :                " (global) elements, but actually has ",
     213           0 :                _data.size(),
     214             :                " elements. Are you using the same sampler?");
     215             : 
     216         128 :   const dof_id_type nc = _sampler.getNumberOfCols(); // convenience
     217         128 :   if (_sampler.getNumberOfLocalRows() % (nc + 1) > 0)
     218           0 :     mooseError(
     219             :         "Sampler does not have an appropriate number of rows. Are you using a Morris sampler?");
     220             : 
     221         128 :   std::vector<std::vector<DataType>> elem_effects(
     222         160 :       nc, std::vector<DataType>(_sampler.getNumberOfLocalRows() / (nc + 1)));
     223         128 :   RealEigenMatrix x(nc + 1, nc);
     224         128 :   std::vector<DataType> y(nc + 1);
     225             : 
     226      147688 :   for (dof_id_type p = 0; p < _sampler.getNumberOfLocalRows(); ++p)
     227             :   {
     228      147560 :     dof_id_type traj = p / (nc + 1);
     229      147560 :     dof_id_type tind = p % (nc + 1);
     230      147560 :     const std::vector<Real> row = _sampler.getNextLocalRow();
     231     1032920 :     for (unsigned int k = 0; k < nc; ++k)
     232      885360 :       x(tind, k) = row[k];
     233      147560 :     y[tind] = _data[p + offset];
     234             : 
     235      147560 :     if (tind == nc)
     236             :     {
     237       21080 :       const std::vector<DataType> ee = computeElementaryEffects(x, y);
     238      147560 :       for (unsigned int k = 0; k < nc; ++k)
     239      126480 :         elem_effects[k][traj] = ee[k];
     240         200 :     }
     241             :   }
     242             : 
     243         128 :   auto & mu = this->_state.value()["mu"];
     244         128 :   auto & mustar = this->_state.value()["mu_star"];
     245         128 :   auto & sig = this->_state.value()["sigma"];
     246         896 :   for (unsigned int k = 0; k < nc; ++k)
     247             :   {
     248         960 :     mu.first[k] = _mu_calc->compute(elem_effects[k], true);
     249         960 :     mustar.first[k] = _mustar_calc->compute(elem_effects[k], true);
     250         960 :     sig.first[k] = _sig_calc->compute(elem_effects[k], true);
     251             : 
     252         768 :     if (_ci_mu_calc)
     253        1536 :       mu.second[k] = _ci_mu_calc->compute(elem_effects[k], true);
     254         768 :     if (_ci_mustar_calc)
     255        1536 :       mustar.second[k] = _ci_mustar_calc->compute(elem_effects[k], true);
     256         768 :     if (_ci_sig_calc)
     257        1536 :       sig.second[k] = _ci_sig_calc->compute(elem_effects[k], true);
     258             :   }
     259         128 : }
     260             : 
     261             : template <>
     262             : std::vector<Real>
     263       20880 : MorrisReporterContext<Real>::computeElementaryEffects(const RealEigenMatrix & x,
     264             :                                                       const std::vector<Real> & y) const
     265             : {
     266       20880 :   const auto k = y.size() - 1;
     267       20880 :   const RealEigenMatrix dx = x.bottomRows(k) - x.topRows(k);
     268       20880 :   RealEigenVector dy(k);
     269      146160 :   for (unsigned int j = 0; j < k; ++j)
     270      125280 :     dy(j) = y[j + 1] - y[j];
     271             : 
     272       41760 :   const RealEigenVector u = dx.fullPivLu().solve(dy);
     273       20880 :   return std::vector<Real>(u.data(), u.data() + u.size());
     274             : }
     275             : 
     276             : template <>
     277             : std::vector<std::vector<Real>>
     278         200 : MorrisReporterContext<std::vector<Real>>::computeElementaryEffects(
     279             :     const RealEigenMatrix & x, const std::vector<std::vector<Real>> & y) const
     280             : {
     281         200 :   const auto k = y.size() - 1;
     282         200 :   const RealEigenMatrix dx = x.bottomRows(k) - x.topRows(k);
     283             :   const auto solver = dx.fullPivLu();
     284         200 :   RealEigenVector dy(k);
     285         200 :   std::vector<std::vector<Real>> ee(k, std::vector<Real>(y[0].size()));
     286         600 :   for (unsigned int i = 0; i < y[0].size(); ++i)
     287             :   {
     288        2800 :     for (unsigned int j = 0; j < k; ++j)
     289        2400 :       dy(j) = y[j + 1][i] - y[j][i];
     290         400 :     const RealEigenVector u = solver.solve(dy);
     291        2800 :     for (unsigned int j = 0; j < k; ++j)
     292        2400 :       ee[j][i] = u(j);
     293             :   }
     294         200 :   return ee;
     295         200 : }
     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);
     300             : template class MorrisReporterContext<std::vector<Real>>;

Generated by: LCOV version 1.14