From fed51cebc39b917e2e6d8020682ec599705cbbeb Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Wed, 15 Oct 2025 12:54:22 +0200 Subject: [PATCH 1/6] Implemented Performance Generator --- .../common/external/generator/perfConf.json | 23 ++ .../external/generator/performanceGenerator.C | 289 ++++++++++++++++++ MC/config/common/ini/GeneratorPerformance.ini | 4 + .../common/ini/GeneratorPerformanceFix.ini | 6 + .../common/ini/tests/GeneratorPerformance.C | 45 +++ .../ini/tests/GeneratorPerformanceFix.C | 43 +++ 6 files changed, 410 insertions(+) create mode 100644 MC/config/common/external/generator/perfConf.json create mode 100644 MC/config/common/external/generator/performanceGenerator.C create mode 100644 MC/config/common/ini/GeneratorPerformance.ini create mode 100644 MC/config/common/ini/GeneratorPerformanceFix.ini create mode 100644 MC/config/common/ini/tests/GeneratorPerformance.C create mode 100644 MC/config/common/ini/tests/GeneratorPerformanceFix.C diff --git a/MC/config/common/external/generator/perfConf.json b/MC/config/common/external/generator/perfConf.json new file mode 100644 index 000000000..770c13550 --- /dev/null +++ b/MC/config/common/external/generator/perfConf.json @@ -0,0 +1,23 @@ +{ + "generators": [ + { + "cocktail": [ + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "external", + "config": { + "fileName": "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/performanceGenerator.C", + "funcName": "Generator_Performance()", + "iniFile": "" + } + } + ] + } + ], + "fractions": [ + 1 + ] +} \ No newline at end of file diff --git a/MC/config/common/external/generator/performanceGenerator.C b/MC/config/common/external/generator/performanceGenerator.C new file mode 100644 index 000000000..557819638 --- /dev/null +++ b/MC/config/common/external/generator/performanceGenerator.C @@ -0,0 +1,289 @@ +// External generator requested in https://its.cern.ch/jira/browse/O2-6235 +// for multidimensional performance studies +namespace o2 +{ + namespace eventgen + { + + class GenPerf : public Generator + { + public: + GenPerf(float fraction = 0.03f, unsigned short int nsig = 100, unsigned short int tag = 1) + { + if (fraction == -1) { + LOG(info) << nsig << " Signal particles will be generated in each event"; + mNSig = nsig; + mFraction = -1.f; + } else if (fraction >= 0) { + LOG(info) << "Fraction based signal generation is enabled"; + LOG(info) << fraction << "*nUE tracks per event will be generated"; + mFraction = fraction; + } else { + LOG(fatal) << "Wrong fraction selected. Accepted values are:"; + LOG(fatal) << "\t -1 => fixed number of tracks per event"; + LOG(fatal) << ">=0 => fraction based signal generation over the number of UE tracks per event"; + exit(1); + } + initGenMap(); + if (genMap.find(tag) == genMap.end()) { + LOG(fatal) << "Wrong tag selected. Accepted values are:"; + for (const auto& [key, _] : genMap) { + LOG(fatal) << "\t" << key; + } + exit(1); + } else { + mTag = tag; + LOG(info) << "Generator with tag " << mTag << " is selected"; + } + Generator::setTimeUnit(1.0); + Generator::setPositionUnit(1.0); + } + + Bool_t generateEvent() override + { + return kTRUE; + } + + Bool_t importParticles() override + { + mNUE = 0; + if ( mFraction != -1) { + // This line assumes that the current generator is run in a cocktail with another generator + // which is run before the current one in a sequential way + mNUE = genList.front()->getParticles().size(); + LOG(debug) << "Number of tracks from UE is " << mNUE; + } + unsigned short nSig = (mFraction == -1) ? mNSig : std::lround(mFraction * mNUE); + LOG(debug) << "Generating additional " << nSig << " particles"; + for (int k = 0; k < nSig; k++){ + mParticles.push_back(genMap[mTag]()); + } + return kTRUE; + } + + private: + float mFraction = 0.03f; // Fraction based generation + unsigned short int mNSig = 0; // Number of particles to generate + unsigned int mNUE = 0; // Number of tracks in the Underlying event + unsigned short int mTag = 1; // Tag to select the generation function + const std::vector> genList = GeneratorHybrid::getGenerators(); + std::map> genMap; + UInt_t mGenID = 42; + + // This is performance test generator with uniform weighting for PDG + TParticle generateParticle0() + { + // 1. Get the singleton instances + TDatabasePDG *pdg = TDatabasePDG::Instance(); + // 2. Define the list of PDG codes + const int ncodes = 13; + const int pdgCodes[ncodes] = { + 310, // K0_s + 421, // D0 + 3122, // Lambda + -3122, // Anti-Lambda + 443, // J/psi + 13, // mu- + 22, // gamma + 23, // Z0 + 1, 2, 3, 4, 5 // Quarks: d, u, s, c, b (t-quark is 6, often excluded for kinematics) + }; + // 3. Randomly select and validate a PDG code + // TMath::Nint(gRandom->Rndm() * ncodes) selects an index from 0 to ncodes-1 safely. + int index = TMath::Nint(gRandom->Rndm() * (ncodes - 1)); + int pdgCode = pdgCodes[index]; + // Check if the particle exists and switch to antiparticle if needed + if (pdg->GetParticle(pdgCode) == nullptr) + { + if (pdg->GetParticle(-pdgCode) != nullptr) + { + pdgCode *= -1; // Use the negative code (antiparticle) + } + else + { + LOG(error) << "Error: PDG code " << pdgCode << " not found in TDatabasePDG. Using Muon (13)."; + pdgCode = 13; + } + } + // 4. Generate Kinematics (p_T, phi, eta) + float pt = 1 / (gRandom->Rndm()); // flat 1/pt distribution + float phi = gRandom->Rndm() * 2.0f * TMath::Pi(); + float eta = 3.0f * (gRandom->Rndm() - 0.5f); // eta from -1.5 to 1.5 + // Initial position (origin) + float xyz[3] = {0.0f, 0.0f, 0.0f}; + // if cosmic, you might want to randomize the vertex position + if (pdgCode == 13 || pdgCode == -13) + { + xyz[0] = (gRandom->Rndm() - 0.5) * 300.0f; // x from -100 to 100 cm + xyz[1] = (gRandom->Rndm() - 0.5) * 300.0f; // y from -100 to 100 cm + xyz[2] = 400; + pt = 1 / (gRandom->Rndm() + 0.01); + eta = gRandom->Gaus() * 0.2; + } + // + // Convert spherical coordinates (pt, phi, eta) to Cartesian (px, py, pz) + float pz = pt * TMath::SinH(eta); + float px = pt * TMath::Cos(phi); + float py = pt * TMath::Sin(phi); + // 5. Calculate Energy (E) from Mass (M) + TParticlePDG *particleInfo = pdg->GetParticle(pdgCode); + double mass = particleInfo ? particleInfo->Mass() : 0.1056; // Default to muon mass if lookup fails + double energy = TMath::Sqrt(px * px + py * py + pz * pz + mass * mass); + + // 6. Create and return the TParticle object by value + // TParticle(pdgCode, trackIndex, Mother, Daughter1, Daughter2, Px, Py, Pz, E, Vx, Vy, Vz, Time) + int status = -1; // Status code, -1 for undefined + // Set your custom performance generator ID (e.g., ID 42) + TParticle generatedParticle(pdgCode, status, -1, -1, -1, -1, px, py, pz, energy, xyz[0], xyz[1], xyz[2], 0.0); + generatedParticle.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(generatedParticle.GetStatusCode(), 0).fullEncoding); + generatedParticle.SetUniqueID(mGenID); + generatedParticle.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + return generatedParticle; + } + + // Particle configuration for ALICE O2 performance testing + struct ParticleSpec + { + int pdgCode; + float fraction; // Relative probability for probe statistics + float pTScale; // Scales pt + }; + + // Optimized for rare probes (J/psi, D0, jets) with flat distributions + const std::vector g_particle_specs = { + // PDG | Fraction | pTScale + {22, 1.0f, 1.0f}, // Photon: High yield for PID/calo + {13, 1.f, 1.0f}, // Muon: Cosmic override applied + {-13, 1.f, 1.0f}, // Anti-muon + {23, 0.1f, 10.0f}, // Z0: Rare, + {310, 1.f, 1.0f}, // K0_s: Common hadron + {421, 0.2f, 1.5f}, // D0 + {443, 0.1f, 5.0f}, // J/psi: Boosted for candle + {3122, 0.5f, 1.0f}, // Lambda + {-3122, 0.5f, 1.0f}, // Anti-Lambda + {211, 1.0f, 1.0f}, // Pi+ + {-211, 1.0f, 1.0f}, // Pi-: + // + {21, 0.1f, 3.0f}, // Gluon: Jet proxy (status=11) + {1, 0.1f, 3.0f}, // d quark: Jet proxy + {-1, 0.1f, 3.0f}, // anti-d + {2, 0.1f, 3.0f}, // u quark: Jet proxy + {-2, 0.1f, 3.0f}, // anti-u + {3, 0.1f, 5.0f}, // s quark: Strange + {-3, 0.1f, 5.0f}, // anti-s + {4, 0.1f, 5.0f}, // c quark: Heavy flavor + {-4, 0.1f, 5.0f}, // anti-c + {5, 0.1f, 8.0f}, // b quark: Very hard + {-5, 0.1f, 8.0f} // anti-b + }; + + // pT bounds: Max pT ~5 TeV (ALICE Pb-Pb energy) + const float kMaxInvPt = 1.0f; // Min pT = 1 GeV + const float kBaseMinInvPt = 2e-4f; // Max pT = 5000 GeV (unscaled) + + // Check if particle is a parton (quark/gluon, status=11) + bool isParton(int& pdgCode) + { + int absCode = TMath::Abs(pdgCode); + return (absCode >= 1 && absCode <= 5) || absCode == 21; + } + + // Generator for flat distributions in pT, eta for calibration + TParticle generateParticle1() + { + TDatabasePDG *pdg = TDatabasePDG::Instance(); + // 1. Weighted Random Selection + static float totalWeight = 0.0f; + if (totalWeight == 0.0f) + { + totalWeight = std::accumulate(g_particle_specs.begin(), g_particle_specs.end(), 0.0f, + [](float sum, const ParticleSpec &spec) + { return sum + spec.fraction; }); + } + float randVal = gRandom->Rndm() * totalWeight; + float cumulativeWeight = 0.0f; + const ParticleSpec *selectedSpec = nullptr; + for (const auto &spec : g_particle_specs) + { + cumulativeWeight += spec.fraction; + if (randVal <= cumulativeWeight) + { + selectedSpec = &spec; + break; + } + } + if (!selectedSpec) + selectedSpec = &g_particle_specs.back(); + int pdgCode = selectedSpec->pdgCode; + float pTScale = selectedSpec->pTScale; + // 2. PDG Validation + if (!pdg->GetParticle(pdgCode)) + { + if (pdg->GetParticle(-pdgCode)) + pdgCode *= -1; + else + { + LOG(error) << "Error: PDG " << pdgCode << " not found. Using muon (13).\n"; + pdgCode = 13; + pTScale = 1.0f; + } + } + // 3. Status: 11 for partons (jets), 1 for final-state + int status = isParton(pdgCode) ? 11 : 1; + // 4. Kinematics (flat 1/pT, max ~5000 GeV / pTScale) + float min_inv_pt = kBaseMinInvPt / pTScale; // E.g., max pT=40,000 GeV for b quarks + float inv_pt = (gRandom->Rndm() / pTScale) * (kMaxInvPt - min_inv_pt) + min_inv_pt; + float pt = 1.0f / inv_pt; + float phi = gRandom->Rndm() * 2.0f * TMath::Pi(); + float eta = gRandom->Rndm() * 3.0f - 1.5f; // ALICE TPC: -1.5 to 1.5 + // Vertex: Delta (embedding handles smearing) + float xyz[3] = {0.0f, 0.0f, 0.0f}; + // 5. Cosmic Muon Override + if (TMath::Abs(pdgCode) == 13) + { + xyz[0] = (gRandom->Rndm() - 0.5f) * 300.0f; + xyz[1] = (gRandom->Rndm() - 0.5f) * 300.0f; + xyz[2] = 400.0f; + inv_pt = (gRandom->Rndm() + 0.01f) / pTScale; // Apply pTScale + pt = 1.0f / inv_pt; + eta = TMath::Max(-4.0, TMath::Min(4.0, gRandom->Gaus(0.0, 0.2))); + status = 1; + } + // 6. Momentum and Energy + float pz = pt * TMath::SinH(eta); + float px = pt * TMath::Cos(phi); + float py = pt * TMath::Sin(phi); + TParticlePDG *particleInfo = pdg->GetParticle(pdgCode); + double mass = particleInfo ? particleInfo->Mass() : 0.1056; + double energy = TMath::Sqrt(px * px + py * py + pz * pz + mass * mass); + // 7. TParticle Creation (quarks/gluons need fragmentation in O2) + TParticle generatedParticle(pdgCode, status, -1, -1, -1, -1, px, py, pz, energy, xyz[0], xyz[1], xyz[2], 0.0); + generatedParticle.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(generatedParticle.GetStatusCode(), 0).fullEncoding); + generatedParticle.SetUniqueID(mGenID); + generatedParticle.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + return generatedParticle; + } + + void initGenMap() + { + genMap[0] = [this]() + { return generateParticle0(); }; + genMap[1] = [this]() + { return generateParticle1(); }; + } + }; + + } // namespace eventgen +} // namespace o2 + +// Performance test generator +// fraction == -1 enables the fixed number of signal particles per event (nsig) +// tag selects the generator type to be used +FairGenerator * +Generator_Performance(const float fraction = 0.03f, const unsigned short int nsig = 100, unsigned short int tag = 1) +{ + auto generator = new o2::eventgen::GenPerf(fraction, nsig, tag); + return generator; +} \ No newline at end of file diff --git a/MC/config/common/ini/GeneratorPerformance.ini b/MC/config/common/ini/GeneratorPerformance.ini new file mode 100644 index 000000000..b832c67fd --- /dev/null +++ b/MC/config/common/ini/GeneratorPerformance.ini @@ -0,0 +1,4 @@ +# Test performance generator for multidimensional studies using fraction based signal particles per event compared to underlying event +# generated with PYTHIA8 pp at 14 TeV +[GeneratorHybrid] +configFile = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/perfConf.json \ No newline at end of file diff --git a/MC/config/common/ini/GeneratorPerformanceFix.ini b/MC/config/common/ini/GeneratorPerformanceFix.ini new file mode 100644 index 000000000..c5349101d --- /dev/null +++ b/MC/config/common/ini/GeneratorPerformanceFix.ini @@ -0,0 +1,6 @@ +# Test performance generator for multidimensional studies using fix number of signal particles per event +# Parameters are in order: fraction of signal particles, fixed number of signal particles per event, tag to select the generator type +# fraction = -1 enables the fixed number of signal particles per event (nsig) +[GeneratorExternal] +fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/performanceGenerator.C +funcName = Generator_Performance(-1, 100, 1) \ No newline at end of file diff --git a/MC/config/common/ini/tests/GeneratorPerformance.C b/MC/config/common/ini/tests/GeneratorPerformance.C new file mode 100644 index 000000000..b3e7ae4bb --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorPerformance.C @@ -0,0 +1,45 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree 'o2sim' in file " << path << "\n"; + return 1; + } + // Get the MCTrack branch + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + // Check if processes with ID 42 are available + // And are 0.03 of the UE tracks + const int processID = 42; // Performance test particle custom process ID + int nEvents = tree->GetEntries(); + short int count_perf = 0; + + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nTracks = tracks->size(); + count_perf = 0; + for (auto &track : *tracks) + { + const auto& process = track.getProcess(); + if (process == 42) + { + count_perf++; + } + } + int UEtracks = nTracks - count_perf; + unsigned short int expSig = std::lround(0.03 * UEtracks); + if (count_perf != expSig) + { + std::cerr << "Event " << i << ": Expected " << expSig << " performance test particles, found " << count_perf << "\n"; + return 1; + } + } + + file.Close(); + return 0; +} \ No newline at end of file diff --git a/MC/config/common/ini/tests/GeneratorPerformanceFix.C b/MC/config/common/ini/tests/GeneratorPerformanceFix.C new file mode 100644 index 000000000..ffda1fb6e --- /dev/null +++ b/MC/config/common/ini/tests/GeneratorPerformanceFix.C @@ -0,0 +1,43 @@ +int External() { + std::string path{"o2sim_Kine.root"}; + TFile file(path.c_str(), "READ"); + if (file.IsZombie()) { + std::cerr << "Cannot open ROOT file " << path << "\n"; + return 1; + } + auto tree = (TTree *)file.Get("o2sim"); + if (!tree) { + std::cerr << "Cannot find tree 'o2sim' in file " << path << "\n"; + return 1; + } + // Get the MCTrack branch + std::vector *tracks{}; + tree->SetBranchAddress("MCTrack", &tracks); + // Check if processes with ID 42 are available + // And are 100 per event + const int processID = 42; // Performance test particle custom process ID + int nEvents = tree->GetEntries(); + short int count_perf = 0; + unsigned short int expSig = 100; // set by default in the ini file + for (int i = 0; i < nEvents; i++) { + tree->GetEntry(i); + int nTracks = tracks->size(); + count_perf = 0; + for (auto &track : *tracks) + { + const auto& process = track.getProcess(); + if (process == 42) + { + count_perf++; + } + } + if (count_perf != expSig) + { + std::cerr << "Event " << i << ": Expected " << expSig << " performance test particles, found " << count_perf << "\n"; + return 1; + } + } + + file.Close(); + return 0; +} \ No newline at end of file From f6c32a7cd0fb76b1732685187dac3252406ba288 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Thu, 16 Oct 2025 15:54:31 +0200 Subject: [PATCH 2/6] Added Decayer Currently the decayer doesn't decay the Z0 particle anyway. I wasn't able to find a configuration that allows the decay of the resonance, might be implemented later, however the baseline is here and the Z0 is disabled from the transport for the time being, making the generator work. --- .../external/generator/performanceGenerator.C | 76 ++++++++++++++++++- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/MC/config/common/external/generator/performanceGenerator.C b/MC/config/common/external/generator/performanceGenerator.C index 557819638..818d9327e 100644 --- a/MC/config/common/external/generator/performanceGenerator.C +++ b/MC/config/common/external/generator/performanceGenerator.C @@ -35,6 +35,8 @@ namespace o2 mTag = tag; LOG(info) << "Generator with tag " << mTag << " is selected"; } + mDecayer = std::make_unique(); + mDecayer->Init(); Generator::setTimeUnit(1.0); Generator::setPositionUnit(1.0); } @@ -66,6 +68,7 @@ namespace o2 unsigned short int mNSig = 0; // Number of particles to generate unsigned int mNUE = 0; // Number of tracks in the Underlying event unsigned short int mTag = 1; // Tag to select the generation function + std::unique_ptr mDecayer; // Pythia8 decayer for particles not present in the physics list of Geant4 (like Z0) const std::vector> genList = GeneratorHybrid::getGenerators(); std::map> genMap; UInt_t mGenID = 42; @@ -137,8 +140,42 @@ namespace o2 TParticle generatedParticle(pdgCode, status, -1, -1, -1, -1, px, py, pz, energy, xyz[0], xyz[1], xyz[2], 0.0); generatedParticle.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(generatedParticle.GetStatusCode(), 0).fullEncoding); generatedParticle.SetUniqueID(mGenID); - generatedParticle.SetBit(ParticleStatus::kToBeDone, // - o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + if (pdgCode == 23) { + generatedParticle.SetBit(ParticleStatus::kToBeDone, false); // Force Z0 to be decayed by the transport + LOG(debug) << "Processing Z0 with DecayerPythia8"; + TLorentzVector *pDec = new TLorentzVector(px, py, pz, energy); + mDecayer->Decay(pdgCode, pDec); + TClonesArray *daughters = new TClonesArray("TParticle"); + mDecayer->ImportParticles(daughters); + unsigned short int nDaughters = daughters->GetEntriesFast(); + if (daughters && nDaughters > 0) { + for (int i = 0; i < daughters->GetEntriesFast(); i++) { + TParticle* daughter = (TParticle*)daughters->At(i); + daughter->SetUniqueID(mGenID); + if (i > 0) + { + daughter->SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + } + else + { + // First daughter is the mother (Z0) + daughter->SetBit(ParticleStatus::kToBeDone, false); + } + LOG(debug) << "Daughter " << i << ": PDG=" << daughter->GetPdgCode() << ", E=" << daughter->Energy() << ", p=(" << daughter->Px() << "," << daughter->Py() << "," << daughter->Pz() << ")"; + mParticles.push_back(*daughter); + } + LOG(debug) << "Z0 decayed into " << daughters->GetEntriesFast() << " particles"; + daughters->Clear("C"); + delete daughters; + } else { + LOG(warn) << "DecayerPythia8 failed to decay Z0 or no daughters found"; + } + delete pDec; + } else { + generatedParticle.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + } return generatedParticle; } @@ -261,8 +298,39 @@ namespace o2 TParticle generatedParticle(pdgCode, status, -1, -1, -1, -1, px, py, pz, energy, xyz[0], xyz[1], xyz[2], 0.0); generatedParticle.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(generatedParticle.GetStatusCode(), 0).fullEncoding); generatedParticle.SetUniqueID(mGenID); - generatedParticle.SetBit(ParticleStatus::kToBeDone, // - o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + if (pdgCode == 23) { + generatedParticle.SetBit(ParticleStatus::kToBeDone, false); // Force Z0 to be decayed by the transport + LOG(debug) << "Processing Z0 with DecayerPythia8"; + TLorentzVector *pDec = new TLorentzVector(px, py, pz, energy); + mDecayer->Decay(pdgCode, pDec); + TClonesArray *daughters = new TClonesArray("TParticle"); + mDecayer->ImportParticles(daughters); + unsigned short int nDaughters = daughters->GetEntriesFast(); + if (daughters && nDaughters > 0) { + for (int i = 0; i < daughters->GetEntriesFast(); i++) { + TParticle* daughter = (TParticle*)daughters->At(i); + daughter->SetUniqueID(mGenID); + if (i > 0) { + daughter->SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + } else { + // First daughter is the mother (Z0) + daughter->SetBit(ParticleStatus::kToBeDone, false); + } + LOG(debug) << "Daughter " << i << ": PDG=" << daughter->GetPdgCode() << ", E=" << daughter->Energy() << ", p=(" << daughter->Px() << "," << daughter->Py() << "," << daughter->Pz() << ")"; + mParticles.push_back(*daughter); + } + LOG(debug) << "Z0 decayed into " << daughters->GetEntriesFast() << " particles"; + daughters->Clear("C"); + delete daughters; + } else { + LOG(warn) << "DecayerPythia8 failed to decay Z0 or no daughters found"; + } + delete pDec; + } else { + generatedParticle.SetBit(ParticleStatus::kToBeDone, // + o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); + } return generatedParticle; } From 62487252ee5b6a6a5211e6c01d4fe4333349448d Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 24 Oct 2025 22:50:01 +0200 Subject: [PATCH 3/6] Removed not working ini + used hybrid singleton --- .../common/external/generator/perfConf.json | 23 ---------- .../external/generator/performanceGenerator.C | 12 +++-- MC/config/common/ini/GeneratorPerformance.ini | 4 -- .../common/ini/tests/GeneratorPerformance.C | 45 ------------------- 4 files changed, 9 insertions(+), 75 deletions(-) delete mode 100644 MC/config/common/external/generator/perfConf.json delete mode 100644 MC/config/common/ini/GeneratorPerformance.ini delete mode 100644 MC/config/common/ini/tests/GeneratorPerformance.C diff --git a/MC/config/common/external/generator/perfConf.json b/MC/config/common/external/generator/perfConf.json deleted file mode 100644 index 770c13550..000000000 --- a/MC/config/common/external/generator/perfConf.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "generators": [ - { - "cocktail": [ - { - "name": "pythia8pp", - "config": "" - }, - { - "name": "external", - "config": { - "fileName": "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/performanceGenerator.C", - "funcName": "Generator_Performance()", - "iniFile": "" - } - } - ] - } - ], - "fractions": [ - 1 - ] -} \ No newline at end of file diff --git a/MC/config/common/external/generator/performanceGenerator.C b/MC/config/common/external/generator/performanceGenerator.C index 818d9327e..96d2c0f88 100644 --- a/MC/config/common/external/generator/performanceGenerator.C +++ b/MC/config/common/external/generator/performanceGenerator.C @@ -52,8 +52,14 @@ namespace o2 if ( mFraction != -1) { // This line assumes that the current generator is run in a cocktail with another generator // which is run before the current one in a sequential way - mNUE = genList.front()->getParticles().size(); - LOG(debug) << "Number of tracks from UE is " << mNUE; + if (!mGenList) { + auto &hybridInstance = GeneratorHybrid::Instance(); + mGenList = &hybridInstance.getGenerators(); + } + if (!mGenList->empty()) { + mNUE = mGenList->front()->getParticles().size(); + LOG(debug) << "Number of tracks from UE is " << mNUE; + } } unsigned short nSig = (mFraction == -1) ? mNSig : std::lround(mFraction * mNUE); LOG(debug) << "Generating additional " << nSig << " particles"; @@ -69,7 +75,7 @@ namespace o2 unsigned int mNUE = 0; // Number of tracks in the Underlying event unsigned short int mTag = 1; // Tag to select the generation function std::unique_ptr mDecayer; // Pythia8 decayer for particles not present in the physics list of Geant4 (like Z0) - const std::vector> genList = GeneratorHybrid::getGenerators(); + const std::vector>* mGenList = nullptr; // Cached generators list std::map> genMap; UInt_t mGenID = 42; diff --git a/MC/config/common/ini/GeneratorPerformance.ini b/MC/config/common/ini/GeneratorPerformance.ini deleted file mode 100644 index b832c67fd..000000000 --- a/MC/config/common/ini/GeneratorPerformance.ini +++ /dev/null @@ -1,4 +0,0 @@ -# Test performance generator for multidimensional studies using fraction based signal particles per event compared to underlying event -# generated with PYTHIA8 pp at 14 TeV -[GeneratorHybrid] -configFile = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/perfConf.json \ No newline at end of file diff --git a/MC/config/common/ini/tests/GeneratorPerformance.C b/MC/config/common/ini/tests/GeneratorPerformance.C deleted file mode 100644 index b3e7ae4bb..000000000 --- a/MC/config/common/ini/tests/GeneratorPerformance.C +++ /dev/null @@ -1,45 +0,0 @@ -int External() { - std::string path{"o2sim_Kine.root"}; - TFile file(path.c_str(), "READ"); - if (file.IsZombie()) { - std::cerr << "Cannot open ROOT file " << path << "\n"; - return 1; - } - auto tree = (TTree *)file.Get("o2sim"); - if (!tree) { - std::cerr << "Cannot find tree 'o2sim' in file " << path << "\n"; - return 1; - } - // Get the MCTrack branch - std::vector *tracks{}; - tree->SetBranchAddress("MCTrack", &tracks); - // Check if processes with ID 42 are available - // And are 0.03 of the UE tracks - const int processID = 42; // Performance test particle custom process ID - int nEvents = tree->GetEntries(); - short int count_perf = 0; - - for (int i = 0; i < nEvents; i++) { - tree->GetEntry(i); - int nTracks = tracks->size(); - count_perf = 0; - for (auto &track : *tracks) - { - const auto& process = track.getProcess(); - if (process == 42) - { - count_perf++; - } - } - int UEtracks = nTracks - count_perf; - unsigned short int expSig = std::lround(0.03 * UEtracks); - if (count_perf != expSig) - { - std::cerr << "Event " << i << ": Expected " << expSig << " performance test particles, found " << count_perf << "\n"; - return 1; - } - } - - file.Close(); - return 0; -} \ No newline at end of file From 765080f6192a5f5c673b8f42a85a3db1405c4011 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Wed, 12 Nov 2025 22:20:34 +0100 Subject: [PATCH 4/6] Implemented two modes for Z0 --- .../external/generator/performanceGenerator.C | 164 +++++++++++------- 1 file changed, 99 insertions(+), 65 deletions(-) diff --git a/MC/config/common/external/generator/performanceGenerator.C b/MC/config/common/external/generator/performanceGenerator.C index 96d2c0f88..59368cfaf 100644 --- a/MC/config/common/external/generator/performanceGenerator.C +++ b/MC/config/common/external/generator/performanceGenerator.C @@ -8,7 +8,8 @@ namespace o2 class GenPerf : public Generator { public: - GenPerf(float fraction = 0.03f, unsigned short int nsig = 100, unsigned short int tag = 1) + GenPerf(float fraction = 0.03f, unsigned short int nsig = 100, unsigned short int tag = 1, bool setDecayZ0 = false) + : mDecayZ0(setDecayZ0) { if (fraction == -1) { LOG(info) << nsig << " Signal particles will be generated in each event"; @@ -35,8 +36,15 @@ namespace o2 mTag = tag; LOG(info) << "Generator with tag " << mTag << " is selected"; } - mDecayer = std::make_unique(); - mDecayer->Init(); + if (mDecayZ0){ + LOG(info) << "Z0 decays will be handled with Pythia8"; + mPythia = std::make_unique(); + // Configure Pythia8 with minimal beam setup for standalone decays + mPythia->readString("WeakSingleBoson:ffbar2gmZ = on"); + mPythia->init(); // Initialize + } else { + LOG(info) << "Z0 decays will be created but not transported (incompatible with Geant4 physics list)"; + } Generator::setTimeUnit(1.0); Generator::setPositionUnit(1.0); } @@ -64,7 +72,20 @@ namespace o2 unsigned short nSig = (mFraction == -1) ? mNSig : std::lround(mFraction * mNUE); LOG(debug) << "Generating additional " << nSig << " particles"; for (int k = 0; k < nSig; k++){ - mParticles.push_back(genMap[mTag]()); + auto part = genMap[mTag](); + if(part.GetPdgCode() == 23) { + if(!mDecayZ0) { + mParticles.push_back(part); + } else { + auto daughters = decayZ0(part); + for (auto &dau : daughters) + { + mParticles.push_back(dau); + } + } + } else { + mParticles.push_back(part); + } } return kTRUE; } @@ -74,7 +95,8 @@ namespace o2 unsigned short int mNSig = 0; // Number of particles to generate unsigned int mNUE = 0; // Number of tracks in the Underlying event unsigned short int mTag = 1; // Tag to select the generation function - std::unique_ptr mDecayer; // Pythia8 decayer for particles not present in the physics list of Geant4 (like Z0) + bool mDecayZ0 = false; // Whether to decay Z0 with Pythia8 + std::unique_ptr mPythia; // Pythia8 instance for particle decays not present in the physics list of Geant4 (like Z0) const std::vector>* mGenList = nullptr; // Cached generators list std::map> genMap; UInt_t mGenID = 42; @@ -148,36 +170,6 @@ namespace o2 generatedParticle.SetUniqueID(mGenID); if (pdgCode == 23) { generatedParticle.SetBit(ParticleStatus::kToBeDone, false); // Force Z0 to be decayed by the transport - LOG(debug) << "Processing Z0 with DecayerPythia8"; - TLorentzVector *pDec = new TLorentzVector(px, py, pz, energy); - mDecayer->Decay(pdgCode, pDec); - TClonesArray *daughters = new TClonesArray("TParticle"); - mDecayer->ImportParticles(daughters); - unsigned short int nDaughters = daughters->GetEntriesFast(); - if (daughters && nDaughters > 0) { - for (int i = 0; i < daughters->GetEntriesFast(); i++) { - TParticle* daughter = (TParticle*)daughters->At(i); - daughter->SetUniqueID(mGenID); - if (i > 0) - { - daughter->SetBit(ParticleStatus::kToBeDone, // - o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); - } - else - { - // First daughter is the mother (Z0) - daughter->SetBit(ParticleStatus::kToBeDone, false); - } - LOG(debug) << "Daughter " << i << ": PDG=" << daughter->GetPdgCode() << ", E=" << daughter->Energy() << ", p=(" << daughter->Px() << "," << daughter->Py() << "," << daughter->Pz() << ")"; - mParticles.push_back(*daughter); - } - LOG(debug) << "Z0 decayed into " << daughters->GetEntriesFast() << " particles"; - daughters->Clear("C"); - delete daughters; - } else { - LOG(warn) << "DecayerPythia8 failed to decay Z0 or no daughters found"; - } - delete pDec; } else { generatedParticle.SetBit(ParticleStatus::kToBeDone, // o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); @@ -305,34 +297,8 @@ namespace o2 generatedParticle.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(generatedParticle.GetStatusCode(), 0).fullEncoding); generatedParticle.SetUniqueID(mGenID); if (pdgCode == 23) { - generatedParticle.SetBit(ParticleStatus::kToBeDone, false); // Force Z0 to be decayed by the transport - LOG(debug) << "Processing Z0 with DecayerPythia8"; - TLorentzVector *pDec = new TLorentzVector(px, py, pz, energy); - mDecayer->Decay(pdgCode, pDec); - TClonesArray *daughters = new TClonesArray("TParticle"); - mDecayer->ImportParticles(daughters); - unsigned short int nDaughters = daughters->GetEntriesFast(); - if (daughters && nDaughters > 0) { - for (int i = 0; i < daughters->GetEntriesFast(); i++) { - TParticle* daughter = (TParticle*)daughters->At(i); - daughter->SetUniqueID(mGenID); - if (i > 0) { - daughter->SetBit(ParticleStatus::kToBeDone, // - o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); - } else { - // First daughter is the mother (Z0) - daughter->SetBit(ParticleStatus::kToBeDone, false); - } - LOG(debug) << "Daughter " << i << ": PDG=" << daughter->GetPdgCode() << ", E=" << daughter->Energy() << ", p=(" << daughter->Px() << "," << daughter->Py() << "," << daughter->Pz() << ")"; - mParticles.push_back(*daughter); - } - LOG(debug) << "Z0 decayed into " << daughters->GetEntriesFast() << " particles"; - daughters->Clear("C"); - delete daughters; - } else { - LOG(warn) << "DecayerPythia8 failed to decay Z0 or no daughters found"; - } - delete pDec; + generatedParticle.SetBit(ParticleStatus::kToBeDone, false); + // Z0 will follow another decay procedure } else { generatedParticle.SetBit(ParticleStatus::kToBeDone, // o2::mcgenstatus::getHepMCStatusCode(generatedParticle.GetStatusCode()) == 1); @@ -347,6 +313,74 @@ namespace o2 genMap[1] = [this]() { return generateParticle1(); }; } + + std::vector decayZ0(TParticle &z0) + { + std::vector subparts; + // Start a Pythia8 event with Z0 + mPythia->next(); + // Find the Z0 and its final products + auto &event = mPythia->event; + for (int j = 0; j < event.size(); ++j) + { + const Pythia8::Particle &p = event[j]; + if (p.id() == 23) // PDG code for Z0 + { + // Push Z0 itself + subparts.push_back(TParticle(p.id(), p.status(), + -1, -1, -1, -1, + p.px(), p.py(), + p.pz(), p.e(), + z0.Vx(), z0.Vy(), z0.Vz(), 0.0)); + subparts.back().SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(p.status(), 0).fullEncoding); + subparts.back().SetUniqueID(mGenID); + subparts.back().SetBit(ParticleStatus::kToBeDone, false); + // Navigate through intermediate Z0s to find final decay products + int iZ0 = j; + while (event[iZ0].daughter1() != 0 && + event[event[iZ0].daughter1()].id() == 23) + { + iZ0 = event[iZ0].daughter1(); + } + // Recursively collect all final-state descendants + std::function collectFinalState = [&](int idx) + { + const Pythia8::Particle &particle = event[idx]; + + if (particle.isFinal()) + { + subparts.push_back(TParticle(particle.id(), particle.status(), + -1, -1, -1, -1, + particle.px(), particle.py(), + particle.pz(), particle.e(), + p.xProd() + z0.Vx(), p.yProd() + z0.Vy(), p.zProd() + z0.Vz(), 0.0)); + subparts.back().SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(particle.status(), 0).fullEncoding); + subparts.back().SetUniqueID(mGenID+1); + subparts.back().SetBit(ParticleStatus::kToBeDone, + o2::mcgenstatus::getHepMCStatusCode(subparts.back().GetStatusCode()) == 1); + } + else + { + // Not final-state, recurse through daughters + int d1 = particle.daughter1(); + int d2 = particle.daughter2(); + if (d1 > 0) + { + for (int k = d1; k <= d2; ++k) + { + collectFinalState(k); + } + } + } + }; + + // Start collecting from the final Z0 + collectFinalState(iZ0); + break; // Found and processed the Z0 + } + } + return subparts; + } }; } // namespace eventgen @@ -356,8 +390,8 @@ namespace o2 // fraction == -1 enables the fixed number of signal particles per event (nsig) // tag selects the generator type to be used FairGenerator * -Generator_Performance(const float fraction = 0.03f, const unsigned short int nsig = 100, unsigned short int tag = 1) +Generator_Performance(const float fraction = 0.03f, const unsigned short int nsig = 100, unsigned short int tag = 1, bool setDecayZ0 = false) { - auto generator = new o2::eventgen::GenPerf(fraction, nsig, tag); + auto generator = new o2::eventgen::GenPerf(fraction, nsig, tag, setDecayZ0); return generator; } \ No newline at end of file From de2da01921b7b32c1edeaad85457426ff6a3f0bd Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Wed, 12 Nov 2025 22:27:33 +0100 Subject: [PATCH 5/6] Commented ini file --- MC/config/common/ini/GeneratorPerformanceFix.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MC/config/common/ini/GeneratorPerformanceFix.ini b/MC/config/common/ini/GeneratorPerformanceFix.ini index c5349101d..fe932cd81 100644 --- a/MC/config/common/ini/GeneratorPerformanceFix.ini +++ b/MC/config/common/ini/GeneratorPerformanceFix.ini @@ -1,6 +1,7 @@ # Test performance generator for multidimensional studies using fix number of signal particles per event # Parameters are in order: fraction of signal particles, fixed number of signal particles per event, tag to select the generator type +# and flag to decay or not Z0 (Pythia8 instance used - false by default) # fraction = -1 enables the fixed number of signal particles per event (nsig) [GeneratorExternal] fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/performanceGenerator.C -funcName = Generator_Performance(-1, 100, 1) \ No newline at end of file +funcName = Generator_Performance(-1, 100, 1) From e6255845101c9a866a8f063e4dcb96017fd9579f Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Thu, 13 Nov 2025 13:41:11 +0100 Subject: [PATCH 6/6] Removed Z0 modes + fixed Z0 decay --- .../common/external/generator/perfConf.json | 23 +++++ .../external/generator/performanceGenerator.C | 89 ++++++++++--------- .../common/ini/GeneratorPerformanceFix.ini | 5 +- 3 files changed, 71 insertions(+), 46 deletions(-) create mode 100644 MC/config/common/external/generator/perfConf.json diff --git a/MC/config/common/external/generator/perfConf.json b/MC/config/common/external/generator/perfConf.json new file mode 100644 index 000000000..c3bdfae0b --- /dev/null +++ b/MC/config/common/external/generator/perfConf.json @@ -0,0 +1,23 @@ +{ + "generators": [ + { + "cocktail": [ + { + "name": "pythia8pp", + "config": "" + }, + { + "name": "external", + "config": { + "fileName": "${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/performanceGenerator.C", + "funcName": "Generator_Performance()", + "iniFile": "" + } + } + ] + } + ], + "fractions": [ + 1 + ] +} diff --git a/MC/config/common/external/generator/performanceGenerator.C b/MC/config/common/external/generator/performanceGenerator.C index 59368cfaf..ce59cdfaa 100644 --- a/MC/config/common/external/generator/performanceGenerator.C +++ b/MC/config/common/external/generator/performanceGenerator.C @@ -1,5 +1,7 @@ // External generator requested in https://its.cern.ch/jira/browse/O2-6235 // for multidimensional performance studies +// Example usage: +// o2-sim -j 8 -o test -n 100 --seed 612 -g hybrid --configKeyValues "GeneratorHybrid.configFile=${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/perfConf.json" namespace o2 { namespace eventgen @@ -8,8 +10,7 @@ namespace o2 class GenPerf : public Generator { public: - GenPerf(float fraction = 0.03f, unsigned short int nsig = 100, unsigned short int tag = 1, bool setDecayZ0 = false) - : mDecayZ0(setDecayZ0) + GenPerf(float fraction = 0.03f, unsigned short int nsig = 100, unsigned short int tag = 1) { if (fraction == -1) { LOG(info) << nsig << " Signal particles will be generated in each event"; @@ -36,15 +37,13 @@ namespace o2 mTag = tag; LOG(info) << "Generator with tag " << mTag << " is selected"; } - if (mDecayZ0){ - LOG(info) << "Z0 decays will be handled with Pythia8"; - mPythia = std::make_unique(); - // Configure Pythia8 with minimal beam setup for standalone decays - mPythia->readString("WeakSingleBoson:ffbar2gmZ = on"); - mPythia->init(); // Initialize - } else { - LOG(info) << "Z0 decays will be created but not transported (incompatible with Geant4 physics list)"; - } + LOG(info) << "Z0 decays are handled with Pythia8"; + mPythia = std::make_unique(); + // Turn off all event generation - we only want to decay our Z0 + mPythia->readString("ProcessLevel:all = off"); + // Disable standard event checks since we're manually building the event + mPythia->readString("Check:event = off"); + mPythia->init(); // Initialize Generator::setTimeUnit(1.0); Generator::setPositionUnit(1.0); } @@ -74,14 +73,10 @@ namespace o2 for (int k = 0; k < nSig; k++){ auto part = genMap[mTag](); if(part.GetPdgCode() == 23) { - if(!mDecayZ0) { - mParticles.push_back(part); - } else { - auto daughters = decayZ0(part); - for (auto &dau : daughters) - { - mParticles.push_back(dau); - } + auto daughters = decayZ0(part); + for (auto &dau : daughters) + { + mParticles.push_back(dau); } } else { mParticles.push_back(part); @@ -95,7 +90,6 @@ namespace o2 unsigned short int mNSig = 0; // Number of particles to generate unsigned int mNUE = 0; // Number of tracks in the Underlying event unsigned short int mTag = 1; // Tag to select the generation function - bool mDecayZ0 = false; // Whether to decay Z0 with Pythia8 std::unique_ptr mPythia; // Pythia8 instance for particle decays not present in the physics list of Geant4 (like Z0) const std::vector>* mGenList = nullptr; // Cached generators list std::map> genMap; @@ -317,10 +311,22 @@ namespace o2 std::vector decayZ0(TParticle &z0) { std::vector subparts; - // Start a Pythia8 event with Z0 - mPythia->next(); - // Find the Z0 and its final products auto &event = mPythia->event; + // Reset event record for new decay + event.reset(); + // Add the Z0 particle to the event record + // Arguments: id, status, mother1, mother2, daughter1, daughter2, + // col, acol, px, py, pz, e, m, scale, pol + // Status code: 91 = incoming particles (needed for proper decay handling) + int iZ0 = event.append(23, 91, 0, 0, 0, 0, 0, 0, + z0.Px(), z0.Py(), z0.Pz(), z0.Energy(), z0.GetMass()); + // Set production vertex + event[iZ0].vProd(z0.Vx(), z0.Vy(), z0.Vz(), 0.0); + // Forcing decay by calling hadron level function + if (!mPythia->forceHadronLevel()) + { + cout << "Warning: Z0 decay failed!" << endl; + } for (int j = 0; j < event.size(); ++j) { const Pythia8::Particle &p = event[j]; @@ -343,39 +349,34 @@ namespace o2 iZ0 = event[iZ0].daughter1(); } // Recursively collect all final-state descendants - std::function collectFinalState = [&](int idx) + std::function collectAllDescendants = [&](int idx) { const Pythia8::Particle &particle = event[idx]; - - if (particle.isFinal()) + subparts.push_back(TParticle(particle.id(), particle.status(), + -1, -1, -1, -1, + particle.px(), particle.py(), + particle.pz(), particle.e(), + p.xProd(), p.yProd(), p.zProd(), p.tProd())); + subparts.back().SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(particle.status(), 0).fullEncoding); + subparts.back().SetUniqueID(mGenID + 1); + subparts.back().SetBit(ParticleStatus::kToBeDone, + o2::mcgenstatus::getHepMCStatusCode(subparts.back().GetStatusCode()) == 1); + // Not final-state, recurse through daughters + if (!particle.isFinal()) { - subparts.push_back(TParticle(particle.id(), particle.status(), - -1, -1, -1, -1, - particle.px(), particle.py(), - particle.pz(), particle.e(), - p.xProd() + z0.Vx(), p.yProd() + z0.Vy(), p.zProd() + z0.Vz(), 0.0)); - subparts.back().SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(particle.status(), 0).fullEncoding); - subparts.back().SetUniqueID(mGenID+1); - subparts.back().SetBit(ParticleStatus::kToBeDone, - o2::mcgenstatus::getHepMCStatusCode(subparts.back().GetStatusCode()) == 1); - } - else - { - // Not final-state, recurse through daughters int d1 = particle.daughter1(); int d2 = particle.daughter2(); if (d1 > 0) { for (int k = d1; k <= d2; ++k) { - collectFinalState(k); + collectAllDescendants(k); } } } }; - // Start collecting from the final Z0 - collectFinalState(iZ0); + collectAllDescendants(iZ0); break; // Found and processed the Z0 } } @@ -390,8 +391,8 @@ namespace o2 // fraction == -1 enables the fixed number of signal particles per event (nsig) // tag selects the generator type to be used FairGenerator * -Generator_Performance(const float fraction = 0.03f, const unsigned short int nsig = 100, unsigned short int tag = 1, bool setDecayZ0 = false) +Generator_Performance(const float fraction = 0.03f, const unsigned short int nsig = 100, unsigned short int tag = 1) { - auto generator = new o2::eventgen::GenPerf(fraction, nsig, tag, setDecayZ0); + auto generator = new o2::eventgen::GenPerf(fraction, nsig, tag); return generator; } \ No newline at end of file diff --git a/MC/config/common/ini/GeneratorPerformanceFix.ini b/MC/config/common/ini/GeneratorPerformanceFix.ini index fe932cd81..b92b2300c 100644 --- a/MC/config/common/ini/GeneratorPerformanceFix.ini +++ b/MC/config/common/ini/GeneratorPerformanceFix.ini @@ -1,7 +1,8 @@ # Test performance generator for multidimensional studies using fix number of signal particles per event # Parameters are in order: fraction of signal particles, fixed number of signal particles per event, tag to select the generator type -# and flag to decay or not Z0 (Pythia8 instance used - false by default) -# fraction = -1 enables the fixed number of signal particles per event (nsig) +# Setting fraction = -1 enables the fixed number of signal particles per event (nsig). +# An hybrid configuration JSON file is provided in ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/perfConf.json to run the generator +# in fraction based mode [GeneratorExternal] fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/common/external/generator/performanceGenerator.C funcName = Generator_Performance(-1, 100, 1)