Skip to content

Commit 5dbd96c

Browse files
authored
[PWGLF] Add generator for strangeness in jets (#2234)
* [PWGLF] Add generator for strangeness in jets * replace primary with physical primary or from HF decays * added missing namespace qualifier * fix headers * fix selection of physical primary particles * remove redundant includes * change jet definition * fix typo
1 parent b664d2d commit 5dbd96c

File tree

3 files changed

+225
-0
lines changed

3 files changed

+225
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[GeneratorExternal]
2+
fileName = ${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator_pythia8_strangeness_in_jets.C
3+
funcName = generateStrangenessInJets(10.0,0.4,4)
4+
5+
[GeneratorPythia8]
6+
config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGLF/pythia8/generator/pythia8_jet_ropes_136tev.cfg
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
int External() {
2+
std::string path{"o2sim_Kine.root"};
3+
4+
TFile file(path.c_str(), "READ");
5+
if (file.IsZombie()) {
6+
std::cerr << "Cannot open ROOT file " << path << "\n";
7+
return 1;
8+
}
9+
10+
auto tree = (TTree *)file.Get("o2sim");
11+
if (!tree) {
12+
std::cerr << "Cannot find tree o2sim in file " << path << "\n";
13+
return 1;
14+
}
15+
std::vector<o2::MCTrack> *tracks{};
16+
tree->SetBranchAddress("MCTrack", &tracks);
17+
18+
auto nXi = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 3312");
19+
auto nOmega = tree->Scan("MCTrack.GetPdgCode()", "TMath::Abs(MCTrack.GetPdgCode()) == 3334");
20+
auto nStr = nXi+nOmega;
21+
22+
if (nStr == 0) {
23+
std::cerr << "No event of interest\n";
24+
return 1;
25+
}
26+
return 0;
27+
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
#if !defined(__CLING__) || defined(__ROOTCLING__)
2+
#include "FairGenerator.h"
3+
#include "FairPrimaryGenerator.h"
4+
#include "Generators/GeneratorPythia8.h"
5+
#include "Pythia8/Pythia.h"
6+
#include "TDatabasePDG.h"
7+
#include "TMath.h"
8+
#include "TParticlePDG.h"
9+
#include "TRandom3.h"
10+
#include "TSystem.h"
11+
#include "TVector2.h"
12+
#include "fairlogger/Logger.h"
13+
#include <cmath>
14+
#include <fstream>
15+
#include <string>
16+
#include <vector>
17+
using namespace Pythia8;
18+
#endif
19+
20+
/// Event generator using Pythia ropes for Ξ and Ω production in jets.
21+
/// Jets are defined as particles within a cone around the highest-pT particle
22+
/// (approximate jet axis) and include charged final-state particles that are
23+
/// either physical primaries or from heavy-flavor decays.
24+
/// Jets must be fully within the acceptance and have pT > 10 GeV.
25+
/// One event of interest is generated after 4 minimum-bias events.
26+
27+
class GeneratorPythia8StrangenessInJet : public o2::eventgen::GeneratorPythia8 {
28+
public:
29+
/// Constructor
30+
GeneratorPythia8StrangenessInJet(double ptJetMin = 10.0, double Rjet = 0.4, int gapSize = 4)
31+
: o2::eventgen::GeneratorPythia8(),
32+
mptJetMin(ptJetMin),
33+
mRjet(Rjet),
34+
mGapSize(gapSize)
35+
{
36+
fmt::printf(
37+
">> Pythia8 generator: Xi/Omega inside jets with pt_{jet} > %.1f GeV, R_{jet} = %.1f, gap = %d\n",
38+
ptJetMin, Rjet, gapSize);
39+
}
40+
/// Destructor
41+
~GeneratorPythia8StrangenessInJet() = default;
42+
43+
bool Init() override {
44+
addSubGenerator(0, "Pythia8 events with Xi/Omega inside jets");
45+
return o2::eventgen::GeneratorPythia8::Init();
46+
}
47+
48+
protected:
49+
/// Check if particle is physical primary or from HF decay
50+
bool isPhysicalPrimaryOrFromHF(const Pythia8::Particle& p, const Pythia8::Event& event)
51+
{
52+
// Select only final-state particles
53+
if (!p.isFinal()) {
54+
return false;
55+
}
56+
57+
// Particle species selection
58+
const int absPdg = std::abs(p.id());
59+
if (absPdg!=211 && absPdg!=321 && absPdg!= 2212 && absPdg!=1000010020 && absPdg!=11 && absPdg!=13)
60+
return false;
61+
62+
// Walk up ancestry
63+
int motherId = p.mother1();
64+
65+
while (motherId > 0) {
66+
67+
// Get mother
68+
const auto& mother = event[motherId];
69+
const int absMotherPdg = std::abs(mother.id());
70+
71+
// Check if particle is from HF decay
72+
if ((absMotherPdg / 100 == 4) || (absMotherPdg / 100 == 5) || (absMotherPdg / 1000 == 4) || (absMotherPdg / 1000 == 5)) {
73+
return true;
74+
}
75+
76+
// Reject non-physical primary hadrons
77+
if (mother.isHadron() && mother.tau0() > 1.0) {
78+
return false;
79+
}
80+
motherId = mother.mother1();
81+
}
82+
return true;
83+
}
84+
85+
// Compute delta phi
86+
double getDeltaPhi(double a1, double a2)
87+
{
88+
double deltaPhi{0.0};
89+
double phi1 = TVector2::Phi_0_2pi(a1);
90+
double phi2 = TVector2::Phi_0_2pi(a2);
91+
double diff = std::fabs(phi1 - phi2);
92+
93+
if (diff <= M_PI)
94+
deltaPhi = diff;
95+
if (diff > M_PI)
96+
deltaPhi = 2.0 * M_PI - diff;
97+
98+
return deltaPhi;
99+
}
100+
101+
bool generateEvent() override {
102+
fmt::printf(">> Generating event %d\n", mGeneratedEvents);
103+
104+
bool genOk = false;
105+
int localCounter{0};
106+
constexpr int kMaxTries{100000};
107+
108+
// Accept mGapSize events unconditionally, then one triggered event
109+
if (mGeneratedEvents % (mGapSize + 1) < mGapSize) {
110+
genOk = GeneratorPythia8::generateEvent();
111+
fmt::printf(">> Gap-trigger accepted event (no strangeness check)\n");
112+
} else {
113+
while (!genOk && localCounter < kMaxTries) {
114+
if (GeneratorPythia8::generateEvent()) {
115+
genOk = selectEvent(mPythia.event);
116+
}
117+
localCounter++;
118+
}
119+
if (!genOk) {
120+
fmt::printf("Failed to generate triggered event after %d tries\n",kMaxTries);
121+
return false;
122+
}
123+
fmt::printf(">> Event accepted after %d iterations (Xi/Omega in jet)\n", localCounter);
124+
}
125+
126+
notifySubGenerator(0);
127+
mGeneratedEvents++;
128+
return true;
129+
}
130+
131+
bool selectEvent(Pythia8::Event &event) {
132+
133+
double etaJet{-999.0}, phiJet{-999.0}, ptMax{0.0};
134+
std::vector<int> particleID;
135+
bool containsXiOrOmega{false};
136+
137+
for (int i = 0; i < event.size(); i++) {
138+
const auto& p = event[i];
139+
if (std::abs(p.id()) == 3312 || std::abs(p.id()) == 3334)
140+
containsXiOrOmega = true;
141+
142+
if (std::abs(p.eta()) > 0.8 || p.pT() < 0.1) continue;
143+
if (!isPhysicalPrimaryOrFromHF(p, event)) continue;
144+
particleID.emplace_back(i);
145+
146+
if (p.pT() > ptMax) {
147+
ptMax = p.pT();
148+
etaJet = p.eta();
149+
phiJet = p.phi();
150+
}
151+
}
152+
if (ptMax == 0.0)
153+
return false;
154+
if (std::abs(etaJet) + mRjet > 0.8)
155+
return false;
156+
if (!containsXiOrOmega)
157+
return false;
158+
159+
double ptJet{0.0};
160+
for (int i = 0 ; i < particleID.size() ; i++) {
161+
int id = particleID[i];
162+
const auto& p = event[id];
163+
164+
double deltaEta = std::abs(p.eta() - etaJet);
165+
double deltaPhi = getDeltaPhi(p.phi(),phiJet);
166+
double deltaR = std::sqrt(deltaEta * deltaEta + deltaPhi * deltaPhi);
167+
if (deltaR < mRjet) {
168+
ptJet += p.pT();
169+
}
170+
}
171+
if (ptJet < mptJetMin)
172+
return false;
173+
174+
return true;
175+
}
176+
177+
private:
178+
double mptJetMin{10.0};
179+
double mRjet{0.4};
180+
int mGapSize{4};
181+
uint64_t mGeneratedEvents{0};
182+
};
183+
184+
///___________________________________________________________
185+
FairGenerator *generateStrangenessInJets(double ptJet = 10.0, double rJet = 0.4, int gap = 4) {
186+
187+
auto myGenerator = new GeneratorPythia8StrangenessInJet(ptJet,rJet,gap);
188+
auto seed = (gRandom->TRandom::GetSeed() % 900000000);
189+
myGenerator->readString("Random:setSeed on");
190+
myGenerator->readString("Random:seed " + std::to_string(seed));
191+
return myGenerator;
192+
}

0 commit comments

Comments
 (0)