LCOV - code coverage report
Current view: top level - src/samplers - ParallelSubsetSimulation.C (source / functions) Hit Total Coverage
Test: idaholab/moose stochastic_tools: #33187 (5aa0b2) with base d7c4bd Lines: 90 96 93.8 %
Date: 2026-06-30 12:24:43 Functions: 7 7 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 "ParallelSubsetSimulation.h"
      11             : #include "AdaptiveMonteCarloUtils.h"
      12             : #include "Normal.h"
      13             : #include "Uniform.h"
      14             : 
      15             : registerMooseObject("StochasticToolsApp", ParallelSubsetSimulation);
      16             : 
      17             : InputParameters
      18          30 : ParallelSubsetSimulation::validParams()
      19             : {
      20          30 :   InputParameters params = Sampler::validParams();
      21          30 :   params.addClassDescription("Parallel Subset Simulation sampler.");
      22          60 :   params.addRequiredParam<std::vector<DistributionName>>(
      23             :       "distributions",
      24             :       "The distribution names to be sampled, the number of distributions provided defines the "
      25             :       "number of columns per matrix.");
      26          60 :   params.addRequiredParam<ReporterName>("output_reporter",
      27             :                                         "Reporter with results of samples created by the SubApp.");
      28          60 :   params.addRequiredParam<ReporterName>("inputs_reporter", "Reporter with input parameters.");
      29          90 :   params.addRangeCheckedParam<Real>("subset_probability",
      30          60 :                                     0.1,
      31             :                                     "subset_probability>0 & subset_probability<=1",
      32             :                                     "Conditional probability of each subset");
      33          60 :   params.addRequiredParam<unsigned int>("num_samplessub", "Number of samples per subset");
      34          60 :   params.addRequiredParam<unsigned int>("num_subsets", "Number of desired subsets");
      35          60 :   params.addParam<unsigned int>("num_parallel_chains",
      36             :                                 "Number of Markov chains to run in parallel, default is based on "
      37             :                                 "the number of processors used.");
      38          60 :   params.addParam<bool>("use_absolute_value", false, "Use absolute value of the sub app output");
      39          60 :   params.addParam<unsigned int>(
      40             :       "num_random_seeds",
      41          60 :       100000,
      42             :       "Initialize a certain number of random seeds. Change from the default only if you have to.");
      43          30 :   return params;
      44           0 : }
      45             : 
      46          16 : ParallelSubsetSimulation::ParallelSubsetSimulation(const InputParameters & parameters)
      47             :   : Sampler(parameters),
      48          16 :     _num_samplessub(getParam<unsigned int>("num_samplessub")),
      49          32 :     _num_subsets(getParam<unsigned int>("num_subsets")),
      50          32 :     _use_absolute_value(getParam<bool>("use_absolute_value")),
      51          32 :     _subset_probability(getParam<Real>("subset_probability")),
      52          32 :     _num_random_seeds(getParam<unsigned int>("num_random_seeds")),
      53          16 :     _outputs(getReporterValue<std::vector<Real>>("output_reporter")),
      54          16 :     _inputs(getReporterValue<std::vector<std::vector<Real>>>("inputs_reporter")),
      55          32 :     _step(getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")->timeStep()),
      56          16 :     _count_max(std::floor(1 / _subset_probability)),
      57          16 :     _subset(0),
      58          16 :     _is_sampling_completed(false)
      59             : {
      60             :   // Fixing the number of rows to the number of processors
      61          16 :   const dof_id_type nchains = isParamValid("num_parallel_chains")
      62          48 :                                   ? getParam<unsigned int>("num_parallel_chains")
      63           0 :                                   : n_processors() / _min_procs_per_row;
      64          16 :   setNumberOfRows(nchains);
      65          16 :   if ((_num_samplessub / nchains) % _count_max > 0)
      66           0 :     mooseError("Number of model evaluations per chain per subset (",
      67           0 :                _num_samplessub / nchains,
      68             :                ") should be a multiple of requested chain length (",
      69           0 :                _count_max,
      70             :                ").");
      71             : 
      72             :   // Filling the `distributions` vector with the user-provided distributions.
      73          64 :   for (const DistributionName & name : getParam<std::vector<DistributionName>>("distributions"))
      74          32 :     _distributions.push_back(&getDistributionByName(name));
      75             : 
      76             :   // Setting the number of columns in the sampler matrix (equal to the number of distributions).
      77          16 :   setNumberOfCols(_distributions.size());
      78             : 
      79             :   /* `inputs_sto` is a member variable that aids in deciding the next set of samples
      80             :   in the Subset Simulation algorithm by storing the input parameter values*/
      81          16 :   _inputs_sto.resize(_distributions.size(), std::vector<Real>(_num_samplessub, 0.0));
      82          16 :   _outputs_sto.resize(_num_samplessub, 0.0);
      83             : 
      84             :   /* `inputs_sorted` is a member variable which also aids in deciding the next set of samples
      85             :   in the Subset Simulation algorithm by storing the sorted input parameter values
      86             :   by their corresponding output values*/
      87          16 :   _inputs_sorted.resize(_distributions.size());
      88             : 
      89             :   /* `markov_seed` is a member variable to store the seed input values for proposing
      90             :   the next set of Markov chain samples.*/
      91          16 :   _markov_seed.resize(_distributions.size());
      92             : 
      93          16 :   setNumberOfRandomSeeds(_num_random_seeds);
      94          16 :   setAutoAdvanceGenerators(false);
      95          16 : }
      96             : 
      97             : const unsigned int &
      98         252 : ParallelSubsetSimulation::getNumSamplesSub() const
      99             : {
     100         252 :   return _num_samplessub;
     101             : }
     102             : 
     103             : const bool &
     104          84 : ParallelSubsetSimulation::getUseAbsoluteValue() const
     105             : {
     106          84 :   return _use_absolute_value;
     107             : }
     108             : 
     109             : const Real &
     110         140 : ParallelSubsetSimulation::getSubsetProbability() const
     111             : {
     112         140 :   return _subset_probability;
     113             : }
     114             : 
     115             : void
     116         112 : ParallelSubsetSimulation::executeSetUp()
     117             : {
     118         112 :   if (_is_sampling_completed)
     119           0 :     mooseError("Internal bug: the adaptive sampling is supposed to be completed but another sample "
     120             :                "has been requested.");
     121             : 
     122         112 :   _subset = (_step * getNumberOfRows()) / _num_samplessub;
     123         112 :   const unsigned int sub_ind = _step - (_num_samplessub / getNumberOfRows()) * _subset;
     124         112 :   const unsigned int offset = sub_ind * getNumberOfRows();
     125             : 
     126             :   // check if we have completed the last sample
     127         112 :   if (_subset >= _num_subsets)
     128             :   {
     129          16 :     _is_sampling_completed = true;
     130          16 :     return;
     131             :   }
     132             : 
     133             :   // Get and store the accepted samples input across all the procs from the previous step
     134         288 :   for (dof_id_type j = 0; j < _distributions.size(); ++j)
     135         576 :     for (dof_id_type ss = 0; ss < getNumberOfRows(); ++ss)
     136         384 :       _inputs_sto[j][ss + offset] = Normal::quantile(_distributions[j]->cdf(_inputs[j][ss]), 0, 1);
     137             : 
     138             :   // Get the accepted sample outputs across all the procs from the previous step
     139             :   std::vector<Real> tmp =
     140          96 :       _use_absolute_value ? AdaptiveMonteCarloUtils::computeVectorABS(_outputs) : _outputs;
     141          96 :   _communicator.allgather(tmp);
     142          96 :   if (tmp.empty())
     143           8 :     tmp.resize(getNumberOfRows(), 0.0);
     144         288 :   for (dof_id_type ss = 0; ss < getNumberOfRows(); ++ss)
     145         192 :     _outputs_sto[ss + offset] = tmp[ss];
     146             : 
     147             :   // These are the subsequent subsets which use Markov Chain Monte Carlo sampling scheme
     148          96 :   if (_subset > 0)
     149             :   {
     150             :     // Check whether the subset index has changed
     151          64 :     if (sub_ind == 0)
     152             :     {
     153             :       // _inputs_sorted contains the input values corresponding to the largest po percentile
     154             :       // output values
     155          32 :       _inputs_sorted = AdaptiveMonteCarloUtils::sortInput(
     156          32 :           _inputs_sto, _outputs_sto, _num_samplessub, _subset_probability);
     157             :     }
     158             : 
     159             :     // Reinitialize the starting inputs values for the next set of Markov chains
     160          64 :     if (sub_ind % _count_max == 0)
     161             :     {
     162          32 :       const unsigned int soffset = (sub_ind / _count_max) * getNumberOfRows();
     163          96 :       for (dof_id_type j = 0; j < _distributions.size(); ++j)
     164          64 :         _markov_seed[j].assign(_inputs_sorted[j].begin() + soffset,
     165          64 :                                _inputs_sorted[j].begin() + soffset + getNumberOfRows());
     166             :     }
     167             :     // Otherwise, use the previously accepted input values to propose the next set of input
     168             :     // values
     169             :     else
     170             :     {
     171          96 :       for (dof_id_type j = 0; j < _distributions.size(); ++j)
     172          64 :         _markov_seed[j].assign(_inputs_sto[j].begin() + offset,
     173          64 :                                _inputs_sto[j].begin() + offset + getNumberOfRows());
     174             :     }
     175             :   }
     176          96 : }
     177             : 
     178             : Real
     179         480 : ParallelSubsetSimulation::computeSample(dof_id_type row_index, dof_id_type col_index)
     180             : {
     181         480 :   unsigned int seed_value = _step > 0 ? (_step - 1) * 2 : 0;
     182         480 :   const dof_id_type n = row_index * getNumberOfCols() + col_index;
     183             :   Real val;
     184             : 
     185         480 :   if (_subset == 0)
     186         160 :     val = getRand(n, seed_value);
     187             :   else
     188             :   {
     189             :     const Real rv =
     190         320 :         Normal::quantile(getRand(n, seed_value), _markov_seed[col_index][row_index], 1.0);
     191         320 :     const Real acceptance_ratio = std::log(Normal::pdf(rv, 0, 1)) -
     192         320 :                                   std::log(Normal::pdf(_markov_seed[col_index][row_index], 0, 1));
     193         320 :     const Real new_sample = acceptance_ratio > std::log(getRand(n, seed_value + 1))
     194         320 :                                 ? rv
     195         320 :                                 : _markov_seed[col_index][row_index];
     196         320 :     val = Normal::cdf(new_sample, 0, 1);
     197             :   }
     198             : 
     199         480 :   return _distributions[col_index]->quantile(val);
     200             : }

Generated by: LCOV version 1.14