Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2021-08-20 02:14:57

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