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
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"