LCOV - code coverage report
Current view: top level - src/samplers - MorrisSampler.C (source / functions) Hit Total Coverage
Test: idaholab/moose stochastic_tools: #32971 (54bef8) with base c6cf66 Lines: 60 61 98.4 %
Date: 2026-05-29 20:40:35 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 "MorrisSampler.h"
      11             : #include "Distribution.h"
      12             : 
      13             : registerMooseObject("StochasticToolsApp", MorrisSampler);
      14             : 
      15             : InputParameters
      16         163 : MorrisSampler::validParams()
      17             : {
      18         163 :   InputParameters params = Sampler::validParams();
      19         163 :   params.addClassDescription("Morris variance-based sensitivity analysis Sampler.");
      20         326 :   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         326 :   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         489 :   params.addRangeCheckedParam<unsigned int>(
      31             :       "levels",
      32         326 :       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         163 :   return params;
      37           0 : }
      38             : 
      39          88 : MorrisSampler::MorrisSampler(const InputParameters & parameters)
      40             :   : Sampler(parameters),
      41          88 :     _num_trajectories(getParam<dof_id_type>("trajectories")),
      42         264 :     _num_levels(getParam<unsigned int>("levels"))
      43             : 
      44             : {
      45         536 :   for (const auto & name : getParam<std::vector<DistributionName>>("distributions"))
      46         360 :     _distributions.push_back(&getDistributionByName(name));
      47             :   const dof_id_type nc = _distributions.size();
      48             : 
      49          88 :   setNumberOfCols(nc);
      50          88 :   setNumberOfRows(_num_trajectories * (nc + 1));
      51             : 
      52          88 :   _b = RealEigenMatrix::Ones(nc + 1, nc).triangularView<Eigen::StrictlyLower>();
      53          88 :   _pstar.resize(nc, nc);
      54          88 :   _j.setOnes(nc + 1, nc);
      55          88 :   _dstar.resize(nc, nc);
      56          88 :   _xstar.resize(nc + 1, nc);
      57          88 :   _bstar.resize(nc + 1, nc);
      58          88 : }
      59             : 
      60             : Real
      61      878400 : MorrisSampler::computeSample(dof_id_type row_index, dof_id_type col_index)
      62             : {
      63      878400 :   const dof_id_type traj = row_index / (getNumberOfCols() + 1);
      64      878400 :   const dof_id_type traj_ind = row_index % (getNumberOfCols() + 1);
      65      878400 :   if (traj != _curr_trajectory)
      66             :   {
      67       20970 :     _curr_trajectory = traj;
      68       20970 :     updateBstar();
      69             :   }
      70      878400 :   return _distributions[col_index]->quantile(_bstar(traj_ind, col_index));
      71             : }
      72             : 
      73             : void
      74       20970 : MorrisSampler::updateBstar()
      75             : {
      76             :   mooseAssert(_curr_trajectory < _num_trajectories,
      77             :               "Current trajectory index is greater than the prescribed number of trajectories.");
      78             : 
      79       20970 :   const dof_id_type nc = getNumberOfCols(); // convenience
      80       20970 :   dof_id_type rn_ind = _curr_trajectory * nc * (nc + 1);
      81             : 
      82       20970 :   _pstar.setZero();
      83             :   // Which parameter to perturb
      84       20970 :   std::vector<dof_id_type> pchoice(nc);
      85             :   std::iota(pchoice.begin(), pchoice.end(), 0);
      86      146520 :   for (dof_id_type c = 0; c < nc; ++c)
      87             :   {
      88      125550 :     const unsigned int ind = nc > 1 ? getRandl(rn_ind++, 0, pchoice.size()) : 0;
      89      125550 :     _pstar(pchoice[ind], c) = 1.0;
      90             :     pchoice.erase(pchoice.begin() + ind);
      91             :   }
      92             : 
      93       20970 :   _dstar.setZero();
      94             :   // Direction of perturbation
      95      146520 :   for (dof_id_type c = 0; c < nc; ++c)
      96      189800 :     _dstar(c, c) = getRand(rn_ind++) < 0.5 ? -1.0 : 1.0;
      97             : 
      98             :   // Initial value
      99      146520 :   for (dof_id_type c = 0; c < nc; ++c)
     100             :   {
     101      125550 :     const auto lind = getRandl(rn_ind++, 0, _num_levels / 2);
     102      125550 :     _xstar.col(c).setConstant((Real)lind * 1.0 / ((Real)_num_levels - 1));
     103             :   }
     104             : 
     105             :   _bstar =
     106       20970 :       _xstar + _num_levels / 4.0 / (_num_levels - 1) * ((2.0 * _b * _pstar - _j) * _dstar + _j);
     107       20970 : }
     108             : 
     109             : LocalRankConfig
     110         176 : MorrisSampler::constructRankConfig(bool batch_mode) const
     111             : {
     112         176 :   std::vector<LocalRankConfig> all_rc(processor_id() + 1);
     113         528 :   for (processor_id_type r = 0; r <= processor_id(); ++r)
     114         352 :     all_rc[r] = rankConfig(
     115         352 :         r, n_processors(), _num_trajectories, _min_procs_per_row, _max_procs_per_row, batch_mode);
     116             :   LocalRankConfig & rc = all_rc.back();
     117             : 
     118         176 :   rc.num_local_sims *= _distributions.size() + 1;
     119             :   bool found_first = false;
     120         528 :   for (auto it = all_rc.rbegin(); it != all_rc.rend(); ++it)
     121         352 :     if (it->is_first_local_rank)
     122             :     {
     123         304 :       if (found_first)
     124         128 :         rc.first_local_sim_index += it->num_local_sims * _distributions.size();
     125             :       else
     126             :         found_first = true;
     127             :     }
     128             : 
     129         176 :   if (!batch_mode)
     130             :   {
     131          88 :     rc.num_local_apps = rc.num_local_sims;
     132          88 :     rc.first_local_app_index = rc.first_local_sim_index;
     133             :   }
     134             : 
     135         176 :   return rc;
     136         176 : }

Generated by: LCOV version 1.14