SeedTestPolicy_module.cc
Go to the documentation of this file.
1 /**
2  * @file SeedTestPolicy_module.cc
3  * @brief Test the NuRandomService.
4  * @author Rob Kutschke (kutschke@fnal.gov), Gianluca Petrillo (petrillo@fnal.gov)
5  * @see NuRandomService.hh
6  */
7 
8 // test library
9 #include "SeedTestUtils.h"
10 
11 // nutools libraries
12 #define NUTOOLS_RANDOMUTILS_NuRandomService_USECLHEP 1
13 #include "nutools/RandomUtils/NuRandomService.h"
14 
15 // framework
24 
25 // supporting libraries
27 
28 // CLHEP libraries
29 #include "CLHEP/Random/RandomEngine.h" // CLHEP::HepRandomEngine
30 #include "CLHEP/Random/JamesRandom.h" // CLHEP::HepJamesRandom
31 #include "CLHEP/Random/RandFlat.h"
32 
33 // C/C++ standard library
34 #include <string>
35 #include <vector>
36 #include <sstream>
37 #include <iomanip> // std::setw()
38 #include <memory> // std::unique_ptr<>
39 
40 
41 namespace testing {
42 
43  /**
44  * @brief Test module for NuRandomService
45  *
46  * The test writes on screen the random seeds it gets.
47  *
48  * Note that the test does not actually get any random number, unless the
49  * *useGenerators* option is turned on.
50  *
51  * Configuration parameters:
52  * - <b>instanceNames</b> (string list): use one random number generator
53  * for each instance name here specified; if not specified, a single
54  * default instance is used
55  * - <b>expectedErrors</b> (unsigned integer, default: 0): expect this number
56  * of errors from NuRandomService, and throw an exception if we get a different
57  * number
58  * - <b>useGenerators</b> (boolean, default: true): uses
59  * art RandomGeneratorService with the seeds from NuRandomService
60  * - <b>perEventSeeds</b> (boolean, default: false): set it to true if the
61  * selected policy gives per-event seeds; in this case, the check of
62  * seed always being the same is skipped
63  *
64  */
66 
67  public:
68 
69  explicit SeedTestPolicy(fhicl::ParameterSet const& pset);
70 
71  virtual void analyze(art::Event const& event) override;
72 
73  virtual void endJob() override;
74 
75  private:
77 
78  std::vector<std::string> instanceNames;
79  std::map<std::string, seed_t> startSeeds; ///< seeds after the constructor
80  unsigned int nExpectedErrors; ///< number of expected errors
81  bool useGenerators; ///< instanciate and use random number generators
82  bool perEventSeeds; ///< whether we expect different seeds on each event
83  std::string const moduleLabel; ///< configured module label
84 
85  unsigned int nErrors = 0; ///< Number of errors detected so far
86 
87  std::unique_ptr<CLHEP::HepRandomEngine> localEngine; ///< self-managed
88  std::map<std::string, CLHEP::HepRandomEngine*> engines;
89 
90  /// Returns whether the engine associated with the specified index is local
91  bool isLocalEngine(size_t iEngine) const;
92 
93  seed_t verifySeed(CLHEP::HepRandomEngine&, std::string const& instanceName);
94 
95  seed_t obtainSeed(std::string instanceName = "");
96 
97  /// Returns whether e is an exception we can handle (and, if so, it handles)
99 
100  }; // class SeedTestPolicy
101 
102 } // namespace testing
103 
104 
105 
106 //------------------------------------------------------------------------------
107 //--- implementation
108 //---
109 
111  : art::EDAnalyzer(pset)
112  , instanceNames (pset.get<std::vector<std::string>>("instanceNames", {}))
113  , nExpectedErrors(pset.get<unsigned int> ("expectedErrors", 0U))
114  , useGenerators (pset.get<bool> ("useGenerators", true))
115  , perEventSeeds (pset.get<bool> ("perEventSeeds", false))
116  , moduleLabel (pset.get<std::string> ("module_label"))
117 {
118 
119  //
120  // print some configuration information
121  //
122  { // anonymous block
123  mf::LogInfo log("SeedTestPolicy");
124  log << "Construct SeedTestPolicy with "
125  << instanceNames.size() << " engine instances:";
126  for (auto const& instanceName: instanceNames)
127  log << " " << instanceName;
128 
129  } // anonymous block
130 
131  auto* Seeds = &*(art::ServiceHandle<rndm::NuRandomService>());
132 
133  // by default, have at least one, default engine instance
134  if (instanceNames.empty()) instanceNames.push_back("");
135 
136  mf::LogInfo log("SeedTestPolicy"); // cumulative log
137 
138  //
139  // register all the engines, and store their seeds
140  //
141  for (std::string const& instanceName: instanceNames) {
142  seed_t const seed = obtainSeed(instanceName);
143  log << "\nSeed for '" << instanceName << "' is: " << seed;
144  startSeeds.emplace(instanceName, seed);
145  } // for first loop (declaration)
146 
147 
148  //
149  // verify the seed of each instance
150  //
151  for (size_t iEngine = 0; iEngine < instanceNames.size(); ++iEngine) {
152  std::string const& instanceName = instanceNames[iEngine];
153 
154  // This involved condition tree ensures that SeedMaster is queried
155  // for a seed exactly once per instance, no matter what.
156  // This is relevant for the error count.
157  // Out of it, a seed is returned.
159  if (isLocalEngine(iEngine)) {
160  if (useGenerators) {
161  localEngine = std::make_unique<CLHEP::HepJamesRandom>();
162  engines.emplace(instanceName, localEngine.get());
163  try {
164  seed = Seeds->defineEngine(*localEngine, instanceName);
165  }
166  catch(art::Exception& e) {
167  if (!handleSeedServiceException(e)) throw;
168  }
169  mf::LogInfo("SeedTestConstruct")
170  << "Engine instance '" << instanceName
171  << "' will be owned by the test module.";
172  } // if use generators
173  else seed = obtainSeed(instanceName);
174  } // if local
175  else { // if managed by art
176  seed = obtainSeed(instanceName);
177  if (useGenerators) {
178  auto& engine = createEngine(seed, "HepJamesRandom", instanceName);
179  // registration still matters for per-event policies
180  Seeds->defineEngine(engine, instanceName);
181  verifySeed(engine, instanceName);
182  engines.emplace(instanceName, &engine);
183  }
184  } // if ... else
185 
186  // check that the seed returned by the service is still the same
187  seed_t const expectedSeed = startSeeds.at(instanceName);
188  if (seed != expectedSeed) {
190  << "NuRandomService returned different seed values for engine instance '"
191  << instanceName << "': first " << expectedSeed << ", now " << seed
192  << "\n";
193  } // if unexpected seed
194  } // for second loop
195 
196  //
197  // An engine with the following label has already been registered
198  // (it's the one managed by RandomGeneratorService).
199  // Registering another should raise an exception
200  // (incidentally, if useGenerators is false we are trying to register nullptr)
201  //
202  bool bBug = false;
203  try {
204  Seeds->declareEngine(instanceNames.front());
205  bBug = true;
206  }
207  catch(std::exception const& e) {
209  }
210  if (bBug) {
212  << "Registration of local engine with duplicate label"
213  " did not throw an exception";
214  }
215 
216 } // testing::SeedTestPolicy::SeedTestPolicy()
217 
218 
219 //------------------------------------------------------------------------------
221 
222  mf::LogVerbatim("SeedTestPolicy")
223  << "SeedTestPolicy::analyze() " << event.id() << " with "
224  << instanceNames.size() << " random engines";
225 
226  if (useGenerators) {
227  for (auto const& instanceName : instanceNames) {
228  //
229  // collect information and resources
230  //
231  seed_t const startSeed = startSeeds.at(instanceName);
232  CLHEP::HepRandomEngine& engine = *engines.at(instanceName);
233 
234  //
235  // check seed (if per event, it should be the opposite)
236  //
237  seed_t const actualSeed = testing::NuRandomService::readSeed(engine);
238  if (perEventSeeds) {
239  if (actualSeed == startSeed) {
240  // this has a ridiculously low chance of begin fortuitous
242  << "per event seed " << actualSeed << " of engine '" << instanceName
243  << "' is the same as at beginning!\n";
244  }
245  }
246  else {
247  if (actualSeed != startSeed) {
249  << "expected seed " << startSeed << " for engine '" << instanceName
250  << "', got " << actualSeed << " instead!\n";
251  }
252  }
253 
254  //
255  // print character statistics
256  //
257  mf::LogVerbatim("SeedTestPolicy")
258  << std::setw(12) << (instanceName.empty()? "<default>": instanceName)
260  << " (seed: " << actualSeed << ")";
261 
262  } // for
263  } // if use generators
264 
265 } // testing::SeedTestPolicy::analyze()
266 
267 
268 //------------------------------------------------------------------------------
269 void
271 {
272  // if we have an unexpected amount of errors, bail out
273  if (nExpectedErrors != nErrors) {
275  e << "SeedTestPolicy: detected " << nErrors << " errors";
276  if (nExpectedErrors) e << ", " << nExpectedErrors << " expected";
277  throw e << "!\n";
278  }
279 } // testing::SeedTestPolicy::endJob()
280 
281 
282 //------------------------------------------------------------------------------
283 
286 {
287  // Returns the seed for the specified engine instance, or 0 in case of
288  // configuration error (in which case, an error counter is increased)
290  try {
292  // currently (v0_00_03), the two calls are actually equivalent
293  seed
294  = instanceName.empty()? seeds->getSeed(): seeds->getSeed(instanceName);
295  }
296  catch(art::Exception& e) {
298 
299  ++nErrors;
300  mf::LogError log("SeedTestPolicy");
301  log << "Detected";
302  if (nErrors > nExpectedErrors) log << " UNEXPECTED";
303  log << " error #" << nErrors << ":\n" << e;
304  }
305  return seed;
306 } // testing::SeedTestPolicy::obtainSeed()
307 
308 
309 bool
311 {
313 
314  ++nErrors;
315  mf::LogError log("SeedTest01");
316  log << "Detected";
317  if (nErrors > nExpectedErrors) log << " UNEXPECTED";
318  log << " error #" << nErrors << ":\n" << e << "\n";
319  return true;
320 } // testing::SeedTestPolicy::handleSeedServiceException()
321 
322 
324  return (i == 0) && (instanceNames.size() != 1);
325 } // testing::SeedTestPolicy::isLocalEngine()
326 
327 
329 testing::SeedTestPolicy::verifySeed(CLHEP::HepRandomEngine& engine,
330  std::string const& instanceName)
331 {
332  seed_t const actualSeed = testing::NuRandomService::readSeed(engine);
333  seed_t const expectedSeed = startSeeds.at(instanceName);
334  // if the expected seed is invalid, we are not even sure it was ever set;
335  // the engine is in an invalid state and that's it
336  if (!rndm::NuRandomService::isSeedValid(expectedSeed)) return actualSeed;
337 
338  if (actualSeed != expectedSeed) {
340  << "expected seed " << expectedSeed << " for engine '" << instanceName
341  << "', got " << actualSeed << " instead!";
342  }
343  return actualSeed;
344 } // testing::SeedTestPolicy::verifySeed()
345 
346 
347 //------------------------------------------------------------------------------
349 
350 //------------------------------------------------------------------------------
base_engine_t & createEngine(seed_t seed)
unsigned int nErrors
Number of errors detected so far.
seed_t getSeed(std::string instanceName)
Returns a seed for the engine with specified instance name.
MaybeLogger_< ELseverityLevel::ELsev_info, true > LogVerbatim
LArSoft test utilities.
virtual void analyze(art::Event const &event) override
virtual void endJob() override
std::string string
Definition: nybbler.cc:12
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
std::string const moduleLabel
configured module label
art::detail::EngineCreator::seed_t seed_t
Type of seed.
Definition: SeedTestUtils.h:32
bool handleSeedServiceException(art::Exception &e)
Returns whether e is an exception we can handle (and, if so, it handles)
STL namespace.
std::vector< std::string > instanceNames
EDAnalyzer(fhicl::ParameterSet const &pset)
Definition: EDAnalyzer.h:27
SeedTestPolicy(fhicl::ParameterSet const &pset)
seed_t verifySeed(CLHEP::HepRandomEngine &, std::string const &instanceName)
const double e
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:69
std::string CreateCharacter(CLHEP::HepRandomEngine &engine)
Creates a "character statistics" using the specified random engine.
bool perEventSeeds
whether we expect different seeds on each event
std::unique_ptr< CLHEP::HepRandomEngine > localEngine
self-managed
static constexpr seed_t InvalidSeed
An invalid seed.
Functions used in NuRandomService tests.
static constexpr bool isSeedValid(seed_t seed)
Returns whether the specified seed is valid.
seed_t obtainSeed(std::string instanceName="")
unsigned int nExpectedErrors
number of expected errors
std::map< std::string, seed_t > startSeeds
seeds after the constructor
Test module for NuRandomService.
Q_EXPORT QTSManip setw(int w)
Definition: qtextstream.h:331
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
std::vector< TrajPoint > seeds
Definition: DataStructs.cxx:13
seed_t readSeed(CLHEP::HepRandomEngine const &engine)
Returns the seed of the specified engine (CLHEP overload)
std::map< std::string, CLHEP::HepRandomEngine * > engines
bool isSeedServiceException(std::exception const &e)
Returns whether the exception looks to be from NuRandomService.
bool isLocalEngine(size_t iEngine) const
Returns whether the engine associated with the specified index is local.
auto const & get(AssnsNode< L, R, D > const &r)
Definition: AssnsNode.h:115
bool useGenerators
instanciate and use random number generators
testing::NuRandomService::seed_t seed_t
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33
Event finding and building.