FairShip
Loading...
Searching...
No Matches
FixedTargetGenerator.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
6
7#include <TGeoManager.h>
8
9#include <array>
10#include <cmath>
11
12#include "BeamSmearingUtils.h"
13#include "FairMCEventHeader.h"
14#include "FairPrimaryGenerator.h"
15#include "HNLPythia8Generator.h"
16#include "MeanMaterialBudget.h"
17#include "Pythia8Plugins/EvtGen.h"
18#include "ShipUnit.h"
19#include "TGeoBBox.h"
20#include "TGeoNode.h"
21#include "TGeoVolume.h"
22#include "TMCProcess.h"
23#include "TMath.h"
24#include "TROOT.h"
25
26using ShipUnit::cm;
27using ShipUnit::mm;
28const Double_t c_light = 2.99792458e+10; // speed of light in cm/sec (c_light
29 // = 2.99792458e+8 * m/s)
30const Double_t mbarn = 1E-3 * 1E-24 * TMath::Na(); // cm^2 * Avogadro
31
32// ----- Default constructor -------------------------------------------
34 fUseRandom1 = kFALSE;
35 fUseRandom3 = kTRUE;
36 fSeed = 0;
37 fMom = 400; // proton
38 fLogger = FairLogger::GetLogger();
39 targetName = "cave_1/target_vacuum_box_1/TargetArea_1/HeVolume_1";
40 xOff = 0;
41 yOff = 0;
42 zOff = 0;
43 fsmearBeam = 8 * mm; // default value for smearing beam (8 mm)
44 fPaintBeam = 5 * cm; // default value for painting beam (5 cm)
45 tauOnly = false;
46 JpsiMainly = false;
47 DrellYan = false;
48 PhotonCollision = false;
49 G4only = false;
50 OnlyMuons = false;
51 maxCrossSection = 0.;
52 EMax = 0;
53 fBoost = 1.;
54 withEvtGen = kFALSE;
55 withNtuple = kFALSE;
56 chicc = 1.7e-3; // prob to produce primary ccbar pair/pot
57 chibb = 1.6e-7; // prob to produce primary bbbar pair/pot
58 nrpotspill = 5.E13; // nr of protons / spill
59 setByHand = kFALSE;
60 Debug = kFALSE;
61 targetFromGeometry = kFALSE;
62 Option = "Primary";
63 wspill = 1.; // event weight == 1 for primary events
64 heartbeat = 1000;
65}
66Bool_t FixedTargetGenerator::InitForCharmOrBeauty(TString fInName, Int_t nev,
67 Double_t npot, Int_t nStart) {
68 Option = "charm";
69 nEntry = nStart;
70 // open input file with charm or beauty
71 fin = TFile::Open(fInName);
72 if (!fin) {
73 LOG(fatal) << "Could not open input file: " << fInName.Data();
74 return kFALSE;
75 }
76 nTree = dynamic_cast<TNtuple*>(
77 fin->FindObjectAny("pythia6")); // old format, simple ntuple
78 nEvents = nTree->GetEntries();
79 nTree->SetBranchAddress("id", &n_id);
80 nTree->SetBranchAddress("px", &n_px);
81 nTree->SetBranchAddress("py", &n_py);
82 nTree->SetBranchAddress("pz", &n_pz);
83 nTree->SetBranchAddress("M", &n_M);
84 nTree->SetBranchAddress("E", &n_E);
85 nTree->SetBranchAddress("mid", &n_mid);
86 nTree->SetBranchAddress("mpx", &n_mpx);
87 nTree->SetBranchAddress("mpy", &n_mpy);
88 nTree->SetBranchAddress("mpz", &n_mpz);
89 nTree->SetBranchAddress("mE", &n_mE);
90 if (nTree->GetBranch("k")) {
91 LOG(info) << "+++has branch+++";
92 nTree->SetBranchAddress("k", &ck);
93 }
94 // check if we deal with charm or beauty:
95 nTree->GetEvent(0);
96 if (!setByHand && n_M > 5) {
97 chicc = chibb;
98 LOG(info) << "automatic detection of beauty, configured for beauty";
99 LOG(info) << "bb cross section / mbias " << chicc;
100 } else {
101 LOG(info) << "cc cross section / mbias " << chicc;
102 }
103 // convert pot to weight corresponding to one spill of 5e13 pot
104 // get histogram with number of pot to normalise
105 // pot are counted double, i.e. for each signal, i.e. pot/2.
106 auto* potHist = dynamic_cast<TH1F*>(fin->Get("2"));
107 if (!potHist) {
108 LOG(fatal) << "Histogram '2' not found in input file";
109 return kFALSE;
110 }
111 Int_t nrcpot =
112 potHist->GetBinContent(1) / 2.; // number of primary interactions
113 wspill = nrpotspill * chicc / nrcpot * nEvents / nev;
114 LOG(info) << "Input file: " << fInName.Data() << " with " << nEvents
115 << " entries, corresponding to nr-pot=" << (nrcpot / chicc);
116 LOG(info) << "weight " << wspill << " corresponding to " << nrpotspill
117 << " p.o.t. per spill for " << nev << " events to process";
118
119 pot = 0.;
120 // Determine fDs on this file for primaries
121 nDsprim = 0;
122 ntotprim = 0;
123
124 return kTRUE;
125}
126// ----- Default Init -------------------------------------------
128 fPythiaP =
129 new Pythia8::Pythia(); // pythia needed also for G4only, to copy 2mu BRs
130 if (Option == "Primary" && !G4only) {
131 fPythiaN = new Pythia8::Pythia();
132 } else if (Option != "charm" && Option != "beauty" && !G4only) {
133 LOG(error) << "Option not known " << Option.Data() << ", abort";
134 }
135 if (fUseRandom1) fRandomEngine = std::make_shared<PyTr1Rng>();
136 if (fUseRandom3) fRandomEngine = std::make_shared<PyTr3Rng>();
137 std::vector<int> r = {221, 221, 223, 223, 113, 331, 333};
138 std::vector<int> c = {6, 7, 5, 7, 5, 6, 9}; // decay channel mumu mumuX
139
140 if (Option == "Primary" && !G4only) {
141 fPythiaP->settings.mode("Beams:idB", 2212);
142 fPythiaN->settings.mode("Beams:idB", 2112);
143 } else {
144 fPythiaP->readString("ProcessLevel:all = off");
145 }
146 std::array<Pythia8::Pythia*, 2> plist = {fPythiaP, fPythiaN};
147 Int_t pcount = 0;
148 for (const auto& fPythia : plist) {
149 if (pcount > 0 && (Option != "Primary" || G4only)) {
150 continue;
151 }
152 pcount += 1;
153 fPythia->setRndmEnginePtr(fRandomEngine);
154 fPythia->settings.mode("Random:seed", fSeed);
155 fPythia->settings.mode("Next:numberCount", heartbeat);
156 if (Option == "Primary") {
157 fPythia->settings.mode("Beams:idA", 2212);
158 fPythia->settings.mode("Beams:frameType", 2);
159 fPythia->settings.parm("Beams:eA", fMom); // codespell:ignore parm
160 fPythia->settings.parm("Beams:eB", 0.); // codespell:ignore parm
161 }
162 if (JpsiMainly) {
163 // use this for all onia productions
164 fPythia->readString("Onia:all(3S1) = on");
165 fPythia->readString(
166 "443:new J/psi J/psi 3 0 0 3.09692 0.00009 3.09602 "
167 " 3.09782 0. 1 1 0 1 0");
168 fPythia->readString("443:addChannel = 1 1. 0 -13 13");
169 fPythia->readString(
170 "553:new Upsilon Upsilon 3 0 0 9.46030 0.00005 "
171 "9.45980 9.46080 0.00000e+00 0 1 0 1 0");
172 fPythia->readString("553:addChannel = 1 1. 0 -13 13");
173 }
174 if (DrellYan) {
175 fPythia->readString("WeakSingleBoson:ffbar2gmZ = on");
176 // Z0/gamma -> mu+ mu-
177 fPythia->readString("23:onMode = off");
178 fPythia->readString("23:onIfAll = 13 13");
179 fPythia->readString("23:mMin = 0.5");
180 fPythia->readString("PhaseSpace:mHatMin = 0.5");
181 fPythia->readString("PartonLevel:FSR = on");
182 fPythia->readString("PDF:pSet = 4");
183 }
184 if (PhotonCollision) {
185 fPythia->readString("PhotonCollision:gmgm2mumu = on");
186 }
187 if (!DrellYan && !PhotonCollision) {
188 fPythia->readString("SoftQCD:inelastic = on");
189 fPythia->readString("PhotonCollision:gmgm2mumu = on");
190 fPythia->readString("PromptPhoton:all = on");
191 fPythia->readString("WeakBosonExchange:all = on");
192 fPythia->readString("WeakSingleBoson:all = on");
193 }
194 if (tauOnly) {
195 fPythia->readString(
196 "431:new D_s+ D_s- 1 3 0 1.96849 0.00000 0.00000 "
197 "0.00000 1.49900e-01 0 1 0 1 0");
198 fPythia->readString(
199 "431:addChannel = 1 0.0640000 0 -15 16");
200 }
201
202 // find all long lived particles in pythia
203 Int_t n = 1;
204 while (n != 0) {
205 n = fPythia->particleData.nextId(n);
206 std::shared_ptr<Pythia8::ParticleDataEntry> p =
207 fPythia->particleData.particleDataEntryPtr(n);
208 if (p->tau0() > 1) {
209 std::string particle = std::to_string(n) + ":mayDecay = false";
210 fPythia->readString(particle);
211 LOG(info) << "Made " << p->name().c_str()
212 << " stable for Pythia, should decay in Geant4";
213 }
214 }
215 // boost branching fraction of rare di-muon decays
216 // eta omega rho0 eta' phi
217 if (fBoost != 1.) {
218 LOG(info) << "Rescale BRs of dimuon decays in Pythia: " << fBoost;
219 for (unsigned int i = 0; i < r.size(); ++i) {
220 std::shared_ptr<Pythia8::ParticleDataEntry> V =
221 fPythia->particleData.particleDataEntryPtr(r[i]);
222 Pythia8::DecayChannel ch = V->channel(c[i]);
223 if (TMath::Abs(ch.product(0)) != 13 ||
224 TMath::Abs(ch.product(1)) != 13) {
225 LOG(info) << "this is not the right decay channel: " << r[i] << " "
226 << c[i];
227 } else {
228 TString tmp = "";
229 tmp += r[i];
230 tmp += ":";
231 tmp += c[i];
232 tmp += ":bRatio =";
233 tmp += fBoost * ch.bRatio();
234 fPythia->readString(tmp.Data());
235 }
236 }
237 }
238 fPythia->init();
239 }
240 // Initialize EvtGen.
241 if (withEvtGen) {
242 // Use EVTGENDATA environment variable to find EvtGen data files
243 const char* evtgendata = getenv("EVTGENDATA");
244 if (!evtgendata) {
245 LOG(fatal) << "EVTGENDATA environment variable not set";
246 }
247 TString DecayFile = TString(evtgendata) + "/DECAY.DEC";
248
249 TString ParticleFile = TString(evtgendata) + "/evt.pdl";
250 std::cout << "Using $EVTGENDATA " << evtgendata << std::endl;
251 EvtExternalGenList* extPtr = new EvtExternalGenList();
252 std::list<EvtDecayBase*> models = extPtr->getListOfModels();
253 TString UdecayFile = getenv("FAIRSHIP");
254 UdecayFile += "/gconfig/USERDECAY.DEC";
255 evtgenP = new Pythia8::EvtGenDecays(fPythiaP, DecayFile.Data(),
256 ParticleFile.Data(), extPtr);
257 evtgenP->readDecayFile(
258 UdecayFile.Data()); // will make update of EvtGen with user decay file
259 // use one instance of EvtGen, requires patch to Pythia8Plugins/EvtGen.h
260 if (Option == "Primary") {
261 evtgenN =
262 new Pythia8::EvtGenDecays(fPythiaN, DecayFile.Data(), "", extPtr);
263 }
264 }
265 if (targetFromGeometry) {
266 // Use geometry-based coordinates passed from run_simScript.py
268 start[0] = xOff;
269 start[1] = yOff;
270 start[2] = startZ + zOff;
271 end[0] = xOff;
272 end[1] = yOff;
273 end[2] = endZ;
274 LOG(info) << "FixedTargetGenerator: Using geometry-based target "
275 "coordinates startZ="
276 << startZ << " endZ=" << endZ;
277 // find maximum interaction length
280 } else if (targetName != "") {
281 // Fallback to fragile TGeo navigation for backward compatibility
283 TGeoNavigator* nav = gGeoManager->GetCurrentNavigator();
284 if (nav->CheckPath(targetName)) {
285 nav->cd(targetName);
286 } else {
287 LOG(fatal) << "Invalid target volume specified";
288 }
289 TGeoNode* target = nav->GetCurrentNode();
290 TObjArray* nodes = target->GetVolume()->GetNodes();
291 // Get the first and last node of the target to calculate the material seen
292 TGeoNode* first = static_cast<TGeoNode*>(nodes->At(0));
293 TGeoNode* last = static_cast<TGeoNode*>(nodes->At(nodes->GetSize() - 1));
294 nav->cd(targetName + "/" + first->GetName());
295 TGeoBBox* sha = static_cast<TGeoBBox*>(first->GetVolume()->GetShape());
296 Double_t dz = sha->GetDZ();
297 Double_t origin[3] = {0, 0, -dz};
298 Double_t master[3] = {0, 0, 0};
299 nav->LocalToMaster(origin, master);
300 startZ = master[2];
301 nav->cd(targetName + "/" + last->GetName());
302 sha = static_cast<TGeoBBox*>(first->GetVolume()->GetShape());
303 dz = sha->GetDZ();
304 origin[2] = +dz;
305 nav->LocalToMaster(origin, master);
306 endZ = master[2];
307 start[0] = xOff;
308 start[1] = yOff;
309 start[2] = startZ + zOff;
310 end[0] = xOff;
311 end[1] = yOff;
312 end[2] = endZ;
313 // find maximum interaction length
316 } else {
317 LOG(fatal) << "No target set.";
318 }
319
320 return kTRUE;
321}
322// -------------------------------------------------------------------------
323
324// ----- Destructor ----------------------------------------------------
326// -------------------------------------------------------------------------
327
328// ----- Passing the event ---------------------------------------------
329Bool_t FixedTargetGenerator::ReadEvent(FairPrimaryGenerator* cpg) {
330 // Calculate beam smearing and painting
331 auto [dx, dy] = CalculateBeamOffset(fsmearBeam, fPaintBeam);
332
333 Double_t zinter = 0;
334 Double_t ZoverA = 1.;
335 if (!targetName.IsNull()) {
336 // calculate primary proton interaction point:
337 // loop over trajectory between start and end to pick an interaction point,
338 // copied from GenieGenerator and adapted to hadrons
339 Double_t prob2int = -1.;
340 Double_t rndm = 0.;
341 Double_t sigma;
342 Double_t zinterStart = start[2];
343 if (Option == "charm" || Option == "beauty") {
344 // simulate more downstream interaction points for interactions down in
345 // the cascade
346 if (!(nTree->GetBranch("k"))) {
347 ck = 1;
348 }
349 } else {
350 ck = 1;
351 }
352 while (ck > 0.5) {
353 while (prob2int < rndm) {
354 // place x,y,z uniform along path
355 zinter = gRandom->Uniform(zinterStart, end[2]);
356 Double_t point[3] = {xOff, yOff, zinter};
358 Double_t interLength = mparam[8];
359 TGeoNode* node = gGeoManager->FindNode(point[0], point[1], point[2]);
360 TGeoMaterial* mat = nullptr;
361 if (node && !gGeoManager->IsOutside()) {
362 mat = node->GetVolume()->GetMaterial();
363 Double_t n = mat->GetDensity() / mat->GetA();
364 ZoverA = mat->GetZ() / mat->GetA();
365 sigma = 1. / (n * mat->GetIntLen()) / mbarn;
366 prob2int = TMath::Exp(-interLength) * sigma / maxCrossSection;
367 } else {
368 prob2int = 0.;
369 }
370 rndm = gRandom->Uniform(0., 1.);
371 }
372 zinterStart = zinter;
373 ck -= 1;
374 }
375 zinter = zinter * cm;
376 }
377 Pythia8::Pythia* fPythia;
378 if (G4only) {
379 cpg->AddTrack(2212, 0., 0., fMom, (xOff + dx) / cm, (yOff + dy) / cm,
380 start[2], -1, kTRUE, -1., 0., 1.);
381 return kTRUE;
382 } else if (Option == "Primary") {
383 if (gRandom->Uniform(0., 1.) < ZoverA) {
384 fPythiaP->next();
385 if (withEvtGen) {
386 evtgenP->decay();
387 }
388 fPythia = fPythiaP;
389 } else {
390 fPythiaN->next();
391 if (withEvtGen) {
392 evtgenN->decay();
393 }
394 fPythia = fPythiaN;
395 }
396 } else {
397 if (nEntry == nEvents) {
398 LOG(info) << "Rewind input file: " << nEntry;
399 nEntry = 0;
400 }
401 nTree->GetEvent(nEntry);
402 nEntry += 1;
403 // sanity check, count number of p.o.t. on input file.
404 Double_t pt = TMath::Sqrt((n_mpx * n_mpx) + (n_mpy * n_mpy));
405 // every event appears twice, i.e.
406 if (pt < 1.e-5 && n_mid == 2212) {
407 pot += +0.5;
408 ntotprim += 1;
409 }
410 Int_t idabs = static_cast<int>(TMath::Abs(n_id));
411 if (idabs == 431) {
412 nDsprim += 1;
413 }
414 fPythiaP->event.reset();
415 Int_t nID1 = n_id;
416 fPythiaP->event.append(static_cast<int>(n_id), 1, 0, 0, n_px, n_py, n_pz,
417 n_E, n_M, 0., 9.);
418 TMCProcess procID = kPTransportation;
419 if (n_mid == 2212 && (n_mpx * n_mpx + n_mpy * n_mpy) < 1E-5) {
420 procID = kPPrimary;
421 } // probably primary and not from cascade
422 cpg->AddTrack(static_cast<int>(n_mid), n_mpx, n_mpy, n_mpz,
423 (xOff + dx) * cm, (yOff + dy) * cm, zinter * cm, -1, kFALSE,
424 n_mE, 0., wspill, procID);
425 // second charm hadron in the event
426 nTree->GetEvent(nEntry);
427 if (nID1 * n_id > 0) {
428 LOG(info) << "same sign charm: " << nEntry << ", " << nID1 << ", "
429 << n_id;
430 }
431 nEntry += 1;
432 fPythiaP->event.append(static_cast<int>(n_id), 1, 0, 0, n_px, n_py, n_pz,
433 n_E, n_M, 0., 9.);
434 fPythiaP->next();
435 fPythia = fPythiaP;
436 }
437 if (withEvtGen) {
438 fPythia->moreDecays();
439 } // let the very short lived resonances decay via Pythia8
440 if (Debug && !G4only) {
441 fPythia->event.list();
442 }
443 TMCProcess procID;
444 for (Int_t ii = 1; ii < fPythia->event.size(); ii++) {
445 Double_t e = fPythia->event[ii].e();
446 Double_t m = fPythia->event[ii].m();
447 Double_t pz = fPythia->event[ii].pz();
448 Int_t id = fPythia->event[ii].id();
449 Bool_t wanttracking = kTRUE;
450 if (e - m < EMax || !fPythia->event[ii].isFinal() || pz < 0) {
451 wanttracking = kFALSE;
452 }
454 // don't track underlying event
455 if (fabs(id) != 13) {
456 wanttracking = kFALSE;
457 }
458 }
459 Double_t z = fPythia->event[ii].zProd() * mm + zinter * cm;
460 Double_t x = fPythia->event[ii].xProd() * mm + xOff * cm + dx * cm;
461 Double_t y = fPythia->event[ii].yProd() * mm + yOff * cm + dy * cm;
462 Double_t tof =
463 fPythia->event[ii].tProd() / (10 * c_light); // to go from mm to s
464 Double_t px = fPythia->event[ii].px();
465 Double_t py = fPythia->event[ii].py();
466 Int_t im = fPythia->event[ii].mother1() - 1;
467 procID = kPPrimary;
468 if (Option != "Primary") {
469 procID = kPDecay;
470 im += 1;
471 if (ii == 1) {
472 procID = kPHadronic;
473 }
474 } else {
475 if (ii < 3) {
476 im = -1;
477 }
478 }
479 cpg->AddTrack(id, px, py, pz, x * cm, y * cm, z * cm, im, wanttracking, e,
480 tof, wspill, procID);
481 if (withNtuple) {
482 int idabs = TMath::Abs(id);
483 if (idabs < 10) {
484 continue;
485 }
486 if (idabs < 18 || idabs == 22 || idabs == 111 || idabs == 221 ||
487 idabs == 223 || idabs == 331 || idabs == 211 || idabs == 113 ||
488 idabs == 333 || idabs == 321 || idabs == 2212) {
489 Int_t moID = fPythia->event[im + 1].id();
490 fNtuple->Fill(0, id, px, py, pz, e, x * cm, y * cm, z * cm, moID);
491 }
492 }
493 }
494 return kTRUE;
495}
496// -------------------------------------------------------------------------
std::pair< Double_t, Double_t > CalculateBeamOffset(Double_t smearBeam, Double_t paintBeam)
const Double_t c_light
const Double_t c_light
const Double_t mbarn
Double_t cm
Double_t m
Double_t mm
Definition: diagrams_e.h:4
Bool_t ReadEvent(FairPrimaryGenerator *) override
GenieGenerator * fMaterialInvestigator
TNtuple * fNtuple
special option for Dark Photon physics studies
Pythia8::Pythia * fPythiaP
Pythia8::EvtGenDecays * evtgenN
Pythia8::Pythia * fPythiaN
don't make it persistent, magic ROOT command
Bool_t InitForCharmOrBeauty(TString fInName, Int_t nev, Double_t npots=5E13, Int_t nStart=0)
Pythia8::EvtGenDecays * evtgenP
std::shared_ptr< Pythia8::RndmEngine > fRandomEngine
double MeanMaterialBudget(std::span< const double, 3 > start, std::span< const double, 3 > end, std::span< double, 10 > mparam)