Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-09-07 04:36:40

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(PathsAndConsumesOfModulesBase const&, ProcessContext const&) {
0432       beginJobEndJobActive_ = true;
0433     }
0434 
0435     void RandomNumberGeneratorService::postBeginJob() { beginJobEndJobActive_ = false; }
0436 
0437     void RandomNumberGeneratorService::preEndJob() { beginJobEndJobActive_ = true; }
0438 
0439     void RandomNumberGeneratorService::postEndJob() { beginJobEndJobActive_ = false; }
0440 
0441     void RandomNumberGeneratorService::preBeginLumi(LuminosityBlock const& lumi) {
0442       if (!restoreStateTag_.label().empty()) {
0443         // Copy from a product in the LuminosityBlock to cache for a particular luminosityBlockIndex
0444         readFromLuminosityBlock(lumi);
0445       }
0446       // Copy from cache to engine the state for a particular luminosityBlockIndex
0447       restoreFromCache(lumiCache_[lumi.index()], lumiEngines_[lumi.index()]);
0448     }
0449 
0450     void RandomNumberGeneratorService::postEventRead(Event const& event) {
0451       if (!restoreStateTag_.label().empty()) {
0452         // This initializes the cache before readFromEvent
0453         snapShot(streamEngines_[event.streamID()], eventCache_[event.streamID()]);
0454 
0455         // copy from Event to event cache
0456         readFromEvent(event);
0457 
0458         // copy from event cache to engines
0459         restoreFromCache(eventCache_[event.streamID()], streamEngines_[event.streamID()]);
0460 
0461       } else {
0462         // copy from engines to event cache
0463         snapShot(streamEngines_[event.streamID()], eventCache_[event.streamID()]);
0464       }
0465 
0466       // if requested write text file from both caches
0467       if (!saveFileName_.empty()) {
0468         saveStatesToFile(saveFileName_, event.streamID(), event.getLuminosityBlock().index());
0469         bool expected = false;
0470         if (saveFileNameRecorded_.compare_exchange_strong(expected, true)) {
0471           std::string fullName = constructSaveFileName();
0472           Service<JobReport> reportSvc;
0473           reportSvc->reportRandomStateFile(fullName);
0474         }
0475       }
0476     }
0477 
0478     void RandomNumberGeneratorService::setLumiCache(LuminosityBlockIndex iLumi,
0479                                                     std::vector<RandomEngineState> const& iStates) {
0480       lumiCache_[iLumi] = iStates;
0481       // Copy from cache to engine the state for a particular luminosityBlockIndex
0482       restoreFromCache(lumiCache_[iLumi], lumiEngines_[iLumi]);
0483     }
0484     void RandomNumberGeneratorService::setEventCache(StreamID iStream, std::vector<RandomEngineState> const& iStates) {
0485       eventCache_[iStream] = iStates;
0486       // copy from event cache to engines
0487       restoreFromCache(eventCache_[iStream], streamEngines_[iStream]);
0488     }
0489 
0490     void RandomNumberGeneratorService::preModuleBeginStream(StreamContext const& sc, ModuleCallingContext const& mcc) {
0491       preModuleStreamCheck(sc, mcc);
0492     }
0493 
0494     void RandomNumberGeneratorService::postModuleBeginStream(StreamContext const& sc, ModuleCallingContext const& mcc) {
0495       postModuleStreamCheck(sc, mcc);
0496     }
0497 
0498     void RandomNumberGeneratorService::preModuleEndStream(StreamContext const& sc, ModuleCallingContext const& mcc) {
0499       preModuleStreamCheck(sc, mcc);
0500     }
0501 
0502     void RandomNumberGeneratorService::postModuleEndStream(StreamContext const& sc, ModuleCallingContext const& mcc) {
0503       postModuleStreamCheck(sc, mcc);
0504     }
0505 
0506     void RandomNumberGeneratorService::preModuleStreamBeginRun(StreamContext const& sc,
0507                                                                ModuleCallingContext const& mcc) {
0508       preModuleStreamCheck(sc, mcc);
0509     }
0510 
0511     void RandomNumberGeneratorService::postModuleStreamBeginRun(StreamContext const& sc,
0512                                                                 ModuleCallingContext const& mcc) {
0513       postModuleStreamCheck(sc, mcc);
0514     }
0515 
0516     void RandomNumberGeneratorService::preModuleStreamEndRun(StreamContext const& sc, ModuleCallingContext const& mcc) {
0517       preModuleStreamCheck(sc, mcc);
0518     }
0519 
0520     void RandomNumberGeneratorService::postModuleStreamEndRun(StreamContext const& sc,
0521                                                               ModuleCallingContext const& mcc) {
0522       postModuleStreamCheck(sc, mcc);
0523     }
0524 
0525     void RandomNumberGeneratorService::preModuleStreamBeginLumi(StreamContext const& sc,
0526                                                                 ModuleCallingContext const& mcc) {
0527       preModuleStreamCheck(sc, mcc);
0528     }
0529 
0530     void RandomNumberGeneratorService::postModuleStreamBeginLumi(StreamContext const& sc,
0531                                                                  ModuleCallingContext const& mcc) {
0532       postModuleStreamCheck(sc, mcc);
0533     }
0534 
0535     void RandomNumberGeneratorService::preModuleStreamEndLumi(StreamContext const& sc,
0536                                                               ModuleCallingContext const& mcc) {
0537       preModuleStreamCheck(sc, mcc);
0538     }
0539 
0540     void RandomNumberGeneratorService::postModuleStreamEndLumi(StreamContext const& sc,
0541                                                                ModuleCallingContext const& mcc) {
0542       postModuleStreamCheck(sc, mcc);
0543     }
0544 
0545     std::vector<RandomEngineState> const& RandomNumberGeneratorService::getLumiCache(
0546         LuminosityBlockIndex const& lumiIndex) const {
0547       return lumiCache_.at(lumiIndex.value());
0548     }
0549 
0550     std::vector<RandomEngineState> const& RandomNumberGeneratorService::getEventCache(StreamID const& streamID) const {
0551       return eventCache_.at(streamID.value());
0552     }
0553 
0554     void RandomNumberGeneratorService::print(std::ostream& os) const {
0555       os << "\n\nRandomNumberGeneratorService dump\n\n";
0556 
0557       os << "    Contents of seedsAndNameMap (label moduleID engineType seeds)\n";
0558       for (auto const& entry : seedsAndNameMap_) {
0559         os << "        " << entry.first << "  " << entry.second.moduleID() << "  " << entry.second.engineName();
0560         for (auto val : entry.second.seeds()) {
0561           os << "  " << val;
0562         }
0563         os << "\n";
0564       }
0565       os << "    nStreams_ = " << nStreams_ << "\n";
0566       os << "    saveFileName_ = " << saveFileName_ << "\n";
0567       os << "    saveFileNameRecorded_ = " << saveFileNameRecorded_ << "\n";
0568       os << "    restoreFileName_ = " << restoreFileName_ << "\n";
0569       os << "    enableChecking_ = " << enableChecking_ << "\n";
0570       os << "    eventSeedOffset_ = " << eventSeedOffset_ << "\n";
0571       os << "    verbose_ = " << verbose_ << "\n";
0572       os << "    restoreStateTag_ = " << restoreStateTag_ << "\n";
0573       os << "    restoreStateBeginLumiTag_ = " << restoreStateBeginLumiTag_ << "\n";
0574 
0575       os << "\n    streamEngines_\n";
0576       unsigned int iStream = 0;
0577       for (auto const& k : streamEngines_) {
0578         os << "        Stream " << iStream << "\n";
0579         for (auto const& i : k) {
0580           os << "        " << i.label();
0581           for (auto const& j : i.seeds()) {
0582             os << " " << j;
0583           }
0584           os << " " << i.engine()->name();
0585           if (i.engine()->name() == std::string("HepJamesRandom")) {
0586             os << "  " << i.engine()->getSeed();
0587           } else if (i.engine()->name() == std::string("MixMaxRng")) {
0588             os << "  " << i.engine()->getSeed();
0589           } else {
0590             os << "  engine does not know seeds";
0591           }
0592           os << "\n";
0593         }
0594         ++iStream;
0595       }
0596       os << "\n    lumiEngines_\n";
0597       unsigned int iLumi = 0;
0598       for (auto const& k : lumiEngines_) {
0599         os << "        lumiIndex " << iLumi << "\n";
0600         for (auto const& i : k) {
0601           os << "        " << i.label();
0602           for (auto const& j : i.seeds()) {
0603             os << " " << j;
0604           }
0605           os << " " << i.engine()->name();
0606           if (i.engine()->name() == std::string("HepJamesRandom")) {
0607             os << "  " << i.engine()->getSeed();
0608           } else if (i.engine()->name() == std::string("MixMaxRng")) {
0609             os << "  " << i.engine()->getSeed();
0610           } else {
0611             os << "  engine does not know seeds";
0612           }
0613           os << "\n";
0614         }
0615         ++iLumi;
0616       }
0617     }
0618 
0619     void RandomNumberGeneratorService::preModuleStreamCheck(StreamContext const& sc, ModuleCallingContext const& mcc) {
0620       if (enableChecking_) {
0621         unsigned int moduleID = mcc.moduleDescription()->id();
0622         std::vector<ModuleIDToEngine>& moduleIDVector = streamModuleIDToEngine_.at(sc.streamID().value());
0623         ModuleIDToEngine target(nullptr, moduleID);
0624         std::vector<ModuleIDToEngine>::iterator iter =
0625             std::lower_bound(moduleIDVector.begin(), moduleIDVector.end(), target);
0626         if (iter != moduleIDVector.end() && iter->moduleID() == moduleID) {
0627           LabelAndEngine* labelAndEngine = iter->labelAndEngine();
0628           iter->setEngineState(labelAndEngine->engine()->put());
0629         }
0630       }
0631     }
0632 
0633     void RandomNumberGeneratorService::postModuleStreamCheck(StreamContext const& sc, ModuleCallingContext const& mcc) {
0634       if (enableChecking_) {
0635         unsigned int moduleID = mcc.moduleDescription()->id();
0636         std::vector<ModuleIDToEngine>& moduleIDVector = streamModuleIDToEngine_.at(sc.streamID().value());
0637         ModuleIDToEngine target(nullptr, moduleID);
0638         std::vector<ModuleIDToEngine>::iterator iter =
0639             std::lower_bound(moduleIDVector.begin(), moduleIDVector.end(), target);
0640         if (iter != moduleIDVector.end() && iter->moduleID() == moduleID) {
0641           LabelAndEngine* labelAndEngine = iter->labelAndEngine();
0642           if (iter->engineState() != labelAndEngine->engine()->put()) {
0643             throw Exception(errors::LogicError)
0644                 << "It is illegal to generate random numbers during beginStream, endStream,\n"
0645                    "beginRun, endRun, beginLumi, endLumi because that makes it very difficult\n"
0646                    "to replay the processing of individual events.  Random numbers were\n"
0647                    "generated during one of these methods for the module with class name\n\""
0648                 << mcc.moduleDescription()->moduleName()
0649                 << "\" "
0650                    "and module label \""
0651                 << mcc.moduleDescription()->moduleLabel() << "\"\n";
0652           }
0653         }
0654       }
0655     }
0656 
0657     void RandomNumberGeneratorService::readFromLuminosityBlock(LuminosityBlock const& lumi) {
0658       Service<TriggerNamesService> tns;
0659       if (tns.isAvailable()) {
0660         if (tns->getProcessName() == restoreStateTag_.process()) {
0661           throw Exception(errors::Configuration)
0662               << "In the configuration for the RandomNumberGeneratorService the\n"
0663               << "restoreStateTag contains the current process which is illegal.\n"
0664               << "The process name in the replay process should have been changed\n"
0665               << "to be different than the original process name and the restoreStateTag\n"
0666               << "should contain either the original process name or an empty process name.\n";
0667         }
0668       }
0669 
0670       Handle<RandomEngineStates> states;
0671       lumi.getByLabel(restoreStateBeginLumiTag_, states);
0672 
0673       if (!states.isValid()) {
0674         throw Exception(errors::ProductNotFound)
0675             << "The RandomNumberGeneratorService is trying to restore\n"
0676             << "the state of the random engines by reading a product from\n"
0677             << "the LuminosityBlock with input tag \"" << restoreStateBeginLumiTag_ << "\".\n"
0678             << "It could not find the product.\n"
0679             << "Either the product in the LuminosityBlock was dropped or\n"
0680             << "not produced or the configured input tag is incorrect or there is a bug somewhere\n";
0681         return;
0682       }
0683       states->getRandomEngineStates(lumiCache_.at(lumi.index()));
0684     }
0685 
0686     void RandomNumberGeneratorService::readFromEvent(Event const& event) {
0687       Handle<RandomEngineStates> states;
0688 
0689       event.getByLabel(restoreStateTag_, states);
0690 
0691       if (!states.isValid()) {
0692         throw Exception(errors::ProductNotFound)
0693             << "The RandomNumberGeneratorService is trying to restore\n"
0694             << "the state of the random engines by reading a product from\n"
0695             << "the Event with input tag \"" << restoreStateTag_ << "\".\n"
0696             << "It could not find the product.\n"
0697             << "Either the product in the Event was dropped or\n"
0698             << "not produced or the configured input tag is incorrect or there is a bug somewhere\n";
0699         return;
0700       }
0701       states->getRandomEngineStates(eventCache_.at(event.streamID()));
0702     }
0703 
0704     void RandomNumberGeneratorService::snapShot(std::vector<LabelAndEngine> const& engines,
0705                                                 std::vector<RandomEngineState>& cache) {
0706       cache.resize(engines.size());
0707       std::vector<RandomEngineState>::iterator state = cache.begin();
0708 
0709       for (std::vector<LabelAndEngine>::const_iterator iter = engines.begin(); iter != engines.end(); ++iter, ++state) {
0710         std::string const& label = iter->label();
0711         state->setLabel(label);
0712         state->setSeed(iter->seeds());
0713 
0714         std::vector<unsigned long> stateL = iter->engine()->put();
0715         state->clearStateVector();
0716         state->reserveStateVector(stateL.size());
0717         for (auto element : stateL) {
0718           state->push_back_stateVector(static_cast<std::uint32_t>(element));
0719         }
0720       }
0721     }
0722 
0723     void RandomNumberGeneratorService::restoreFromCache(std::vector<RandomEngineState> const& cache,
0724                                                         std::vector<LabelAndEngine>& engines) {
0725       std::vector<LabelAndEngine>::iterator labelAndEngine = engines.begin();
0726       for (auto const& cachedState : cache) {
0727         std::string const& engineLabel = cachedState.getLabel();
0728 
0729         std::vector<std::uint32_t> const& engineState = cachedState.getState();
0730         std::vector<unsigned long> engineStateL;
0731         engineStateL.reserve(engineState.size());
0732         for (auto const& value : engineState) {
0733           engineStateL.push_back(static_cast<unsigned long>(value));
0734         }
0735 
0736         std::vector<std::uint32_t> const& engineSeeds = cachedState.getSeed();
0737         std::vector<long> engineSeedsL;
0738         engineSeedsL.reserve(engineSeeds.size());
0739         for (auto const& val : engineSeeds) {
0740           long seedL = static_cast<long>(val);
0741           engineSeedsL.push_back(seedL);
0742 
0743           // There is a dangerous conversion from std::uint32_t to long
0744           // that occurs above. In the next 2 lines we check the
0745           // behavior is what we need for the service to work
0746           // properly.  This conversion is forced on us by the
0747           // CLHEP and ROOT interfaces. If the assert ever starts
0748           // to fail we will have to come up with a way to deal
0749           // with this.
0750           std::uint32_t seedu32 = static_cast<std::uint32_t>(seedL);
0751           assert(val == seedu32);
0752         }
0753 
0754         assert(labelAndEngine != engines.end() && engineLabel == labelAndEngine->label());
0755         std::shared_ptr<CLHEP::HepRandomEngine> const& engine = labelAndEngine->engine();
0756 
0757         // We need to handle each type of engine differently because each
0758         // has different requirements on the seed or seeds.
0759         if (engineStateL[0] == CLHEP::engineIDulong<CLHEP::HepJamesRandom>()) {
0760           checkEngineType(engine->name(), std::string("HepJamesRandom"), engineLabel);
0761 
0762           // These two lines actually restore the seed and engine state.
0763           engine->setSeed(engineSeedsL[0], 0);
0764           engine->get(engineStateL);
0765 
0766           labelAndEngine->setSeed(engineSeeds[0], 0);
0767         } else if (engineStateL[0] == CLHEP::engineIDulong<CLHEP::RanecuEngine>()) {
0768           checkEngineType(engine->name(), std::string("RanecuEngine"), engineLabel);
0769 
0770           // This line actually restores the engine state.
0771           engine->get(engineStateL);
0772 
0773           labelAndEngine->setSeed(engineSeeds[0], 0);
0774           labelAndEngine->setSeed(engineSeeds[1], 1);
0775         } else if (engineStateL[0] == CLHEP::engineIDulong<CLHEP::MixMaxRng>()) {
0776           checkEngineType(engine->name(), std::string("MixMaxRng"), engineLabel);
0777 
0778           // This line actually restores the engine state.
0779           engine->setSeed(engineSeedsL[0], 0);
0780           engine->get(engineStateL);
0781 
0782           labelAndEngine->setSeed(engineSeeds[0], 0);
0783         } else if (engineStateL[0] == CLHEP::engineIDulong<TRandomAdaptor>()) {
0784           checkEngineType(engine->name(), std::string("TRandom3"), engineLabel);
0785 
0786           // This line actually restores the engine state.
0787           engine->setSeed(engineSeedsL[0], 0);
0788           engine->get(engineStateL);
0789 
0790           labelAndEngine->setSeed(engineSeeds[0], 0);
0791         } else {
0792           // This should not be possible because this code should be able to restore
0793           // any kind of engine whose state can be saved.
0794           throw Exception(errors::Unknown)
0795               << "The RandomNumberGeneratorService is trying to restore the state\n"
0796                  "of the random engines.  The state in the event indicates an engine\n"
0797                  "of an unknown type.  This should not be possible unless you are\n"
0798                  "running with an old code release on a new file that was created\n"
0799                  "with a newer release which had new engine types added.  In this case\n"
0800                  "the only solution is to use a newer release.  In any other case, notify\n"
0801                  "the EDM developers because this should not be possible\n";
0802         }
0803         ++labelAndEngine;
0804       }
0805     }
0806 
0807     void RandomNumberGeneratorService::checkEngineType(std::string const& typeFromConfig,
0808                                                        std::string const& typeFromEvent,
0809                                                        std::string const& engineLabel) const {
0810       if (typeFromConfig != typeFromEvent) {
0811         throw Exception(errors::Configuration)
0812             << "The RandomNumberGeneratorService is trying to restore\n"
0813             << "the state of the random engine for the module \"" << engineLabel << "\".  An\n"
0814             << "error was detected because the type of the engine in the\n"
0815             << "input file and the configuration file do not match.\n"
0816             << "In the configuration file the type is \"" << typeFromConfig << "\".\nIn the input file the type is \""
0817             << typeFromEvent << "\".  If\n"
0818             << "you are not generating any random numbers in this module, then\n"
0819             << "remove the line in the configuration file that gives it\n"
0820             << "a seed and the error will go away.  Otherwise, you must give\n"
0821             << "this module the same engine type in the configuration file or\n"
0822             << "stop trying to restore the random engine state.\n";
0823       }
0824     }
0825 
0826     void RandomNumberGeneratorService::saveStatesToFile(std::string const& fileName,
0827                                                         StreamID const& streamID,
0828                                                         LuminosityBlockIndex const& lumiIndex) {
0829       std::ofstream& outFile = *outFiles_.at(streamID);
0830 
0831       if (!outFile.is_open()) {
0832         std::stringstream file;
0833         file << fileName;
0834         if (nStreams_ > 1) {
0835           file << "_" << streamID.value();
0836         }
0837 
0838         outFile.open(file.str().c_str(), std::ofstream::out | std::ofstream::trunc);
0839 
0840         if (!outFile) {
0841           throw Exception(errors::Configuration)
0842               << "Unable to open the file \"" << file.str() << "\" to save the state of the random engines.\n";
0843         }
0844       }
0845 
0846       outFile.seekp(0, std::ios_base::beg);
0847       outFile << "<RandomEngineStates>\n";
0848 
0849       outFile << "<Event>\n";
0850       writeStates(eventCache_.at(streamID), outFile);
0851       outFile << "</Event>\n";
0852 
0853       outFile << "<Lumi>\n";
0854       writeStates(lumiCache_.at(lumiIndex), outFile);
0855       outFile << "</Lumi>\n";
0856 
0857       outFile << "</RandomEngineStates>\n";
0858       outFile.flush();
0859     }
0860 
0861     void RandomNumberGeneratorService::writeStates(std::vector<RandomEngineState> const& v, std::ofstream& outFile) {
0862       for (auto& state : v) {
0863         std::vector<std::uint32_t> const& seedVector = state.getSeed();
0864         std::vector<std::uint32_t>::size_type seedVectorLength = seedVector.size();
0865 
0866         std::vector<std::uint32_t> const& stateVector = state.getState();
0867         std::vector<std::uint32_t>::size_type stateVectorLength = stateVector.size();
0868 
0869         outFile << "<ModuleLabel>\n" << state.getLabel() << "\n</ModuleLabel>\n";
0870 
0871         outFile << "<SeedLength>\n" << seedVectorLength << "\n</SeedLength>\n";
0872         outFile << "<InitialSeeds>\n";
0873         writeVector(seedVector, outFile);
0874         outFile << "</InitialSeeds>\n";
0875         outFile << "<FullStateLength>\n" << stateVectorLength << "\n</FullStateLength>\n";
0876         outFile << "<FullState>\n";
0877         writeVector(stateVector, outFile);
0878         outFile << "</FullState>\n";
0879       }
0880     }
0881 
0882     void RandomNumberGeneratorService::writeVector(VUint32 const& v, std::ofstream& outFile) {
0883       if (v.empty())
0884         return;
0885       size_t numItems = v.size();
0886       for (size_t i = 0; i < numItems; ++i) {
0887         if (i != 0 && i % 10 == 0)
0888           outFile << "\n";
0889         outFile << std::setw(13) << v[i];
0890       }
0891       outFile << "\n";
0892     }
0893 
0894     std::string RandomNumberGeneratorService::constructSaveFileName() const {
0895       char directory[1500];
0896       std::string fullName(getcwd(directory, sizeof(directory)) ? directory : "/PathIsTooBig");
0897       fullName += "/" + saveFileName_;
0898       return fullName;
0899     }
0900 
0901     void RandomNumberGeneratorService::readEventStatesFromTextFile(std::string const& fileName,
0902                                                                    std::vector<RandomEngineState>& cache) {
0903       std::string whichStates("<Event>");
0904       readStatesFromFile(fileName, cache, whichStates);
0905     }
0906 
0907     void RandomNumberGeneratorService::readLumiStatesFromTextFile(std::string const& fileName,
0908                                                                   std::vector<RandomEngineState>& cache) {
0909       std::string whichStates("<Lumi>");
0910       readStatesFromFile(fileName, cache, whichStates);
0911     }
0912 
0913     void RandomNumberGeneratorService::readStatesFromFile(std::string const& fileName,
0914                                                           std::vector<RandomEngineState>& cache,
0915                                                           std::string const& whichStates) {
0916       std::ifstream inFile;
0917       inFile.open(fileName.c_str(), std::ifstream::in);
0918       if (!inFile) {
0919         throw Exception(errors::Configuration)
0920             << "Unable to open the file \"" << fileName << "\" to restore the random engine states.\n";
0921       }
0922 
0923       std::string text;
0924       inFile >> text;
0925       if (!inFile.good() || text != std::string("<RandomEngineStates>")) {
0926         throw Exception(errors::Configuration)
0927             << "Attempting to read file with random number engine states.\n"
0928             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0929             << "Cannot read the file header word.\n";
0930       }
0931       bool saveToCache = false;
0932       while (readEngineState(inFile, cache, whichStates, saveToCache)) {
0933       }
0934     }
0935 
0936     bool RandomNumberGeneratorService::readEngineState(std::istream& is,
0937                                                        std::vector<RandomEngineState>& cache,
0938                                                        std::string const& whichStates,
0939                                                        bool& saveToCache) {
0940       std::string leading;
0941       std::string trailing;
0942       std::string moduleLabel;
0943       std::vector<std::uint32_t>::size_type seedVectorSize;
0944       std::vector<std::uint32_t> seedVector;
0945       std::vector<std::uint32_t>::size_type stateVectorSize;
0946       std::vector<std::uint32_t> stateVector;
0947 
0948       // First we need to look for the special strings
0949       // that mark the end of the file and beginning and
0950       // and end of the data for different sections.
0951 
0952       is >> leading;
0953       if (!is.good()) {
0954         throw Exception(errors::Configuration)
0955             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0956             << "Cannot read next field and did not hit the end yet.\n";
0957       }
0958 
0959       // This marks the end of the file. We are done.
0960       if (leading == std::string("</RandomEngineStates>"))
0961         return false;
0962 
0963       // This marks the end of a section of the data
0964       if (leading == std::string("</Event>") || leading == std::string("</Lumi>")) {
0965         saveToCache = false;
0966         return true;
0967       }
0968 
0969       // This marks the beginning of a section
0970       if (leading == std::string("<Event>") || leading == std::string("<Lumi>")) {
0971         saveToCache = (leading == whichStates);
0972         return true;
0973       }
0974 
0975       // Process the next engine state
0976 
0977       is >> moduleLabel >> trailing;
0978       if (!is.good() || leading != std::string("<ModuleLabel>") || trailing != std::string("</ModuleLabel>")) {
0979         throw Exception(errors::Configuration)
0980             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0981             << "Cannot read a module label when restoring random engine states.\n";
0982       }
0983 
0984       is >> leading >> seedVectorSize >> trailing;
0985       if (!is.good() || leading != std::string("<SeedLength>") || trailing != std::string("</SeedLength>")) {
0986         throw Exception(errors::Configuration)
0987             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0988             << "Cannot read seed vector length when restoring random engine states.\n";
0989       }
0990 
0991       is >> leading;
0992       if (!is.good() || leading != std::string("<InitialSeeds>")) {
0993         throw Exception(errors::Configuration)
0994             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
0995             << "Cannot read beginning of InitialSeeds when restoring random engine states.\n";
0996       }
0997 
0998       if (seedVectorSize > maxSeeds) {
0999         throw Exception(errors::Configuration)
1000             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1001             << "The number of seeds exceeds 64K.\n";
1002       }
1003 
1004       readVector(is, seedVectorSize, seedVector);
1005 
1006       is >> trailing;
1007       if (!is.good() || trailing != std::string("</InitialSeeds>")) {
1008         throw Exception(errors::Configuration)
1009             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1010             << "Cannot read end of InitialSeeds when restoring random engine states.\n";
1011       }
1012 
1013       is >> leading >> stateVectorSize >> trailing;
1014       if (!is.good() || leading != std::string("<FullStateLength>") || trailing != std::string("</FullStateLength>")) {
1015         throw Exception(errors::Configuration)
1016             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1017             << "Cannot read state vector length when restoring random engine states.\n";
1018       }
1019 
1020       is >> leading;
1021       if (!is.good() || leading != std::string("<FullState>")) {
1022         throw Exception(errors::Configuration)
1023             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1024             << "Cannot read beginning of FullState when restoring random engine states.\n";
1025       }
1026 
1027       if (stateVectorSize > maxStates) {
1028         throw Exception(errors::Configuration)
1029             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1030             << "The number of states exceeds 64K.\n";
1031       }
1032 
1033       readVector(is, stateVectorSize, stateVector);
1034 
1035       is >> trailing;
1036       if (!is.good() || trailing != std::string("</FullState>")) {
1037         throw Exception(errors::Configuration)
1038             << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1039             << "Cannot read end of FullState when restoring random engine states.\n";
1040       }
1041 
1042       if (saveToCache) {
1043         RandomEngineState randomEngineState;
1044         randomEngineState.setLabel(moduleLabel);
1045         std::vector<RandomEngineState>::iterator state =
1046             std::lower_bound(cache.begin(), cache.end(), randomEngineState);
1047 
1048         if (state != cache.end() && moduleLabel == state->getLabel()) {
1049           if (seedVector.size() != state->getSeed().size() || stateVector.size() != state->getState().size()) {
1050             throw Exception(errors::Configuration)
1051                 << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1052                 << "Vectors containing engine state are the incorrect size for the type of random engine.\n";
1053           }
1054           state->setSeed(seedVector);
1055           state->setState(stateVector);
1056         }
1057       }
1058       return true;
1059     }
1060 
1061     void RandomNumberGeneratorService::readVector(std::istream& is, unsigned numItems, std::vector<std::uint32_t>& v) {
1062       v.clear();
1063       v.reserve(numItems);
1064       std::uint32_t data;
1065       for (unsigned i = 0; i < numItems; ++i) {
1066         is >> data;
1067         if (!is.good()) {
1068           throw Exception(errors::Configuration)
1069               << "File \"" << restoreFileName_ << "\" is ill-structured or otherwise corrupted.\n"
1070               << "Cannot read vector when restoring random engine states.\n";
1071         }
1072         v.push_back(data);
1073       }
1074     }
1075 
1076     void RandomNumberGeneratorService::createEnginesInVector(std::vector<LabelAndEngine>& engines,
1077                                                              unsigned int seedOffset,
1078                                                              unsigned int eventSeedOffset,
1079                                                              std::vector<ModuleIDToEngine>& moduleIDVector) {
1080       // The vectors we will fill here will be the same size as
1081       // or smaller than seedsAndNameMap_.
1082       engines.reserve(seedsAndNameMap_.size());
1083       moduleIDVector.reserve(seedsAndNameMap_.size());
1084 
1085       for (auto const& i : seedsAndNameMap_) {
1086         unsigned int moduleID = i.second.moduleID();
1087         if (moduleID != std::numeric_limits<unsigned int>::max()) {
1088           std::string const& label = i.first;
1089           std::string const& name = i.second.engineName();
1090           VUint32 const& seeds = i.second.seeds();
1091 
1092           if (name == "RanecuEngine") {
1093             std::shared_ptr<CLHEP::HepRandomEngine> engine = std::make_shared<CLHEP::RanecuEngine>();
1094             engines.emplace_back(label, seeds, engine);
1095             resetEngineSeeds(engines.back(), name, seeds, seedOffset, eventSeedOffset);
1096           }
1097           // For the other engines, one seed is required
1098           else {
1099             long int seedL = static_cast<long int>(seeds[0]);
1100 
1101             if (name == "HepJamesRandom") {
1102               std::shared_ptr<CLHEP::HepRandomEngine> engine = std::make_shared<CLHEP::HepJamesRandom>(seedL);
1103               engines.emplace_back(label, seeds, engine);
1104               if (seedOffset != 0 || eventSeedOffset != 0) {
1105                 resetEngineSeeds(engines.back(), name, seeds, seedOffset, eventSeedOffset);
1106               }
1107             } else if (name == "MixMaxRng") {
1108               std::shared_ptr<CLHEP::HepRandomEngine> engine = std::make_shared<CLHEP::MixMaxRng>(seedL);
1109               engines.emplace_back(label, seeds, engine);
1110               if (seedOffset != 0 || eventSeedOffset != 0) {
1111                 resetEngineSeeds(engines.back(), name, seeds, seedOffset, eventSeedOffset);
1112               }
1113             } else {  // TRandom3, currently the only other possibility
1114 
1115               // There is a dangerous conversion from std::uint32_t to long
1116               // that occurs above. In the next 2 lines we check the
1117               // behavior is what we need for the service to work
1118               // properly.  This conversion is forced on us by the
1119               // CLHEP and ROOT interfaces. If the assert ever starts
1120               // to fail we will have to come up with a way to deal
1121               // with this.
1122               std::uint32_t seedu32 = static_cast<std::uint32_t>(seedL);
1123               assert(seeds[0] == seedu32);
1124 
1125               std::shared_ptr<CLHEP::HepRandomEngine> engine = std::make_shared<TRandomAdaptor>(seedL);
1126               engines.emplace_back(label, seeds, engine);
1127               if (seedOffset != 0 || eventSeedOffset != 0) {
1128                 resetEngineSeeds(engines.back(), name, seeds, seedOffset, eventSeedOffset);
1129               }
1130             }
1131           }
1132           moduleIDVector.emplace_back(&engines.back(), moduleID);
1133         }  // if moduleID valid
1134       }  // loop over seedsAndMap
1135       std::sort(moduleIDVector.begin(), moduleIDVector.end());
1136     }
1137 
1138     void RandomNumberGeneratorService::resetEngineSeeds(LabelAndEngine& labelAndEngine,
1139                                                         std::string const& engineName,
1140                                                         VUint32 const& seeds,
1141                                                         std::uint32_t offset1,
1142                                                         std::uint32_t offset2) {
1143       if (engineName == "RanecuEngine") {
1144         assert(seeds.size() == 2U);
1145         // Wrap around if the offsets push the seed over the maximum allowed value
1146         std::uint32_t mod = maxSeedRanecu + 1U;
1147         offset1 %= mod;
1148         offset2 %= mod;
1149         std::uint32_t seed0 = (seeds[0] + offset1) % mod;
1150         seed0 = (seed0 + offset2) % mod;
1151         labelAndEngine.setSeed(seed0, 0);
1152         labelAndEngine.setSeed(seeds[1], 1);
1153         long int seedL[2];
1154         seedL[0] = static_cast<long int>(seed0);
1155         seedL[1] = static_cast<long int>(seeds[1]);
1156         labelAndEngine.engine()->setSeeds(seedL, 0);
1157       } else {
1158         assert(seeds.size() == 1U);
1159 
1160         if (engineName == "HepJamesRandom" || engineName == "MixMaxRng") {
1161           // Wrap around if the offsets push the seed over the maximum allowed value
1162           std::uint32_t mod = maxSeedHepJames + 1U;
1163           offset1 %= mod;
1164           offset2 %= mod;
1165           std::uint32_t seed0 = (seeds[0] + offset1) % mod;
1166           seed0 = (seed0 + offset2) % mod;
1167           labelAndEngine.setSeed(seed0, 0);
1168 
1169           long int seedL = static_cast<long int>(seed0);
1170           labelAndEngine.engine()->setSeed(seedL, 0);
1171         } else {
1172           assert(engineName == "TRandom3");
1173           // Wrap around if the offsets push the seed over the maximum allowed value
1174           // We have to be extra careful with this one because it may also go beyond
1175           // the values 32 bits can hold
1176           std::uint32_t max32 = maxSeedTRandom3;
1177           std::uint32_t seed0 = seeds[0];
1178           if ((max32 - seed0) >= offset1) {
1179             seed0 += offset1;
1180           } else {
1181             seed0 = offset1 - (max32 - seed0) - 1U;
1182           }
1183           if ((max32 - seed0) >= offset2) {
1184             seed0 += offset2;
1185           } else {
1186             seed0 = offset2 - (max32 - seed0) - 1U;
1187           }
1188           labelAndEngine.setSeed(seed0, 0);
1189 
1190           long seedL = static_cast<long>(seed0);
1191 
1192           // There is a dangerous conversion from std::uint32_t to long
1193           // that occurs above. In the next 2 lines we check the
1194           // behavior is what we need for the service to work
1195           // properly.  This conversion is forced on us by the
1196           // CLHEP and ROOT interfaces. If the assert ever starts
1197           // to fail we will have to come up with a way to deal
1198           // with this.
1199           std::uint32_t seedu32 = static_cast<std::uint32_t>(seedL);
1200           assert(seed0 == seedu32);
1201 
1202           labelAndEngine.engine()->setSeed(seedL, 0);
1203         }
1204       }
1205     }
1206   }  // namespace service
1207 }  // namespace edm