LCOV - code coverage report
Current view: top level - src/samplers - Sampler.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 173 198 87.4 %
Date: 2026-05-29 20:35:17 Functions: 22 27 81.5 %
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             : // STL includes
      11             : #include <iterator>
      12             : 
      13             : // MOOSE includes
      14             : #include "Sampler.h"
      15             : 
      16             : InputParameters
      17        3635 : Sampler::validParams()
      18             : {
      19        3635 :   InputParameters params = MooseObject::validParams();
      20        3635 :   params += SetupInterface::validParams();
      21        3635 :   params += DistributionInterface::validParams();
      22        7270 :   params.addClassDescription("A base class for distribution sampling.");
      23             : 
      24        3635 :   ExecFlagEnum & exec_enum = params.set<ExecFlagEnum>("execute_on", true);
      25        3635 :   exec_enum.addAvailableFlags(EXEC_PRE_MULTIAPP_SETUP);
      26        7270 :   exec_enum = {EXEC_INITIAL};
      27             : 
      28       14540 :   params.addParam<unsigned int>("seed", 0, "Random number generator initial seed");
      29        7270 :   params.registerBase("Sampler");
      30        7270 :   params.registerSystemAttributeName("Sampler");
      31             : 
      32             :   // Define the allowable limits for data returned by getSamples/getLocalSamples/getNextLocalRow
      33             :   // to prevent system for going over allowable limits. The DenseMatrix object uses unsigned int
      34             :   // for size definition, so as start the limits will be based the max of unsigned int. Note,
      35             :   // the values here are the limits of the number of items in the complete container. dof_id_type
      36             :   // is used just in case in the future we need more.
      37       10905 :   params.addParam<dof_id_type>("limit_get_global_samples",
      38        7270 :                                0.1 * std::numeric_limits<unsigned int>::max(),
      39             :                                "The maximum allowed number of items in the DenseMatrix returned by "
      40             :                                "getGlobalSamples method.");
      41       10905 :   params.addParam<dof_id_type>(
      42             :       "limit_get_local_samples",
      43        7270 :       0.1 * std::numeric_limits<unsigned int>::max(),
      44             :       "The maximum allowed number of items in the DenseMatrix returned by getLocalSamples method.");
      45       10905 :   params.addParam<dof_id_type>(
      46             :       "limit_get_next_local_row",
      47        7270 :       0.1 * std::numeric_limits<unsigned int>::max(),
      48             :       "The maximum allowed number of items in the std::vector returned by getNextLocalRow method.");
      49             : 
      50       10905 :   params.addParam<unsigned int>(
      51             :       "min_procs_per_row",
      52        7270 :       1,
      53             :       "This will ensure that the sampler is partitioned properly when "
      54             :       "'MultiApp/*/min_procs_per_app' is specified. It is not recommended to use otherwise.");
      55        7270 :   params.addParam<unsigned int>(
      56             :       "max_procs_per_row",
      57        7270 :       std::numeric_limits<unsigned int>::max(),
      58             :       "This will ensure that the sampler is partitioned properly when "
      59             :       "'MultiApp/*/max_procs_per_app' is specified. It is not recommended to use otherwise.");
      60        3635 :   return params;
      61        3635 : }
      62             : 
      63         312 : Sampler::Sampler(const InputParameters & parameters)
      64             :   : MooseObject(parameters),
      65             :     SetupInterface(this),
      66             :     DistributionInterface(this),
      67             :     PerfGraphInterface(this),
      68             :     SamplerInterface(this),
      69             :     VectorPostprocessorInterface(this),
      70             :     ReporterInterface(this),
      71         312 :     _min_procs_per_row(getParam<unsigned int>("min_procs_per_row") > n_processors()
      72         312 :                            ? n_processors()
      73         936 :                            : getParam<unsigned int>("min_procs_per_row")),
      74         624 :     _max_procs_per_row(getParam<unsigned int>("max_procs_per_row")),
      75         312 :     _n_rows(0),
      76         312 :     _n_cols(0),
      77         312 :     _n_seeds(1),
      78         312 :     _next_local_row_requires_state_restore(true),
      79         312 :     _initialized(false),
      80         312 :     _needs_reinit(true),
      81         312 :     _has_executed(false),
      82         624 :     _limit_get_global_samples(getParam<dof_id_type>("limit_get_global_samples")),
      83         624 :     _limit_get_local_samples(getParam<dof_id_type>("limit_get_local_samples")),
      84         624 :     _limit_get_next_local_row(getParam<dof_id_type>("limit_get_next_local_row")),
      85         936 :     _auto_advance_generators(true)
      86             : {
      87         312 : }
      88             : 
      89             : void
      90         303 : Sampler::init()
      91             : {
      92             :   // The init() method is private so it is un-likely to be called, but just in case the following
      93             :   // was added to help avoid future mistakes.
      94         303 :   if (_initialized)
      95           0 :     mooseError("The Sampler::init() method is called automatically and should not be called.");
      96             : 
      97             :   // Initialize the parallel partition of sample to return
      98         303 :   reinit();
      99             : 
     100             :   // Seed the "master" seed generator
     101         606 :   const unsigned int seed = getParam<unsigned int>("seed");
     102         303 :   MooseRandom seed_generator;
     103         303 :   seed_generator.seed(0, seed);
     104             : 
     105             :   // See the "secondary" generator that will be used for the random number generation
     106         303 :   _generators.resize(_n_seeds);
     107         606 :   for (std::size_t i = 0; i < _n_seeds; ++i)
     108             :   {
     109         303 :     const auto gseed = seed_generator.randl(0);
     110         303 :     _generators[i] = std::make_unique<MooseRandomStateless>(gseed);
     111             :   }
     112             : 
     113             :   // Mark class as initialized, which locks out certain methods
     114         303 :   _initialized = true;
     115         303 : }
     116             : 
     117             : void
     118         307 : Sampler::reinit()
     119             : {
     120         307 :   _rank_config.first = constructRankConfig(false);
     121         307 :   _rank_config.second = constructRankConfig(true);
     122         307 :   if (_rank_config.first.num_local_sims != _rank_config.second.num_local_sims ||
     123         307 :       _rank_config.first.first_local_sim_index != _rank_config.second.first_local_sim_index ||
     124         307 :       _rank_config.first.is_first_local_rank != _rank_config.second.is_first_local_rank)
     125           0 :     mooseError("Sampler has inconsistent partitionings for normal and batch mode.");
     126             : 
     127         307 :   _n_local_rows = _rank_config.first.is_first_local_rank ? _rank_config.first.num_local_sims : 0;
     128         307 :   _local_row_begin = _rank_config.first.first_local_sim_index;
     129         307 :   _local_row_end = _local_row_begin + _n_local_rows;
     130             : 
     131             :   // Set the next row iterator index
     132         307 :   _next_local_row = _local_row_begin;
     133             : 
     134             :   // Create communicator that only has processors with rows
     135         307 :   _communicator.split(_n_local_rows > 0 ? 1 : MPI_UNDEFINED, processor_id(), _local_comm);
     136             : 
     137             :   // Update reinit() flag (see execute method)
     138         307 :   _needs_reinit = false;
     139         307 : }
     140             : 
     141             : LocalRankConfig
     142         614 : Sampler::constructRankConfig(bool batch_mode) const
     143             : {
     144         614 :   return rankConfig(
     145        1228 :       processor_id(), n_processors(), _n_rows, _min_procs_per_row, _max_procs_per_row, batch_mode);
     146             : }
     147             : 
     148             : void
     149         340 : Sampler::setNumberOfRows(dof_id_type n_rows)
     150             : {
     151         340 :   if (n_rows == 0)
     152           3 :     mooseError("The number of rows cannot be zero.");
     153             : 
     154         337 :   _needs_reinit = true;
     155         337 :   _n_rows = n_rows;
     156         337 : }
     157             : 
     158             : void
     159         309 : Sampler::setNumberOfCols(dof_id_type n_cols)
     160             : {
     161         309 :   if (n_cols == 0)
     162           3 :     mooseError("The number of columns cannot be zero.");
     163             : 
     164         306 :   _needs_reinit = true;
     165         306 :   _n_cols = n_cols;
     166         306 : }
     167             : 
     168             : void
     169           6 : Sampler::setNumberOfRandomSeeds(std::size_t n_seeds)
     170             : {
     171           6 :   if (_initialized)
     172           3 :     mooseError("The 'setNumberOfRandomSeeds()' method can not be called after the Sampler has been "
     173             :                "initialized; "
     174             :                "this method should be called in the constructor of the Sampler object.");
     175             : 
     176           3 :   if (n_seeds == 0)
     177           3 :     mooseError("The number of seeds must be larger than zero.");
     178             : 
     179           0 :   _n_seeds = n_seeds;
     180           0 : }
     181             : 
     182             : void
     183         295 : Sampler::execute()
     184             : {
     185         295 :   executeSetUp();
     186         271 :   if (_needs_reinit)
     187           4 :     reinit();
     188             : 
     189         271 :   if (_has_executed)
     190           0 :     advanceGeneratorsInternal(_n_rows * _n_cols);
     191             : 
     192         271 :   executeTearDown();
     193         271 :   _has_executed = true;
     194         271 : }
     195             : 
     196             : DenseMatrix<Real>
     197         175 : Sampler::getGlobalSamples()
     198             : {
     199         875 :   TIME_SECTION("getGlobalSamples", 1, "Retrieving Global Samples");
     200             : 
     201         175 :   checkReinitStatus();
     202             : 
     203         172 :   if (_n_rows * _n_cols > _limit_get_global_samples)
     204           3 :     paramError("limit_get_global_samples",
     205             :                "The number of entries in the DenseMatrix (",
     206           3 :                _n_rows * _n_cols,
     207             :                ") exceeds the allowed limit of ",
     208           3 :                _limit_get_global_samples,
     209             :                ".");
     210             : 
     211         169 :   _next_local_row_requires_state_restore = true;
     212         169 :   DenseMatrix<Real> output(_n_rows, _n_cols);
     213         169 :   computeSampleMatrix(output);
     214         332 :   return output;
     215         166 : }
     216             : 
     217             : DenseMatrix<Real>
     218         122 : Sampler::getLocalSamples()
     219             : {
     220         610 :   TIME_SECTION("getLocalSamples", 1, "Retrieving Local Samples");
     221             : 
     222         122 :   checkReinitStatus();
     223             : 
     224         119 :   if (_n_local_rows * _n_cols > _limit_get_local_samples)
     225           4 :     paramError("limit_get_local_samples",
     226             :                "The number of entries in the DenseMatrix (",
     227           4 :                _n_local_rows * _n_cols,
     228             :                ") exceeds the allowed limit of ",
     229           4 :                _limit_get_local_samples,
     230             :                ".");
     231             : 
     232         115 :   DenseMatrix<Real> output(_n_local_rows, _n_cols);
     233         115 :   if (_n_local_rows == 0)
     234           0 :     return output;
     235             : 
     236         115 :   _next_local_row_requires_state_restore = true;
     237         115 :   computeLocalSampleMatrix(output);
     238         115 :   return output;
     239         115 : }
     240             : 
     241             : std::vector<Real>
     242     1200342 : Sampler::getNextLocalRow()
     243             : {
     244     1200342 :   checkReinitStatus();
     245             : 
     246     1200339 :   if (_next_local_row_requires_state_restore)
     247             :   {
     248          83 :     _next_local_row_requires_state_restore = false;
     249             : 
     250          83 :     if (_n_cols > _limit_get_next_local_row)
     251           3 :       paramError("limit_get_next_local_row",
     252             :                  "The number of entries in the std::vector (",
     253             :                  _n_cols,
     254             :                  ") exceeds the allowed limit of ",
     255           3 :                  _limit_get_next_local_row,
     256             :                  ".");
     257             :   }
     258             : 
     259     1200336 :   std::vector<Real> output(_n_cols);
     260     1200336 :   computeSampleRow(_next_local_row, output);
     261             :   mooseAssert(output.size() == _n_cols, "The row of sample data is not sized correctly.");
     262     1200336 :   _next_local_row++;
     263             : 
     264     1200336 :   if (_next_local_row == _local_row_end)
     265             :   {
     266          75 :     _next_local_row = _local_row_begin;
     267          75 :     _next_local_row_requires_state_restore = true;
     268             :   }
     269             : 
     270     1200336 :   return output;
     271           0 : }
     272             : 
     273             : void
     274         169 : Sampler::computeSampleMatrix(DenseMatrix<Real> & matrix)
     275             : {
     276         845 :   TIME_SECTION("computeSampleMatrix", 2, "Computing Sample Matrix");
     277             : 
     278     3001683 :   for (dof_id_type i = 0; i < _n_rows; ++i)
     279             :   {
     280     3001517 :     std::vector<Real> row(_n_cols, 0);
     281     3001517 :     computeSampleRow(i, row);
     282             :     mooseAssert(row.size() == _n_cols, "The row of sample data is not sized correctly.");
     283             :     mooseAssert(!_needs_reinit,
     284             :                 "Changing the size of the sample must not occur during matrix access.");
     285     3001514 :     std::copy(row.begin(), row.end(), matrix.get_values().begin() + i * _n_cols);
     286     3001514 :   }
     287         166 : }
     288             : 
     289             : void
     290         115 : Sampler::computeLocalSampleMatrix(DenseMatrix<Real> & matrix)
     291             : {
     292         575 :   TIME_SECTION("computeLocalSampleMatrix", 2, "Computing Local Sample Matrix");
     293             : 
     294     1200717 :   for (dof_id_type i = _local_row_begin; i < _local_row_end; ++i)
     295             :   {
     296     1200602 :     std::vector<Real> row(_n_cols, 0);
     297     1200602 :     computeSampleRow(i, row);
     298             :     mooseAssert(row.size() == _n_cols, "The row of sample data is not sized correctly.");
     299             :     mooseAssert(!_needs_reinit,
     300             :                 "Changing the size of the sample must not occur during matrix access.");
     301     1200602 :     std::copy(
     302     1200602 :         row.begin(), row.end(), matrix.get_values().begin() + ((i - _local_row_begin) * _n_cols));
     303     1200602 :   }
     304         115 : }
     305             : 
     306             : void
     307     5402455 : Sampler::computeSampleRow(dof_id_type i, std::vector<Real> & data)
     308             : {
     309    10821861 :   for (dof_id_type j = 0; j < _n_cols; ++j)
     310             :   {
     311     5419409 :     data[j] = computeSample(i, j);
     312             :     mooseAssert(!_needs_reinit,
     313             :                 "Changing the size of the sample must not occur during matrix access.");
     314             :   }
     315     5402452 : }
     316             : 
     317             : void
     318           0 : Sampler::advanceGenerators(const dof_id_type count)
     319             : {
     320           0 :   TIME_SECTION("advanceGenerators", 2, "Advancing Generators");
     321             : 
     322           0 :   for (std::size_t j = 0; j < _generators.size(); ++j)
     323           0 :     advanceGenerator(j, count);
     324           0 : }
     325             : void
     326           0 : Sampler::advanceGenerator(const unsigned int seed_index, const dof_id_type count)
     327             : {
     328             :   mooseAssert(seed_index < _generators.size(), "The seed number index does not exists.");
     329           0 :   _generators[seed_index]->advance(count);
     330           0 : }
     331             : 
     332             : void
     333           0 : Sampler::advanceGeneratorsInternal(const dof_id_type count)
     334             : {
     335           0 :   if (_auto_advance_generators)
     336           0 :     advanceGenerators(count);
     337           0 : }
     338             : 
     339             : void
     340           0 : Sampler::setAutoAdvanceGenerators(const bool state)
     341             : {
     342           0 :   _auto_advance_generators = state;
     343           0 : }
     344             : 
     345             : Real
     346       10416 : Sampler::getRand(std::size_t n, unsigned int index) const
     347             : {
     348             :   mooseAssert(index < _generators.size(), "The seed number index does not exists.");
     349       10416 :   return _generators[index]->rand(n);
     350             : }
     351             : 
     352             : unsigned int
     353           0 : Sampler::getRandl(std::size_t n, unsigned int lower, unsigned int upper, unsigned int index) const
     354             : {
     355             :   mooseAssert(index < _generators.size(), "The seed number index does not exists.");
     356           0 :   return _generators[index]->randl(n, lower, upper);
     357             : }
     358             : 
     359             : dof_id_type
     360          31 : Sampler::getNumberOfRows() const
     361             : {
     362          31 :   checkReinitStatus();
     363          28 :   return _n_rows;
     364             : }
     365             : 
     366             : dof_id_type
     367       10419 : Sampler::getNumberOfCols() const
     368             : {
     369       10419 :   checkReinitStatus();
     370       10416 :   return _n_cols;
     371             : }
     372             : 
     373             : dof_id_type
     374           3 : Sampler::getNumberOfLocalRows() const
     375             : {
     376           3 :   checkReinitStatus();
     377           0 :   return _n_local_rows;
     378             : }
     379             : 
     380             : dof_id_type
     381          81 : Sampler::getLocalRowBegin() const
     382             : {
     383          81 :   checkReinitStatus();
     384          78 :   return _local_row_begin;
     385             : }
     386             : 
     387             : dof_id_type
     388     1200382 : Sampler::getLocalRowEnd() const
     389             : {
     390     1200382 :   checkReinitStatus();
     391     1200379 :   return _local_row_end;
     392             : }
     393             : 
     394             : void
     395     2411555 : Sampler::checkReinitStatus() const
     396             : {
     397     2411555 :   if (_needs_reinit)
     398          24 :     mooseError("A call to 'setNumberOfRows()/Columns()' was made after initialization, as such the "
     399             :                "expected Sampler output has changed and a new sample must be created. However, a "
     400             :                "call to Sampler::reinit() was not performed. The renit() method is automatically "
     401             :                "called during Sampler execution, which occurs according to the 'execute_on' "
     402             :                "settings of the Sampler object. An adjustment to this parameter may be required. "
     403             :                "It is recommended that calls to 'setNumberOfRows()/Columns() occur within the "
     404             :                "Sampler::executeSetUp() method; this will ensure that the reinitialize is handled "
     405             :                "correctly. Have a nice day.");
     406     2411531 : }

Generated by: LCOV version 1.14