LCOV - code coverage report
Current view: top level - include/samplers - Sampler.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 5 8 62.5 %
Date: 2025-07-17 01:28:37 Functions: 5 7 71.4 %
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             : #pragma once
      11             : #include "Shuffle.h"
      12             : #include "DenseMatrix.h"
      13             : #include "MooseObject.h"
      14             : #include "MooseRandom.h"
      15             : #include "SetupInterface.h"
      16             : #include "DistributionInterface.h"
      17             : #include "PerfGraphInterface.h"
      18             : #include "SamplerInterface.h"
      19             : #include "MultiApp.h"
      20             : #include "VectorPostprocessorInterface.h"
      21             : #include "ReporterInterface.h"
      22             : 
      23             : /**
      24             :  * This is the base class for Samplers as used within the Stochastic Tools module.
      25             :  *
      26             :  * A sampler is responsible for sampling distributions and providing an API for providing
      27             :  * the sample data to objects. The sampler is designed to handle any number of random number
      28             :  * generators.
      29             :  *
      30             :  * The main methods in this object is the getNextLocalRow/getSamples/getLocalSamples methods which
      31             :  * return the distribution samples. These methods will return the same results for each call
      32             :  * regardless of the number of calls, with getNextLocalRow being the exception because it is
      33             :  * designed be used in an iterative approach.
      34             :  *
      35             :  * Samplers support the use of "execute_on", which when called results in new set of random numbers,
      36             :  * thus after execute() calls to getSamples/getLocalSamples() methods will now produce a new set of
      37             :  * random numbers from calls prior to the execute() call.
      38             :  *
      39             :  * Not for MOOSE developers: Great care was taken to design the structure of this class to limit
      40             :  * access to the critical portions of this object while making it extensible. Please consider
      41             :  * carefully the impacts of altering the API.
      42             :  */
      43             : class Sampler : public MooseObject,
      44             :                 public SetupInterface,
      45             :                 public DistributionInterface,
      46             :                 public PerfGraphInterface,
      47             :                 public SamplerInterface,
      48             :                 public VectorPostprocessorInterface,
      49             :                 public ReporterInterface
      50             : {
      51             : public:
      52             :   enum class SampleMode
      53             :   {
      54             :     GLOBAL = 0,
      55             :     LOCAL = 1
      56             :   };
      57             : 
      58             :   static InputParameters validParams();
      59             :   Sampler(const InputParameters & parameters);
      60             : 
      61             :   // The public members define the API that is exposed to application developers that are using
      62             :   // Sampler objects to perform calculations, so be very careful when adding items here since
      63             :   // they are exposed to any other object via the SamplerInterface.
      64             :   //
      65             :   // It is also important to point out that when Sampler objects, when used, are not const. This is
      66             :   // due to the fact that calling the various get methods below must store various pieces of data as
      67             :   // well as control the state of the random number generators.
      68             : 
      69             :   ///@{
      70             :   /**
      71             :    * Return the sampled complete or distributed sample data.
      72             :    *
      73             :    * Use these with caution as they can result in a large amount of memory use, the preferred
      74             :    * method for accessing Sampler data is the getNextLocalRow() method.
      75             :    */
      76             :   DenseMatrix<Real> getGlobalSamples();
      77             :   DenseMatrix<Real> getLocalSamples();
      78             :   ///@}
      79             : 
      80             :   /**
      81             :    * Return the "next" local row. This is designed to be used within a loop using the
      82             :    * getLocalRowBegin/End methods as such:
      83             :    *
      84             :    * for (dof_id_type i = getLocalRowBegin(); i < getLocalRowEnd(); ++i)
      85             :    *     std::vector<Real> row = getNextLocalRow();
      86             :    *
      87             :    * This is the preferred method for accessing Sampler data.
      88             :    *
      89             :    * Calls to getNextLocalRow() will continue to return the next row of data until the last local
      90             :    * row has been reached, it will then start again at the beginning if called again. Also, calls
      91             :    * to getNextLocalRow() can be partial, followed by call(s) to getSamples or getLocalSamples.
      92             :    * Continued calls to getNextLocalRow() will still continue to give the next row as if the
      93             :    * other get calls were not made. However, when this occurs calls to restore and advance the
      94             :    * generators are made after each call to getSamples or getLocalSamples, so this generally
      95             :    * should be avoided.
      96             :    */
      97             :   std::vector<Real> getNextLocalRow();
      98             : 
      99             :   /**
     100             :    * Return the number of samples.
     101             :    * @return The total number of rows that exist in all DenseMatrix values from the
     102             :    * getSamples/getLocalSamples methods.
     103             :    */
     104             :   dof_id_type getNumberOfRows() const;
     105             :   dof_id_type getNumberOfCols() const;
     106             :   dof_id_type getNumberOfLocalRows() const;
     107             : 
     108             :   ///@{
     109             :   /**
     110             :    * Return the beginning/end local row index for this processor
     111             :    */
     112             :   dof_id_type getLocalRowBegin() const;
     113             :   dof_id_type getLocalRowEnd() const;
     114             :   ///@}
     115             : 
     116             :   /**
     117             :    * Reference to rank configuration defining the partitioning of the sampler matrix
     118             :    * This is primarily used by MultiApps to ensure consistent partitioning
     119             :    */
     120             :   const LocalRankConfig & getRankConfig(bool batch_mode) const
     121             :   {
     122             :     return batch_mode ? _rank_config.second : _rank_config.first;
     123             :   }
     124             : 
     125             :   /**
     126             :    * Returns true if the adaptive sampling is completed
     127             :    */
     128           0 :   virtual bool isAdaptiveSamplingCompleted() const
     129             :   {
     130           0 :     mooseError("This method should be overridden in adaptive sampling classes.");
     131             :     return false;
     132             :   }
     133             : 
     134             :   /**
     135             :    * Return the parallel communicator
     136             :    */
     137             :   libMesh::Parallel::Communicator & getLocalComm() { return _local_comm; }
     138             : 
     139             : protected:
     140             :   /**
     141             :    * Enum describing the type of parallel communication to perform.
     142             :    *
     143             :    * Some routines require specific communication methods that not all processors
     144             :    * see, these IDs will determine how that routine is performed:
     145             :    *  - NONE routine is not distrubuted and things all can happen locally
     146             :    *  - LOCAL routine is distributed on all processors
     147             :    *  - SEMI_LOCAL routine is distributed only on processors that own rows
     148             :    */
     149             :   enum CommMethod
     150             :   {
     151             :     NONE = 0,
     152             :     LOCAL = 1,
     153             :     SEMI_LOCAL = 2
     154             :   };
     155             : 
     156             :   // The following methods are the basic methods that should be utilized my most application
     157             :   // developers that are creating a custom Sampler.
     158             : 
     159             :   ///@{
     160             :   /**
     161             :    * These methods must be called within the constructor of child classes to define the size of
     162             :    * the matrix to be created.
     163             :    */
     164             :   void setNumberOfRows(dof_id_type n_rows);
     165             :   void setNumberOfCols(dof_id_type n_cols);
     166             :   ///@}
     167             : 
     168             :   /**
     169             :    * Set the number of seeds required by the sampler. The Sampler will generate
     170             :    * additional seeds as needed. This function should be called in the constructor
     171             :    * of child objects.
     172             :    * @param number The required number of random seeds, by default this is called with 1.
     173             :    */
     174             :   void setNumberOfRandomSeeds(std::size_t number);
     175             : 
     176             :   /**
     177             :    * Get the next random number from the generator.
     178             :    * @param index The index of the seed, by default this is zero. To add additional seeds
     179             :    *              indices call the setNumberOfRequiedRandomSeeds method.
     180             :    *
     181             :    * @return A double for the random number, this is double because MooseRandom class uses double.
     182             :    */
     183             :   Real getRand(unsigned int index = 0);
     184             : 
     185             :   /**
     186             :    * Get the next random integer from the generator within the specified range [lower, upper)
     187             :    * @param index The index of the seed, by default this is zero. To add additional seeds
     188             :    *              indices call the setNumberOfRequiedRandomSeeds method.
     189             :    * @param lower Lower bounds
     190             :    * @param upper Upper bounds
     191             :    *
     192             :    * @return A integer for the random number
     193             :    */
     194             :   uint32_t getRandl(unsigned int index, uint32_t lower, uint32_t upper);
     195             : 
     196             :   /**
     197             :    * Base class must override this method to supply the sample distribution data.
     198             :    * @param row_index The row index of sample value to compute.
     199             :    * @param col_index The column index of sample value to compute.
     200             :    * @return The value for the given row and column.
     201             :    */
     202             :   virtual Real computeSample(dof_id_type row_index, dof_id_type col_index) = 0;
     203             : 
     204             :   ///@{
     205             :   /**
     206             :    * Setup method called prior and after looping through distributions.
     207             :    *
     208             :    * These methods should not be called directly, each is automatically called by the public
     209             :    * getGlobalSamples() or getLocalSamples() methods.
     210             :    */
     211         407 :   virtual void sampleSetUp(const SampleMode /*mode*/) {}
     212         392 :   virtual void sampleTearDown(const SampleMode /*mode*/) {}
     213             :   ///@}
     214             : 
     215             :   // The following methods are advanced methods that should not be needed by application developers,
     216             :   // but exist for special cases.
     217             : 
     218             :   ///@{
     219             :   /**
     220             :    * Methods to populate the global or local sample matrix.
     221             :    * @param matrix The correctly sized matrix of sample values to populate
     222             :    *
     223             :    * These methods should not be called directly, each is automatically called by the public
     224             :    * getGlobalSamples() or getLocalSamples() methods.
     225             :    */
     226             :   virtual void computeSampleMatrix(DenseMatrix<Real> & matrix);
     227             :   virtual void computeLocalSampleMatrix(DenseMatrix<Real> & matrix);
     228             :   ///@}
     229             : 
     230             :   ///@{
     231             :   /**
     232             :    * Method to populate a complete row of sample data.
     233             :    * @param i The global row index to compute
     234             :    * @param data The correctly sized vector of sample value to poplulate
     235             : 
     236             :    * This method should not be called directly, it is automatically called by the public
     237             :    * getGlobalSamples(), getLocalSamples(), or getNextLocalRow() methods.
     238             :    */
     239             :   virtual void computeSampleRow(dof_id_type i, std::vector<Real> & data);
     240             : 
     241             :   /**
     242             :    * Method for advancing the random number generator(s) by the supplied number or calls to rand().
     243             :    *
     244             :    * TODO: This should be updated if the If the random number generator is updated to type that
     245             :    * supports native advancing.
     246             :    */
     247             :   virtual void advanceGenerators(const dof_id_type count);
     248             :   virtual void advanceGenerator(const unsigned int seed_index, const dof_id_type count);
     249             :   void setAutoAdvanceGenerators(const bool state);
     250             : 
     251             :   /**
     252             :    * Helper for shuffling a vector of data in-place; the default assumes data is distributed
     253             :    *
     254             :    * NOTE: This will advance the generator by the size of the supplied vector.
     255             :    */
     256             :   template <typename T>
     257             :   void shuffle(std::vector<T> & data,
     258             :                const std::size_t seed_index = 0,
     259             :                const CommMethod method = CommMethod::LOCAL);
     260             : 
     261             :   //@{
     262             :   /**
     263             :    * Callbacks for before and after execute.
     264             :    *
     265             :    * These were added to support of dynamic sampler sizes. Recall that execute is simply to advance
     266             :    * the state of the generator such that the next sample will be unique. These methods allow
     267             :    * operations before and after the call to generator advancement.
     268             :    */
     269           0 :   virtual void executeSetUp() {}
     270          23 :   virtual void executeTearDown() {}
     271             :   ///@}
     272             : 
     273             :   //@{
     274             :   /**
     275             :    * Here we save/restore generator states
     276             :    */
     277         366 :   void saveGeneratorState() { _generator.saveState(); }
     278         407 :   void restoreGeneratorState() { _generator.restoreState(); }
     279             :   //@}
     280             : 
     281             :   /**
     282             :    * This is where the sampler partitioning is defined. It is NOT recommended to
     283             :    * override this function unless you know EXACTLY what you are doing
     284             :    */
     285             :   virtual LocalRankConfig constructRankConfig(bool batch_mode) const;
     286             : 
     287             :   /// The minimum number of processors that are associated with a set of rows
     288             :   const dof_id_type _min_procs_per_row;
     289             :   /// The maximum number of processors that are associated with a set of rows
     290             :   const dof_id_type _max_procs_per_row;
     291             : 
     292             :   /// Communicator that was split based on samples that have rows
     293             :   libMesh::Parallel::Communicator _local_comm;
     294             : 
     295             : private:
     296             :   ///@{
     297             :   /**
     298             :    * Functions called by MOOSE to setup the Sampler for use. The primary purpose is to partition
     299             :    * the DenseMatrix rows for parallel distribution. A separate methods are required so that the
     300             :    * set methods can be called within the constructors of child objects, see
     301             :    * FEProblemBase::addSampler method. The reinit was added to support re-partitioning to allow
     302             :    * for dynamic changes in sampler size.
     303             :    *
     304             :    * This init() method is called by FEProblemBase::addSampler; it should not be called elsewhere.
     305             :    */
     306             :   void init();   // sets up MooseRandom
     307             :   void reinit(); // partitions sampler output
     308             :   ///@}
     309             :   friend void FEProblemBase::addSampler(const std::string & type,
     310             :                                         const std::string & name,
     311             :                                         InputParameters & parameters);
     312             :   /**
     313             :    * Store the state of the MooseRandom generator so that new calls to
     314             :    * getGlobalSamples/getLocalSamples methods will create new numbers.
     315             :    *
     316             :    * The execute() method is called in the init() method of this class and
     317             :    * FEProblemBase::executeSamplers; it should not be called elsewhere.
     318             :    */
     319             :   void execute();
     320             :   friend void FEProblemBase::objectExecuteHelper<Sampler>(const std::vector<Sampler *> & objects);
     321             : 
     322             :   /**
     323             :    * Helper function for reinit() errors.
     324             :    **/
     325             :   void checkReinitStatus() const;
     326             : 
     327             :   /**
     328             :    * Advance method for internal use that considers the auto advance flag.
     329             :    */
     330             :   void advanceGeneratorsInternal(const dof_id_type count);
     331             : 
     332             :   /// Random number generator, don't give users access. Control it via the interface from this class.
     333             :   MooseRandom _generator;
     334             : 
     335             :   /// Number of rows for this processor
     336             :   dof_id_type _n_local_rows;
     337             : 
     338             :   /// Global row index for start of data for this processor
     339             :   dof_id_type _local_row_begin;
     340             : 
     341             :   /// Global row index for end of data for this processor
     342             :   dof_id_type _local_row_end;
     343             : 
     344             :   /// Total number of rows in the sample matrix
     345             :   dof_id_type _n_rows;
     346             : 
     347             :   /// Total number of columns in the sample matrix
     348             :   dof_id_type _n_cols;
     349             : 
     350             :   /// Number of seeds
     351             :   std::size_t _n_seeds;
     352             : 
     353             :   /// Iterator index for getNextLocalRow method
     354             :   dof_id_type _next_local_row;
     355             : 
     356             :   /// Flag for restoring state during getNextLocalRow iteration
     357             :   bool _next_local_row_requires_state_restore;
     358             : 
     359             :   /// Flag to indicate if the init method for this class was called
     360             :   bool _initialized;
     361             : 
     362             :   /// Flag to indicate if the reinit method should be called during execute
     363             :   bool _needs_reinit;
     364             : 
     365             :   /// Flag for initial execute to allow the first set of random numbers to be always be the same
     366             :   bool _has_executed;
     367             : 
     368             :   /// Max number of entries for matrix returned by getGlobalSamples
     369             :   const dof_id_type _limit_get_global_samples;
     370             : 
     371             :   /// Max number of entries for matrix returned by getLocalSamples
     372             :   const dof_id_type _limit_get_local_samples;
     373             : 
     374             :   /// Max number of entries for matrix returned by getNextLocalRow
     375             :   const dof_id_type _limit_get_next_local_row;
     376             : 
     377             :   /// The partitioning of the sampler matrix, built in reinit()
     378             :   /// first is for normal mode and send is for batch mode
     379             :   std::pair<LocalRankConfig, LocalRankConfig> _rank_config;
     380             : 
     381             :   /// Flag for disabling automatic generator advancing
     382             :   bool _auto_advance_generators;
     383             : };
     384             : 
     385             : template <typename T>
     386             : void
     387             : Sampler::shuffle(std::vector<T> & data, const std::size_t seed_index, const CommMethod method)
     388             : {
     389             :   if (method == CommMethod::NONE)
     390             :     MooseUtils::shuffle<T>(data, _generator, seed_index, nullptr);
     391             :   else if (method == CommMethod::LOCAL)
     392             :     MooseUtils::shuffle<T>(data, _generator, seed_index, &_communicator);
     393             :   else if (method == CommMethod::SEMI_LOCAL)
     394             :     MooseUtils::shuffle<T>(data, _generator, seed_index, &_local_comm);
     395             : }

Generated by: LCOV version 1.14