Skip to content

Commit 07a16ff

Browse files
authored
[PWGHF] Add possibility to use EVTGEN in HF generator + config for B2Jpsi (#2378)
* Add EVTGEN in HF generator + config for B2Jpsi * update comment * Update decays * Add test macro * Revert to default test file name * Fix generator in case of no usage of EVTGEN decayer * Change number of test events * Revert number of test events for charm baryon config * Increase number of test events for SigmaC bkg MC
1 parent 0b48fc4 commit 07a16ff

7 files changed

Lines changed: 4780 additions & 3 deletions

MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
R__LOAD_LIBRARY(EvtGen)
2+
R__ADD_INCLUDE_PATH($EVTGEN_ROOT/include)
3+
14
#include "FairGenerator.h"
25
#include "Generators/GeneratorPythia8.h"
36
#include "Generators/GeneratorPythia8Param.h"
7+
#include "EvtGen/EvtGen.hh"
48
#include "Pythia8/Pythia.h"
9+
#include "Pythia8Plugins/EvtGen.h"
510
#include "TRandom.h"
611
#include "TDatabasePDG.h"
712
#include <fairlogger/Logger.h>
@@ -33,6 +38,9 @@ public:
3338
mHadronPdgList = hadronPdgList;
3439
mPartPdgToReplaceList = partPdgToReplaceList;
3540
mFreqReplaceList = freqReplaceList;
41+
mEvtGen = nullptr;
42+
mUseEvtGen = false;
43+
mEvtGenDecTable = "";
3644
// Ds1*(2700), Ds1*(2860), Ds3*(2860), Xic(3055)+, Xic(3080)+, Xic(3055)0, Xic(3080)0, LambdaC(2625), LambdaC(2595), LambdaC(2860), LambdaC(2880), LambdaC(2940), ThetaC(3100)
3745
mCustomPartPdgs = {30433, 40433, 437, 4315, 4316, 4325, 4326, 4124, 14122, 24124, 24126, 4125, 9422111};
3846
mCustomPartMasses[30433] = 2.714f;
@@ -114,6 +122,10 @@ public:
114122
pdgToReplace.push_back(mPartPdgToReplaceList[iRepl].at(0));
115123
}
116124

125+
if (mUseEvtGen) {
126+
mEvtGen = new Pythia8::EvtGenDecays(&mPythia, mEvtGenDecTable.data(), gSystem->ExpandPathName("$EVTGEN_ROOT/share/EvtGen/evt.pdl"));
127+
}
128+
117129
return o2::eventgen::GeneratorPythia8::Init();
118130
}
119131

@@ -135,6 +147,14 @@ public:
135147
{
136148
return mUsedSeed;
137149
};
150+
void setUseEvtGenDecayer(std::string evtGenDecTable = "") {
151+
mUseEvtGen = true;
152+
if (evtGenDecTable.empty()) {
153+
mEvtGenDecTable = gSystem->ExpandPathName("$EVTGEN_ROOT/share/EvtGen/DECAY.DEC");
154+
} else {
155+
mEvtGenDecTable = gSystem->ExpandPathName(evtGenDecTable.data());
156+
}
157+
}
138158

139159
protected:
140160
//__________________________________________________________________
@@ -167,6 +187,10 @@ protected:
167187
{
168188
if (GeneratorPythia8::generateEvent())
169189
{
190+
if (mUseEvtGen) {
191+
mEvtGen->decay();
192+
checkConsistency();
193+
}
170194
genOk = selectEvent();
171195
}
172196
}
@@ -179,6 +203,12 @@ protected:
179203
while (!genOk)
180204
{
181205
genOk = GeneratorPythia8::generateEvent();
206+
if (genOk) {
207+
if (mUseEvtGen) {
208+
mEvtGen->decay();
209+
checkConsistency();
210+
}
211+
}
182212
}
183213
notifySubGenerator(0);
184214
}
@@ -197,6 +227,8 @@ protected:
197227

198228
for (auto iPart{0}; iPart < mPythia.event.size(); ++iPart)
199229
{
230+
231+
200232
// search for Q-Qbar mother with at least one Q in rapidity window
201233
if (!isGoodAtPartonLevel)
202234
{
@@ -352,11 +384,22 @@ protected:
352384
return true;
353385
}
354386

387+
void checkConsistency() {
388+
for (int iPart{1}; iPart<mPythia.event.size(); ++iPart) {
389+
// verify if all particles of decay chain are in the TDatabasePDG
390+
// taken from https://github.com/AliceO2Group/O2DPG/blob/master/MC/config/PWGDQ/EvtGen/GeneratorEvtGen.C
391+
if (!TDatabasePDG::Instance()->GetParticle(abs(mPythia.event[iPart].id()))) {
392+
// std::cout << "Particle code non known in TDatabasePDG " << mPythia.event[iPart].id() << " - set pdg = 89" << std::endl;
393+
mPythia.event[iPart].id(89);
394+
}
395+
}
396+
}
397+
355398
private:
356399
// Interface to override import particles
357400
Pythia8::Event mOutputEvent;
358401

359-
// Properties of selection
402+
// Properties of selection
360403
int mQuarkPdg;
361404
float mQuarkRapidityMin;
362405
float mQuarkRapidityMax;
@@ -379,6 +422,12 @@ protected:
379422

380423
// Control alternate trigger on different hadrons
381424
std::vector<int> mHadronPdgList = {};
425+
426+
// EVTGEN decayer from PYTHIA8 plugins
427+
Pythia8::EvtGenDecays *mEvtGen;
428+
bool mUseEvtGen;
429+
std::string mEvtGenDecTable;
430+
382431
};
383432

384433
// Predefined generators:
@@ -446,3 +495,20 @@ FairGenerator *GeneratorPythia8GapHF(int inputTriggerRatio, float yQuarkMin = -1
446495

447496
return myGen;
448497
}
498+
499+
// Beauty-enriched with EvtGen decayer
500+
FairGenerator *GeneratorPythia8GapTriggeredBeautyWithEvtGen(int inputTriggerRatio, float yQuarkMin = -1.5, float yQuarkMax = 1.5, float yHadronMin = -1.5, float yHadronMax = 1.5, std::vector<int> hadronPdgList = {}, std::vector<std::array<int, 2>> partPdgToReplaceList = {}, std::vector<float> freqReplaceList = {}, std::string decayTable = "")
501+
{
502+
auto myGen = new GeneratorPythia8GapTriggeredHF(inputTriggerRatio, std::vector<int>{5}, hadronPdgList, partPdgToReplaceList, freqReplaceList);
503+
auto seed = (gRandom->TRandom::GetSeed() % 900000000);
504+
myGen->setUsedSeed(seed);
505+
myGen->readString("Random:setSeed on");
506+
myGen->readString("Random:seed " + std::to_string(seed));
507+
myGen->setQuarkRapidity(yQuarkMin, yQuarkMax);
508+
if (hadronPdgList.size() != 0)
509+
{
510+
myGen->setHadronRapidity(yHadronMin, yHadronMax);
511+
}
512+
myGen->setUseEvtGenDecayer(decayTable);
513+
return myGen;
514+
}

MC/config/PWGHF/ini/GeneratorHFTrigger_Bforced.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#NEV_TEST> 20
1+
#NEV_TEST> 50
22
[GeneratorExternal]
33
fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C
44
funcName=GeneratorPythia8GapTriggeredBeauty(3, -5, 5)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#NEV_TEST> 20
2+
### The external generator derives from GeneratorPythia8.
3+
[GeneratorExternal]
4+
fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C
5+
funcName=GeneratorPythia8GapTriggeredBeautyWithEvtGen(5, -1.5, 1.5, -1.5, 1.5, {}, {}, {}, "${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/decayer/EVTGEN_DECAY_B2JPSI.DEC")
6+
7+
[GeneratorPythia8]
8+
config=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/pythia8/generator/pythia8_Mode2_noforcedecays.cfg
9+
includePartonEvent=false

MC/config/PWGHF/ini/GeneratorHF_D2H_ccbar_and_bbbar_gap5_Mode2_corrBkgSigmaC.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#NEV_TEST> 40
1+
#NEV_TEST> 100
22
### The external generator derives from GeneratorPythia8.
33
[GeneratorExternal]
44
fileName=${O2DPG_MC_CONFIG_ROOT}/MC/config/PWGHF/external/generator/generator_pythia8_gaptriggered_hf.C
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
int External() {
2+
std::string path{"o2sim_Kine.root"};
3+
4+
int checkPdgQuark{5};
5+
float ratioTrigger = 1./5; // one event triggered out of 5
6+
7+
std::vector<int> checkPdgHadron{443, 511, 521, 531, 5122};
8+
std::map<int, std::vector<std::vector<int>>> checkHadronDecays{ // sorted pdg of daughters
9+
{443, {{-11, 11}, {-13, 13}}}, // J/psi
10+
{511, {{313, 443}}}, // B0
11+
{521, {{321, 443}}}, // B+
12+
{531, {{333, 443}}}, // Bs0
13+
{5122, {{321, 443, 2212}}} // Lb0
14+
};
15+
16+
TFile file(path.c_str(), "READ");
17+
if (file.IsZombie()) {
18+
std::cerr << "Cannot open ROOT file " << path << "\n";
19+
return 1;
20+
}
21+
22+
auto tree = (TTree *)file.Get("o2sim");
23+
std::vector<o2::MCTrack> *tracks{};
24+
tree->SetBranchAddress("MCTrack", &tracks);
25+
o2::dataformats::MCEventHeader *eventHeader = nullptr;
26+
tree->SetBranchAddress("MCEventHeader.", &eventHeader);
27+
28+
int nEventsMB{}, nEventsInj{};
29+
int nSignals{}, nSignalGoodDecay{};
30+
int nJPsiToEE{}, nJPsiToMuMu{};
31+
auto nEvents = tree->GetEntries();
32+
33+
for (int i = 0; i < nEvents; i++) {
34+
tree->GetEntry(i);
35+
36+
// check subgenerator information
37+
if (eventHeader->hasInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID)) {
38+
bool isValid = false;
39+
int subGeneratorId = eventHeader->getInfo<int>(o2::mcgenid::GeneratorProperty::SUBGENERATORID, isValid);
40+
if (subGeneratorId == 0) {
41+
nEventsMB++;
42+
} else if (subGeneratorId == checkPdgQuark) {
43+
nEventsInj++;
44+
}
45+
}
46+
47+
for (auto &track : *tracks) {
48+
auto pdg = track.GetPdgCode();
49+
if (std::find(checkPdgHadron.begin(), checkPdgHadron.end(), std::abs(pdg)) != checkPdgHadron.end()) { // found signal
50+
nSignals++; // count signal PDG
51+
52+
std::vector<int> pdgsDecay{};
53+
std::vector<int> pdgsDecayAntiPart{};
54+
for (int j{track.getFirstDaughterTrackId()}; j <= track.getLastDaughterTrackId(); ++j) {
55+
auto pdgDau = tracks->at(j).GetPdgCode();
56+
if (pdg == 443 && pdgDau == 22) {
57+
continue;
58+
}
59+
pdgsDecay.push_back(pdgDau);
60+
if (pdgDau != 333) { // phi is antiparticle of itself
61+
pdgsDecayAntiPart.push_back(-pdgDau);
62+
} else {
63+
pdgsDecayAntiPart.push_back(pdgDau);
64+
}
65+
}
66+
67+
std::sort(pdgsDecay.begin(), pdgsDecay.end());
68+
std::sort(pdgsDecayAntiPart.begin(), pdgsDecayAntiPart.end());
69+
70+
for (auto &decay : checkHadronDecays[std::abs(pdg)]) {
71+
if (pdgsDecay == decay || pdgsDecayAntiPart == decay) {
72+
nSignalGoodDecay++;
73+
if (pdg == 443) {
74+
if (decay == std::vector{-11, 11}) {
75+
nJPsiToEE++;
76+
} else if (decay == std::vector{-13, 13}) {
77+
nJPsiToMuMu++;
78+
}
79+
}
80+
break;
81+
}
82+
}
83+
}
84+
}
85+
}
86+
87+
std::cout << "--------------------------------\n";
88+
std::cout << "# Events: " << nEvents << "\n";
89+
std::cout << "# MB events: " << nEventsMB << "\n";
90+
std::cout << Form("# events injected with %d quark pair: ", checkPdgQuark) << nEventsInj << "\n";
91+
std::cout <<"# signal hadrons: " << nSignals << "\n";
92+
std::cout <<"# signal hadrons decaying in the correct channel: " << nSignalGoodDecay << "\n";
93+
std::cout <<"# J/Psi decaying to e+e-: " << nJPsiToEE << "\n";
94+
std::cout <<"# J/Psi decaying to mu+mu-: " << nJPsiToMuMu << "\n";
95+
96+
if (nEventsMB < nEvents * (1 - ratioTrigger) * 0.95 || nEventsMB > nEvents * (1 - ratioTrigger) * 1.05) { // we put some tolerance since the number of generated events is small
97+
std::cerr << "Number of generated MB events different than expected\n";
98+
return 1;
99+
}
100+
if (nEventsInj < nEvents * ratioTrigger * 0.95 || nEventsInj > nEvents * ratioTrigger * 1.05) {
101+
std::cerr << "Number of generated events injected with " << checkPdgQuark << " different than expected\n";
102+
return 1;
103+
}
104+
105+
float fracForcedDecays = nSignals ? float(nSignalGoodDecay) / nSignals : 0.0f;
106+
float uncFracForcedDecays = nSignals ? std::sqrt(fracForcedDecays * (1 - fracForcedDecays) / nSignals) : 1.0f;
107+
if (1 - fracForcedDecays > 0.5 + uncFracForcedDecays) { // at least 50% in the main decay channels (we also have correlated backgrounds, mostly from other B decays)
108+
std::cerr << "Fraction of signals decaying into the correct channel " << fracForcedDecays << " lower than expected\n";
109+
return 1;
110+
}
111+
112+
return 0;
113+
}

0 commit comments

Comments
 (0)