RandomNumberGenerator.h
Go to the documentation of this file.
1 #ifndef art_Framework_Services_Optional_RandomNumberGenerator_h
2 #define art_Framework_Services_Optional_RandomNumberGenerator_h
3 // vim: set sw=2 expandtab :
4 
5 // ==================================================================
6 // A service to maintain multiple independent random number engines.
7 // ==================================================================
8 //
9 // Via this RandomNumberGenerator, the CLHEP random number engines are
10 // made available such that a client module may establish and
11 // subsequently employ any number of independent engines, each of any
12 // of the CLHEP engine types. Any created random number engines are
13 // owned either by art, or by an external party. There is no use case
14 // of the RandomNumberGenerator service for which the user owns the
15 // engine.
16 //
17 // Any producer, analyzer, or filter module of the legacy or
18 // replicated threading types may use this service as needed (this
19 // header file is implicitly included for any such modules). However,
20 // by design, source modules are permitted to make no use of this
21 // service. In addition, no output modules, nor any modules of the
22 // shared threading type may use this service.
23 //
24 // Creating an engine
25 // ------------------
26 //
27 // Each engine to be used by a module must be created in that module's
28 // constructor. Any modules that desire to create an engine must
29 // explicitly call the non-default base-class constructor (e.g.):
30 //
31 // MyProducer(Parameters const& p,
32 // ProcessingFrame const& frame)
33 // : ReplicatedProducer{p, frame}
34 // {}
35 //
36 // Creating an engine involves specifying:
37 // - An integer seed value to initialize the engine's state
38 // - The desired kind of engine ("HepJamesRandom" by default)
39 // - A label string (empty by default)
40 // Within a module, no two engines may share an identical label.
41 //
42 // The above three items of information are supplied in a single call
43 // to a function named createEngine(). Because two of the three items
44 // have defaults (and may therefore be omitted), the call may take any
45 // of several forms; the following three examples therefore have
46 // equivalent effect:
47 //
48 // createEngine(seed)
49 // createEngine(seed, "HepJamesRandom")
50 // createEngine(seed, "HepJamesRandom", "")
51 //
52 // Each such call returns a non-const reference to the newly-created
53 // engine, that is owned by the framework. The returned reference can
54 // be safely and immediately used by the CLHEP library to create a
55 // random number distribution. For example:
56 //
57 // CLHEP::RandFlat dist{createEngine(...)};
58 //
59 // In rare circumstances, the reference to the engine may be stored as
60 // a module-class data member.
61 //
62 // Creating the global engine
63 // --------------------------
64 //
65 // CLHEP provides the notion of a global engine, and Geant4 makes use
66 // of this feature. It is recommended that the designated Geant4
67 // module (and no other) should create this global engine.
68 //
69 // The Service recognizes the notation "G4Engine" to create the engine
70 // that is used by Geant4. It is strongly recommended to ignore the
71 // resulting engine reference, leaving exclusive future engine use to
72 // Geant4 itself:
73 //
74 // createEngine(seed, "G4Engine");
75 //
76 // Digression: obtaining a seed value
77 // ----------------------------------
78 //
79 // As noted above, creating an engine involves specifying a seed
80 // value. Determining this value is at the discretion of the module
81 // creating the engine, and can be done in any of several manners.
82 // Here are some possibilities to get you started:
83 //
84 // - Each CLHEP engine has a default seed value. To specify the use
85 // of this default seed, use the magic value -1 as the seed
86 // argument in the createEngine() call:
87 //
88 // createEngine(-1);
89 //
90 // - Specify a (non-negative) constant of your choice as the seed
91 // argument in the createEngine() call:
92 //
93 // createEngine(13597);
94 //
95 // - Obtain a seed value from the module's ParameterSet, typically
96 // with some fallback value in case the ParameterSet omits the
97 // specified parameter:
98 //
99 // createEngine(pset.get<int>("seed", 13597));
100 //
101 // - For replicated modules, care must be taken *by the user* to
102 // ensure that each module copy is initialized with the desired
103 // seed. If a module's configuration specifies a seed of 13597,
104 // then a reasonable engine creation may look like:
105 //
106 // createEngine(pset.get<int>("seed", 13597) +
107 // frame.scheduleID().id());
108 //
109 // where the 'frame' is passed in as a replicated-module
110 // constructor argument. This way, each module copy for a
111 // configured replicated module is guaranteed to have a different
112 // seeds wrt. each other.
113 //
114 // Configuring the Service
115 // -----------------------
116 //
117 // TODO: draft this section
118 //
119 // TODO: assess the following remarks from the placeholder
120 // implementation and determine whether they are still valid/useful.
121 //
122 // When a separate Producer module is also included in the path, the
123 // state of all the engines managed by this service can be saved to
124 // the event. Then in a later process, the RandomNumberGenerator is
125 // capable of restoring the state of the engines from the event in
126 // order to be able to exactly reproduce the earlier process.
127 // ==================================================================
128 
129 #include "CLHEP/Random/RandomEngine.h"
135 #include "fhiclcpp/types/Atom.h"
136 #include "fhiclcpp/types/Name.h"
137 
138 #include <map>
139 #include <memory>
140 #include <mutex>
141 #include <string>
142 #include <vector>
143 
144 namespace CLHEP {
145  class HepRandomEngine;
146 } // namespace CLHEP
147 
148 namespace art {
149 
150  class ActivityRegistry;
151  class Event;
152  class ScheduleContext;
153 
154  namespace detail {
155  class EngineCreator;
156  }
157 
159 
160  friend class EventProcessor;
161  friend class detail::EngineCreator;
162  friend class RandomNumberSaver;
163 
164  public:
165  // Used by createEngine, restoreSnapshot_, and restoreFromFile_.
166  enum class EngineSource { Seed = 1, File = 2, Product = 3 };
167 
168  static long constexpr maxCLHEPSeed{900000000}; // non MixMaxRng
169  static long constexpr useDefaultSeed{-1};
170  using base_engine_t = CLHEP::HepRandomEngine;
171  using seed_t = long;
172 
173  struct Config {
174  template <typename T>
176  using Name = fhicl::Name;
178  Atom<std::string> defaultEngineKind{
179  Name{"defaultEngineKind"},
180  Comment{
181  "The 'defaultEngineKind' parameter can be any of the following:\n\n"
182  " 'DRand48Engine'\n"
183  " 'DualRand'\n"
184  " 'Hurd160Engine'\n"
185  " 'Hurd288Engine'\n"
186  " 'HepJamesRandom' (art default)\n"
187  " 'MixMaxRng' (CLHEP default)\n"
188  " 'MTwistEngine'\n"
189  " 'RanecuEngine'\n"
190  " 'Ranlux64Engine'\n"
191  " 'RanluxEngine'\n"
192  " 'RanshiEngine'\n"
193  " 'TripleRand'\n"},
194  "HepJamesRandom"};
195  Atom<std::string> restoreStateLabel{
196  Name{"restoreStateLabel"},
197  Comment{
198  "The 'restoreStateLabel' parameter specifies the input tag used\n"
199  "to restore the random number engine states, as stored in the\n"
200  "data product produced by the RandomNumberSaver module.\n"},
201  ""};
203  Name{"saveTo"},
204  Comment{
205  "The 'saveTo' and 'restoreFrom' parameters are filenames\n"
206  "that indicate where the engine states should be saved-to or\n"
207  "restored-from, respectively. Engine states are saved at the\n"
208  "end of an art job. This allows a user to (e.g.) send a Ctrl+C\n"
209  "signal during debugging, which will then save the engine states\n"
210  "to the specified file during a graceful shutdown. The user can\n"
211  "then restore the engine states from the file and rerun the job,\n"
212  "skipping to the appropriate event, but with the correct random\n"
213  "engine states."},
214  ""};
215  Atom<std::string> restoreFrom{Name{"restoreFrom"}, ""};
217  Name{"debug"},
218  Comment{"Enable printout of random engine states for debugging."},
219  false};
221  Name{"nPrint"},
222  Comment{
223  "Limit the number of printouts to the specified value.\n"
224  "This parameter can be specified only if 'debug' above is true."},
225  [this] { return debug(); },
226  10u};
227  };
228 
230 
231  // Special Member Functions
235  RandomNumberGenerator& operator=(RandomNumberGenerator const&) = delete;
236  RandomNumberGenerator& operator=(RandomNumberGenerator&&) = delete;
237 
238  std::string const&
240  {
241  return defaultEngineKind_;
242  }
243 
244  private:
245  // Engine establishment
246  // Accessible to user modules through friendship. Should only be used in
247  // ctors.
248  CLHEP::HepRandomEngine& createEngine(
249  ScheduleID sid,
250  std::string const& module_label,
251  long seed,
252  std::string const& kind_of_engine_to_make,
253  std::string const& engine_label = {});
254 
255  void validate_(std::string const& user_specified_engine_kind,
256  long user_specified_seed) noexcept(false);
257 
258  // Snapshot management helpers
259  void takeSnapshot_(ScheduleID);
260  void restoreSnapshot_(ScheduleID, Event const&);
261  std::vector<RNGsnapshot> const& accessSnapshot_(ScheduleID) const;
262 
263  // File management helpers
264  // TODO: Determine if this facility is necessary.
265  void saveToFile_();
266  void restoreFromFile_();
267 
268  // Debugging helpers
269  void print_() const;
270  bool invariant_holds_(ScheduleID);
271 
272  // Callbacks from the framework
273  void preProcessEvent(Event const&, ScheduleContext);
274  void postProcessEvent(Event const&, ScheduleContext);
275  void postBeginJob();
276  void postEndJob();
277 
278  // Protects all data members.
279  mutable std::recursive_mutex mutex_{};
280 
282 
283  // Product key for restoring from a snapshot
285 
286  // File name for saving state
288 
289  // File name for restoring state
291 
292  // Tracing and debug controls
293  bool const debug_;
294  unsigned const nPrint_;
295 
296  // Guard against tardy engine creation
297  bool engine_creation_is_okay_{true};
298 
299  // Per-schedule data
300  struct ScheduleData {
301  // The labeled random number engines for this stream.
302  // Indexed by engine label.
303  std::map<std::string, std::shared_ptr<CLHEP::HepRandomEngine>> dict_{};
304 
305  // The most recent source of the labeled random number engines for this
306  // stream. Indexed by engine label. When EngineSource == Seed, this means
307  // an engine with the given label has been created by createEngine(sid,
308  // seed, ...). When EngineSource == File, this means the engine was
309  // created by restoring it from a file. When EngineSource == Product, this
310  // means the engine was created by restoring it from a snapshot data
311  // product with module label "restoreStateLabel".
312  std::map<std::string, EngineSource> tracker_{};
313 
314  // The requested engine kind for each labeled random number engine for
315  // this stream. Indexed by engine label.
316  std::map<std::string, std::string> kind_{};
317 
318  // The random engine number state snapshots taken for this stream.
319  std::vector<RNGsnapshot> snapshot_{};
320  };
322  };
323 
324 } // namespace art
325 
327 
328 #endif /* art_Framework_Services_Optional_RandomNumberGenerator_h */
329 
330 // Local Variables:
331 // mode: c++
332 // End:
std::string string
Definition: nybbler.cc:12
std::string const restoreFromFilename_
ChannelGroupService::Name Name
PerScheduleContainer< ScheduleData > data_
std::string const & defaultEngineKind() const noexcept
#define DECLARE_ART_SERVICE(svc, scope)
CLHEP::HepRandomEngine base_engine_t
#define Comment
Definition: types.h:32