LCOV - code coverage report
Current view: top level - src/trainers - PolynomialChaosTrainer.C (source / functions) Hit Total Coverage
Test: idaholab/moose stochastic_tools: f45d79 Lines: 102 106 96.2 %
Date: 2025-07-25 05:00:46 Functions: 5 5 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 "PolynomialChaosTrainer.h"
      11             : #include "Sampler.h"
      12             : #include "CartesianProduct.h"
      13             : 
      14             : registerMooseObject("StochasticToolsApp", PolynomialChaosTrainer);
      15             : 
      16             : InputParameters
      17        1020 : PolynomialChaosTrainer::validParams()
      18             : {
      19        1020 :   InputParameters params = SurrogateTrainer::validParams();
      20        1020 :   params.addClassDescription("Computes and evaluates polynomial chaos surrogate model.");
      21        2040 :   params.addRequiredParam<unsigned int>("order", "Maximum polynomial order.");
      22        2040 :   params.addRequiredParam<std::vector<DistributionName>>(
      23             :       "distributions", "Names of the distributions samples were taken from.");
      24        2040 :   MooseEnum rtype("integration ols auto", "auto");
      25        2040 :   params.addParam<MooseEnum>(
      26             :       "regression_type",
      27             :       rtype,
      28             :       "The type of regression to perform for finding polynomial coefficents.");
      29        2040 :   params.addParam<Real>("penalty", 0.0, "Ridge regularization penalty factor for OLS regression.");
      30             : 
      31        1020 :   params.suppressParameter<MooseEnum>("response_type");
      32        1020 :   return params;
      33        1020 : }
      34             : 
      35         510 : PolynomialChaosTrainer::PolynomialChaosTrainer(const InputParameters & parameters)
      36             :   : SurrogateTrainer(parameters),
      37         510 :     _predictor_row(getPredictorData()),
      38        1530 :     _order(declareModelData<unsigned int>("_order", getParam<unsigned int>("order"))),
      39        1020 :     _ndim(declareModelData<unsigned int>("_ndim", _sampler.getNumberOfCols())),
      40        1020 :     _tuple(declareModelData<std::vector<std::vector<unsigned int>>>(
      41        1020 :         "_tuple", StochasticTools::MultiDimPolynomialGenerator::generateTuple(_ndim, _order))),
      42        1020 :     _ncoeff(declareModelData<std::size_t>("_ncoeff", _tuple.size())),
      43        1020 :     _coeff(declareModelData<std::vector<Real>>("_coeff")),
      44        1020 :     _poly(declareModelData<std::vector<std::unique_ptr<const PolynomialQuadrature::Polynomial>>>(
      45             :         "_poly")),
      46        1020 :     _ridge_penalty(getParam<Real>("penalty")),
      47        1020 :     _quad_sampler(dynamic_cast<QuadratureSampler *>(&_sampler))
      48             : {
      49             :   // Check if number of distributions is correct
      50         510 :   if (_ndim != _sampler.getNumberOfCols())
      51           0 :     paramError("distributions",
      52             :                "Sampler number of columns does not match number of inputted distributions.");
      53             : 
      54        1020 :   auto rtype_enum = getParam<MooseEnum>("regression_type");
      55         510 :   if (rtype_enum == "auto")
      56         430 :     _rtype = _quad_sampler ? 0 : 1;
      57             :   else
      58          80 :     _rtype = rtype_enum == "integration" ? 0 : 1;
      59             : 
      60         510 :   if (_rtype == 0 && _quad_sampler &&
      61         350 :       (!_pvals.empty() || _pcols.size() != _sampler.getNumberOfCols()))
      62           0 :     paramError("sampler",
      63             :                "QuadratureSampler must use all Sampler columns for training, and cannot be"
      64             :                " used with other Reporters - otherwise, quadrature integration does not work.");
      65         510 :   if (_rtype == 0 && _ridge_penalty != 0.0)
      66           0 :     paramError("penalty",
      67             :                "Ridge regularization penalty is only relevant if 'regression_type = ols'.");
      68             : 
      69             :   // Make polynomials
      70        2744 :   for (const auto & nm : getParam<std::vector<DistributionName>>("distributions"))
      71        3448 :     _poly.push_back(PolynomialQuadrature::makePolynomial(&getDistributionByName(nm)));
      72             : 
      73             :   // Create calculators for standardization
      74         510 :   if (_rtype == 1)
      75             :   {
      76          80 :     _calculators.resize(_ncoeff * 3);
      77        1056 :     for (const auto & term : make_range(_ncoeff))
      78             :     {
      79        1952 :       _calculators[3 * term] = StochasticTools::makeCalculator(MooseEnumItem("mean"), *this);
      80        1952 :       _calculators[3 * term + 1] = StochasticTools::makeCalculator(MooseEnumItem("stddev"), *this);
      81        2928 :       _calculators[3 * term + 2] = StochasticTools::makeCalculator(MooseEnumItem("sum"), *this);
      82             :     }
      83             :   }
      84         510 : }
      85             : 
      86             : void
      87         606 : PolynomialChaosTrainer::preTrain()
      88             : {
      89         606 :   _coeff.assign(_ncoeff, 0.0);
      90             : 
      91         606 :   if (_rtype == 1)
      92             :   {
      93         176 :     if (getCurrentSampleSize() < _ncoeff)
      94           0 :       paramError("order",
      95             :                  "Number of data points (",
      96             :                  getCurrentSampleSize(),
      97             :                  ") must be greater than the number of terms in the polynomial (",
      98             :                  _ncoeff,
      99             :                  ").");
     100         176 :     _matrix.resize(_ncoeff, _ncoeff);
     101         176 :     _rhs.resize(_ncoeff);
     102        3392 :     for (auto & calc : _calculators)
     103             :       calc->initializeCalculator();
     104         176 :     _r_sum = 0.0;
     105             :   }
     106         606 : }
     107             : 
     108             : void
     109      180980 : PolynomialChaosTrainer::train()
     110             : {
     111             :   // Evaluate polynomials to avoid duplication
     112      180980 :   DenseMatrix<Real> poly_val(_ndim, _order);
     113     1231260 :   for (unsigned int d = 0; d < _ndim; ++d)
     114     5321880 :     for (unsigned int i = 0; i < _order; ++i)
     115     4271600 :       poly_val(d, i) = _poly[d]->compute(i, _predictor_row[d], /*normalize=*/_rtype == 0);
     116             : 
     117             :   // Evaluate multi-dimensional polynomials
     118      180980 :   std::vector<Real> basis(_ncoeff, 1.0);
     119    17013700 :   for (const auto i : make_range(_ncoeff))
     120   112225920 :     for (const auto d : make_range(_ndim))
     121    95393200 :       basis[i] *= poly_val(d, _tuple[i][d]);
     122             : 
     123             :   // For integration
     124      180980 :   if (_rtype == 0)
     125             :   {
     126      176580 :     const Real fact = (*_rval) * (_quad_sampler ? _quad_sampler->getQuadratureWeight(_row) : 1.0);
     127    16948900 :     for (const auto i : make_range(_ncoeff))
     128    16772320 :       _coeff[i] += fact * basis[i];
     129             :   }
     130             :   // For least-squares
     131             :   else
     132             :   {
     133             :     // Loop over coefficients
     134       64800 :     for (const auto i : make_range(_ncoeff))
     135             :     {
     136             :       // Matrix is symmetric, so we'll add the upper diagonal later
     137      540800 :       for (const auto j : make_range(i + 1))
     138      480400 :         _matrix(i, j) += basis[i] * basis[j];
     139       60400 :       _rhs(i) += basis[i] * (*_rval);
     140             : 
     141      241600 :       for (unsigned int c = i * 3; c < (i + 1) * 3; ++c)
     142             :         _calculators[c]->updateCalculator(basis[i]);
     143             :     }
     144        4400 :     _r_sum += (*_rval);
     145             : 
     146        4400 :     if (_ridge_penalty != 0.0)
     147       32000 :       for (const auto i : make_range(_ncoeff))
     148       30000 :         _matrix(i, i) += _ridge_penalty;
     149             :   }
     150      180980 : }
     151             : 
     152             : void
     153         606 : PolynomialChaosTrainer::postTrain()
     154             : {
     155         606 :   if (_rtype == 0)
     156             :   {
     157         430 :     gatherSum(_coeff);
     158         430 :     if (!_quad_sampler)
     159       46080 :       for (std::size_t i = 0; i < _ncoeff; ++i)
     160       46000 :         _coeff[i] /= getCurrentSampleSize();
     161             :   }
     162             :   else
     163             :   {
     164             :     gatherSum(_matrix.get_values());
     165             :     gatherSum(_rhs.get_values());
     166        3392 :     for (auto & calc : _calculators)
     167        3216 :       calc->finalizeCalculator(true);
     168         176 :     gatherSum(_r_sum);
     169             : 
     170         176 :     std::vector<Real> mu(_ncoeff);
     171         176 :     std::vector<Real> sig(_ncoeff);
     172         176 :     std::vector<Real> sum_pf(_ncoeff);
     173        1248 :     for (const auto i : make_range(_ncoeff))
     174             :     {
     175        1072 :       mu[i] = i > 0 ? _calculators[3 * i]->getValue() : 0.0;
     176        1072 :       sig[i] = i > 0 ? _calculators[3 * i + 1]->getValue() : 1.0;
     177        1072 :       sum_pf[i] = _calculators[3 * i + 2]->getValue();
     178             :     }
     179             : 
     180         176 :     const Real n = getCurrentSampleSize();
     181        1248 :     for (const auto i : make_range(_ncoeff))
     182             :     {
     183        8864 :       for (const auto j : make_range(i + 1))
     184             :       {
     185        7792 :         _matrix(i, j) -= (mu[j] * sum_pf[i] + mu[i] * sum_pf[j]);
     186        7792 :         _matrix(i, j) += n * mu[i] * mu[j];
     187        7792 :         _matrix(i, j) /= (sig[i] * sig[j]);
     188        7792 :         _matrix(j, i) = _matrix(i, j);
     189             :       }
     190        1072 :       _rhs(i) = (_rhs(i) - mu[i] * _r_sum) / sig[i];
     191             :     }
     192             : 
     193         176 :     DenseVector<Real> sol;
     194         176 :     _matrix.lu_solve(_rhs, sol);
     195         176 :     _coeff = sol.get_values();
     196             : 
     197        1072 :     for (unsigned int i = 1; i < _ncoeff; ++i)
     198             :     {
     199         896 :       _coeff[i] /= sig[i];
     200         896 :       _coeff[0] -= _coeff[i] * mu[i];
     201             :     }
     202             :   }
     203         606 : }

Generated by: LCOV version 1.14