Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class myEvtRandomEngine : public EvtRandomEngine {

double random() override;

void setSeed(unsigned long int seed) override;

unsigned long int lastSeed() const override;

void setRandomEngine(CLHEP::HepRandomEngine* v) { the_engine = v; }

CLHEP::HepRandomEngine* engine() const { return the_engine; }
Expand All @@ -40,5 +44,6 @@ class myEvtRandomEngine : public EvtRandomEngine {
void throwNullPtr() const;

CLHEP::HepRandomEngine* the_engine;
unsigned long int m_lastSeed = 0;
};
#endif
103 changes: 66 additions & 37 deletions GeneratorInterface/EvtGenInterface/plugins/EvtGen/EvtGenInterface.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include "EvtGenBase/EvtDecayBase.hh"
#include "EvtGenBase/EvtId.hh"
#include "EvtGenBase/EvtDecayTable.hh"
#include "EvtGenBase/EvtPDL.hh"
#include "EvtGenBase/EvtRandom.hh"
#include "EvtGenBase/EvtParticle.hh"
#include "EvtGenBase/EvtParticleFactory.hh"
#include "EvtGenBase/EvtHepMCEvent.hh"
Expand All @@ -47,14 +49,15 @@
#include <cstdlib>
#include <cstdio>

thread_local std::unique_ptr<EvtGen> gen::EvtGenInterface::m_EvtGen{};

using namespace gen;
using namespace edm;

CLHEP::HepRandomEngine* EvtGenInterface::fRandomEngine;

EvtGenInterface::EvtGenInterface(const ParameterSet& pset) {
fPSet = new ParameterSet(pset);
the_engine = new myEvtRandomEngine(nullptr);
fPSet = std::make_unique<ParameterSet>(pset);
the_engine = std::make_unique<myEvtRandomEngine>(nullptr);
}

void EvtGenInterface::SetDefault_m_PDGs() {
Expand Down Expand Up @@ -284,19 +287,29 @@ void EvtGenInterface::SetDefault_m_PDGs() {
}
}

// Destructor is a no-op: m_EvtGen is a thread_local std::unique_ptr that cleans up at
// thread exit.
EvtGenInterface::~EvtGenInterface() {}

void EvtGenInterface::init() {
// flags for pythia8
fSpecialSettings.push_back("Pythia8:ParticleDecays:mixB = off");
//
void EvtGenInterface::ensureEvtGenOnThread() {
// This thread already has its EvtGen
if (m_EvtGen)
return;

edm::FileInPath decay_table(fPSet->getParameter<std::string>("decay_table"));
edm::FileInPath pdt(fPSet->getParameter<edm::FileInPath>("particle_property_file"));

bool usePythia = fPSet->getUntrackedParameter<bool>("use_internal_pythia", true);
bool useTauola = fPSet->getUntrackedParameter<bool>("use_internal_tauola", true);
bool usePhotos = fPSet->getUntrackedParameter<bool>("use_internal_photos", true);

if (fPSet->existsAs<bool>("use_internal_fsr", false)) {
edm::LogWarning("EvtGenInterface::ensureEvtGenOnThread")
<< "The 'use_internal_fsr' parameter is deprecated and has no effect. "
"Please use 'fsr_model' instead (set to 'none' to disable FSR, or "
"'photos', 'sherpa', or 'vincia' to select the FSR model).";
}

std::string fsrModel = fPSet->getParameter<std::string>("fsr_model");

//Setup evtGen following instructions on http://evtgen.warwick.ac.uk/docs/external/
bool convertPythiaCodes = fPSet->getUntrackedParameter<bool>(
Expand All @@ -305,8 +318,8 @@ void EvtGenInterface::init() {
std::getenv("PYTHIA8DATA"); // Specify the pythia xml data directory to use the default PYTHIA8DATA location

if (tmp == nullptr) {
edm::LogError("EvtGenInterface::~EvtGenInterface")
<< "EvtGenInterface::init() PYTHIA8DATA not defined. Terminating program ";
edm::LogError("EvtGenInterface::ensureEvtGenOnThread")
<< "PYTHIA8DATA not defined. Terminating program ";
exit(0);
}

Expand All @@ -317,8 +330,19 @@ void EvtGenInterface::init() {
// Set up the default external generator list: Photos, Pythia and/or Tauola
EvtExternalGenList genList(convertPythiaCodes, pythiaDir, photonType, useEvtGenRandom);
EvtAbsRadCorr* radCorrEngine = nullptr;
if (usePhotos)
radCorrEngine = genList.getPhotosModel(); // Get interface to radiative correction engine
if (fsrModel == "none") {
// FSR disabled
} else if (fsrModel == "photos") {
radCorrEngine = genList.getPhotosModel();
} else if (fsrModel == "sherpa") {
radCorrEngine = genList.getSherpaPhotonsModel(1e-7, 1, 0);
} else if (fsrModel == "vincia") {
radCorrEngine = genList.getVinciaQEDModel(1.0e-7);
} else {
throw cms::Exception("Configuration")
<< "EvtGenInterface: unknown fsr_model '" << fsrModel
<< "'. Allowed values are 'none', 'photos', 'sherpa', or 'vincia'.";
}
std::list<EvtDecayBase*> extraModels = genList.getListOfModels(); // get interface to Pythia and Tauola
std::list<EvtDecayBase*> myExtraModels;
for (unsigned int i = 0; i < extraModels.size(); i++) {
Expand All @@ -339,21 +363,12 @@ void EvtGenInterface::init() {
std::list<EvtDecayBase*>::iterator it = userModels.begin();
std::advance(it, i);
TString name = (*it)->getName();
edm::LogInfo("EvtGenInterface::~EvtGenInterface") << "Adding user model: " << name;
edm::LogInfo("EvtGenInterface::ensureEvtGenOnThread") << "Adding user model: " << name;
myExtraModels.push_back(*it);
}

// Set up the incoherent (1) or coherent (0) B mixing option
BmixingOption = fPSet->getUntrackedParameter<int>("B_Mixing", 1);
if (BmixingOption != 0 && BmixingOption != 1) {
throw cms::Exception("Configuration") << "EvtGenProducer requires B_Mixing to be 0 (coherent) or 1 (incoherent) \n"
"Please fix this in your configuration.";
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
// Create the EvtGen generator object, passing the external generators
m_EvtGen = new EvtGen(
decay_table.fullPath().c_str(), pdt.fullPath().c_str(), the_engine, radCorrEngine, &myExtraModels, BmixingOption);
m_EvtGen = std::make_unique<EvtGen>(
decay_table.fullPath().c_str(), pdt.fullPath().c_str(), the_engine.get(), radCorrEngine, &myExtraModels, BmixingOption);

// Add additional user information
if (fPSet->exists("user_decay_file")) {
Expand All @@ -370,9 +385,8 @@ void EvtGenInterface::init() {
int tmp_creation = mkstemp(user_decay_tmp);
FILE* tmpf = std::fopen(user_decay_tmp, "w");
if (!tmpf || (tmp_creation == -1)) {
edm::LogError("EvtGenInterface::~EvtGenInterface")
<< "EvtGenInterface::init() fails when trying to open a temporary file for embedded user.dec. Terminating "
"program ";
edm::LogError("EvtGenInterface::ensureEvtGenOnThread")
<< "fails when trying to open a temporary file for embedded user.dec. Terminating program ";
exit(0);
}
for (unsigned int i = 0; i < user_decay_lines.size(); i++) {
Expand All @@ -382,6 +396,23 @@ void EvtGenInterface::init() {
std::fclose(tmpf);
m_EvtGen->readUDecay(user_decay_tmp);
}
}

void EvtGenInterface::init() {
// flags for pythia8
fSpecialSettings.push_back("Pythia8:ParticleDecays:mixB = off");

// Set up the incoherent (1) or coherent (0) B mixing option. Must be set before
// ensureEvtGenOnThread() because the EvtGen ctor consumes it.
BmixingOption = fPSet->getUntrackedParameter<int>("B_Mixing", 1);
if (BmixingOption != 0 && BmixingOption != 1) {
throw cms::Exception("Configuration") << "EvtGenProducer requires B_Mixing to be 0 (coherent) or 1 (incoherent) \n"
"Please fix this in your configuration.";
}

// Construct the EvtGen instance for this thread (no-op if already constructed). Streams
// running decay() later on a different thread will lazy-construct on that thread instead.
ensureEvtGenOnThread();

// setup pdgid which the generator/hadronizer should not decay
if (fPSet->exists("operates_on_particles")) {
Expand Down Expand Up @@ -452,6 +483,10 @@ void EvtGenInterface::init() {
}

HepMC::GenEvent* EvtGenInterface::decay(HepMC::GenEvent* evt) {
// If the framework scheduled this stream onto a thread that has not yet built an EvtGen,
// construct one now. No-op on threads that already initialised in init().
ensureEvtGenOnThread();

if (the_engine->engine() == nullptr) {
throw edm::Exception(edm::errors::LogicError)
<< "The EvtGen code attempted to use a random number engine while\n"
Expand Down Expand Up @@ -496,8 +531,8 @@ HepMC::GenEvent* EvtGenInterface::decay(HepMC::GenEvent* evt) {
}
EvtId idEvt = EvtPDL::evtIdFromStdHep(idHep);
int ipart = idEvt.getId();
EvtDecayTable* evtDecayTable = EvtDecayTable::getInstance();
if (!isforced && isDefaultEvtGen && ipart != -1 && evtDecayTable->getNMode(ipart) != 0) {
EvtDecayTable& evtDecayTable = EvtDecayTable::getInstance();
if (!isforced && isDefaultEvtGen && ipart != -1 && evtDecayTable.getNMode(ipart) != 0) {
addToHepMC(*p, idEvt, evt, true); // generate decay and remove daugther if they are forced
}
}
Expand Down Expand Up @@ -616,17 +651,11 @@ void EvtGenInterface::update_particles(HepMC::GenParticle* partHep, HepMC::GenEv

void EvtGenInterface::setRandomEngine(CLHEP::HepRandomEngine* v) {
the_engine->setRandomEngine(v);
fRandomEngine = v;
EvtRandom::setRandomEngine(the_engine.get());
}

double EvtGenInterface::flat() {
if (!fRandomEngine) {
throw cms::Exception("LogicError")
<< "EvtGenInterface::flat: Attempt to generate random number when engine pointer is null\n"
<< "This might mean that the code was modified to generate a random number outside the\n"
<< "event and beginLuminosityBlock methods, which is not allowed.\n";
}
return fRandomEngine->flat();
return the_engine->random();
}

bool EvtGenInterface::findLastinChain(HepMC::GenParticle*& p) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace gen {
const std::vector<int>& operatesOnParticles() override { return m_PDGs; }
HepMC::GenEvent* decay(HepMC::GenEvent*) override;
void setRandomEngine(CLHEP::HepRandomEngine* v) override;
static double flat();
double flat();

private:
bool addToHepMC(HepMC::GenParticle* partHep, const EvtId& idEvt, HepMC::GenEvent* theEvent, bool del_daug);
Expand All @@ -52,8 +52,10 @@ namespace gen {
bool findLastinChain(HepMC::GenParticle*& p);
bool hasnoDaughter(HepMC::GenParticle* p);
void go_through_daughters(EvtParticle* part);
void ensureEvtGenOnThread();

EvtGen* m_EvtGen; // EvtGen main object
// One EvtGen per thread
static thread_local std::unique_ptr<EvtGen> m_EvtGen;

std::vector<EvtId> forced_id; // EvtGen Id's of particles which are to be forced by EvtGen
std::vector<int> forced_pdgids; // PDG Id's of particles which are to be forced by EvtGen
Expand All @@ -65,10 +67,9 @@ namespace gen {
std::vector<double> polarize_pol;
std::map<int, float> polarizations;
int BmixingOption = 1;
edm::ParameterSet* fPSet;
std::unique_ptr<edm::ParameterSet> fPSet;

static CLHEP::HepRandomEngine* fRandomEngine;
myEvtRandomEngine* the_engine;
std::unique_ptr<myEvtRandomEngine> the_engine;
};
} // namespace gen
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ double myEvtRandomEngine::random() {
return the_engine->flat();
}

void myEvtRandomEngine::setSeed(unsigned long int seed) { m_lastSeed = seed; }

unsigned long int myEvtRandomEngine::lastSeed() const { return m_lastSeed; }

void myEvtRandomEngine::throwNullPtr() const {
throw edm::Exception(edm::errors::LogicError) << "The EvtGen code attempted to a generate random number while\n"
<< "the engine pointer was null. This might mean that the code\n"
Expand Down
1 change: 1 addition & 0 deletions GeneratorInterface/ExternalDecays/BuildFile.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<use name="FWCore/Concurrency"/>
<use name="FWCore/ParameterSet"/>
<use name="FWCore/Framework"/>
<use name="SimG4Core/Application"/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(as I commented already in the earlier issue) I think this dependence on SimG4Core should be avoided. I opened #51041 to move the ThreadHandoff to FWCore/Utilities.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, merged

<use name="heppdt"/>
<use name="clhep"/>
<use name="hepmc"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace lhef {

namespace gen {

class EvtGenThreadOwner;
class EvtGenInterfaceBase;
class TauolaInterfaceBase;
class PhotosInterfaceBase;
Expand All @@ -45,8 +46,9 @@ namespace gen {

private:
bool fIsInitialized;
std::unique_ptr<EvtGenThreadOwner> fThreadOwner;
std::unique_ptr<EvtGenInterfaceBase> fEvtGenInterface;
//std::unique_ptr<TauolaInterfaceBase> fTauolaInterface;
//std::unique_ptr<EvtGenInterfaceBase> fEvtGenInterface;
//std::unique_ptr<PhotosInterfaceBase> fPhotosInterface;
std::vector<int> fPDGs;
std::vector<std::string> fSpecialSettings;
Expand Down
34 changes: 34 additions & 0 deletions GeneratorInterface/ExternalDecays/interface/EvtGenThreadOwner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#ifndef gen_EvtGenThreadOwner_h
#define gen_EvtGenThreadOwner_h

// Pins one dedicated pthread per stream and marshals every EvtGen call onto it.
// Mirrors SimG4Core's OscarMTProducer + omt::ThreadHandoff pattern

#include "SimG4Core/Application/interface/ThreadHandoff.h"
#include "FWCore/ServiceRegistry/interface/ServiceRegistry.h"

namespace gen {

class EvtGenThreadOwner {
public:
explicit EvtGenThreadOwner(int stackSize) : m_handoff{stackSize} {}

EvtGenThreadOwner(const EvtGenThreadOwner&) = delete;
EvtGenThreadOwner& operator=(const EvtGenThreadOwner&) = delete;

template <typename F>
void run(F&& f) {
auto token = edm::ServiceRegistry::instance().presentToken();
m_handoff.runAndWait([token, &f]() {
edm::ServiceRegistry::Operate guard{token};
f();
});
}

private:
omt::ThreadHandoff m_handoff;
};

}

#endif
Loading