FairShip
Loading...
Searching...
No Matches
Pythia8Generator.cxx
Go to the documentation of this file.
1// SPDX-License-Identifier: LGPL-3.0-or-later
2// SPDX-FileCopyrightText: Copyright CERN for the benefit of the SHiP
3// Collaboration
4
5#include "Pythia8Generator.h"
6
7#include <TGeoManager.h>
8
9#include <cmath>
10
11#include "FairPrimaryGenerator.h"
12#include "HNLPythia8Generator.h"
13#include "MeanMaterialBudget.h"
14#include "TGeoBBox.h"
15#include "TGeoNode.h"
16#include "TGeoVolume.h"
17#include "TMath.h"
18#include "TROOT.h"
19#include "TSystem.h"
20const Double_t cm = 10.; // pythia units are mm
21const Double_t c_light = 2.99792458e+10; // speed of light in cm/sec (c_light
22 // = 2.99792458e+8 * m/s)
23Int_t counter = 0;
24const Double_t mbarn = 1E-3 * 1E-24 * TMath::Na(); // cm^2 * Avogadro
25
26// ----- Default constructor -------------------------------------------
28 fUseRandom1 = kFALSE;
29 fUseRandom3 = kTRUE;
30 fId = 2212; // proton
31 fMom = 400; // proton
32 fFDs = 7.7 / 10.4; // correction for Pythia6 to match measured Ds production
33 fInputFile = nullptr;
34 targetName = "";
35 xOff = 0;
36 yOff = 0;
37 targetFromGeometry = kFALSE;
38 fPythia = new Pythia8::Pythia();
39}
40// -------------------------------------------------------------------------
41
42// ----- Default constructor -------------------------------------------
44 if (fUseRandom1) fRandomEngine = std::make_shared<PyTr1Rng>();
45 if (fUseRandom3) fRandomEngine = std::make_shared<PyTr3Rng>();
46 if (fextFile) {
47 fInputFile = TFile::Open(fextFile->c_str());
48 LOG(info) << "Open external file with charm or beauty hadrons: "
49 << *fextFile;
50 if (!fInputFile) {
51 LOG(fatal) << "Error opening input file.";
52 return kFALSE;
53 }
54 fTree = fInputFile->Get<TTree>("pythia6");
55 fNevents = fTree->GetEntries();
56 fn = firstEvent;
57 fTree->SetBranchAddress("id", &hid); // particle id
58 fTree->SetBranchAddress("px", &hpx); // momentum
59 fTree->SetBranchAddress("py", &hpy);
60 fTree->SetBranchAddress("pz", &hpz);
61 fTree->SetBranchAddress("E", &hE);
62 fTree->SetBranchAddress("M", &hM);
63 fTree->SetBranchAddress("mid", &mid); // mother
64 fTree->SetBranchAddress("mpx", &mpx); // momentum
65 fTree->SetBranchAddress("mpy", &mpy);
66 fTree->SetBranchAddress("mpz", &mpz);
67 fTree->SetBranchAddress("mE", &mE);
68 if (fTree->GetBranch("k")) {
69 fTree->SetBranchAddress("k", &ck);
70 if (fTree->GetBranch("a0")) {
71 for (Int_t i = 0; i < 16; i++) {
72 TString na = "a";
73 na += i;
74 fTree->SetBranchAddress(na, &(ancestors[i]));
75 TString ns = "s";
76 ns += i;
77 fTree->SetBranchAddress(ns, &(subprocCodes[i]));
78 }
79 }
80 } else {
81 ck[0] = 1;
82 subprocCodes[0] = 88;
83 ancestors[0] = 2212;
84 }
85 fPythia->readString("ProcessLevel:all = off");
86 // find all long lived particles in pythia
87 Int_t n = 1;
88 while (n != 0) {
89 n = fPythia->particleData.nextId(n);
90 std::shared_ptr<Pythia8::ParticleDataEntry> p =
91 fPythia->particleData.particleDataEntryPtr(n);
92 if (p->tau0() > 1) {
93 std::string particle = std::to_string(n) + ":mayDecay = false";
94 fPythia->readString(particle);
95 LOGF(info, "Made %s stable for Pythia, should decay in Geant4",
96 p->name().c_str());
97 }
98 }
99 } else {
100 fPythia->setRndmEnginePtr(fRandomEngine);
101 fPythia->settings.mode("Beams:idA", fId);
102 fPythia->settings.mode("Beams:idB", 2212);
103 fPythia->settings.mode("Beams:frameType", 2);
104 fPythia->settings.parm("Beams:eA", fMom); // codespell:ignore parm
105 fPythia->settings.parm("Beams:eB", 0.); // codespell:ignore parm
106 }
107 fPythia->init();
108 if (targetFromGeometry) {
109 // Use geometry-based coordinates passed from run_simScript.py
111 start[0] = xOff;
112 start[1] = yOff;
113 start[2] = startZ;
114 end[0] = xOff;
115 end[1] = yOff;
116 end[2] = endZ;
117 LOG(info)
118 << "Pythia8Generator: Using geometry-based target coordinates startZ="
119 << startZ << " endZ=" << endZ;
120 // find maximum interaction length
123 } else if (targetName != "") {
124 // Fallback to fragile TGeo navigation for backward compatibility
126 TGeoVolume* top = gGeoManager->GetTopVolume();
127 TGeoNode* target = top->FindNode(targetName);
128 if (!target) {
129 LOGF(error, "target not found, %s, program will crash",
130 targetName.Data());
131 }
132 Double_t z_middle = target->GetMatrix()->GetTranslation()[2];
133 TGeoBBox* sha = dynamic_cast<TGeoBBox*>(target->GetVolume()->GetShape());
134 startZ = z_middle - sha->GetDZ();
135 endZ = z_middle + sha->GetDZ();
136 start[0] = xOff;
137 start[1] = yOff;
138 start[2] = startZ;
139 end[0] = xOff;
140 end[1] = yOff;
141 end[2] = endZ;
142 // find maximum interaction length
145 }
146 return kTRUE;
147}
148// -------------------------------------------------------------------------
149
150// ----- Destructor ----------------------------------------------------
152// -------------------------------------------------------------------------
153
154// ----- Passing the event ---------------------------------------------
155Bool_t Pythia8Generator::ReadEvent(FairPrimaryGenerator* cpg) {
156 Double_t x, y, z, px, py, pz, dl, e, tof;
157 Int_t im, id, key;
158 fnRetries = 0;
159 // take charm hadrons from external file
160 // correct eventually for too much primary Ds produced by pythia6
161 key = 0;
162 bool l = true;
163 while (l) {
164 if (fn == fNevents) {
165 LOG(warning) << "End of input file. Rewind.";
166 }
167 fTree->GetEntry((fn + 1) % fNevents);
168 // check that this and next entry is charm, otherwise continue reading
169 l = false;
170 if (static_cast<int>(mE[0]) == 0) {
171 l = true;
172 }
173 bool isDs = false;
174 if (static_cast<int>(fabs(hid[0])) == 431) {
175 isDs = true;
176 }
177 fTree->GetEntry(fn % fNevents);
178 if (static_cast<int>(mE[0]) == 0) {
179 l = true;
180 }
181 fn++;
182 if (static_cast<int>(fabs(hid[0])) == 431 || isDs) {
183 Double_t rnr = gRandom->Uniform(0, 1);
184 if (rnr > fFDs) {
185 l = true;
186 fn++;
187 }
188 }
189 }
190 Double_t zinter = 0;
191 if (targetName != "") {
192 // calculate primary proton interaction point:
193 // loop over trajectory between start and end to pick an interaction point,
194 // copied from GenieGenerator and adapted to hadrons
195 Double_t prob2int = -1.;
196 Double_t rndm = 0.;
197 Double_t sigma;
198 Double_t zinterStart = start[2];
199 // simulate more downstream interaction points for interactions down in the
200 // cascade
201 Int_t nInter = ck[0];
202 if (nInter > 16) {
203 nInter = 16;
204 }
205 for (Int_t nI = 0; nI < nInter; nI++) {
206 // if (!subprocCodes[nI]<90){continue;} //if process is not inelastic, go
207 // to next. Changed by taking now collision length
208 prob2int = -1.;
209 Double_t intLengthFactor = 1; // for nucleons
210 if (TMath::Abs(ancestors[nI]) < 1000) {
211 intLengthFactor = 1.16;
212 } // for mesons
213 // Fe: nuclear /\ 16.77 cm pion 20.42 cm f=1.22
214 // W: nuclear /\ 9.946 cm pion 11.33 cm f=1.14
215 // Mo: nuclear /\ 15.25 cm pion 17.98 cm f=1.18
216 while (prob2int < rndm) {
217 // place x,y,z uniform along path
218 zinter = gRandom->Uniform(zinterStart, end[2]);
219 Double_t point[3] = {xOff, yOff, zinter};
221 Double_t interLength =
222 mparam[8] * intLengthFactor *
223 1.7; // 1.7 = interaction length / collision length from PDG Tables
224 TGeoNode* node = gGeoManager->FindNode(point[0], point[1], point[2]);
225 TGeoMaterial* mat = nullptr;
226 if (node && !gGeoManager->IsOutside()) {
227 mat = node->GetVolume()->GetMaterial();
228 Double_t n = mat->GetDensity() / mat->GetA();
229 sigma = 1. / (n * mat->GetIntLen()) /
230 mbarn; // no need to multiply with intLengthFactor, will
231 // cancel in next equation.
232 prob2int = TMath::Exp(-interLength) * sigma / maxCrossSection;
233 } else {
234 prob2int = 0.;
235 }
236 rndm = gRandom->Uniform(0., 1.);
237 }
238 zinterStart = zinter;
239 }
240 zinter = zinter * cm;
241 }
242 for (Int_t c = 0; c < 2; c++) {
243 if (c > 0) {
244 fTree->GetEntry(fn % fNevents);
245 fn++;
246 }
247 fPythia->event.reset();
248 id = static_cast<Int_t>(hid[0]);
249 fPythia->event.append(id, 1, 0, 0, hpx[0], hpy[0], hpz[0], hE[0], hM[0], 0.,
250 9.);
251 // simulate displaced vertex, Pythia8 will not do it
252 Double_t tau0 = fPythia->particleData.tau0(id); // ctau in mm
253 dl = gRandom->Exp(tau0) / hM[0]; // mm/GeV
254 fPythia->next();
255 Int_t addedParticles = 0;
256 if (c == 0) {
257 // original particle responsible for interaction, "mother of charm" from
258 // external file
259 px = mpx[0];
260 py = mpy[0];
261 pz = mpz[0];
262 x = 0.;
263 y = 0.;
264 z = zinter;
265 id = mid[0];
266 cpg->AddTrack(id, px, py, pz, x / cm, y / cm, z / cm, -1, false);
267 addedParticles += 1;
268 }
269 for (Int_t ii = 1; ii < fPythia->event.size(); ii++) {
270 id = fPythia->event[ii].id();
271 Bool_t wanttracking = false;
272 if (fPythia->event[ii].isFinal()) {
273 wanttracking = true;
274 }
275 if (ii > 1) {
276 z = fPythia->event[ii].zProd() + dl * fPythia->event[1].pz() + zinter;
277 x = fPythia->event[ii].xProd() + dl * fPythia->event[1].px();
278 y = fPythia->event[ii].yProd() + dl * fPythia->event[1].py();
279 tof = fPythia->event[ii].tProd() / (10 * c_light) +
280 dl * fPythia->event[1].e() / cm / c_light;
281 } else {
282 z = fPythia->event[ii].zProd() + zinter;
283 x = fPythia->event[ii].xProd();
284 y = fPythia->event[ii].yProd();
285 tof =
286 fPythia->event[ii].tProd() / (10 * c_light); // to go from mm to s
287 }
288 pz = fPythia->event[ii].pz();
289 px = fPythia->event[ii].px();
290 py = fPythia->event[ii].py();
291 e = fPythia->event[ii].e();
292 im = fPythia->event[ii].mother1() + key;
293
294 if (ii == 1) {
295 im = 0;
296 }
297 cpg->AddTrack(id, px, py, pz, x / cm, y / cm, z / cm, im, wanttracking, e,
298 tof, 1.);
299 addedParticles += 1;
300 }
301 key += addedParticles - 1; // pythia counts from 1
302 }
303 counter += 1;
304 // now the underlying event
305 bool lx = true;
306 while (lx) {
307 fTree->GetEntry(fn % fNevents);
308 lx = false;
309 if (mE[0] == 0) {
310 lx = true;
311 fn++;
312 cpg->AddTrack((Int_t)hid[0], hpx[0], hpy[0], hpz[0],
313 (mpx[0] + fPythia->event[0].xProd()) / cm,
314 (mpy[0] + fPythia->event[0].yProd()) / cm,
315 (mpz[0] + fPythia->event[0].zProd()) / cm + zinter / cm, -1,
316 true);
317 // mpx,mpy,mpz are the vertex coordinates with respect to charm hadron,
318 // first particle in Pythia is (system)
319 }
320 }
321
322 return kTRUE;
323}
324// -------------------------------------------------------------------------
const Double_t c_light
const Double_t mbarn
Int_t counter
const Double_t cm
const Double_t c_light
const Double_t mbarn
Double_t cm
Definition: diagrams_e.h:4
~Pythia8Generator() override
Double_t mparam[10]
Double_t maxCrossSection
Bool_t Init() override
Float_t subprocCodes[16]
Bool_t ReadEvent(FairPrimaryGenerator *) override
Float_t ancestors[16]
Pythia8::Pythia * fPythia
GenieGenerator * fMaterialInvestigator
TTree * fTree
don't make it persistent, magic ROOT command
std::shared_ptr< Pythia8::RndmEngine > fRandomEngine
std::optional< std::string > fextFile
Definition: Generator.h:47
Int_t firstEvent
Definition: Generator.h:48
double MeanMaterialBudget(std::span< const double, 3 > start, std::span< const double, 3 > end, std::span< double, 10 > mparam)