Skip to content

Commit 57772e6

Browse files
authored
MC/PWGEM: HF Cocktail (#1623)
* ccbar and bbbar hooks * HF cocktail with forced decays * switch off decays in generator * fix daughter IDs * add tests * include partonic event * preserve Pythia status code of hadron that is forced to decay * added seed setting for external generator
1 parent 3b0d1fd commit 57772e6

File tree

7 files changed

+435
-0
lines changed

7 files changed

+435
-0
lines changed
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#include "Generators/DecayerPythia8.h"
2+
#include "Generators/GeneratorPythia8.h"
3+
#include "SimulationDataFormat/MCGenProperties.h"
4+
#include "SimulationDataFormat/ParticleStatus.h"
5+
#include "TLorentzVector.h"
6+
7+
namespace o2 {
8+
namespace eventgen {
9+
10+
class DecayerPythia8ForceDecays : public DecayerPythia8 {
11+
public:
12+
DecayerPythia8ForceDecays(){
13+
mPythia.readString("Random:setSeed = on");
14+
char* alien_proc_id = getenv("ALIEN_PROC_ID");
15+
int seed;
16+
if (alien_proc_id != NULL) {
17+
seed = atoi(alien_proc_id);
18+
LOG(info) << "Seed for DecayerPythia8 set to ALIEN_PROC_ID: " << seed;
19+
} else {
20+
LOG(info) << "Unable to retrieve ALIEN_PROC_ID";
21+
LOG(info) << "Setting seed for DecayerPyhtia8 to 0 (random)";
22+
seed = 0;
23+
}
24+
mPythia.readString("Random:seed = "+std::to_string(seed));
25+
}
26+
~DecayerPythia8ForceDecays() = default;
27+
28+
29+
void calculateWeights(std::vector<int> &pdgs) {
30+
TLorentzVector mom = TLorentzVector(0., 0., 0., 9999999.);
31+
for (int pdg : pdgs) {
32+
Decay(pdg, &mom); // do one fake decay to initalize everything correctly
33+
auto particleData = mPythia.particleData.particleDataEntryPtr(pdg);
34+
double weight = 0.;
35+
for (int c = 0; c < particleData->sizeChannels(); c++) {
36+
weight += particleData->channel(c).currentBR();
37+
}
38+
LOG(info) << "PDG = " << pdg
39+
<< ": sum of branching ratios of active decay channels = "
40+
<< weight;
41+
mWeights[pdg] = weight;
42+
mPythia.particleData.mayDecay(pdg, false);
43+
}
44+
}
45+
46+
void forceDecays(std::vector<TParticle> &mParticles, int mother_pos) {
47+
TParticle *p = &mParticles[mother_pos];
48+
int pdg = p->GetPdgCode();
49+
TLorentzVector mom = TLorentzVector(p->Px(), p->Py(), p->Pz(), p->Energy());
50+
Decay(pdg, &mom);
51+
TClonesArray daughters = TClonesArray("TParticle");
52+
int nParticles = ImportParticles(&daughters);
53+
int mcGenStatus = o2::mcgenstatus::getGenStatusCode(p->GetStatusCode());
54+
p->SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(2, -mcGenStatus).fullEncoding);
55+
p->SetBit(ParticleStatus::kToBeDone, false);
56+
double mother_weight = p->GetWeight();
57+
TParticle *mother = static_cast<TParticle *>(daughters[0]);
58+
int mParticles_size = mParticles.size();
59+
p->SetFirstDaughter(mother->GetFirstDaughter() + mParticles_size - 1);
60+
p->SetLastDaughter(mother->GetLastDaughter() + mParticles_size - 1);
61+
for (int j = 1; j < nParticles;
62+
j++) { // start loop at 1 to not include mother
63+
TParticle *d = static_cast<TParticle *>(daughters[j]);
64+
double decay_weight = mWeights[abs(pdg)];
65+
if (decay_weight == 0) {
66+
LOG(error) << "Decaying particle (PDG = " << pdg
67+
<< ") with decay weight = 0. Did you set the pdg codes for "
68+
"calculating weights correctly?";
69+
}
70+
d->SetWeight(decay_weight * mother_weight);
71+
if (d->GetStatusCode() == 1) {
72+
d->SetStatusCode(
73+
o2::mcgenstatus::MCGenStatusEncoding(1, 91).fullEncoding);
74+
d->SetBit(ParticleStatus::kToBeDone, true);
75+
} else {
76+
d->SetStatusCode(
77+
o2::mcgenstatus::MCGenStatusEncoding(2, -91).fullEncoding);
78+
d->SetBit(ParticleStatus::kToBeDone, false);
79+
}
80+
int firstmother = d->GetFirstMother();
81+
int firstdaughter = d->GetFirstDaughter();
82+
int lastdaughter = d->GetLastDaughter();
83+
if (firstmother == 0) {
84+
d->SetFirstMother(mother_pos);
85+
d->SetLastMother(mother_pos);
86+
} else {
87+
d->SetFirstMother(firstmother + mParticles_size - 1);
88+
d->SetLastMother(firstmother + mParticles_size - 1);
89+
}
90+
if (firstdaughter == 0) {
91+
d->SetFirstDaughter(-1);
92+
} else {
93+
d->SetFirstDaughter(firstdaughter + mParticles_size - 1);
94+
}
95+
if (lastdaughter == 0) {
96+
d->SetLastDaughter(-1);
97+
} else {
98+
d->SetLastDaughter(lastdaughter + mParticles_size - 1);
99+
}
100+
mParticles.push_back(*d);
101+
}
102+
}
103+
104+
private:
105+
std::map<int, double> mWeights;
106+
};
107+
108+
class GeneratorPythia8ForcedDecays : public GeneratorPythia8 {
109+
110+
public:
111+
GeneratorPythia8ForcedDecays(){
112+
mPythia.readString("Random:setSeed = on");
113+
char* alien_proc_id = getenv("ALIEN_PROC_ID");
114+
int seed;
115+
if (alien_proc_id != NULL) {
116+
seed = atoi(alien_proc_id);
117+
LOG(info) << "Seed for GeneratorPythia8 set to ALIEN_PROC_ID: " << seed;
118+
} else {
119+
LOG(info) << "Unable to retrieve ALIEN_PROC_ID";
120+
LOG(info) << "Setting seed for GeneratorPyhtia8 to 0 (random)";
121+
seed = 0;
122+
}
123+
mPythia.readString("Random:seed = "+std::to_string(seed));
124+
}
125+
~GeneratorPythia8ForcedDecays() = default;
126+
127+
// overriden methods
128+
bool Init() override { return GeneratorPythia8::Init() && InitDecayer(); };
129+
bool importParticles() override {
130+
return GeneratorPythia8::importParticles() && makeForcedDecays();
131+
};
132+
133+
void setPDGs(TString pdgs) {
134+
TObjArray *obj = pdgs.Tokenize(";");
135+
for (int i = 0; i < obj->GetEntriesFast(); i++) {
136+
std::string spdg = obj->At(i)->GetName();
137+
int pdg = std::stoi(spdg);
138+
mPdgCodes.push_back(pdg);
139+
LOG(info) << "Force decay of PDG = " << pdg;
140+
}
141+
}
142+
143+
protected:
144+
bool InitDecayer() {
145+
mDecayer = new DecayerPythia8ForceDecays();
146+
mDecayer->Init();
147+
mDecayer->calculateWeights(mPdgCodes);
148+
for (int pdg : mPdgCodes) {
149+
mPythia.particleData.mayDecay(pdg, false);
150+
}
151+
return true;
152+
}
153+
154+
bool makeForcedDecays() {
155+
int mParticles_size = mParticles.size();
156+
for (int i = 0; i < mParticles_size; i++) {
157+
int pdg = mParticles[i].GetPdgCode();
158+
if (std::find(mPdgCodes.begin(), mPdgCodes.end(), abs(pdg)) !=
159+
mPdgCodes.end()) {
160+
mDecayer->forceDecays(mParticles, i);
161+
mParticles_size = mParticles.size();
162+
}
163+
}
164+
return true;
165+
}
166+
167+
private:
168+
DecayerPythia8ForceDecays *mDecayer;
169+
std::vector<int> mPdgCodes;
170+
};
171+
172+
} // namespace eventgen
173+
} // namespace o2
174+
175+
FairGenerator *GeneratePythia8ForcedDecays(TString pdgs) {
176+
auto gen = new o2::eventgen::GeneratorPythia8ForcedDecays();
177+
gen->setPDGs(pdgs);
178+
return gen;
179+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[GeneratorExternal]
2+
fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C
3+
funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332;511;521;531;5122;5132;5232;5332")
4+
5+
[GeneratorPythia8]
6+
config = ${O2DPG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail.cfg
7+
hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C
8+
hooksFuncName = pythia8_userhooks_bbbar(-9999.,9999.)
9+
includePartonEvent=true
10+
11+
[DecayerPythia8]
12+
config[0] = ${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg
13+
config[1] = ${O2DPG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic.cfg
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[GeneratorExternal]
2+
fileName = ${O2DPG_ROOT}/MC/config/PWGEM/external/generator/Generator_pythia8_forcedDecays.C
3+
funcName=GeneratePythia8ForcedDecays("411;421;431;4122;4232;4132;4332")
4+
5+
[GeneratorPythia8]
6+
config = ${O2DPG_ROOT}/MC/config/PWGEM/pythia8/generator/pythia8_hf_cocktail.cfg
7+
hooksFileName = ${O2DPG_ROOT}/MC/config/PWGHF/pythia8/hooks/pythia8_userhooks_qqbar.C
8+
hooksFuncName = pythia8_userhooks_ccbar(-9999.,9999.)
9+
includePartonEvent=true
10+
11+
[DecayerPythia8]
12+
config[0] = ${O2DPG_ROOT}/MC/config/common/pythia8/decayer/base.cfg
13+
config[1] = ${O2DPG_ROOT}/MC/config/PWGEM/pythia8/decayer/force_semileptonic.cfg
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
int External()
2+
{
3+
4+
std::string path{"o2sim_Kine.root"};
5+
TFile file(path.c_str(), "READ");
6+
if (file.IsZombie()) {
7+
std::cerr << "Cannot open ROOT file " << path << "\n";
8+
return 1;
9+
}
10+
11+
auto tree = (TTree*)file.Get("o2sim");
12+
std::vector<o2::MCTrack>* tracks{};
13+
tree->SetBranchAddress("MCTrack", &tracks);
14+
auto nEvents = tree->GetEntries();
15+
for (int i = 0; i < nEvents; i++) {
16+
tree->GetEntry(i);
17+
bool found_b = false;
18+
bool found_bbar = false;
19+
for (auto& track : *tracks) {
20+
int pdg = track.GetPdgCode();
21+
if (pdg == 5){
22+
found_b = true;
23+
}
24+
if (pdg == -5){
25+
found_bbar = true;
26+
}
27+
if ( abs(pdg) == 511 || abs(pdg) == 521 || abs(pdg) == 531 || abs(pdg) == 5122 || abs(pdg) == 5132 || abs(pdg) == 5232 || abs(pdg) == 5332){
28+
int ifirstdaughter = track.getFirstDaughterTrackId();
29+
int ilastdaughter = track.getLastDaughterTrackId();
30+
if (ifirstdaughter == -1 || ilastdaughter == -1){
31+
std::cerr << "Found beauty hadron that does not have daughters" << "\n";
32+
return 1;
33+
}
34+
bool found_electron = false;
35+
for (int j = ifirstdaughter; j<= ilastdaughter; j++){
36+
auto track2 = (*tracks)[j];
37+
if ( abs(track2.GetPdgCode())==11){
38+
found_electron = true;
39+
if (!(track2.getWeight() < 0.999)){
40+
std::cerr << "Found electron from forced decay with weight 1" << "\n";
41+
return 1;
42+
}
43+
if (!track2.getToBeDone()){
44+
std::cerr << "Found electron from forced decay that is not transported" << "\n";
45+
return 1;
46+
}
47+
}
48+
}
49+
if (!found_electron){
50+
std::cerr << "Found beauty hadron that does not decay to electron" << "\n";
51+
return 1;
52+
}
53+
}
54+
}
55+
if ((!found_b) || (!found_bbar)){
56+
std::cerr << "Found event without b-bbar pair" << "\n";
57+
return 1;
58+
}
59+
}
60+
return 0;
61+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
int External()
2+
{
3+
4+
std::string path{"o2sim_Kine.root"};
5+
TFile file(path.c_str(), "READ");
6+
if (file.IsZombie()) {
7+
std::cerr << "Cannot open ROOT file " << path << "\n";
8+
return 1;
9+
}
10+
11+
auto tree = (TTree*)file.Get("o2sim");
12+
std::vector<o2::MCTrack>* tracks{};
13+
tree->SetBranchAddress("MCTrack", &tracks);
14+
auto nEvents = tree->GetEntries();
15+
for (int i = 0; i < nEvents; i++) {
16+
tree->GetEntry(i);
17+
bool found_c = false;
18+
bool found_cbar = false;
19+
for (auto& track : *tracks) {
20+
int pdg = track.GetPdgCode();
21+
if (pdg == 4){
22+
found_c = true;
23+
}
24+
if (pdg == -4){
25+
found_cbar = true;
26+
}
27+
if ( abs(pdg) == 411 || abs(pdg) == 421 || abs(pdg) == 431 || abs(pdg) == 4122 || abs(pdg) == 4232 || abs(pdg) == 4132 || abs(pdg) == 4332){
28+
int ifirstdaughter = track.getFirstDaughterTrackId();
29+
int ilastdaughter = track.getLastDaughterTrackId();
30+
if (ifirstdaughter == -1 || ilastdaughter == -1){
31+
std::cerr << "Found charm hadron that does not have daughters" << "\n";
32+
return 1;
33+
}
34+
bool found_electron = false;
35+
for (int j = ifirstdaughter; j<= ilastdaughter; j++){
36+
auto track2 = (*tracks)[j];
37+
if ( abs(track2.GetPdgCode())==11){
38+
found_electron = true;
39+
if (!(track2.getWeight() < 0.999)){
40+
std::cerr << "Found electron from forced decay with weight 1" << "\n";
41+
return 1;
42+
}
43+
if (!track2.getToBeDone()){
44+
std::cerr << "Found electron from forced decay that is not transported" << "\n";
45+
return 1;
46+
}
47+
}
48+
}
49+
if (!found_electron){
50+
std::cerr << "Found charm hadron that does not decay to electron" << "\n";
51+
return 1;
52+
}
53+
}
54+
}
55+
if ((!found_c) || (!found_cbar)){
56+
std::cerr << "Found event without c-cbar pair" << "\n";
57+
return 1;
58+
}
59+
}
60+
return 0;
61+
}

0 commit comments

Comments
 (0)