LCOV - code coverage report
Current view: top level - src/reporters - SobolReporter.C (source / functions) Hit Total Coverage
Test: idaholab/moose stochastic_tools: f45d79 Lines: 123 132 93.2 %
Date: 2025-07-25 05:00:46 Functions: 16 16 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 "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         128 : SobolReporter::validParams()
      28             : {
      29         128 :   InputParameters params = GeneralReporter::validParams();
      30         128 :   params.addClassDescription("Compute SOBOL statistics values of a given VectorPostprocessor or "
      31             :                              "Reporter objects and vectors.");
      32         256 :   params.addParam<SamplerName>("sampler", "SobolSampler object.");
      33             : 
      34         256 :   params.addParam<std::vector<VectorPostprocessorName>>(
      35             :       "vectorpostprocessors",
      36             :       "List of VectorPostprocessor(s) to utilized for statistic computations.");
      37             : 
      38         256 :   params.addParam<std::vector<ReporterName>>(
      39             :       "reporters", {}, "List of Reporter values to utilized for statistic computations.");
      40             : 
      41         256 :   params.addParam<std::vector<Real>>(
      42             :       "ci_levels",
      43         128 :       std::vector<Real>(),
      44             :       "A vector of confidence levels to consider, values must be in (0, 1).");
      45         256 :   params.addParam<unsigned int>(
      46             :       "ci_replicates",
      47         256 :       10000,
      48             :       "The number of replicates to use when computing confidence level intervals.");
      49         256 :   params.addParam<unsigned int>("ci_seed",
      50         256 :                                 1,
      51             :                                 "The random number generator seed used for creating replicates "
      52             :                                 "while computing confidence level intervals.");
      53         128 :   return params;
      54           0 : }
      55             : 
      56          64 : SobolReporter::SobolReporter(const InputParameters & parameters)
      57             :   : GeneralReporter(parameters),
      58          64 :     _sobol_sampler(getSampler<SobolSampler>("sampler")),
      59         128 :     _ci_levels(getParam<std::vector<Real>>("ci_levels")),
      60         128 :     _ci_replicates(getParam<unsigned int>("ci_replicates")),
      61         128 :     _ci_seed(getParam<unsigned int>("ci_seed")),
      62          64 :     _initialized(false)
      63             : {
      64             :   // CI levels error checking
      65          64 :   if (!_ci_levels.empty())
      66             :   {
      67          64 :     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          64 :     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         256 :   if ((!isParamValid("reporters") && !isParamValid("vectorpostprocessors")) ||
      74         160 :       (getParam<std::vector<ReporterName>>("reporters").empty() &&
      75         128 :        getParam<std::vector<VectorPostprocessorName>>("vectorpostprocessors").empty()))
      76           0 :     mooseError(
      77             :         "The 'vectorpostprocessors' and/or 'reporters' parameters must be defined and non-empty.");
      78          64 : }
      79             : 
      80             : void
      81          64 : SobolReporter::initialize()
      82             : {
      83          64 :   if (_initialized)
      84             :     return;
      85             : 
      86             :   // Stats for Reporters
      87         128 :   if (isParamValid("reporters"))
      88             :   {
      89             :     std::vector<std::string> unsupported_types;
      90         128 :     const auto & reporter_names = getParam<std::vector<ReporterName>>("reporters");
      91         144 :     for (const auto & r_name : reporter_names)
      92             :     {
      93          80 :       if (hasReporterValueByName<std::vector<Real>>(r_name))
      94          64 :         declareValueHelper<Real>(r_name);
      95          16 :       else if (hasReporterValueByName<std::vector<std::vector<Real>>>(r_name))
      96          16 :         declareValueHelper<std::vector<Real>>(r_name);
      97             :       else
      98           0 :         unsupported_types.emplace_back(r_name.getCombinedName());
      99             :     }
     100             : 
     101          64 :     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          64 :   }
     107             : 
     108             :   // Stats for VPP
     109         128 :   if (isParamValid("vectorpostprocessors"))
     110             :   {
     111          64 :     const auto & vpp_names = getParam<std::vector<VectorPostprocessorName>>("vectorpostprocessors");
     112          64 :     for (const auto & vpp_name : vpp_names)
     113             :     {
     114             :       const VectorPostprocessor & vpp_object =
     115          32 :           _fe_problem.getVectorPostprocessorObjectByName(vpp_name);
     116          32 :       const std::set<std::string> & vpp_vectors = vpp_object.getVectorNames();
     117          64 :       for (const auto & vec_name : vpp_vectors)
     118             :       {
     119          32 :         ReporterName r_name(vpp_name, vec_name);
     120          32 :         declareValueHelper<Real>(r_name);
     121             :       }
     122             :     }
     123             :   }
     124             : 
     125          64 :   _initialized = true;
     126             : }
     127             : 
     128             : void
     129          40 : SobolReporter::store(nlohmann::json & json) const
     130             : {
     131          40 :   Reporter::store(json);
     132          40 :   if (!_ci_levels.empty())
     133          40 :     json["confidence_intervals"] = {{"method", "percentile"},
     134             :                                     {"levels", _ci_levels},
     135             :                                     {"replicates", _ci_replicates},
     136         520 :                                     {"seed", _ci_seed}};
     137             : 
     138          80 :   json["num_params"] = _sobol_sampler.getNumberOfCols();
     139          40 :   json["indices"].push_back("FIRST_ORDER");
     140          40 :   json["indices"].push_back("TOTAL");
     141          40 :   if (_sobol_sampler.resample())
     142          60 :     json["indices"].push_back("SECOND_ORDER");
     143         560 : }
     144             : 
     145             : template <typename T>
     146             : void
     147         112 : SobolReporter::declareValueHelper(const ReporterName & r_name)
     148             : {
     149         112 :   const auto & mode = _fe_problem.getReporterData().getReporterMode(r_name);
     150         112 :   const auto & data = getReporterValueByName<std::vector<T>>(r_name);
     151         112 :   const std::string s_name = r_name.getObjectName() + "_" + r_name.getValueName();
     152         112 :   if (!_ci_levels.empty())
     153         336 :     declareValueByName<SobolState<T>, SobolReporterContext<std::vector<T>, T>>(
     154             :         s_name,
     155             :         REPORTER_MODE_ROOT,
     156             :         data,
     157             :         mode,
     158             :         _sobol_sampler,
     159         336 :         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         112 : }
     167             : 
     168             : template <typename InType, typename OutType>
     169         112 : 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         112 :     _data(data),
     178         112 :     _data_mode(mode),
     179         112 :     _sampler(sampler),
     180         224 :     _calc(other, "SOBOL", _sampler.resample())
     181             : {
     182         112 : }
     183             : 
     184             : template <typename InType, typename OutType>
     185         112 : 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         112 :   : SobolReporterContext<InType, OutType>(other, producer, state, data, mode, sampler)
     197             : {
     198         224 :   _ci_calc_ptr =
     199             :       StochasticTools::makeBootstrapCalculator<std::vector<InType>, std::vector<OutType>>(
     200             :           ci_method, other, ci_levels, ci_replicates, ci_seed, _calc);
     201         112 : }
     202             : 
     203             : template <typename InType, typename OutType>
     204             : void
     205         112 : SobolReporterContext<InType, OutType>::finalize()
     206             : {
     207         112 :   auto & val = this->_state.value(); // Convenience
     208         112 :   val.first.clear();
     209         112 :   val.second.clear();
     210             : 
     211         112 :   const bool is_dist = _data_mode == REPORTER_MODE_DISTRIBUTED;
     212         112 :   if (is_dist || this->processor_id() == 0)
     213             :   {
     214             :     const std::size_t ncol =
     215         112 :         _sampler.resample() ? 2 * _sampler.getNumberOfCols() + 2 : _sampler.getNumberOfCols() + 2;
     216         112 :     const std::vector<InType> data_reshape =
     217             :         StochasticTools::reshapeVector(_data, ncol, /*row_major =*/true);
     218             : 
     219         224 :     val.first = _calc.compute(data_reshape, is_dist);
     220         112 :     if (_ci_calc_ptr)
     221         224 :       val.second = _ci_calc_ptr->compute(data_reshape, is_dist);
     222         112 :   }
     223             : 
     224         112 :   ReporterGeneralContext<SobolState<OutType>>::finalize();
     225         112 : }
     226             : 
     227             : template <typename InType, typename OutType>
     228             : void
     229          70 : SobolReporterContext<InType, OutType>::store(nlohmann::json & json) const
     230             : {
     231          70 :   storeSobol(json, this->_state.value(), _sampler.getNumberOfCols());
     232          70 : }
     233             : 
     234             : template <typename InType, typename OutType>
     235             : void
     236         140 : SobolReporterContext<InType, OutType>::storeSobol(nlohmann::json & json,
     237             :                                                   const SobolState<OutType> & val,
     238             :                                                   unsigned int nparam)
     239             : {
     240         140 :   if (val.first.empty())
     241           0 :     return;
     242             : 
     243             :   // Convenience
     244         140 :   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         140 :   first_order.first.assign(val.first.begin(), val.first.begin() + nparam);
     250         140 :   total.first.assign(val.first.begin() + nparam, val.first.begin() + nparam + nparam);
     251         140 :   if (nlevels > 0)
     252             :   {
     253          70 :     first_order.second.resize(nlevels);
     254          70 :     total.second.resize(nlevels);
     255         280 :     for (const auto & l : make_range(nlevels))
     256             :     {
     257         210 :       first_order.second[l].assign(val.second[l].begin(), val.second[l].begin() + nparam);
     258         210 :       total.second[l].assign(val.second[l].begin() + nparam,
     259             :                              val.second[l].begin() + nparam + nparam);
     260             :     }
     261             :   }
     262         140 :   json["FIRST_ORDER"] = first_order;
     263         280 :   json["TOTAL"] = total;
     264             : 
     265         140 :   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         130 :     second_order.first.resize(nparam, std::vector<OutType>(nparam));
     271         790 :     for (const auto & i : make_range(nparam))
     272         660 :       second_order.first[i][i] = val.first[i];
     273             :     unsigned int ind = 2 * nparam;
     274         790 :     for (const auto & i : make_range(nparam))
     275        2070 :       for (const auto & j : make_range(i + 1, nparam))
     276             :       {
     277        1410 :         second_order.first[i][j] = val.first[ind++];
     278        1410 :         second_order.first[j][i] = second_order.first[i][j];
     279             :       }
     280             : 
     281         130 :     if (nlevels > 0)
     282             :     {
     283          60 :       second_order.second.resize(nlevels, second_order.first);
     284             : 
     285         250 :       for (const auto & l : make_range(nlevels))
     286             :       {
     287        1250 :         for (const auto & i : make_range(nparam))
     288        1060 :           second_order.second[l][i][i] = val.second[l][i];
     289             :         ind = 2 * nparam;
     290        1250 :         for (const auto & i : make_range(nparam))
     291        3550 :           for (const auto & j : make_range(i + 1, nparam))
     292             :           {
     293        2490 :             second_order.second[l][i][j] = val.second[l][ind++];
     294        2490 :             second_order.second[l][j][i] = second_order.second[l][i][j];
     295             :           }
     296             :       }
     297             :     }
     298         260 :     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>>;

Generated by: LCOV version 1.14