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 : }
|