Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2025-04-13 22:50:03

0001 // -*- C++ -*-
0002 //
0003 // Package:     RandomEngine
0004 // Class  :     RandomNumberGeneratorService
0005 //
0006 // Implementation:
0007 //     <Notes on implementation>
0008 //
0009 // Original Author:  Chris Jones, W. David Dagenhart
0010 //   Created:  Tue Mar  7 09:43:46 EST 2006 (originally in FWCore/Services)
0011 //
0012 
0013 #include "RandomNumberGeneratorService.h"
0014 
0015 #include "DataFormats/Common/interface/Handle.h"
0016 #include "DataFormats/Provenance/interface/ModuleDescription.h"
0017 #include "FWCore/Framework/interface/ConsumesCollector.h"
0018 #include "FWCore/Framework/interface/Event.h"
0019 #include "FWCore/Framework/interface/LuminosityBlock.h"
0020 #include "FWCore/Framework/interface/TriggerNamesService.h"
0021 #include "FWCore/MessageLogger/interface/JobReport.h"
0022 #include "FWCore/ParameterSet/interface/ConfigurationDescriptions.h"
0023 #include "FWCore/ParameterSet/interface/ParameterDescription.h"
0024 #include "FWCore/ParameterSet/interface/ParameterSet.h"
0025 #include "FWCore/ParameterSet/interface/ParameterSetDescription.h"
0026 #include "FWCore/ParameterSet/interface/ParameterWildcard.h"
0027 #include "FWCore/ServiceRegistry/interface/CurrentModuleOnThread.h"
0028 #include "FWCore/ServiceRegistry/interface/ActivityRegistry.h"
0029 #include "FWCore/ServiceRegistry/interface/ModuleCallingContext.h"
0030 #include "FWCore/ServiceRegistry/interface/Service.h"
0031 #include "FWCore/ServiceRegistry/interface/StreamContext.h"
0032 #include "FWCore/ServiceRegistry/interface/SystemBounds.h"
0033 #include "FWCore/Utilities/interface/EDMException.h"
0034 #include "FWCore/Utilities/interface/Exception.h"
0035 #include "FWCore/Utilities/interface/LuminosityBlockIndex.h"
0036 #include "FWCore/Utilities/interface/StreamID.h"
0037 #include "IOMC/RandomEngine/interface/TRandomAdaptor.h"
0038 #include "IOMC/RandomEngine/interface/cloneEngine.h"
0039 #include "SimDataFormats/RandomEngine/interface/RandomEngineState.h"
0040 #include "SimDataFormats/RandomEngine/interface/RandomEngineStates.h"
0041 
0042 #include "CLHEP/Random/engineIDulong.h"
0043 #include "CLHEP/Random/JamesRandom.h"
0044 #include "CLHEP/Random/RanecuEngine.h"
0045 #include "CLHEP/Random/MixMaxRng.h"
0046 
0047 #include <algorithm>
0048 #include <cassert>
0049 #include <ostream>
0050 #include <sstream>
0051 #include <unistd.h>
0052 
0053 namespace edm {
0054   namespace service {
0055 
0056     const std::vector<std::uint32_t>::size_type RandomNumberGeneratorService::maxSeeds = 65536U;
0057     const std::vector<std::uint32_t>::size_type RandomNumberGeneratorService::maxStates = 65536U;
0058     const std::uint32_t RandomNumberGeneratorService::maxSeedRanecu = 2147483647U;
0059     const std::uint32_t RandomNumberGeneratorService::maxSeedHepJames = 900000000U;
0060     const std::uint32_t RandomNumberGeneratorService::maxSeedTRandom3 = 4294967295U;
0061 
0062     RandomNumberGeneratorService::RandomNumberGeneratorService(ParameterSet const& pset,
0063                                                                ActivityRegistry& activityRegistry)
0064         : nStreams_(0),
0065           saveFileName_(pset.getUntrackedParameter<std::string>("saveFileName")),
0066           saveFileNameRecorded_(false),
0067           restoreFileName_(pset.getUntrackedParameter<std::string>("restoreFileName")),
0068           enableChecking_(pset.getUntrackedParameter<bool>("enableChecking")),
0069           eventSeedOffset_(pset.getUntrackedParameter<unsigned>("eventSeedOffset")),
0070           verbose_(pset.getUntrackedParameter<bool>("verbose")) {
0071       if (pset.exists("restoreStateTag")) {
0072         restoreStateTag_ = pset.getUntrackedParameter<edm::InputTag>("restoreStateTag");
0073         if (restoreStateTag_.process().empty()) {
0074           restoreStateTag_ = edm::InputTag(restoreStateTag_.label(), "", edm::InputTag::kSkipCurrentProcess);
0075         }
0076       } else {
0077         restoreStateTag_ = edm::InputTag(
0078             pset.getUntrackedParameter<std::string>("restoreStateLabel"), "", edm::InputTag::kSkipCurrentProcess);
0079       }
0080       restoreStateBeginLumiTag_ = edm::InputTag(restoreStateTag_.label(), "beginLumi", restoreStateTag_.process());
0081 
0082       if (!restoreFileName_.empty() && !restoreStateTag_.label().empty()) {
0083         throw Exception(errors::Configuration) << "In the configuration for the RandomNumberGeneratorService both\n"
0084                                                << "restoreFileName and restoreStateLabel were set to nonempty values\n"
0085                                                << "which is illegal.  It is impossible to restore the random engine\n"
0086                                                << "states two different ways in the same process.\n";
0087       }
0088 
0089       // The saveFileName must correspond to a file name without any path specification.
0090       // Throw if that is not true.
0091       if (!saveFileName_.empty() && (saveFileName_.find('/') != std::string::npos)) {
0092         throw Exception(errors::Configuration)
0093             << "The saveFileName parameter must be a simple file name with no path\n"
0094             << "specification. In the configuration, it was given the value \"" << saveFileName_ << "\"\n";
0095       }
0096 
0097       std::uint32_t initialSeed;
0098       VUint32 initialSeedSet;
0099       std::string engineName;
0100 
0101       std::vector<std::string> pSets = pset.getParameterNamesForType<ParameterSet>();
0102       for (auto const& label : pSets) {
0103         ParameterSet const& modulePSet = pset.getParameterSet(label);
0104         engineName = modulePSet.getUntrackedParameter<std::string>("engineName", std::string("HepJamesRandom"));
0105 
0106         bool initialSeedExists = modulePSet.exists("initialSeed");
0107         bool initialSeedSetExists = modulePSet.exists("initialSeedSet");
0108 
0109         if (initialSeedExists && initialSeedSetExists) {
0110           throw Exception(errors::Configuration) << "For the module with the label \"" << label << "\",\n"
0111                                                  << "both the parameters \"initialSeed\" and \"initialSeedSet\"\n"
0112                                                  << "have been set in the configuration. You must set one or\n"
0113                                                  << "the other.  It is illegal to set both.\n";
0114         } else if (!initialSeedExists && !initialSeedSetExists) {
0115           throw Exception(errors::Configuration) << "For the module with the label \"" << label << "\",\n"
0116                                                  << "neither the parameter \"initialSeed\" nor \"initialSeedSet\"\n"
0117                                                  << "has been set in the configuration. You must set one or\n"
0118                                                  << "the other.\n";
0119         } else if (initialSeedExists) {
0120           initialSeed = modulePSet.getUntrackedParameter<std::uint32_t>("initialSeed");
0121           initialSeedSet.clear();
0122           initialSeedSet.push_back(initialSeed);
0123         } else if (initialSeedSetExists) {
0124           initialSeedSet = modulePSet.getUntrackedParameter<VUint32>("initialSeedSet");
0125         }
0126         seedsAndNameMap_.insert(std::pair<std::string, SeedsAndName>(label, SeedsAndName(initialSeedSet, engineName)));
0127 
0128         // For the CLHEP::RanecuEngine case, require a seed set containing exactly two seeds.
0129         if (engineName == std::string("RanecuEngine")) {
0130           if (initialSeedSet.size() != 2U) {
0131             throw Exception(errors::Configuration)
0132                 << "Random engines of type \"RanecuEngine\" require 2 seeds\n"
0133                 << "be specified with the parameter named \"initialSeedSet\".\n"
0134                 << "Either \"initialSeedSet\" was not in the configuration\n"
0135                 << "or its size was not 2 for the module with label \"" << label << "\".\n";
0136           }
0137           if (initialSeedSet[0] > maxSeedRanecu ||
0138               initialSeedSet[1] > maxSeedRanecu) {  // They need to fit in a 31 bit integer
0139             throw Exception(errors::Configuration)
0140                 << "The RanecuEngine seeds should be in the range 0 to " << maxSeedRanecu << ".\n"
0141                 << "The seeds passed to the RandomNumberGenerationService from the\n"
0142                    "configuration file were "
0143                 << initialSeedSet[0] << " and " << initialSeedSet[1] << "\nThis was for the module with label \""
0144                 << label << "\".\n";
0145           }
0146         }
0147         // For the other engines, one seed is required
0148         else {
0149           if (initialSeedSet.size() != 1U) {
0150             throw Exception(errors::Configuration)
0151                 << "Random engines of type \"HepJamesRandom\", \"TRandom3\" and \"MixMaxRng\" \n"
0152                 << "require exactly 1 seed be specified in the configuration.\n"
0153                 << "There were " << initialSeedSet.size() << " seeds set for the\n"
0154                 << "module with label \"" << label << "\".\n";
0155           }
0156           if (engineName == "HepJamesRandom") {
0157             if (initialSeedSet[0] > maxSeedHepJames) {
0158               throw Exception(errors::Configuration)
0159                   << "The CLHEP::HepJamesRandom engine seed should be in the range 0 to " << maxSeedHepJames << ".\n"
0160                   << "The seed passed to the RandomNumberGenerationService from the\n"
0161                      "configuration file was "
0162                   << initialSeedSet[0] << ".  This was for \n"
0163                   << "the module with label " << label << ".\n";
0164             }
0165           } else if (engineName == "MixMaxRng") {
0166             if (initialSeedSet[0] > maxSeedTRandom3) {
0167               throw Exception(errors::Configuration)
0168                   << "The CLHEP::MixMaxRng engine seed should be in the range 0 to " << maxSeedTRandom3 << ".\n"
0169                   << "The seed passed to the RandomNumberGenerationService from the\n"
0170                      "configuration file was "
0171                   << initialSeedSet[0] << ".  This was for \n"
0172                   << "the module with label " << label << ".\n";
0173             }
0174           } else if (engineName == "TRandom3") {
0175             if (initialSeedSet[0] > maxSeedTRandom3) {
0176               throw Exception(errors::Configuration)
0177                   << "The CLHEP::MixMaxRng engine seed should be in the range 0 to " << maxSeedTRandom3 << ".\n"
0178                   << "The seed passed to the RandomNumberGenerationService from the\n"
0179                      "configuration file was "
0180                   << initialSeedSet[0] << ".  This was for \n"
0181                   << "the module with label " << label << ".\n";
0182             }
0183           } else {
0184             throw Exception(errors::Configuration)
0185                 << "The random engine name, \"" << engineName << "\", does not correspond to a supported engine.\n"
0186                 << "This engine was configured for the module with label \"" << label << "\"";
0187           }
0188         }
0189       }
0190       activityRegistry.watchPreModuleConstruction(this, &RandomNumberGeneratorService::preModuleConstruction);
0191       activityRegistry.watchPreModuleDestruction(this, &RandomNumberGeneratorService::preModuleDestruction);
0192 
0193       activityRegistry.watchPreallocate(this, &RandomNumberGeneratorService::preallocate);
0194 
0195       activityRegistry.watchPreBeginJob(this, &RandomNumberGeneratorService::preBeginJob);
0196       activityRegistry.watchPostBeginJob(this, &RandomNumberGeneratorService::postBeginJob);
0197       activityRegistry.watchPreEndJob(this, &RandomNumberGeneratorService::preEndJob);
0198       activityRegistry.watchPostEndJob(this, &RandomNumberGeneratorService::postEndJob);
0199 
0200       if (enableChecking_) {
0201         activityRegistry.watchPreModuleBeginStream(this, &RandomNumberGeneratorService::preModuleBeginStream);
0202         activityRegistry.watchPostModuleBeginStream(this, &RandomNumberGeneratorService::postModuleBeginStream);
0203 
0204         activityRegistry.watchPreModuleEndStream(this, &RandomNumberGeneratorService::preModuleEndStream);
0205         activityRegistry.watchPostModuleEndStream(this, &RandomNumberGeneratorService::postModuleEndStream);
0206 
0207         activityRegistry.watchPreModuleStreamBeginRun(this, &RandomNumberGeneratorService::preModuleStreamBeginRun);
0208         activityRegistry.watchPostModuleStreamBeginRun(this, &RandomNumberGeneratorService::postModuleStreamBeginRun);
0209 
0210         activityRegistry.watchPreModuleStreamEndRun(this, &RandomNumberGeneratorService::preModuleStreamEndRun);
0211         activityRegistry.watchPostModuleStreamEndRun(this, &RandomNumberGeneratorService::postModuleStreamEndRun);
0212 
0213         activityRegistry.watchPreModuleStreamBeginLumi(this, &RandomNumberGeneratorService::preModuleStreamBeginLumi);
0214         activityRegistry.watchPostModuleStreamBeginLumi(this, &RandomNumberGeneratorService::postModuleStreamBeginLumi);
0215 
0216         activityRegistry.watchPreModuleStreamEndLumi(this, &RandomNumberGeneratorService::preModuleStreamEndLumi);
0217         activityRegistry.watchPostModuleStreamEndLumi(this, &RandomNumberGeneratorService::postModuleStreamEndLumi);
0218       }
0219     }
0220 
0221     RandomNumberGeneratorService::~RandomNumberGeneratorService() {}
0222 
0223     void RandomNumberGeneratorService::consumes(ConsumesCollector&& iC) const {
0224       iC.consumes<RandomEngineStates, InLumi>(restoreStateBeginLumiTag_);
0225       iC.consumes<RandomEngineStates>(restoreStateTag_);
0226     }
0227 
0228     CLHEP::HepRandomEngine& RandomNumberGeneratorService::getEngine(StreamID const& streamID) {
0229       ModuleCallingContext const* mcc = CurrentModuleOnThread::getCurrentModuleOnThread();
0230       if (mcc == nullptr || beginJobEndJobActive_) {
0231         throw Exception(errors::LogicError)
0232             << "RandomNumberGeneratorService::getEngine\n"
0233                "Requested a random number engine from the RandomNumberGeneratorService\n"
0234                "while ModuleCallingContext is null or during beginJob or endJob transitions.\n";
0235       }
0236       unsigned int moduleID = mcc->moduleDescription()->id();
0237 
0238       std::vector<ModuleIDToEngine>& moduleIDVector = streamModuleIDToEngine_.at(streamID.value());
0239       ModuleIDToEngine target(nullptr, moduleID);
0240       std::vector<ModuleIDToEngine>::iterator iter =
0241           std::lower_bound(moduleIDVector.begin(), moduleIDVector.end(), target);
0242       if (iter == moduleIDVector.end() || iter->moduleID() != moduleID) {
0243         throw Exception(errors::Configuration)
0244             << "The module with label \"" << mcc->moduleDescription()->moduleLabel()
0245             << "\" requested a random number engine from the \n"
0246                "RandomNumberGeneratorService, but that module was not configured\n"
0247                "for random numbers.  An engine is created only if a seed(s) is provided\n"
0248                "in the configuration file.  Please add the following PSet to the\n"
0249                "configuration file for the RandomNumberGeneratorService:\n\n"
0250                "  "
0251             << mcc->moduleDescription()->moduleLabel()
0252             << " = cms.PSet(\n"
0253                "    initialSeed = cms.untracked.uint32(your_seed),\n"
0254                "    engineName = cms.untracked.string('TRandom3')\n"
0255                "  )\n"
0256                "where you replace \"your_seed\" with a number and add a comma if necessary\n"
0257                "The \"engineName\" parameter is optional. If absent the default is \"HepJamesRandom\".\n";
0258       }
0259       return *iter->labelAndEngine()->engine();
0260     }
0261 
0262     CLHEP::HepRandomEngine& RandomNumberGeneratorService::getEngine(LuminosityBlockIndex const& lumiIndex) {
0263       ModuleCallingContext const* mcc = CurrentModuleOnThread::getCurrentModuleOnThread();
0264       if (mcc == nullptr || beginJobEndJobActive_) {
0265         throw Exception(errors::LogicError)
0266             << "RandomNumberGeneratorService::getEngine\n"
0267                "Requested a random number engine from the RandomNumberGeneratorService\n"
0268                "while ModuleCallingContext is null or during beginJob or endJob transitions.\n";
0269       }
0270       unsigned int moduleID = mcc->moduleDescription()->id();
0271 
0272       std::vector<ModuleIDToEngine>& moduleIDVector = lumiModuleIDToEngine_.at(lumiIndex.value());
0273       ModuleIDToEngine target(nullptr, moduleID);
0274       std::vector<ModuleIDToEngine>::iterator iter =
0275           std::lower_bound(moduleIDVector.begin(), moduleIDVector.end(), target);
0276       if (iter == moduleIDVector.end() || iter->moduleID() != moduleID) {
0277         throw Exception(errors::Configuration)
0278             << "The module with label \"" << mcc->moduleDescription()->moduleLabel()
0279             << "\" requested a random number engine from the \n"
0280                "RandomNumberGeneratorService, but that module was not configured\n"
0281                "for random numbers.  An engine is created only if a seed(s) is provided\n"
0282                "in the configuration file.  Please add the following PSet to the\n"
0283                "configuration file for the RandomNumberGeneratorService:\n\n"
0284                "  "
0285             << mcc->moduleDescription()->moduleLabel()
0286             << " = cms.PSet(\n"
0287                "    initialSeed = cms.untracked.uint32(your_seed),\n"
0288                "    engineName = cms.untracked.string('TRandom3')\n"
0289                "  )\n"
0290                "where you replace \"your_seed\" with a number and add a comma if necessary\n"
0291                "The \"engineName\" parameter is optional. If absent the default is \"HepJamesRandom\".\n";
0292       }
0293       return *iter->labelAndEngine()->engine();
0294     }
0295 
0296     std::unique_ptr<CLHEP::HepRandomEngine> RandomNumberGeneratorService::cloneEngine(
0297         LuminosityBlockIndex const& lumiIndex) {
0298       return edm::cloneEngine(getEngine(lumiIndex));
0299     }
0300 
0301     // PROBABLY TO BE DELETED, This returns the configured seed without
0302     // any of the modifications for streams or the offset configuration
0303     // parameter. Maybe useful to use for debugging/checks, but dangerous if one tries
0304     // to create your own engines using it. It is difficult to get the offsets
0305     // for streams/forking/offset parameters correct and almost certainly would break
0306     // replay.
0307     std::uint32_t RandomNumberGeneratorService::mySeed() const {
0308       std::string label;
0309       ModuleCallingContext const* mcc = CurrentModuleOnThread::getCurrentModuleOnThread();
0310       if (mcc == nullptr || beginJobEndJobActive_) {
0311         throw Exception(errors::LogicError)
0312             << "RandomNumberGeneratorService::mySeed()\n"
0313                "Requested a random number seed from the RandomNumberGeneratorService\n"
0314                "while ModuleCallingContext is null or during beginJob or endJob transitions.\n";
0315       } else {
0316         label = mcc->moduleDescription()->moduleLabel();
0317       }
0318 
0319       std::map<std::string, SeedsAndName>::const_iterator iter = seedsAndNameMap_.find(label);
0320       if (iter == seedsAndNameMap_.end()) {
0321         throw Exception(errors::Configuration)
0322             << "The module with label \"" << label
0323             << "\" requested a random number seed from the \n"
0324                "RandomNumberGeneratorService, but that module was not configured\n"
0325                "for random numbers.  An engine is created only if a seed(s) is provided\n"
0326                "in the configuration file.  Please add the following PSet to the\n"
0327                "configuration file for the RandomNumberGeneratorService:\n\n"
0328                "  "
0329             << label
0330             << " = cms.PSet(\n"
0331                "    initialSeed = cms.untracked.uint32(your_seed),\n"
0332                "    engineName = cms.untracked.string('TRandom3')\n"
0333                "  )\n"
0334                "where you replace \"your_seed\" with a number and add a comma if necessary\n"
0335                "The \"engineName\" parameter is optional. If absent the default is \"HepJamesRandom\".\n";
0336       }
0337       return iter->second.seeds()[0];
0338     }
0339 
0340     void RandomNumberGeneratorService::fillDescriptions(ConfigurationDescriptions& descriptions) {
0341       ParameterSetDescription desc;
0342 
0343       std::string emptyString;
0344       edm::InputTag emptyInputTag("", "", "");
0345 
0346       desc.addNode(edm::ParameterDescription<edm::InputTag>("restoreStateTag", emptyInputTag, false) xor
0347                    edm::ParameterDescription<std::string>("restoreStateLabel", emptyString, false));
0348 
0349       desc.addUntracked<std::string>("saveFileName", emptyString);
0350       desc.addUntracked<std::string>("restoreFileName", emptyString);
0351       desc.addUntracked<bool>("enableChecking", false);
0352       desc.addUntracked<unsigned>("eventSeedOffset", 0U);
0353       desc.addUntracked<bool>("verbose", false);
0354 
0355       ParameterSetDescription val;
0356       val.addOptionalUntracked<std::uint32_t>("initialSeed");
0357       val.addOptionalUntracked<std::vector<std::uint32_t> >("initialSeedSet");
0358       val.addOptionalUntracked<std::string>("engineName");
0359 
0360       ParameterWildcard<ParameterSetDescription> wnode("*", RequireZeroOrMore, true, val);
0361       wnode.setComment("The name of each ParameterSet will be the associated module label.");
0362       desc.addNode(wnode);
0363 
0364       descriptions.add("RandomNumberGeneratorService", desc);
0365     }
0366 
0367     void RandomNumberGeneratorService::preModuleConstruction(ModuleDescription const& description) {
0368       std::map<std::string, SeedsAndName>::iterator iter = seedsAndNameMap_.find(description.moduleLabel());
0369       if (iter != seedsAndNameMap_.end()) {
0370         iter->second.setModuleID(description.id());
0371       }
0372     }
0373 
0374     void RandomNumberGeneratorService::preModuleDestruction(ModuleDescription const& description) {
0375       std::map<std::string, SeedsAndName>::iterator iter = seedsAndNameMap_.find(description.moduleLabel());
0376       if (iter != seedsAndNameMap_.end()) {
0377         iter->second.setModuleID(SeedsAndName::kInvalid);
0378       }
0379     }
0380 
0381     void RandomNumberGeneratorService::preallocate(SystemBounds const& sb) {
0382       nStreams_ = sb.maxNumberOfStreams();
0383       assert(nStreams_ >= 1);
0384       if (!restoreFileName_.empty() && nStreams_ != 1) {
0385         throw Exception(errors::Configuration)
0386             << "Configuration is illegal. The RandomNumberGeneratorService is configured\n"
0387             << "to run replay using a text file to input the random engine states and\n"
0388             << "the number of streams is greater than 1. Either set the\n"
0389             << "parameter named \"restoreFileName\" in the RandomNumberGeneratorService\n"
0390             << "to the empty string or set the parameter \"numberOfStreams\" in the top\n"
0391             << "level options parameter set to 1. (Probably these are the default values\n"
0392             << "and just not setting the parameters will also work)\n";
0393       }
0394       unsigned int nConcurrentLumis = sb.maxNumberOfConcurrentLuminosityBlocks();
0395 
0396       streamModuleIDToEngine_.resize(nStreams_);
0397       lumiModuleIDToEngine_.resize(nConcurrentLumis);
0398       streamEngines_.resize(nStreams_);
0399       lumiEngines_.resize(nConcurrentLumis);
0400       eventCache_.resize(nStreams_);
0401       lumiCache_.resize(nConcurrentLumis);
0402       outFiles_.resize(nStreams_);
0403 
0404       for (unsigned int iStream = 0; iStream < nStreams_; ++iStream) {
0405         unsigned int seedOffset = iStream;
0406         createEnginesInVector(streamEngines_[iStream], seedOffset, eventSeedOffset_, streamModuleIDToEngine_[iStream]);
0407         if (!saveFileName_.empty()) {
0408           outFiles_[iStream] = std::make_shared<std::ofstream>();  // propagate_const<T> has no reset() function
0409         }
0410       }
0411       for (unsigned int iLumi = 0; iLumi < nConcurrentLumis; ++iLumi) {
0412         unsigned int seedOffset = nStreams_;
0413         createEnginesInVector(lumiEngines_[iLumi], seedOffset, 0, lumiModuleIDToEngine_[iLumi]);
0414         snapShot(lumiEngines_[iLumi], lumiCache_[iLumi]);
0415         if (!restoreFileName_.empty()) {
0416           readLumiStatesFromTextFile(restoreFileName_, lumiCache_[iLumi]);
0417         }
0418       }
0419 
0420       if (!restoreFileName_.empty()) {
0421         // There is guaranteed to be one stream in this case
0422         snapShot(streamEngines_[0], eventCache_[0]);
0423         readEventStatesFromTextFile(restoreFileName_, eventCache_[0]);
0424         restoreFromCache(eventCache_[0], streamEngines_[0]);
0425       }
0426       if (verbose_) {
0427         print(std::cout);
0428       }
0429     }
0430 
0431     void RandomNumberGeneratorService::preBeginJob(ProcessContext const&) { beginJobEndJobActive_ = true; }
0432 
0433     void RandomNumberGeneratorService::postBeginJob() { beginJobEndJobActive_ = false; }
0434 
0435     void RandomNumberGeneratorService::preEndJob() { beginJobEndJobActive_ = true; }
0436 
0437     void RandomNumberGeneratorService::postEndJob() { beginJobEndJobActive_ = false; }
0438 
0439     void RandomNumberGeneratorService::preBeginLumi(LuminosityBlock const& lumi) {
0440       if (!restoreStateTag_.label().empty()) {
0441         // Copy from a product in the LuminosityBlock to cache for a particular luminosityBlockIndex
0442         readFromLuminosityBlock(lumi);
0443       }
0444       // Copy from cache to engine the state for a particular luminosityBlockIndex
0445       restoreFromCache(lumiCache_[lumi.index()], lumiEngines_[lumi.index()]);
0446     }
0447 
0448     void RandomNumberGeneratorService::postEventRead(Event const& event) {
0449       if (!restoreStateTag_.label().empty()) {
0450         // This initializes the cache before readFromEvent
0451         snapShot(streamEngines_[event.streamID()], eventCache_[event.streamID()]);
0452 
0453         // copy from Event to event cache
0454         readFromEvent(event);
0455 
0456         // copy from event cache to engines
0457         restoreFromCache(eventCache_[event.streamID()], streamEngines_[event.streamID()]);
0458 
0459       } else {
0460         // copy from engines to event cache
0461         snapShot(streamEngines_[event.streamID()], eventCache_[event.streamID()]);
0462       }
0463 
0464       // if requested write text file from both caches
0465       if (!saveFileName_.empty()) {
0466         saveStatesToFile(saveFileName_, event.streamID(), event.getLuminosityBlock().index());
0467         bool expected = false;
0468         if (saveFileNameRecorded_.compare_exchange_strong(expected, true)) {
0469           std::string fullName = constructSaveFileName();
0470           Service<JobReport> reportSvc;
0471           reportSvc->reportRandomStateFile(fullName);
0472         }
0473       }
0474     }
0475 
0476     void RandomNumberGeneratorService::setLumiCache(LuminosityBlockIndex iLumi,
0477                                                     std::vector<RandomEngineState> const& iStates) {
0478       lumiCache_[iLumi] = iStates;
0479       // Copy from cache to engine the state for a particular luminosityBlockIndex
0480       restoreFromCache(lumiCache_[iLumi], lumiEngines_[iLumi]);
0481     }
0482     void RandomNumberGeneratorService::setEventCache(StreamID iStream, std::vector<RandomEngineState> const& iStates) {
0483       eventCache_[iStream] = iStates;
0484       // copy from event cache to engines
0485       restoreFromCache(eventCache_[iStream], streamEngines_[iStream]);
0486     }
0487 
0488     void RandomNumberGeneratorService::preModuleBeginStream(StreamContext const& sc, ModuleCallingContext const& mcc) {
0489       preModuleStreamCheck(sc, mcc);
0490     }
0491 
0492     void RandomNumberGeneratorService::postModuleBeginStream(StreamContext const& sc, ModuleCallingContext const& mcc) {
0493       postModuleStreamCheck(sc, mcc);
0494     }
0495 
0496     void RandomNumberGeneratorService::preModuleEndStream(StreamContext const& sc, ModuleCallingContext const& mcc) {
0497       preModuleStreamCheck(sc, mcc);
0498     }
0499 
0500     void RandomNumberGeneratorService::postModuleEndStream(StreamContext const& sc, ModuleCallingContext const& mcc) {
0501       postModuleStreamCheck(sc, mcc);
0502     }
0503 
0504     void RandomNumberGeneratorService::preModuleStreamBeginRun(StreamContext const& sc,
0505                                                                ModuleCallingContext const& mcc) {
0506       preModuleStreamCheck(sc, mcc);
0507     }
0508 
0509     void RandomNumberGeneratorService::postModuleStreamBeginRun(StreamContext const& sc,
0510                                                                 ModuleCallingContext const& mcc) {
0511       postModuleStreamCheck(sc, mcc);
0512     }
0513 
0514     void RandomNumberGeneratorService::preModuleStreamEndRun(StreamContext const& sc, ModuleCallingContext const& mcc) {
0515       preModuleStreamCheck(sc, mcc);
0516     }
0517 
0518     void RandomNumberGeneratorService::postModuleStreamEndRun(StreamContext const& sc,
0519                                                               ModuleCallingContext const& mcc) {
0520       postModuleStreamCheck(sc, mcc);
0521     }
0522 
0523     void RandomNumberGeneratorService::preModuleStreamBeginLumi(StreamContext const& sc,
0524                                                                 ModuleCallingContext const& mcc) {
0525       preModuleStreamCheck(sc, mcc);
0526     }
0527 
0528     void RandomNumberGeneratorService::postModuleStreamBeginLumi(StreamContext const& sc,
0529                                                                  ModuleCallingContext const& mcc) {
0530       postModuleStreamCheck(sc, mcc);
0531     }
0532 
0533     void RandomNumberGeneratorService::preModuleStreamEndLumi(StreamContext const& sc,
0534                                                               ModuleCallingContext const& mcc) {
0535       preModuleStreamCheck(sc, mcc);
0536     }
0537 
0538     void RandomNumberGeneratorService::postModuleStreamEndLumi(StreamContext const& sc,
0539                                                                ModuleCallingContext const& mcc) {
0540       postModuleStreamCheck(sc, mcc);
0541     }
0542 
0543     std::vector<RandomEngineState> const& RandomNumberGeneratorService::getLumiCache(
0544         LuminosityBlockIndex const& lumiIndex) const {
0545       return lumiCache_.at(lumiIndex.value());
0546     }
0547 
0548     std::vector<RandomEngineState> const& RandomNumberGeneratorService::getEventCache(StreamID const& streamID) const {
0549       return eventCache_.at(streamID.value());
0550     }
0551 
0552     void RandomNumberGeneratorService::print(std::ostream& os) const {
0553       os << "\n\nRandomNumberGeneratorService dump\n\n";
0554 
0555       os << "    Contents of seedsAndNameMap (label moduleID engineType seeds)\n";
0556       for (auto const& entry : seedsAndNameMap_) {
0557         os << "        " << entry.first << "  " << entry.second.moduleID() << "  " << entry.second.engineName();
0558         for (auto val : entry.second.seeds()) {
0559           os << "  " << val;
0560         }
0561         os << "\n";
0562       }
0563       os << "    nStreams_ = " << nStreams_ << "\n";
0564       os << "    saveFileName_ = " << saveFileName_ << "\n";
0565       os << "    saveFileNameRecorded_ = " << saveFileNameRecorded_ << "\n";
0566       os << "    restoreFileName_ = " << restoreFileName_ << "\n";
0567       os << "    enableChecking_ = " << enableChecking_ << "\n";
0568       os << "    eventSeedOffset_ = " << eventSeedOffset_ << "\n";
0569       os << "    verbose_ = " << verbose_ << "\n";
0570       os << "    restoreStateTag_ = " << restoreStateTag_ << "\n";
0571       os << "    restoreStateBeginLumiTag_ = " << restoreStateBeginLumiTag_ << "\n";
0572 
0573       os << "\n    streamEngines_\n";
0574       unsigned int iStream = 0;
0575       for (auto const& k : streamEngines_) {
0576         os << "        Stream " << iStream << "\n";
0577         for (auto const& i : k) {
0578           os << "        " << i.label();
0579           for (auto const& j : i.seeds()) {
0580             os << " " << j;
0581           }
0582           os << " " << i.engine()->name();
0583           if (i.engine()->name() == std::string("HepJamesRandom")) {
0584             os << "  " << i.engine()->getSeed();
0585           } else if (i.engine()->name() == std::string("MixMaxRng")) {
0586             os << "  " << i.engine()->getSeed();
0587           } else {
0588             os << "  engine does not know seeds";
0589           }
0590           os << "\n";
0591         }
0592         ++iStream;
0593       }
0594       os << "\n    lumiEngines_\n";
0595       unsigned int iLumi = 0;
0596       for (auto const& k : lumiEngines_) {
0597         os << "        lumiIndex " << iLumi << "\n";
0598         for (auto const& i : k) {
0599           os << "        " << i.label();
0600           for (auto const& j : i.seeds()) {
0601             os << " " << j;
0602           }
0603           os << " " << i.engine()->name();
0604           if (i.engine()->name() == std::string("HepJamesRandom")) {
0605             os << "  " << i.engine()->getSeed();
0606           } else if (i.engine()->name() == std::string("MixMaxRng")) {
0607             os << "  " << i.engine()->getSeed();
0608           } else {
0609             os << "  engine does not know seeds";
0610           }
0611           os << "\n";
0612         }
0613         ++iLumi;
0614       }
0615     }
0616 
0617     void RandomNumberGeneratorService::preModuleStreamCheck(StreamContext const& sc, ModuleCallingContext const& mcc) {
0618       if (enableChecking_) {
0619         unsigned int moduleID = mcc.moduleDescription()->id();
0620         std::vector<ModuleIDToEngine>& moduleIDVector = streamModuleIDToEngine_.at(sc.streamID().value());
0621         ModuleIDToEngine target(nullptr, moduleID);
0622         std::vector<ModuleIDToEngine>::iterator iter =
0623             std::lower_bound(moduleIDVector.begin(), moduleIDVector.end(), target);
0624         if (iter != moduleIDVector.end() && iter->moduleID() == moduleID) {
0625           LabelAndEngine* labelAndEngine = iter->labelAndEngine();
0626           iter->setEngineState(labelAndEngine->engine()->put());
0627         }
0628       }
0629     }
0630 
0631     void RandomNumberGeneratorService::postModuleStreamCheck(StreamContext const& sc, ModuleCallingContext const& mcc) {
0632       if (enableChecking_) {
0633         unsigned int moduleID = mcc.moduleDescription()->id();
0634         std::vector<ModuleIDToEngine>& moduleIDVector = streamModuleIDToEngine_.at(sc.streamID().value());
0635         ModuleIDToEngine target(nullptr, moduleID);
0636         std::vector<ModuleIDToEngine>::iterator iter =
0637             std::lower_bound(moduleIDVector.begin(), moduleIDVector.end(), target);
0638         if (iter != moduleIDVector.end() && iter->moduleID() == moduleID) {
0639           LabelAndEngine* labelAndEngine = iter->labelAndEngine();
0640           if (iter->engineState() != labelAndEngine->engine()->put()) {
0641             throw Exception(errors::LogicError)
0642                 << "It is illegal to generate random numbers during beginStream, endStream,\n"
0643                    "beginRun, endRun, beginLumi, endLumi because that makes it very difficult\n"
0644                    "to replay the processing of individual events.  Random numbers were\n"
0645                    "generated during one of these methods for the module with class name\n\""
0646                 << mcc.moduleDescription()->moduleName()
0647                 << "\" "
0648                    "and module label \""
0649                 << mcc.moduleDescription()->moduleLabel() << "\"\n";
0650           }
0651         }
0652       }
0653     }
0654 
0655     void RandomNumberGeneratorService::readFromLuminosityBlock(LuminosityBlock const& lumi) {
0656       Service<TriggerNamesService> tns;
0657       if (tns.isAvailable()) {
0658         if (tns->getProcessName() == restoreStateTag_.process()) {
0659           throw Exception(errors::Configuration)
0660               << "In the configuration for the RandomNumberGeneratorService the\n"
0661               << "restoreStateTag contains the current process which is illegal.\n"
0662               << "The process name in the replay process should have been changed\n"
0663               << "to be different than the original process name and the restoreStateTag\n"
0664               << "should contain either the original process name or an empty process name.\n";
0665         }
0666       }
0667 
0668       Handle<RandomEngineStates> states;
0669       lumi.getByLabel(restoreStateBeginLumiTag_, states);
0670 
0671       if (!states.isValid()) {
0672         throw Exception(errors::ProductNotFound)
0673             << "The RandomNumberGeneratorService is trying to restore\n"
0674             << "the state of the random engines by reading a product from\n"
0675             << "the LuminosityBlock with input tag \"" << restoreStateBeginLumiTag_ << "\".\n"
0676             << "It could not find the product.\n"
0677             << "Either the product in the LuminosityBlock was dropped or\n"
0678             << "not produced or the configured input tag is incorrect or there is a bug somewhere\n";
0679         return;
0680       }
0681       states->getRandomEngineStates(lumiCache_.at(lumi.index()));
0682     }
0683 
0684     void RandomNumberGeneratorService::readFromEvent(Event const& event) {
0685       Handle<RandomEngineStates> states;
0686 
0687       event.getByLabel(restoreStateTag_, states);
0688 
0689       if (!states.isValid()) {
0690         throw Exception(errors::ProductNotFound)
0691             << "The RandomNumberGeneratorService is trying to restore\n"
0692             << "the state of the random engines by reading a product from\n"
0693             << "the Event with input tag \"" << restoreStateTag_ << "\".\n"
0694             << "It could not find the product.\n"
0695             << "Either the product in the Event was dropped or\n"
0696             << "not produced or the configured input tag is incorrect or there is a bug somewhere\n";
0697         return;
0698       }
0699       states->getRandomEngineStates(eventCache_.at(event.streamID()));
0700     }
0701 
0702     void RandomNumberGeneratorService::snapShot(std::vector<LabelAndEngine> const& engines,
0703                                                 std::vector<RandomEngineState>& cache) {
0704       cache.resize(engines.size());
0705       std::vector<RandomEngineState>::iterator state = cache.begin();
0706 
0707       for (std::vector<LabelAndEngine>::const_iterator iter = engines.begin(); iter != engines.end(); ++iter, ++state) {
0708         std::string const& label = iter->label();
0709         state->setLabel(label);
0710         state->setSeed(iter->seeds());
0711 
0712         std::vector<unsigned long> stateL = iter->engine()->put();
0713         state->clearStateVector();
0714         state->reserveStateVector(stateL.size());
0715         for (auto element : stateL) {
0716           state->push_back_stateVector(static_cast<std::uint32_t>(element));
0717         }
0718       }
0719     }
0720 
0721     void RandomNumberGeneratorService::restoreFromCache(std::vector<RandomEngineState> const& cache,
0722                                                         std::vector<LabelAndEngine>& engines) {
0723       std::vector<LabelAndEngine>::iterator labelAndEngine = engines.begin();
0724       for (auto const& cachedState : cache) {
0725         std::string const& engineLabel = cachedState.getLabel();
0726 
0727         std::vector<std::uint32_t> const& engineState = cachedState.getState();
0728         std::vector<unsigned long> engineStateL;
0729         engineStateL.reserve(engineState.size());
0730         for (auto const& value : engineState) {
0731           engineStateL.push_back(static_cast<unsigned long>(value));
0732         }
0733 
0734         std::vector<std::uint32_t> const& engineSeeds = cachedState.getSeed();
0735         std::vector<long> engineSeedsL;
0736         engineSeedsL.reserve(engineSeeds.size());
0737         for (auto const& val : engineSeeds) {
0738           long seedL = static_cast<long>(val);
0739           engineSeedsL.push_back(seedL);
0740 
0741           // There is a dangerous conversion from std::uint32_t to long
0742           // that occurs above. In the next 2 lines we check the
0743           // behavior is what we need for the service to work
0744           // properly.  This conversion is forced on us by the
0745           // CLHEP and ROOT interfaces. If the assert ever starts
0746           // to fail we will have to come up with a way to deal
0747           // with this.
0748           std::uint32_t seedu32 = static_cast<std::uint32_t>(seedL);
0749           assert(val == seedu32);
0750         }
0751 
0752         assert(labelAndEngine != engines.end() && engineLabel == labelAndEngine->label());
0753         std::shared_ptr<CLHEP::HepRandomEngine> const& engine = labelAndEngine->engine();
0754 
0755         // We need to handle each type of engine differently because each
0756         // has different requirements on the seed or seeds.
0757         if (engineStateL[0] == CLHEP::engineIDulong<CLHEP::HepJamesRandom>()) {
0758           checkEngineType(engine->name(), std::string("HepJamesRandom"), engineLabel);
0759 
0760           // These two lines actually restore the seed and engine state.
0761           engine->setSeed(engineSeedsL[0], 0);
0762           engine->get(engineStateL);
0763 
0764           labelAndEngine->setSeed(engineSeeds[0], 0);
0765         } else if (engineStateL[0] == CLHEP::engineIDulong<CLHEP::RanecuEngine>()) {
0766           checkEngineType(engine->name(), std::string("RanecuEngine"), engineLabel);
0767 
0768           // This line actually restores the engine state.
0769           engine->get(engineStateL);
0770 
0771           labelAndEngine->setSeed(engineSeeds[0], 0);
0772           labelAndEngine->setSeed(engineSeeds[1], 1);
0773         } else if (engineStateL[0] == CLHEP::engineIDulong<CLHEP::MixMaxRng>()) {
0774           checkEngineType(engine->name(), std::string("MixMaxRng"), engineLabel);
0775 
0776           // This line actually restores the engine state.
0777           engine->setSeed(engineSeedsL[0], 0);
0778           engine->get(engineStateL);
0779 
0780           labelAndEngine->setSeed(engineSeeds[0], 0);
0781         } else if (engineStateL[0] == CLHEP::engineIDulong<TRandomAdaptor>()) {
0782           checkEngineType(engine->name(), std::string("TRandom3"), engineLabel);
0783 
0784           // This line actually restores the engine state.
0785           engine->setSeed(engineSeedsL[0], 0);
0786           engine->get(engineStateL);
0787 
0788           labelAndEngine->setSeed(engineSeeds[0], 0);
0789         } else {
0790           // This should not be possible because this code should be able to restore
0791           // any kind of engine whose state can be saved.
0792           throw Exception(errors::Unknown)
0793               << "The RandomNumberGeneratorService is trying to restore the state\n"
0794                  "of the random engines.  The state in the event indicates an engine\n"
0795                  "of an unknown type.  This should not be possible unless you are\n"
0796                  "running with an old code release on a new file that was created\n"
0797                  "with a newer release which had new engine types added.  In this case\n"
0798                  "the only solution is to use a newer release.  In any other case, notify\n"
0799                  "the EDM developers because this should not be possible\n";
0800         }
0801         ++labelAndEngine;
0802       }
0803     }
0804 
0805     void RandomNumberGeneratorService::checkEngineType(std::string const& typeFromConfig,
0806                                                        std::string const& typeFromEvent,
0807                                                        std::string const& engineLabel) const {
0808       if (typeFromConfig != typeFromEvent) {
0809         throw Exception(errors::Configuration)
0810             << "The RandomNumberGeneratorService is trying to restore\n"
0811             << "the state of the random engine for the module \"" << engineLabel << "\".  An\n"
0812             << "error was detected because the type of the engine in the\n"
0813             << "input file and the configuration file do not match.\n"
0814             << "In the configuration file the type is \"" << typeFromConfig << "\".\nIn the input file the type is \""
0815             << typeFromEvent << "\".  If\n"
0816             << "you are not generating any random numbers in this module, then\n"
0817             << "remove the line in the configuration file that gives it\n"
0818             << "a seed and the error will go away.  Otherwise, you must give\n"
0819             << "this module the same engine type in the configuration file or\n"
0820             << "stop trying to restore the random engine state.\n";
0821       }
0822     }
0823 
0824     void RandomNumberGeneratorService::saveStatesToFile(std::string const& fileName,
0825                                                         StreamID const& streamID,
0826                                                         LuminosityBlockIndex const& lumiIndex) {
0827       std::ofstream& outFile = *outFiles_.at(streamID);
0828 
0829       if (!outFile.is_open()) {
0830         std::stringstream file;
0831         file << fileName;
0832         if (nStreams_ > 1) {
0833           file << "_" << streamID.value();
0834         }
0835 
0836         outFile.open(file.str().c_str(), std::ofstream::out | std::ofstream::trunc);
0837 
0838         if (!outFile) {
0839           throw Exception(errors::Configuration)
0840               << "Unable to open the file \"" << file.str() << "\" to save the state of the random engines.\n";
0841         }
0842       }
0843 
0844       outFile.seekp(0, std::ios_base::beg);
0845       outFile << "<RandomEngineStates>\n";
0846 
0847       outFile << "<Event>\n";
0848       writeStates(eventCache_.at(streamID), outFile);
0849       outFile << "</Event>\n";
0850 
0851       outFile << "<Lumi>\n";
0852       writeStates(lumiCache_.at(lumiIndex), outFile);
0853       outFile << "</Lumi>\n";
0854 
0855       outFile << "</RandomEngineStates>\n";
0856       outFile.flush();
0857     }
0858 
0859     void RandomNumberGeneratorService::writeStates(std::vector<RandomEngineState> const& v, std::ofstream& outFile) {
0860       for (auto& state : v) {
0861         std::vector<std::uint32_t> const& seedVector = state.getSeed();
0862         std::vector<std::uint32_t>::size_type seedVectorLength = seedVector.size();
0863 
0864         std::vector<std::uint32_t> const& stateVector = state.getState();
0865         std::vector<std::uint32_t>::size_type stateVectorLength = stateVector.size();
0866 
0867         outFile << "<ModuleLabel>\n" << state.getLabel() << "\n</ModuleLabel>\n";
0868 
0869         outFile << "<SeedLength>\n" << seedVectorLength << "\n</SeedLength>\n";
0870         outFile << "<InitialSeeds>\n";
0871         writeVector(seedVector, outFile);
0872         outFile << "</InitialSeeds>\n";
0873         outFile << "<FullStateLength>\n" << stateVectorLength << "\n</FullStateLength>\n";
0874         outFile << "<FullState>\n";
0875         writeVector(stateVector, outFile);
0876         outFile << "</FullState>\n";
0877       }
0878     }
0879 
0880     void RandomNumberGeneratorService::writeVector(VUint32 const& v, std::ofstream& outFile) {
0881       if (v.empty())
0882         return;
0883       size_t numItems = v.size();
0884       for (size_t i = 0; i < numItems; ++i) {
0885         if (i != 0 && i % 10 == 0)
0886           outFile << "\n";
0887         outFile << std::setw(13) << v[i];
0888       }
0889       outFile << "\n";
0890     }
0891 
0892     std::string RandomNumberGeneratorService::constructSaveFileName() const {
0893       char directory[1500];
0894       std::string fullName(getcwd(directory, sizeof(directory)) ? directory : "/PathIsTooBig");
0895       fullName += "/" + saveFileName_;
0896       return fullName;
0897     }
0898 
0899     void RandomNumberGeneratorService::readEventStatesFromTextFile(std::string const& fileName,
0900                                                                    std::vector<RandomEngineState>& cache) {
0901       std::string whichStates("<Event>");
0902       readStatesFromFile(fileName, cache, whichStates);
0903     }
0904 
0905     void RandomNumberGeneratorService::readLumiStatesFromTextFile(std::string const& fileName,
0906                                                                   std::vector<RandomEngineState>& cache) {
0907       std::string whichStates("<Lumi>");
0908       readStatesFromFile(fileName, cache, whichStates);
0909     }
0910 
0911     void RandomNumberGeneratorService::readStatesFromFile(std::string const& fileName,
0912                                                           std::vector<RandomEngineState>& cache,
0913                                                           std::string const& whichStates) {
0914       std::ifstream inFile;
0915       inFile.open(fileName.c_str(), std::ifstream::in);
0916       if (!inFile) {
0917         throw Exception(errors::Configuration)
0918             << "Unable to open the file \"" << fileName << "\" to restore the random engine states.\n";
0919       }
0920 
0921       std::string text;
0922       inFile >> text;
0923       if (!inFile.good() || text != std::string("<RandomEngineStates>")) {
0924         throw Exception(errors::Configuration)
0925             << "Attempting to read file with random number engine states.\n"
0926             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0927             << "Cannot read the file header word.\n";
0928       }
0929       bool saveToCache = false;
0930       while (readEngineState(inFile, cache, whichStates, saveToCache)) {
0931       }
0932     }
0933 
0934     bool RandomNumberGeneratorService::readEngineState(std::istream& is,
0935                                                        std::vector<RandomEngineState>& cache,
0936                                                        std::string const& whichStates,
0937                                                        bool& saveToCache) {
0938       std::string leading;
0939       std::string trailing;
0940       std::string moduleLabel;
0941       std::vector<std::uint32_t>::size_type seedVectorSize;
0942       std::vector<std::uint32_t> seedVector;
0943       std::vector<std::uint32_t>::size_type stateVectorSize;
0944       std::vector<std::uint32_t> stateVector;
0945 
0946       // First we need to look for the special strings
0947       // that mark the end of the file and beginning and
0948       // and end of the data for different sections.
0949 
0950       is >> leading;
0951       if (!is.good()) {
0952         throw Exception(errors::Configuration)
0953             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0954             << "Cannot read next field and did not hit the end yet.\n";
0955       }
0956 
0957       // This marks the end of the file. We are done.
0958       if (leading == std::string("</RandomEngineStates>"))
0959         return false;
0960 
0961       // This marks the end of a section of the data
0962       if (leading == std::string("</Event>") || leading == std::string("</Lumi>")) {
0963         saveToCache = false;
0964         return true;
0965       }
0966 
0967       // This marks the beginning of a section
0968       if (leading == std::string("<Event>") || leading == std::string("<Lumi>")) {
0969         saveToCache = (leading == whichStates);
0970         return true;
0971       }
0972 
0973       // Process the next engine state
0974 
0975       is >> moduleLabel >> trailing;
0976       if (!is.good() || leading != std::string("<ModuleLabel>") || trailing != std::string("</ModuleLabel>")) {
0977         throw Exception(errors::Configuration)
0978             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0979             << "Cannot read a module label when restoring random engine states.\n";
0980       }
0981 
0982       is >> leading >> seedVectorSize >> trailing;
0983       if (!is.good() || leading != std::string("<SeedLength>") || trailing != std::string("</SeedLength>")) {
0984         throw Exception(errors::Configuration)
0985             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0986             << "Cannot read seed vector length when restoring random engine states.\n";
0987       }
0988 
0989       is >> leading;
0990       if (!is.good() || leading != std::string("<InitialSeeds>")) {
0991         throw Exception(errors::Configuration)
0992             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0993             << "Cannot read beginning of InitialSeeds when restoring random engine states.\n";
0994       }
0995 
0996       if (seedVectorSize > maxSeeds) {
0997         throw Exception(errors::Configuration)
0998             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0999             << "The number of seeds exceeds 64K.\n";
1000       }
1001 
1002       readVector(is, seedVectorSize, seedVector);
1003 
1004       is >> trailing;
1005       if (!is.good() || trailing != std::string("</InitialSeeds>")) {
1006         throw Exception(errors::Configuration)
1007             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1008             << "Cannot read end of InitialSeeds when restoring random engine states.\n";
1009       }
1010 
1011       is >> leading >> stateVectorSize >> trailing;
1012       if (!is.good() || leading != std::string("<FullStateLength>") || trailing != std::string("</FullStateLength>")) {
1013         throw Exception(errors::Configuration)
1014             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1015             << "Cannot read state vector length when restoring random engine states.\n";
1016       }
1017 
1018       is >> leading;
1019       if (!is.good() || leading != std::string("<FullState>")) {
1020         throw Exception(errors::Configuration)
1021             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1022             << "Cannot read beginning of FullState when restoring random engine states.\n";
1023       }
1024 
1025       if (stateVectorSize > maxStates) {
1026         throw Exception(errors::Configuration)
1027             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1028             << "The number of states exceeds 64K.\n";
1029       }
1030 
1031       readVector(is, stateVectorSize, stateVector);
1032 
1033       is >> trailing;
1034       if (!is.good() || trailing != std::string("</FullState>")) {
1035         throw Exception(errors::Configuration)
1036             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1037             << "Cannot read end of FullState when restoring random engine states.\n";
1038       }
1039 
1040       if (saveToCache) {
1041         RandomEngineState randomEngineState;
1042         randomEngineState.setLabel(moduleLabel);
1043         std::vector<RandomEngineState>::iterator state =
1044             std::lower_bound(cache.begin(), cache.end(), randomEngineState);
1045 
1046         if (state != cache.end() && moduleLabel == state->getLabel()) {
1047           if (seedVector.size() != state->getSeed().size() || stateVector.size() != state->getState().size()) {
1048             throw Exception(errors::Configuration)
1049                 << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1050                 << "Vectors containing engine state are the incorrect size for the type of random engine.\n";
1051           }
1052           state->setSeed(seedVector);
1053           state->setState(stateVector);
1054         }
1055       }
1056       return true;
1057     }
1058 
1059     void RandomNumberGeneratorService::readVector(std::istream& is, unsigned numItems, std::vector<std::uint32_t>& v) {
1060       v.clear();
1061       v.reserve(numItems);
1062       std::uint32_t data;
1063       for (unsigned i = 0; i < numItems; ++i) {
1064         is >> data;
1065         if (!is.good()) {
1066           throw Exception(errors::Configuration)
1067               << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1068               << "Cannot read vector when restoring random engine states.\n";
1069         }
1070         v.push_back(data);
1071       }
1072     }
1073 
1074     void RandomNumberGeneratorService::createEnginesInVector(std::vector<LabelAndEngine>& engines,
1075                                                              unsigned int seedOffset,
1076                                                              unsigned int eventSeedOffset,
1077                                                              std::vector<ModuleIDToEngine>& moduleIDVector) {
1078       // The vectors we will fill here will be the same size as
1079       // or smaller than seedsAndNameMap_.
1080       engines.reserve(seedsAndNameMap_.size());
1081       moduleIDVector.reserve(seedsAndNameMap_.size());
1082 
1083       for (auto const& i : seedsAndNameMap_) {
1084         unsigned int moduleID = i.second.moduleID();
1085         if (moduleID != std::numeric_limits<unsigned int>::max()) {
1086           std::string const& label = i.first;
1087           std::string const& name = i.second.engineName();
1088           VUint32 const& seeds = i.second.seeds();
1089 
1090           if (name == "RanecuEngine") {
1091             std::shared_ptr<CLHEP::HepRandomEngine> engine = std::make_shared<CLHEP::RanecuEngine>();
1092             engines.emplace_back(label, seeds, engine);
1093             resetEngineSeeds(engines.back(), name, seeds, seedOffset, eventSeedOffset);
1094           }
1095           // For the other engines, one seed is required
1096           else {
1097             long int seedL = static_cast<long int>(seeds[0]);
1098 
1099             if (name == "HepJamesRandom") {
1100               std::shared_ptr<CLHEP::HepRandomEngine> engine = std::make_shared<CLHEP::HepJamesRandom>(seedL);
1101               engines.emplace_back(label, seeds, engine);
1102               if (seedOffset != 0 || eventSeedOffset != 0) {
1103                 resetEngineSeeds(engines.back(), name, seeds, seedOffset, eventSeedOffset);
1104               }
1105             } else if (name == "MixMaxRng") {
1106               std::shared_ptr<CLHEP::HepRandomEngine> engine = std::make_shared<CLHEP::MixMaxRng>(seedL);
1107               engines.emplace_back(label, seeds, engine);
1108               if (seedOffset != 0 || eventSeedOffset != 0) {
1109                 resetEngineSeeds(engines.back(), name, seeds, seedOffset, eventSeedOffset);
1110               }
1111             } else {  // TRandom3, currently the only other possibility
1112 
1113               // There is a dangerous conversion from std::uint32_t to long
1114               // that occurs above. In the next 2 lines we check the
1115               // behavior is what we need for the service to work
1116               // properly.  This conversion is forced on us by the
1117               // CLHEP and ROOT interfaces. If the assert ever starts
1118               // to fail we will have to come up with a way to deal
1119               // with this.
1120               std::uint32_t seedu32 = static_cast<std::uint32_t>(seedL);
1121               assert(seeds[0] == seedu32);
1122 
1123               std::shared_ptr<CLHEP::HepRandomEngine> engine = std::make_shared<TRandomAdaptor>(seedL);
1124               engines.emplace_back(label, seeds, engine);
1125               if (seedOffset != 0 || eventSeedOffset != 0) {
1126                 resetEngineSeeds(engines.back(), name, seeds, seedOffset, eventSeedOffset);
1127               }
1128             }
1129           }
1130           moduleIDVector.emplace_back(&engines.back(), moduleID);
1131         }  // if moduleID valid
1132       }  // loop over seedsAndMap
1133       std::sort(moduleIDVector.begin(), moduleIDVector.end());
1134     }
1135 
1136     void RandomNumberGeneratorService::resetEngineSeeds(LabelAndEngine& labelAndEngine,
1137                                                         std::string const& engineName,
1138                                                         VUint32 const& seeds,
1139                                                         std::uint32_t offset1,
1140                                                         std::uint32_t offset2) {
1141       if (engineName == "RanecuEngine") {
1142         assert(seeds.size() == 2U);
1143         // Wrap around if the offsets push the seed over the maximum allowed value
1144         std::uint32_t mod = maxSeedRanecu + 1U;
1145         offset1 %= mod;
1146         offset2 %= mod;
1147         std::uint32_t seed0 = (seeds[0] + offset1) % mod;
1148         seed0 = (seed0 + offset2) % mod;
1149         labelAndEngine.setSeed(seed0, 0);
1150         labelAndEngine.setSeed(seeds[1], 1);
1151         long int seedL[2];
1152         seedL[0] = static_cast<long int>(seed0);
1153         seedL[1] = static_cast<long int>(seeds[1]);
1154         labelAndEngine.engine()->setSeeds(seedL, 0);
1155       } else {
1156         assert(seeds.size() == 1U);
1157 
1158         if (engineName == "HepJamesRandom" || engineName == "MixMaxRng") {
1159           // Wrap around if the offsets push the seed over the maximum allowed value
1160           std::uint32_t mod = maxSeedHepJames + 1U;
1161           offset1 %= mod;
1162           offset2 %= mod;
1163           std::uint32_t seed0 = (seeds[0] + offset1) % mod;
1164           seed0 = (seed0 + offset2) % mod;
1165           labelAndEngine.setSeed(seed0, 0);
1166 
1167           long int seedL = static_cast<long int>(seed0);
1168           labelAndEngine.engine()->setSeed(seedL, 0);
1169         } else {
1170           assert(engineName == "TRandom3");
1171           // Wrap around if the offsets push the seed over the maximum allowed value
1172           // We have to be extra careful with this one because it may also go beyond
1173           // the values 32 bits can hold
1174           std::uint32_t max32 = maxSeedTRandom3;
1175           std::uint32_t seed0 = seeds[0];
1176           if ((max32 - seed0) >= offset1) {
1177             seed0 += offset1;
1178           } else {
1179             seed0 = offset1 - (max32 - seed0) - 1U;
1180           }
1181           if ((max32 - seed0) >= offset2) {
1182             seed0 += offset2;
1183           } else {
1184             seed0 = offset2 - (max32 - seed0) - 1U;
1185           }
1186           labelAndEngine.setSeed(seed0, 0);
1187 
1188           long seedL = static_cast<long>(seed0);
1189 
1190           // There is a dangerous conversion from std::uint32_t to long
1191           // that occurs above. In the next 2 lines we check the
1192           // behavior is what we need for the service to work
1193           // properly.  This conversion is forced on us by the
1194           // CLHEP and ROOT interfaces. If the assert ever starts
1195           // to fail we will have to come up with a way to deal
1196           // with this.
1197           std::uint32_t seedu32 = static_cast<std::uint32_t>(seedL);
1198           assert(seed0 == seedu32);
1199 
1200           labelAndEngine.engine()->setSeed(seedL, 0);
1201         }
1202       }
1203     }
1204   }  // namespace service
1205 }  // namespace edm