19#include <fairlogger/Logger.h>
20#include <TMCProcess.h>
39constexpr double kSpeedOfLightMMPerS = 2.99792458e11;
42constexpr long kMaxSeed = 900000000;
45constexpr unsigned int kGRandomSeedXor = 0x5eed0d3c;
48const char*
const kVertexLimits[] = {
"ParticleDecays:limitTau",
"ParticleDecays:limitRadius",
"ParticleDecays:limitCylinder"};
50std::string expand(std::string
const&
path)
52 TString expanded =
path;
53 gSystem->ExpandPathName(expanded);
54 return expanded.Data();
58bool setSeed(::Pythia8::Pythia& pythia,
long seed)
61 LOG(
error) <<
"Failed to init \'EventDecayer\': negative seed " <<
seed;
65 seed = gRandom->TRandom::GetSeed() ^ kGRandomSeedXor;
66 LOG(info) <<
"EventDecayer: using random seed from gRandom ^ " << kGRandomSeedXor <<
" % " << kMaxSeed + 1;
69 if (!pythia.readString(
"Random:setSeed on") || !pythia.readString(
"Random:seed " +
std::to_string(seed))) {
70 LOG(
error) <<
"Failed to init \'EventDecayer\': cannot set the Pythia8 seed " <<
seed;
77bool parsePdg(std::string
const& code,
int& pdg)
81 pdg = std::abs(std::stoi(code, &used));
82 return used == code.size();
83 }
catch (std::exception
const&) {
89bool skipDecays(::Pythia8::ParticleData& particleData, std::string
const& skipPdg)
91 std::istringstream skip(skipPdg);
93 while (skip >> code) {
95 if (!parsePdg(code, pdg)) {
96 LOG(
error) <<
"Failed to init \'EventDecayer\': skipPdg entry " << code <<
" is not a PDG code";
99 if (particleData.isParticle(pdg)) {
100 particleData.mayDecay(pdg,
false);
102 LOG(info) <<
"EventDecayer: skipPdg entry " << pdg <<
" is unknown to Pythia8, so never decayed anyway";
113 auto add = [](std::map<int, long>& to, std::map<int, long>
const& from) {
114 for (
auto const& [pdg,
count] : from) {
122 for (
auto const& [pdg, lifetimes] :
other.lifetimeMismatch) {
130 LOG(info) <<
"EventDecayer: decayed pdg " << pdg <<
": " <<
count;
133 LOG(warn) <<
"EventDecayer: Pythia8 failed to decay pdg " << pdg <<
": " <<
count;
136 LOG(warn) <<
"EventDecayer: pdg " << pdg <<
" left for transport is unknown to O2DatabasePDG: " <<
count;
139 LOG(warn) <<
"EventDecayer: pdg " << pdg <<
" left for transport is not in the Geant4 table (o2-sim may define it at runtime): " <<
count;
142 LOG(warn) <<
"EventDecayer: lifetime of pdg " << pdg <<
" differs between Pythia8 (ctau " << lifetimes.first
143 <<
" mm) and Geant4 (ctau " << lifetimes.second <<
" mm)";
151 std::unordered_map<int, G4ParticleInfo> table;
152 std::ifstream in(
path);
154 LOG(
error) <<
"EventDecayer: cannot read Geant4 particle table " <<
path;
158 while (std::getline(in, line)) {
159 if (line.empty() || line[0] ==
'#') {
162 std::istringstream
fields(line);
165 if (!(
fields >> pdg >> info.name >> info.ctauMM >> info.shortLived >> info.hasDecayTable)) {
166 LOG(warn) <<
"EventDecayer: skipping malformed line in " <<
path <<
": " << line;
178 mPythia.readString(
"ProcessLevel:all off");
181 LOG(info) <<
"Init \'EventDecayer\' with following parameters";
185 if (!setSeed(mPythia,
param.seed)) {
191 for (
int i = 0;
i < 8; ++
i) {
196 auto config = expand(
entry);
197 LOG(info) <<
"Reading configuration from file: " << config;
198 if (!mPythia.readFile(config,
true)) {
199 LOG(
error) <<
"Failed to init \'EventDecayer\': problems with configuration file " << config;
202 mConfigs.push_back(config);
205 for (
auto name : kVertexLimits) {
206 if (mPythia.settings.flag(
name)) {
207 LOG(
error) <<
"EventDecayer does not support " <<
name <<
", switching it off";
208 mPythia.settings.flag(
name,
false);
211 if (
param.tau0Max > 0.) {
212 mPythia.settings.flag(
"ParticleDecays:limitTau0",
true);
213 mPythia.settings.parm(
"ParticleDecays:tau0Max",
param.tau0Max);
216 if (!skipDecays(mPythia.particleData,
param.skipPdg)) {
220 mVerbose =
param.verbose;
221 mPythia.readString(
param.showChanged ?
"Init:showChangedParticleData on" :
"Init:showChangedParticleData off");
223 if (!mPythia.init()) {
224 LOG(
error) <<
"Failed to init \'EventDecayer\': init returned with error";
227 mSeed = mPythia.settings.mode(
"Random:seed");
228 LOG(info) <<
"EventDecayer: Pythia8 seed " << mSeed;
229 mTau0Max = mPythia.settings.flag(
"ParticleDecays:limitTau0") ? mPythia.settings.parm(
"ParticleDecays:tau0Max") : 0.;
231 LOG(info) <<
"EventDecayer: species with tau0 above " << mTau0Max <<
" mm/c stay undecayed";
234 mG4Particles.clear();
235 mHasG4Table = !
param.g4ParticleTable.empty();
237 auto path = expand(
param.g4ParticleTable);
239 if (mG4Particles.empty()) {
240 LOG(
error) <<
"Failed to init \'EventDecayer\': no particles in Geant4 particle table " <<
path;
243 LOG(info) <<
"EventDecayer: read " << mG4Particles.size() <<
" particles from Geant4 particle table " <<
path;
245 LOG(info) <<
"EventDecayer: no Geant4 particle table given, skipping the Geant4 PDG and lifetime check";
255 for (
auto const& config : mConfigs) {
256 summary += (summary.empty() ?
"" :
";") + config;
263 for (
auto name : kVertexLimits) {
264 if (mPythia.settings.flag(
name)) {
273bool EventDecayer::decayable(
int pdg)
const
275 auto& particleData = mPythia.particleData;
276 return particleData.mayDecay(pdg) && particleData.canDecay(pdg) && !(mTau0Max > 0. && particleData.tau0(pdg) > mTau0Max);
282 const int nOriginal = particles.size();
283 for (
int i = 0;
i < nOriginal; ++
i) {
284 auto const& particle = particles[
i];
285 auto pdg = particle.GetPdgCode();
289 if (decayOne(particles,
i)) {
296 checkParticles(particles,
report);
303bool EventDecayer::decayOne(std::vector<TParticle>& particles,
int index)
306 const TParticle mother = particles[
index];
307 auto&
event = mPythia.event;
310 const double px = mother.Px() * mMomentumUnit;
311 const double py = mother.Py() * mMomentumUnit;
312 const double pz = mother.Pz() * mMomentumUnit;
313 const double e = mother.Energy() * mEnergyUnit;
314 const double m2 =
e *
e - px * px - py * py - pz * pz;
315 event.append(mother.GetPdgCode(), 1, 0, 0, px, py, pz,
e, m2 > 0. ? std::sqrt(m2) : -
std::sqrt(-m2));
316 event[0].vProd(0., 0., 0., 0.);
317 event[0].tau(
event[0].tau0() * mPythia.rndm.exp());
318 if (!mPythia.moreDecays() ||
event[0].isFinal()) {
325 for (
int k = 1; k <
event.size(); ++k) {
326 auto const&
entry =
event[k];
333 const int offset = particles.size();
336 const double toPosition = 0.1 / mPositionUnit;
337 const double toTime = 1. / (kSpeedOfLightMMPerS * mTimeUnit);
339 for (
int k = 1; k <
event.size(); ++k) {
340 auto const&
entry =
event[k];
341 int mother1 = toIndex(
entry.mother1());
342 int mother2 = (
entry.mother2() > 0 &&
entry.mother2() !=
entry.mother1()) ? toIndex(
entry.mother2()) : -1;
343 int daughter1 =
entry.daughter1() > 0 ? toIndex(
entry.daughter1()) : -1;
344 int daughter2 =
entry.daughter2() > 0 ? toIndex(
entry.daughter2()) : daughter1;
345 TParticle product(
entry.id(), 0, mother1, mother2, daughter1, daughter2,
346 entry.px() / mMomentumUnit,
entry.py() / mMomentumUnit,
entry.pz() / mMomentumUnit,
entry.e() / mEnergyUnit,
347 mother.Vx() +
entry.xProd() * toPosition, mother.Vy() +
entry.yProd() * toPosition,
348 mother.Vz() +
entry.zProd() * toPosition, mother.T() +
entry.tProd() * toTime);
349 bool isFinal =
entry.isFinal();
351 product.SetWeight(mother.GetWeight());
352 product.SetUniqueID(kPPrimary);
353 particles.push_back(product);
356 auto& decayed = particles[
index];
359 decayed.SetFirstDaughter(toIndex(
event[0].daughter1()));
360 decayed.SetLastDaughter(toIndex(
event[0].daughter2() > 0 ?
event[0].daughter2() :
event[0].daughter1()));
366void EventDecayer::checkParticles(std::vector<TParticle>
const& particles, EventDecayerReport&
report)
const
369 for (
auto const& particle : particles) {
373 auto pdg = particle.GetPdgCode();
374 if (!pdgDB->GetParticle(pdg)) {
375 report.unknownInO2[pdg]++;
378 if (mHasG4Table && std::abs(pdg) < 1000000000 && mG4Particles.find(pdg) == mG4Particles.end()) {
379 report.unknownInG4[pdg]++;
384void EventDecayer::checkLifetimes(EventDecayerReport&
report)
const
390 auto g4 = mG4Particles.find(pdg);
391 if (g4 == mG4Particles.end() || g4->second.ctauMM == 0.) {
394 double pythiaCtau = mPythia.particleData.tau0(pdg);
395 double g4Ctau = g4->second.ctauMM;
396 if (std::abs(pythiaCtau - g4Ctau) / g4Ctau > 0.01) {
397 report.lifetimeMismatch[pdg] = {pythiaCtau, g4Ctau};
std::vector< std::shared_ptr< arrow::Field > > fields
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
char error[CURL_ERROR_SIZE]
Utility functions for MC particles.
static TDatabasePDG * Instance()
static const EventDecayerParam & Instance()
bool vertexLimitsActive() const
EventDecayerReport decay(std::vector< TParticle > &particles)
std::string configSummary() const
static void encodeParticleStatusAndTracking(TParticle &particle, bool wanttracking=true)
GLuint const GLchar * name
GLsizei const GLchar *const * path
void report(gsl::span< o2::InteractionTimeRecord > irs, int threshold, bool verbose)
std::unordered_map< int, G4ParticleInfo > loadG4ParticleTable(std::string const &path)
Reads the Geant4 particle table written by o2-sim-g4-particle-table.
int const float const TrackSeed< NLayers > & seed
int getGenStatusCode(MCGenStatusEncoding enc)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Counts of what one or more EventDecayer::decay calls did, per PDG code.
std::map< int, long > unknownInG4
std::map< int, std::pair< double, double > > lifetimeMismatch
std::map< int, long > failed
void merge(EventDecayerReport const &other)
std::map< int, long > unknownInO2
std::map< int, long > decayed
One entry of the Geant4 particle table.
VectorOfTObjectPtrs other
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"