LCOV - code coverage report
Current view: top level - src/samplers - MorrisSampler.C (source / functions) Hit Total Coverage
Test: idaholab/moose stochastic_tools: f45d79 Lines: 61 62 98.4 %
Date: 2025-07-25 05:00:46 Functions: 6 6 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 "MorrisSampler.h"
      11             : #include "Distribution.h"
      12             : 
      13             : registerMooseObject("StochasticToolsApp", MorrisSampler);
      14             : 
      15             : InputParameters
      16         382 : MorrisSampler::validParams()
      17             : {
      18         382 :   InputParameters params = Sampler::validParams();
      19         382 :   params.addClassDescription("Morris variance-based sensitivity analysis Sampler.");
      20         764 :   params.addRequiredParam<std::vector<DistributionName>>(
      21             :       "distributions",
      22             :       "The distribution names to be sampled, the number of distributions provided defines the "
      23             :       "number of columns per matrix.");
      24         764 :   params.addRequiredRangeCheckedParam<dof_id_type>(
      25             :       "trajectories",
      26             :       "trajectories > 0",
      27             :       "Number of unique trajectories to perform. The higher number of these usually means a more "
      28             :       "accurate sensitivity evaluation, but it is proportional to the number of required model "
      29             :       "evaluations: 'trajectoris' x (number of 'distributions' + 1).");
      30        1146 :   params.addRangeCheckedParam<unsigned int>(
      31             :       "levels",
      32         764 :       4,
      33             :       "levels % 2 = 0 & levels > 0",
      34             :       "The number of levels in the sampling. This determines the discretization of the input "
      35             :       "space, more levels means finer discretization and more possible model perturbations.");
      36         382 :   return params;
      37           0 : }
      38             : 
      39         222 : MorrisSampler::MorrisSampler(const InputParameters & parameters)
      40             :   : Sampler(parameters),
      41         222 :     _num_trajectories(getParam<dof_id_type>("trajectories")),
      42         666 :     _num_levels(getParam<unsigned int>("levels"))
      43             : 
      44             : {
      45        1374 :   for (const auto & name : getParam<std::vector<DistributionName>>("distributions"))
      46         930 :     _distributions.push_back(&getDistributionByName(name));
      47             : 
      48         222 :   setNumberOfCols(_distributions.size());
      49         222 :   setNumberOfRows(_num_trajectories * (_distributions.size() + 1));
      50         222 : }
      51             : 
      52             : void
      53         364 : MorrisSampler::sampleSetUp(const Sampler::SampleMode /*mode*/)
      54             : {
      55         364 :   const dof_id_type nc = getNumberOfCols();
      56         364 :   _b = RealEigenMatrix::Ones(nc + 1, nc).triangularView<Eigen::StrictlyLower>();
      57         364 :   _pstar.resize(nc, nc);
      58         364 :   _j.setOnes(nc + 1, nc);
      59         364 :   _dstar.resize(nc, nc);
      60         364 :   _xstar.resize(nc + 1, nc);
      61         364 :   _bstar.resize(nc + 1, nc);
      62         364 : }
      63             : 
      64             : Real
      65     1756800 : MorrisSampler::computeSample(dof_id_type row_index, dof_id_type col_index)
      66             : {
      67     1756800 :   const dof_id_type traj_ind = row_index % (getNumberOfCols() + 1);
      68     1756800 :   if (traj_ind == 0 && col_index == 0)
      69       42000 :     updateBstar();
      70     1756800 :   return _distributions[col_index]->quantile(_bstar(traj_ind, col_index));
      71             : }
      72             : 
      73             : void
      74       42000 : MorrisSampler::updateBstar()
      75             : {
      76       42000 :   const dof_id_type nc = getNumberOfCols(); // convenience
      77       42000 :   _pstar.setZero();
      78             :   // Which parameter to perturb
      79       42000 :   std::vector<dof_id_type> pchoice(nc);
      80             :   std::iota(pchoice.begin(), pchoice.end(), 0);
      81      293280 :   for (dof_id_type c = 0; c < nc; ++c)
      82             :   {
      83      251280 :     const unsigned int ind = nc > 1 ? getRandl(0, 0, pchoice.size()) : 0;
      84      251280 :     _pstar(pchoice[ind], c) = 1.0;
      85             :     pchoice.erase(pchoice.begin() + ind);
      86             :   }
      87             : 
      88       42000 :   _dstar.setZero();
      89             :   // Direction of perturbation
      90      293280 :   for (dof_id_type c = 0; c < nc; ++c)
      91      379900 :     _dstar(c, c) = getRand() < 0.5 ? -1.0 : 1.0;
      92             : 
      93             :   // Initial value
      94      293280 :   for (dof_id_type c = 0; c < nc; ++c)
      95             :   {
      96      251280 :     const auto lind = getRandl(0, 0, _num_levels / 2);
      97      251280 :     _xstar.col(c).setConstant((Real)lind * 1.0 / ((Real)_num_levels - 1));
      98             :   }
      99             : 
     100             :   _bstar =
     101       42000 :       _xstar + _num_levels / 4.0 / (_num_levels - 1) * ((2.0 * _b * _pstar - _j) * _dstar + _j);
     102             : 
     103             :   // This matrix represent _n_cols * (_n_cols + 1) samples, but so far we have only
     104             :   // advanced the generator 3 * _n_cols times. For the generator state restore
     105             :   // to work properly, we need to finish advancing the generator
     106       42000 :   if (nc > 2)
     107       42000 :     advanceGenerator(0, nc * (nc - 2));
     108       42000 : }
     109             : 
     110             : LocalRankConfig
     111         444 : MorrisSampler::constructRankConfig(bool batch_mode) const
     112             : {
     113         444 :   std::vector<LocalRankConfig> all_rc(processor_id() + 1);
     114        1332 :   for (processor_id_type r = 0; r <= processor_id(); ++r)
     115         888 :     all_rc[r] = rankConfig(
     116         888 :         r, n_processors(), _num_trajectories, _min_procs_per_row, _max_procs_per_row, batch_mode);
     117             :   LocalRankConfig & rc = all_rc.back();
     118             : 
     119         444 :   rc.num_local_sims *= _distributions.size() + 1;
     120             :   bool found_first = false;
     121        1332 :   for (auto it = all_rc.rbegin(); it != all_rc.rend(); ++it)
     122         888 :     if (it->is_first_local_rank)
     123             :     {
     124         776 :       if (found_first)
     125         332 :         rc.first_local_sim_index += it->num_local_sims * _distributions.size();
     126             :       else
     127             :         found_first = true;
     128             :     }
     129             : 
     130         444 :   if (!batch_mode)
     131             :   {
     132         222 :     rc.num_local_apps = rc.num_local_sims;
     133         222 :     rc.first_local_app_index = rc.first_local_sim_index;
     134             :   }
     135             : 
     136         444 :   return rc;
     137             : }

Generated by: LCOV version 1.14