NuRandomService.h
Go to the documentation of this file.
1 /**
2  * @file NuRandomService.h
3  * @brief An art service to assist in the distribution of guaranteed unique seeds to all engines within an art job.
4  * @author Gianluca Petrillo (petrillo@fnal.gov), Rob Kutschke (kutschke@fnal.gov)
5  * @date 2013/03/14 19:54:49
6  * @see NuRandomService_service.cc
7  *
8  */
9 
10 
11 #ifndef NUTOOLS_RANDOMUTILS_NuRandomService_H
12 #define NUTOOLS_RANDOMUTILS_NuRandomService_H 1
13 
14 #ifndef NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP
15 /// Define to zero to exclude special CLHEP random engine support
16 # define NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP 1
17 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP
18 
19 // ROOT libraries
20 #ifndef NUTOOLS_RANDOMUTILS_NuRandomService_USEROOT
21 /// Define to non-zero to include special ROOT random generator support
22 # define NUTOOLS_RANDOMUTILS_NuRandomService_USEROOT 0
23 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USEROOT
24 
25 
26 // C/C++ standard libraries
27 #include <functional>
28 #include <string>
29 #include <utility> // std::forward()
30 #include <initializer_list>
31 
32 // Some helper classes.
33 #include "nutools/RandomUtils/ArtState.h"
34 #include "nutools/RandomUtils/Providers/SeedMaster.h"
35 
36 // CLHEP libraries
37 #if (NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP)
38 # include "CLHEP/Random/RandomEngine.h"
39 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP
40 
41 // ROOT libraries
42 #if (NUTOOLS_RANDOMUTILS_NuRandomService_USEROOT)
43 # include "TRandom.h"
44 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USEROOT
45 
46 // From art and its tool chain.
47 #include "fhiclcpp/ParameterSet.h"
52 
53 // Forward declarations
54 namespace art {
55  class ActivityRegistry;
56  class ModuleDescription;
57  class ModuleContext;
58  class Run;
59  class SubRun;
60 }
61 
62 namespace rndm {
63 
64  /**
65  * @brief An art service to assist in the distribution of guaranteed unique
66  * seeds to all engines within an art job.
67  * @see SeedMaster
68  *
69  * NuRandomService centrally manages seeds for random generator engines.
70  *
71  * The NuRandomService acts as an interface between art framework and the
72  * SeedMaster class.
73  *
74  * The documentation is mantained in the SeedMaster class.
75  * The configuration of NuRandomService is exactly the same as SeedMaster's,
76  * and in art it's read from `services.NuRandomService`.
77  * The following documentation describes features of NuRandomService that are
78  * built on top of SeedMaster to have a more convenient interaction within
79  * the art framework.
80  *
81  * Before asking NuRandomService for its seed, an engine must be in some way
82  * registered. Once the engine is registered, its original seed can be queried
83  * again by calling `getSeed()` methods.
84  *
85  *
86  * Glossary
87  * ---------
88  *
89  * Here "engine" means a class that is able to generate random numbers
90  * according to a flat distribution.
91  * Both art and NuRandomService are module-based, that means that the engines
92  * are in the context of a specific module instance, and different module
93  * instances have independent engines.
94  * That is the reason why you don't need to specify anything about the module
95  * when creating or obtaining a random engine, and it is also the reason why
96  * engines outside module context are not supported by the framework.
97  *
98  * Each module can need more than one engine. A module can have any number of
99  * engines, and each of them is identified by an "instance name" that is
100  * unique within the module.
101  * Nonetheless, most modules need just one engine. In that case, a default
102  * instance name can be used (that is an empty string).
103  *
104  * A "seeder" is a callable object (e.g. a function) that sets the seed of
105  * a certain engine. The seeder is expected to find out by its own which
106  * engine it has to seed, and fot that it is provided an engine ID.
107  *
108  *
109  * Registration of a random generator engine
110  * ------------------------------------------
111  *
112  * Registration must happen in art module constructor, in one of the following
113  * ways:
114  * * by asking this service to create an engine via RandomNumberGenerator
115  * (see `createEngine()` methods) [discouraged]
116  * * by registering an existing engine and its seeding function
117  * (see `registerEngine()` methods)
118  * * by just declaring that an engine exists
119  * (see `declareEngine()` and `getSeed()` methods)
120  * The first method and, when a seeder or an engine is provided, also the
121  * second method, set the seed of the engine they register (see below).
122  * In the third case, it is generally the caller's responsibility to seed the
123  * engine. The registration of an engine which has been only declared can be
124  * "completed" by calling `defineEngine()` to provide the actual seeder for
125  * that engine. The pair of calls `declareEngine()`/`defineEngine()` (or
126  * `getSeed()`/`defineEngine()`) is equivalent to a single call to
127  * `registerEngine()`, with the added flexibility of having the seed for the
128  * engine already available before the registration is completed.
129  *
130  * The use of createEngine() class of function is discouraged and this
131  * function might be removed in the future, because of its non-clean use of
132  * module interfaces.
133  * The recommended approach is more prolix; here an example for an engine with
134  * a non-default instance name:
135  *
136  * std::string const instanceName = "instanceName";
137  * auto& Seeds = *(art::ServiceHandle<rndm::NuRandomService>());
138  *
139  * // declare an engine; NuRandomService associates an (unknown) engine, in
140  * // the current module and an instance name, with a seed (returned)
141  * auto const seed = Seeds.declareEngine(instanceName);
142  *
143  * // now create the engine (for example, use art); seed will be set
144  * auto& engine = createEngine(seed, "HepJamesRandom", instanceName);
145  *
146  * // finally, complete the registration; seed will be set again
147  * Seeds.defineEngine(engine);
148  *
149  * This is equivalent to the discouraged call
150  *
151  * auto& Seeds = *(art::ServiceHandle<rndm::NuRandomService>());
152  * Seeds.createEngine(*this, "HepJamesRandom", "instanceName");
153  *
154  * Please read carefully the documentation of the method of your choice, since
155  * they have different requirements and apply to different usage patterns.
156  *
157  * The registration must happen in the constructor of the module.
158  * That is because we don't want engines to be initialized in the middle of a
159  * job.
160  *
161  *
162  * Setting the seed of an engine
163  * ------------------------------
164  *
165  * NuRandomService is able to set the seed of an engine when the engine is
166  * registered via either:
167  * * `createEngine()` (creation of a new CLHEP engine)
168  * * `registerEngine()` (registration of an engine or a seeder function),
169  * if the registered seeder function is valid (non-null) or if a CLHEP
170  * engine is being registered (in which case the seeder is automatically
171  * created valid)
172  * * `defineEngine()` (registration of a seeder for an engine that was
173  * already declared), again if the seed is valid
174  * NuRandomService is *not* able to automatically set the seed of an engine if
175  * it was registered via either:
176  * * `declareEngine()` (declaration of the existence of an engine),
177  * that does not even require the engine to exist
178  * * `getSeed()` (query of a seed), when it (implicitly) declares an engine
179  * which had not been declared yet
180  *
181  * If NuRandomService is able to set the seed, it will do so only once, as soon as
182  * it can.
183  * This means that if the policy allows the seed to be known immediately,
184  * the seed will be set on registration. In the case of a per-event policy
185  * that requires the presence of an event, the seed can be known only
186  * when the event is available, and NuRandomService will set the seed before the
187  * module the engine is associated with starts its main processing method
188  * (produce(), filter() or analyze()).
189  *
190  *
191  * Changing the seeder
192  * --------------------
193  *
194  * Currently, changing the seeder of an engine after the engine has been
195  * fully registered is not supported. As a consequence, changing the engine
196  * is also not supported.
197  *
198  * Since only the seeder function is registered in NuRandomService, a seeder
199  * function that is flexible enough to change the engine it seeds may work
200  * around this limitation.
201  *
202  *
203  * Querying the seed of an engine
204  * -------------------------------
205  *
206  * If necessary, the seed that NuRandomService has assigned to an engine can be
207  * requested by one of the following two calls:
208  *
209  * art::ServiceHandle<NuRandomService>->getSeed();
210  * art::ServiceHandle<NuRandomService>->getSeed("instanceName");
211  *
212  * depending on whether the engine has a non-empty instance name,
213  * Note that this call implicitly "declares" the engine it refers to.
214  *
215  * For most policies, the seed is set according to the configuration, once
216  * for all. In those cases, `getSeed()` will always return the same value.
217  * If the policy prescribes different seeds at different times, the method
218  * returns the seed that is assigned to the engine at the time of the call.
219  *
220  * Also note that the seed assigned by NuRandomService might not match the current
221  * seed of the engine, if:
222  * * NuRandomService is not in charge of setting the seed of the engine,
223  * and the engine seed has not been set yet
224  * * the seed was reset directly after NuRandomService set the engine seed
225  * Both circumstances should be avoided.
226  *
227  *
228  * Creating the engines independently of NuRandomService
229  * --------------------------------------------------
230  *
231  * A number of things must happen for an engine to correctly work with
232  * NuRandomService:
233  * * the engine instance needs to exist or be created
234  * * the engine must be "registered" into NuRandomService
235  * * the seed must be obtained from NuRandomService
236  * * the seed must be provided to the engine
237  *
238  * A recipe for creating a ROOT engine and let NuRandomService take care of its
239  * seeds is:
240  *
241  * // create the engine; ROOT will set some (temporary) seed
242  * fRandom = std::make_unique<TRandom3>();
243  *
244  * // declare the engine; NuRandomService associates its seeder, in
245  * // the current module and an instance name, with a seed (returned);
246  * // the seed is also set in the engine
247  * auto& Seeds = *(art::ServiceHandle<rndm::NuRandomService>());
248  * Seeds.registerEngine(TRandomSeeder(fRandom), "instanceName");
249  *
250  * Here `fRandom` is supposed to be a member function of the module.
251  * The `TRandomSeeder` object is an object that knows how to set the seed
252  * of the TRandom-derived ROOT generator passed as constructor argument.
253  * For an example of implementation, see the source code of NuRandomService.
254  *
255  *
256  * Overriding the seed from NuRandomService at run time
257  * -------------------------------------------------
258  *
259  * NuRandomService (and SeedMaster, which the former relies upon) will decide
260  * which seed to give to each registered engine and, when possible
261  * (see above), will set that seed too.
262  *
263  * All registration functions offer an extended signature to tell NuRandomService
264  * that if there is an explicitly configured seed, that should take presedence
265  * over the one automatically assigned by SeedMaster policy.
266  * This extended signature includes:
267  * * a FHiCL parameter set
268  * * a configuration parameter name, or a list of them
269  * NuRandomService will look in the specified parameter set and if it finds a
270  * value corresponding to any of the specified parameter names, will set
271  * the seed of the engine to that value, and it will mark the engine as
272  * "frozen" (meaning that NuRandomService will not ever set a seed again on that
273  * engine). [see also the exception to this rule below]
274  *
275  * The typical use of this function is to read a parameter ("Seed" is the
276  * suggested name) from the configuration of the module. Note that this is in
277  * contrast with the location where NuRandomService seeds are normally configured,
278  * that is in the configuration of NuRandomService service itself.
279  * For example:
280  *
281  * // within module construction:
282  * art::ServiceHandle<rndm::NuRandomService>()->registerEngine
283  * (engine, "instanceName", pset, { "Seed", "MySeed" });
284  *
285  * Here NuRandomService will provide a seed only if it does not find in the
286  * parameter set `pset` any of the specified configuration parameters (first
287  * "Seed", then "MySeed" in this example). In other words, if "Seed" exists,
288  * its value is used as seed, otherwise if "MySeed" exists, its value is used
289  * instead, and otherwise NuRandomService is given control on that seed.
290  * The exception is that if the specified seed is a "magic value", the
291  * `InvalidSeed` (`0`), it is interpreted as a request to ignore the parameter
292  * and use the service to get the seed. This is made as a quick way to remove
293  * the seed override from an existing FHiCL file with one line.
294  * Note that if NuRandomService does not get the control on the seed,
295  * policies that reseed on event-by-event basis will not act on the engine.
296  *
297  *
298  * Engines outside of the module context
299  * ======================================
300  *
301  * It is possible to have engines that are not associated with any module.
302  * If no module is current, an engine will be registered in a "global"
303  * context. This happens during service construction, and once the service
304  * construction phase is completed, no more global engines can be registered.
305  * Would one ever need to access the seed of such engine, a specific
306  * interface needs to be used: `getGlobalSeed()` to get the configured seed
307  * at the beginning of the job, or `getGlobalCurrentSeed()` to get the seed
308  * specific for the current event, if any. These are equivalent to the module
309  * context methods `getSeed()` and `getCurrentSeed()`.
310  *
311  * The art service RandomNumberGenerator does not support the creation of
312  * module-independent engines. The ownership of each global engine is by the
313  * using service, as well as the management of engine's lifetime.
314  * After an engine has been instantiated, it can be registered with
315  * `registerEngine()`. Likewise, a global engine can be declared first with
316  * `declareEngine()`, then instantiated, and then optionally defined with
317  * `defineEngine()`. This is completely analogous to the module-context
318  * engines. The only difference is that no `createEngine()` interface is
319  * available for global engines.
320  * Whether these methods create a global or module engine depends only
321  * on the context they are called in.
322  *
323  * NuRandomService does not manage the engine life in any way. If a service owns
324  * an engine, it also needs a way to access it, as nothing equivalent to
325  * RandomNumberGenerator's `getEngine()` is provided.
326  * NuRandomService does manage the seeding of the registered engines, even the
327  * global ones. If the seed policy involves a event-dependent seed, all global
328  * engines are seeded together at the beginning of the event, before any
329  * module is executed. This mechanism relies on the fact that NuRandomService gets
330  * its preProcessEvent callback executed before the ones of the services that
331  * own any engine. This is guaranteed if the service constructors invoke
332  * NuRandomService before they register their callbacks.
333  *
334  * For an example of usage, see GlobalEngineUserTestService service in the
335  * test suite. Here is an excerpt using a ROOT TRandom3 engine, that can
336  * be constructed without seed. In the service constructor, the code:
337  *
338  * // create a new engine, that we own (engine be a class data member)
339  * engine = std::make_unique<TRandom3>();
340  * auto seed = Seeds.registerEngine
341  * (rndm::NuRandomService::TRandomSeeder(engine.get()), "MyService");
342  *
343  * // MyService callback registrations in the ActivityRegistry
344  * // should follow the first call to NuRandomService!
345  *
346  * where rndm::NuRandomService::TRandomSeeder is a seeder class for TRandom
347  * engines (optionally provided in NuRandomService.h, and very easy to write).
348  * This excerpt creates and owns a TRandom3 engine, and then it registers it
349  * to NuRandomService, which immediately sets its seed and will take care of
350  * reseeding the engine at each event, should the policy require it.
351  * The service will access the engine as a data member, and should it need the
352  * seed it will use:
353  *
354  * art::ServiceHandle<rndm::NuRandomService>()->getGlobalSeed("MyService");
355  *
356  * Note that is a good idea to give the engine an instance name after the
357  * service name itself, since the instance name is global and it's the only
358  * thing distinguishing global engines, and name conflicts between different
359  * services may easily arise.
360  *
361  */
363  public:
365  using engine_t = CLHEP::HepRandomEngine;
366 
367  using SeedMaster_t = SeedMaster<seed_t>; ///< type of object providing seeds
368 
369  using EngineId = SeedMaster_t::EngineId; ///< type of random engine ID
370 
371  /// An invalid seed
372  static constexpr seed_t InvalidSeed = SeedMaster_t::InvalidSeed;
373 
375 
376  // Accept compiler written d'tor. Not copyable or assignable.
377  // This class is not copyable or assignable: these methods are not implemented.
378  NuRandomService(NuRandomService const&) = delete;
379  NuRandomService const& operator=(NuRandomService const&) = delete;
380  NuRandomService(NuRandomService&&) = delete;
381  NuRandomService const& operator=(NuRandomService&&) = delete;
382  ~NuRandomService() = default;
383 
384  /// Returns whether the specified seed is valid
385  static constexpr bool isSeedValid(seed_t seed)
386  { return seed != InvalidSeed; }
387 
388 
389  /**
390  * @brief Returns a seed for the engine with specified instance name
391  * @param instanceName name of the engine instance
392  * @return a seed for the engine with specified instance name
393  * @see getGlobalSeed()
394  *
395  * The seed for an engine in the context of the current module is returned.
396  * If you need the seed for an engine outside that context, use
397  * `getGlobalSeed()` instead.
398  *
399  * The engine needs to have been registered before, in any of the supported
400  * ways.
401  * If it has not, this call will declare it with declareEngine()
402  * and no further registration will be allowed.
403  *
404  * While this method can be called at any time, the registration of an
405  * engine can happen only at construction time, and it will make the call to
406  * this method fail if it is called at any other time.
407  */
408  seed_t getSeed(std::string instanceName);
409 
410  /**
411  * @brief Returns a seed for the engine with default instance name
412  * @return a seed for the engine with default instance name
413  *
414  * This method is equivalent to getSeed(std::string) with an empty instance
415  * name.
416  */
417  seed_t getSeed();
418 
419 
420  /**
421  * @brief Returns a seed for the global engine with specified instance name
422  * @param instanceName name of the engine instance
423  * @return a seed for the global engine with specified instance name
424  * @see getSeed()
425  *
426  * A "global" engine is not bound to a specific execution context.
427  * The only context NuRandomService is aware of is the module, so this
428  * translates into engines that are not bound to any module.
429  * To instruct NuRandomService to ignore the current context (that may be
430  * a running module, or no running module at all), `getGlobalSeed()` is
431  * used instead of `getSeed()`, that will consider the context and in fact
432  * consider the absence of context an error.
433  *
434  * The engine needs to have been registered before, in any of the supported
435  * ways.
436  * If it has not, this call will declare it with declareEngine()
437  * and no further registration will be allowed.
438  *
439  * While this method can be called at any time, the registration of an
440  * engine can happen only at construction time, and it will make the call to
441  * this method fail if it is called at any other time.
442  */
443  seed_t getGlobalSeed(std::string instanceName);
444 
445 
446  /// Returns the last computed seed for specified engine of current module
447  seed_t getCurrentSeed(std::string instanceName) const
448  { return seeds.getCurrentSeed(qualify_engine_label(instanceName)); }
449 
450  /// Returns the last computed seed for the default engine of current module
452  { return seeds.getCurrentSeed(qualify_engine_label()); }
453 
454  /// Returns the last computed seed for the specified global engine
456  { return seeds.getCurrentSeed(qualify_global_engine(instanceName)); }
457 
458 
459 #if (NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP)
460  /// @{
461  /**
462  * @name Create and register an engine
463  *
464  * The life time of the engine is managed by art::RandomNumberGenerator,
465  * while the seeding is managed by this service.
466  *
467  * This is a replacement of art::detail::EngineCreator-derived classes.
468  * The use of createEngine() class of function is discouraged and this
469  * function might be removed in the future, because of its non-clean use of
470  * module interfaces.
471  * The recommended approach is more verbose;
472  * here an example for an engine with a non-default instance name:
473  *
474  * std::string const instanceName = "instanceName";
475  * auto& Seeds = *(art::ServiceHandle<rndm::NuRandomService>());
476  *
477  * // declare an engine; NuRandomService associates an (unknown) engine, in
478  * // the current module and an instance name, with a seed (returned)
479  * auto const seed = Seeds.declareEngine(instanceName);
480  *
481  * // now create the engine (for example, use art); seed will be set
482  * auto& engine = createEngine(seed, "HepJamesRandom", instanceName);
483  *
484  * // finally, complete the registration; seed will be set again
485  * Seeds.defineEngine(engine);
486  *
487  * This is equivalent to the discouraged call
488  *
489  * auto& Seeds = *(art::ServiceHandle<rndm::NuRandomService>());
490  * Seeds.createEngine(*this, "HepJamesRandom", "instanceName");
491  *
492  */
493 
494  //@{
495  /**
496  * @brief Creates an engine with RandomNumberGenerator service
497  * @param module module who will own the new engine
498  * @param type the type of engine
499  * @param instance the name of the engine instance
500  * @return the seed used
501  * @see registerEngine()
502  *
503  * This method creates a new engine by calling
504  * RandomNumberGenerator::createEngine() with the seed from getSeed().
505  * The meaning of the two parameters is the same as in that function,
506  * including the somehow inconvenient order of the arguments
507  *
508  * @note The module parameter is needed since the interface to create
509  * an engine by RandomNumberGenerator service is private and open only
510  * to friends.
511  */
512  template <typename Module>
513  [[nodiscard]] std::reference_wrapper<engine_t> createEngine
515 
516  template <typename Module>
517  [[nodiscard]] std::reference_wrapper<engine_t> createEngine(Module& module);
518  //@}
519 
520  //@{
521  /**
522  * @brief Creates an engine with RandomNumberGenerator service
523  * @param module module who will own the new engine
524  * @param type the type of engine
525  * @param instance the name of the engine instance
526  * @param pset parameter set to read parameters from
527  * @param pname name or names of the seed parameters
528  * @return the seed used
529  * @see registerEngine()
530  *
531  * This method creates a new engine by calling
532  * RandomNumberGenerator::createEngine() with the seed from getSeed().
533  * The meaning of the two parameters is the same as in that function,
534  * including the somehow inconvenient order of the arguments
535  *
536  * The engine seed is set. First, the seed is retrieved from the specified
537  * configuration, looking for the first of the parameters in pname that is
538  * available. If no parameter is found, the seed is obtained from
539  * NuRandomService.
540  *
541  * @note The module parameter is needed since the interface to create
542  * an engine by RandomNumberGenerator service is private and open only
543  * to friends.
544  */
545  template <typename Module>
546  [[nodiscard]] std::reference_wrapper<engine_t> createEngine(
547  Module& module, std::string type, std::string instance,
548  fhicl::ParameterSet const& pset, std::string pname
549  )
550  { return createEngine(module, type, instance, pset, { pname }); }
551 
552  template <typename Module>
553  [[nodiscard]] std::reference_wrapper<engine_t> createEngine(
554  Module& module, std::string type, std::string instance,
555  fhicl::ParameterSet const& pset, std::initializer_list<std::string> pnames
556  );
557 
558  template <typename Module>
559  [[nodiscard]] std::reference_wrapper<engine_t> createEngine(
560  Module& module, std::string type,
561  fhicl::ParameterSet const& pset, std::string pname
562  )
563  { return createEngine(module, type, "", pset, pname); }
564 
565  template <typename Module>
566  [[nodiscard]] std::reference_wrapper<engine_t> createEngine(
567  Module& module, std::string type,
568  fhicl::ParameterSet const& pset, std::initializer_list<std::string> pnames
569  )
570  { return createEngine(module, type, "", pset, pnames); }
571 
572  template <typename Module>
573  [[nodiscard]] std::reference_wrapper<engine_t> createEngine(
574  Module& module,
575  fhicl::ParameterSet const& pset, std::string pname
576  )
577  { return createEngine(module, pset, { pname }); }
578 
579  template <typename Module>
580  [[nodiscard]] std::reference_wrapper<engine_t> createEngine(
581  Module& module,
582  fhicl::ParameterSet const& pset, std::initializer_list<std::string> pnames
583  );
584  //@}
585 
586  /// @}
587 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP
588 
589  /// @{
590  /// @name Register an existing engine
591  ///
592  /// The life time of the engine is under user's control, while the seeding
593  /// is managed by this service.
594  ///
595 
596  /**
597  * @brief Registers an existing engine with NuRandomService
598  * @param seeder function used to set the seed of the existing engine
599  * @param instance name of the engine
600  * @return the seed assigned to the engine (may be invalid)
601  * @see createEngine()
602  *
603  * This function works similarly to createEngine(), but it uses an existing
604  * engine instead of creating a new one by RandomNumberGenerator service.
605  * The seeder function must be provided for the service to be of any use:
606  * registerEngine() will set the seed immediately, and the seeder function
607  * will be used to set the seed for policies that do that on each event.
608  * The instance name must also be unique, since for NuRandomService purposes
609  * the registered engine is no different from any other, created by
610  * RandomNumberGenerator or not.
611  *
612  * Three standard functions are provided as seeders, for use with
613  * RandomNumberGenerator engines (RandomNumberGeneratorSeeder()),
614  * with a CLHEP::HepRandomEngine (CLHEPengineSeeder class) and with ROOT's
615  * TRandom (TRandomSeeder class). Note that CLHEP and ROOT classes are not
616  * compiled in NuRandomService by default, and the recommendation is to take
617  * their implementation as an example and create your own after them).
618  * Any seeder function with the prototype of NuRandomService::Seeder_t:
619  *
620  * void Seeder(EngineId const&, seed_t);
621  *
622  * or a functor with
623  *
624  * void operator() (EngineId const&, seed_t);
625  *
626  * can be used as seeder.
627  */
628  seed_t registerEngine
629  (SeedMaster_t::Seeder_t seeder, std::string instance = "" );
630 
631  //@{
632  /**
633  * @brief Registers an existing engine with NuRandomService
634  * @param seeder function used to set the seed of the existing engine
635  * @param instance name of the engine
636  * @param pset parameter set to read parameters from
637  * @param pname name or names of the seed parameters
638  * @return the seed assigned to the engine (may be invalid)
639  * @see createEngine()
640  *
641  * These functions work similarly to registerEngine(), but the preferred
642  * way to obtain the seed is from configuration.
643  * First, the seed is retrieved from the specified
644  * configuration, looking for the first of the parameters in pname that is
645  * avaialble. If no parameter is found, the seed is obtained from
646  * NuRandomService.
647  */
649  SeedMaster_t::Seeder_t seeder, std::string instance,
650  fhicl::ParameterSet const& pset, std::string pname
651  )
652  { return registerEngine(seeder, instance, pset, { pname }); }
653  seed_t registerEngine(
654  SeedMaster_t::Seeder_t seeder, std::string instance,
655  fhicl::ParameterSet const& pset, std::initializer_list<std::string> pnames
656  );
658  SeedMaster_t::Seeder_t seeder,
659  fhicl::ParameterSet const& pset, std::string pname
660  )
661  { return registerEngine(seeder, "", pset, pname); }
663  SeedMaster_t::Seeder_t seeder,
664  fhicl::ParameterSet const& pset, std::initializer_list<std::string> pnames
665  )
666  { return registerEngine(seeder, "", pset, pnames); }
667  //@}
668 
669 #if (NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP)
670  /**
671  * @brief Registers an existing CLHEP engine with NuRandomService
672  * @param engine a reference to the CLHEP random generator engine
673  * @param instance name of the engine
674  * @param pset parameter set to read parameters from
675  * @param pname name or names of the seed parameters
676  * @return the seed assigned to the engine (may be invalid)
677  *
678  * The specified engine is not managed.
679  * It may be owned by RandomNumberGenerator service.
680  *
681  * The engine is expected to be valid as long as this service performs
682  * reseeding.
683  */
684  seed_t registerEngine
685  (CLHEP::HepRandomEngine& engine, std::string instance = "")
686  { return registerEngine(CLHEPengineSeeder(engine), instance); }
688  CLHEP::HepRandomEngine& engine, std::string instance,
689  fhicl::ParameterSet const& pset, std::initializer_list<std::string> pnames
690  )
691  {
692  return registerEngine
693  (CLHEPengineSeeder(engine), instance, pset, pnames);
694  }
695 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP
696  /// @}
697 
698 
699  /// @{
700  /// @name Declare the presence of an engine
701 
702  /**
703  * @brief Declares the presence of an engine with a given instance name
704  * @param instance name of the instance of the engine (empty by default)
705  * @return the seed assigned to the engine (may be invalid)
706  *
707  * The existence of an engine with the specified instance name is recorded,
708  * and a seed is assigned to it. The engine will be identified by the
709  * instance name and by context information (the current module).
710  *
711  * Differently from createEngine() and registerEngine(), the actual
712  * existence of a engine is not required. It is up to the user to manage
713  * the engine, if any at all, including the seeding.
714  */
715  seed_t declareEngine(std::string instance = "");
716 
717  /**
718  * @brief Declares the presence of an engine with a given instance name
719  * @param instance name of the instance of the engine
720  * @param pset parameter set where to find a possible fixed seed request
721  * @param pname the name of the parameter for the fixed seed request
722  * @return the seed assigned to the engine (may be invalid)
723  *
724  * The existence of an engine with the specified instance name is recorded,
725  * and a seed is assigned to it. The engine will be identified by the
726  * instance name and by context information (the current module).
727  *
728  * The preferred way to obtain the seed is from configuration.
729  * First, the seed is retrieved from the specified configuration,
730  * looking for the first of the parameters in pname that is available.
731  * If no parameter is found, the seed is obtained from NuRandomService.
732  *
733  * Differently from createEngine() and registerEngine(), the actual
734  * existence of a engine is not required. It is up to the user to manage
735  * the engine, if any at all, including the seeding.
736  */
737  seed_t declareEngine
738  (std::string instance, fhicl::ParameterSet const& pset, std::string pname)
739  { return declareEngine(instance, pset, { pname }); }
740 
741  /**
742  * @brief Declares the presence of an engine with a given instance name
743  * @param instance name of the instance of the engine
744  * @param pset parameter set where to find a possible fixed seed request
745  * @param pnames name of the parameters for the fixed seed request
746  * @return the seed assigned to the engine (may be invalid)
747  * @see declareEngine(std::string, fhicl::ParameterSet const&, std::string)
748  *
749  * This method provides the same function as
750  * declareEngine(std::string, fhicl::ParameterSet const&, std::string),
751  * but it can pick the seed from the first parameter among the ones in pset
752  * whose name is in pnames.
753  */
754  seed_t declareEngine(
755  std::string instance,
756  fhicl::ParameterSet const& pset, std::initializer_list<std::string> pnames
757  );
758 
759  /**
760  * @brief Declares the presence of an engine with a default instance name
761  * @param pset parameter set where to find a possible fixed seed request
762  * @param pname the name of the parameter for the fixed seed request
763  * @return the seed assigned to the engine (may be invalid)
764  * @see declareEngine(fhicl::ParameterSet const&, std::string)
765  *
766  * This method provides the same function as
767  * declareEngine(std::string, fhicl::ParameterSet const&, std::string),
768  * but it gives the engine a empty instance name.
769  */
771  { return declareEngine("", pset, pname); }
772 
773  /**
774  * @brief Declares the presence of an engine with a default instance name
775  * @param pset parameter set where to find a possible fixed seed request
776  * @param pnames name of the parameters for the fixed seed request
777  * @return the seed assigned to the engine (may be invalid)
778  * @see declareEngine(std::string, fhicl::ParameterSet const&, std::string)
779  *
780  * This method provides the same function as
781  * declareEngine(std::string, fhicl::ParameterSet const&, std::string),
782  * but it can pick the seed from the first parameter among the ones in pset
783  * whose name is in pnames.
784  * Also, it gives the engine a empty instance name.
785  */
787  fhicl::ParameterSet const& pset, std::initializer_list<std::string> pnames
788  )
789  { return declareEngine("", pset, pnames); }
790  /// @}
791 
792  /// @{
793  /**
794  * @brief Defines a seeder for a previously declared engine
795  * @param seeder seeder associated to the engine
796  * @param instance name of engine instance (default: empty)
797  * @return the seed assigned to the engine (may be invalid)
798  * @see declareEngine()
799  *
800  * The seeder is the same object as in registerEngine().
801  * This function can be used to finalise the declaration of an engine.
802  * If the engine was just declared with declareEngine() (as opposed to
803  * registered with registerEngine() or created with createEngine()),
804  * "defining" the engine will hook it to NuRandomService, that will take care
805  * of setting seeds automatically when needed.
806  * This step is not mandatory, but no automatic seeding will happen if it is
807  * omitted.
808  */
809  seed_t defineEngine
810  (SeedMaster_t::Seeder_t seeder, std::string instance = {});
811 
812 #if (NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP)
813  /**
814  * @brief Defines a seeder for a previously declared engine
815  * @param instance name of engine instance
816  * @param engine CLHEP engine to be associated to the instance
817  * @return the seed assigned to the engine (may be invalid)
818  * @see declareEngine()
819  *
820  * This method operates on the default engine instance and performs the
821  * same operations as defineEngine(std::string, Seeder_t).
822  * A seeder is internally created for the CLHEP random engine.
823  */
824  seed_t defineEngine
825  (CLHEP::HepRandomEngine& engine, std::string instance = {})
826  { return defineEngine(CLHEPengineSeeder(engine), instance); }
827 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP
828  /// @}
829 
830 
831  /// Prints known (EngineId,seed) pairs.
832  template<class Stream>
833  void print(Stream&& out) const
834  { seeds.print(std::forward<Stream>(out)); }
835 
836  /// Prints to the framework Info logger
837  void print() const { print(mf::LogInfo("NuRandomService")); }
838 
839 #if (NUTOOLS_RANDOMUTILS_NuRandomService_USEROOT)
840  /// Seeder_t functor setting the seed of a ROOT TRandom engine (untested!)
841  class TRandomSeeder {
842  public:
843  TRandomSeeder(TRandom* engine): pRandom(engine) {}
844  void operator() (EngineId const&, seed_t seed)
845  { if (pRandom) pRandom->SetSeed(seed); }
846  protected:
847  TRandom* pRandom = nullptr;
848  }; // class TRandomSeeder
849 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USEROOT
850 
851 #if (NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP)
852  /// Seeder_t functor setting the seed of a CLHEP::HepRandomEngine engine (untested!)
854  public:
855  CLHEPengineSeeder(CLHEP::HepRandomEngine& e): engine(e) {}
856  CLHEPengineSeeder(CLHEP::HepRandomEngine* e): engine(*e) {}
857  void operator() (EngineId const&, seed_t seed)
858  {
859  engine.setSeed(seed, 0);
860  MF_LOG_DEBUG("CLHEPengineSeeder")
861  << "CLHEP engine: '" << engine.name() << "'[" << ((void*) &engine)
862  << "].setSeed(" << seed << ", 0)";
863  }
864  protected:
865  CLHEP::HepRandomEngine& engine;
866  }; // class CLHEPengineSeeder
867 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP
868 
869  private:
870 
871  /// Class managing the seeds
873 
874  /**
875  * Helper to track state of art.
876  *
877  * The state is updated by NuRandomService itself, and therefore knows only
878  * about what it is notified about, when it is notified about.
879  * For example, service construction phase may start before the service
880  * was even constructed, but the state will be updated only on NuRandomService
881  * construction.
882  */
884 
885  /// Control the level of information messages.
886  int verbosity = 0;
887  bool bPrintEndOfJobSummary = false; ///< print a summary at the end of job
888 
889  /// Register an engine and seeds it with the seed from the master
890  seed_t registerEngineID(
891  EngineId const& id,
893  );
894 
895  /// Set the seeder of an existing engine
896  seed_t defineEngineID(EngineId const& id, SeedMaster_t::Seeder_t seeder);
897 
898 
899  /// Returns whether the specified engine is already registered
900  bool hasEngine(EngineId const& id) const { return seeds.hasEngine(id); }
901 
902  // Main logic for computing and validating a seed.
903  seed_t getSeed(EngineId const&);
904 
905  // Main logic for computing and validating a seed.
906  seed_t getEventSeed(EngineId const&);
907 
908  /**
909  * @brief Reseeds the specified engine instance in the current module
910  * @param instance the name of the engine instance
911  * @return the seed set, or InvalidSeed if no reseeding happened
912  */
913  seed_t reseedInstance(EngineId const& id);
914 
915  //@{
916  /// Reseeds all the engines in the current module
917  void reseedModule(std::string currentModule);
918  void reseedModule();
919  //@}
920 
921  /// Reseed all the global engines
922  void reseedGlobal();
923 
924  /// Registers the engine ID into SeedMaster
925  seed_t prepareEngine(EngineId const& id, SeedMaster_t::Seeder_t seeder);
926 
927  // Helper functions for all policies
928  void ensureValidState(bool bGlobal = false) const;
929 
930  //@{
931  /// Returns a fully qualified EngineId
932  EngineId qualify_engine_label
933  (std::string moduleLabel, std::string instanceName) const;
934  EngineId qualify_engine_label(std::string instanceName = "") const;
935  EngineId qualify_global_engine(std::string instanceName = "") const
936  { return EngineId(instanceName, EngineId::global); }
937  //@}
938 
939  //@{
940  /// Reads the seed from the first of the specified parameters available
941  /// @return whether any parameter was found
942  static bool readSeedParameter
943  (seed_t& seed, fhicl::ParameterSet const& pset, std::string pname)
944  { return readSeedParameter(seed, pset, { pname }); }
945  static bool readSeedParameter(
946  seed_t& seed, fhicl::ParameterSet const& pset,
947  std::initializer_list<std::string> pnames
948  );
949  //@}
950 
951  /// Query a seed from the seed master
952  seed_t querySeed(EngineId const& id);
953 
954  /// Helper to retrieve a seed including configuration
955  /// @return the seed, and whether it is fixed (that is, from configuration)
956  std::pair<seed_t, bool> findSeed(
957  EngineId const& id,
958  fhicl::ParameterSet const& pset, std::initializer_list<std::string> pnames
959  );
960 
961  /// Forces NuRandomService not to change the seed of the specified engine
962  void freezeSeed(EngineId const& id, seed_t frozen_seed);
963 
964  /// Registers an engine and its seeder
965  void registerEngineAndSeeder
966  (EngineId const& id, SeedMaster_t::Seeder_t seeder);
967 
968  /// Calls the seeder with the specified seed and engine ID
969  seed_t seedEngine(EngineId const& id) { return seeds.reseed(id); }
970 
971  // Call backs that will be called by art.
972  void preModuleConstruction (art::ModuleDescription const& md);
973  void postModuleConstruction(art::ModuleDescription const&);
974  void preModuleBeginRun (art::ModuleContext const& mc);
975  void postModuleBeginRun (art::ModuleContext const&);
976  void preProcessEvent (art::Event const& evt, art::ScheduleContext);
977  void preModule (art::ModuleContext const& mc);
978  void postModule (art::ModuleContext const&);
979  void postProcessEvent (art::Event const&, art::ScheduleContext);
980  void preModuleEndJob (art::ModuleDescription const& md);
981  void postModuleEndJob (art::ModuleDescription const&);
982  void postEndJob ();
983 
984 
985  }; // class NuRandomService
986 
987 
988 #if (NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP)
989  //----------------------------------------------------------------------------
990  // FIXME: See if the engine preparation can be done similarly to how
991  // it is described in the "Create and register an engine"
992  // documentation above.
993 
994  //----------------------------------------------------------------------------
995  template <typename Module>
996  std::reference_wrapper<NuRandomService::engine_t>
997  NuRandomService::createEngine(Module& module,
998  std::string type,
999  std::string instance /* = "" */)
1000  {
1001  EngineId id = qualify_engine_label(instance);
1002  auto& engine = module.createEngine(0, type, instance);
1003  const seed_t seed = prepareEngine(id, CLHEPengineSeeder{engine});
1004  engine.setSeed(seed, 0);
1005  mf::LogInfo("NuRandomService")
1006  << "Seeding " << type << " engine \"" << id.artName()
1007  << "\" with seed " << seed << ".";
1008  return engine;
1009  } // NuRandomService::createEngine(strings)
1010 
1011  template <typename Module>
1012  std::reference_wrapper<NuRandomService::engine_t>
1013  NuRandomService::createEngine(Module& module)
1014  {
1015  EngineId id = qualify_engine_label();
1016  auto& engine = module.createEngine(0);
1017  const seed_t seed = prepareEngine(id, CLHEPengineSeeder{engine});
1018  engine.setSeed(seed, 0);
1019  mf::LogInfo("NuRandomService")
1020  << "Seeding default-type engine \"" << id.artName()
1021  << "\" with seed " << seed << ".";
1022  return engine;
1023  } // NuRandomService::createEngine()
1024 
1025  template <typename Module>
1026  std::reference_wrapper<NuRandomService::engine_t>
1027  NuRandomService::createEngine(Module& module,
1028  std::string type,
1029  std::string instance,
1030  fhicl::ParameterSet const& pset,
1031  std::initializer_list<std::string> pnames)
1032  {
1033  EngineId id = qualify_engine_label(instance);
1034  auto& engine = module.createEngine(0, type, instance);
1035  registerEngineAndSeeder(id, CLHEPengineSeeder{engine});
1036  auto const [seed, frozen] = findSeed(id, pset, pnames);
1037  engine.setSeed(seed, 0);
1038  mf::LogInfo("NuRandomService")
1039  << "Seeding " << type << " engine \"" << id.artName()
1040  << "\" with seed " << seed << ".";
1041  if (frozen) freezeSeed(id, seed);
1042  return engine;
1043  } // NuRandomService::createEngine(ParameterSet)
1044 
1045 
1046  template <typename Module>
1047  std::reference_wrapper<NuRandomService::engine_t>
1048  NuRandomService::createEngine(Module& module,
1049  fhicl::ParameterSet const& pset,
1050  std::initializer_list<std::string> pnames)
1051  {
1052  EngineId id = qualify_engine_label();
1053  auto& engine = module.createEngine(0);
1054  registerEngineAndSeeder(id, CLHEPengineSeeder{engine});
1055  auto const [seed, frozen] = findSeed(id, pset, pnames);
1056  engine.setSeed(seed, 0);
1057  mf::LogInfo("NuRandomService")
1058  << "Seeding default-type engine \"" << id.artName()
1059  << "\" with seed " << seed << ".";
1060  if (frozen) freezeSeed(id, seed);
1061  return engine;
1062  } // NuRandomService::createEngine(ParameterSet)
1063 
1064 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP
1065 
1066 } // namespace rndm
1067 
1069 
1070 #endif // NUTOOLS_RANDOMUTILS_NuRandomService_H
std::reference_wrapper< engine_t > createEngine(Module &module, fhicl::ParameterSet const &pset, std::string pname)
CLHEP::HepRandomEngine engine_t
void print(Stream &&out) const
Prints known (EngineId,seed) pairs.
std::reference_wrapper< engine_t > createEngine(Module &module, std::string type, fhicl::ParameterSet const &pset, std::string pname)
Seeder_t functor setting the seed of a CLHEP::HepRandomEngine engine (untested!)
seed_t registerEngine(CLHEP::HepRandomEngine &engine, std::string instance, fhicl::ParameterSet const &pset, std::initializer_list< std::string > pnames)
seed_t getCurrentSeed() const
Returns the last computed seed for the default engine of current module.
std::string string
Definition: nybbler.cc:12
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
seed_t getCurrentSeed(std::string instanceName) const
Returns the last computed seed for specified engine of current module.
const std::string instance
seed_t seedEngine(EngineId const &id)
Calls the seeder with the specified seed and engine ID.
seed_t registerEngine(SeedMaster_t::Seeder_t seeder, fhicl::ParameterSet const &pset, std::initializer_list< std::string > pnames)
#define DECLARE_ART_SERVICE(svc, scope)
Definition: ServiceMacros.h:86
static constexpr seed_t InvalidSeed
An invalid seed.
Definition: SeedMaster.h:258
seed_t registerEngine(SeedMaster_t::Seeder_t seeder, fhicl::ParameterSet const &pset, std::string pname)
bool hasEngine(EngineId const &id) const
Returns whether the specified engine is already registered.
Definition: SeedMaster.h:277
const double e
EngineId qualify_global_engine(std::string instanceName="") const
SeedMasterHelper::EngineId EngineId
type of engine ID
Definition: SeedMaster.h:213
#define Module
void print() const
Prints to the framework Info logger.
std::function< void(EngineId const &, seed_t)> Seeder_t
type of a function setting a seed
Definition: SeedMaster.h:216
Describe the current state of art processing, as understood by the NuRandomService.
Definition: ArtState.h:32
CLHEPengineSeeder(CLHEP::HepRandomEngine *e)
NuRandomServiceHelper::ArtState state
std::reference_wrapper< engine_t > createEngine(Module &module, std::string type, std::string instance, fhicl::ParameterSet const &pset, std::string pname)
Creates an engine with RandomNumberGenerator service.
Identifier for a engine, made of module name and optional instance name.
Definition: EngineId.h:22
static constexpr bool isSeedValid(seed_t seed)
Returns whether the specified seed is valid.
seed_t declareEngine(fhicl::ParameterSet const &pset, std::string pname)
Declares the presence of an engine with a default instance name.
CLHEPengineSeeder(CLHEP::HepRandomEngine &e)
seed_t getGlobalCurrentSeed(std::string instanceName) const
Returns the last computed seed for the specified global engine.
std::vector< TrajPoint > seeds
Definition: DataStructs.cxx:13
static QCString currentModule
name of the current enclosing module
#define MF_LOG_DEBUG(id)
seed_t registerEngine(SeedMaster_t::Seeder_t seeder, std::string instance, fhicl::ParameterSet const &pset, std::string pname)
Registers an existing engine with NuRandomService.
bool hasEngine(EngineId const &id) const
Returns whether the specified engine is already registered.
std::reference_wrapper< engine_t > createEngine(Module &module, std::string type, fhicl::ParameterSet const &pset, std::initializer_list< std::string > pnames)
seed_t reseed(EngineId const &id)
Reseeds the specified engine with a global seed (if any)
Definition: SeedMaster.h:558
TCEvent evt
Definition: DataStructs.cxx:7
seed_t declareEngine(fhicl::ParameterSet const &pset, std::initializer_list< std::string > pnames)
Declares the presence of an engine with a default instance name.
An art service to assist in the distribution of guaranteed unique seeds to all engines within an art ...
SeedMaster_t seeds
Class managing the seeds.
art::detail::EngineCreator::seed_t seed_t