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

Generated by: LCOV version 1.14