Project
Loading...
Searching...
No Matches
GeneratorPythia8.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
17#include <fairlogger/Logger.h>
18#include "TParticle.h"
19#include "TF1.h"
20#include "TRandom.h"
24#if PYTHIA_VERSION_INTEGER >= 8310
25#include "Pythia8/HIInfo.h"
26#else
27#include "Pythia8/HIUserHooks.h"
28#endif
29#include "Pythia8Plugins/PowhegHooks.h"
30#include "TString.h"
31#include "TSystem.h"
34#include <filesystem>
36
37#include <iostream>
38#include <unordered_map>
39#include <numeric>
40
41namespace o2
42{
43namespace eventgen
44{
45
47
48/*****************************************************************/
49/*****************************************************************/
50
51// the default construct uses the GeneratorPythia8Param singleton to extract a config and delegates
52// to the proper constructor
54{
55 LOG(info) << "GeneratorPythia8 constructed from GeneratorPythia8Param ConfigurableParam";
56}
57
58/*****************************************************************/
59
60GeneratorPythia8::GeneratorPythia8(Pythia8GenConfig const& config) : Generator("ALICEo2", "ALICEo2 Pythia8 Generator")
61{
65
66 mInterface = reinterpret_cast<void*>(&mPythia);
67 mInterfaceName = "pythia8";
68
69 // Decrease Pythia8 verbosity when running in Hyperloop
70 if (mIsHyperloop) {
71 LOG(info) << "Simulation running in Hyperloop => reducing Pythia8 logs";
72 mPythia.readString("Print:quiet on");
73 }
74
75 LOG(info) << "Instance \'Pythia8\' generator with following parameters";
76 LOG(info) << "config: " << config.config;
77 LOG(info) << "hooksFileName: " << config.hooksFileName;
78 LOG(info) << "hooksFuncName: " << config.hooksFuncName;
79
80 mGenConfig = config;
81
85}
86
87/*****************************************************************/
88
89GeneratorPythia8::GeneratorPythia8(const Char_t* name, const Char_t* title) : Generator(name, title)
90{
93 mInterface = reinterpret_cast<void*>(&mPythia);
94 mInterfaceName = "pythia8";
95}
96
98{
99 // check first of all if Init not yet called and seed not <0
100 if (mIsInitialized) {
101 return false;
102 }
103 if (seed < 0) {
104 return false;
105 }
106 // sets the initial seed and applies the correct Pythia
107 // range
108 mInitialRNGSeed = seed % (MAX_SEED + 1);
109 LOG(info) << "GeneratorPythia8: Setting initial seed to " << mInitialRNGSeed;
110 return true;
111}
112
113/*****************************************************************/
115{
120
123
124 auto seed = mInitialRNGSeed;
125 if (seed == -1) {
126 // Will use the mInitialRNGSeed if it was set.
127 // Otherwise will seed the generator with the state of
128 // TRandom::GetSeed. This is the seed that is influenced from
129 // SimConfig --seed command line options options.
130 seed = gRandom->TRandom::GetSeed(); // this uses the "original" seed
131 // we advance the seed by one so that the next Pythia8 generator gets a different value
132 if (mThisPythia8InstanceID > 0) {
133 gRandom->Rndm();
134 LOG(info) << "Multiple Pythia8 generator instances detected .. automatically adjusting seed further to avoid overlap ";
135 seed = seed ^ gRandom->GetSeed(); // this uses the "current" seed
136 }
137 // apply max seed cuttof
138 seed = seed % (MAX_SEED + 1);
139 LOG(info) << "GeneratorPythia8: Using random seed from gRandom % 900000001: " << seed;
140 }
141 mPythia.readString("Random:setSeed on");
142 mPythia.readString("Random:seed " + std::to_string(seed));
143}
144
145/*****************************************************************/
146
148{
153
159
161 if (!mConfig.empty()) {
162 std::stringstream ss(mConfig);
163 std::string config;
164 while (getline(ss, config, ' ')) {
165 TString expandedConfig = config;
166 gSystem->ExpandPathName(expandedConfig);
167 config = expandedConfig.Data();
168 LOG(info) << "Reading configuration from file: " << config;
169 if (!mPythia.readFile(config, true)) {
170 LOG(fatal) << "Failed to init \'Pythia8\': problems with configuration file "
171 << config;
172 return false;
173 }
174 }
175 }
176
178 if (!mHooksFileName.empty()) {
179 LOG(info) << "Applying \'Pythia8\' user hooks: " << mHooksFileName << " -> " << mHooksFuncName;
180 auto hooks = o2::conf::GetFromMacro<Pythia8::UserHooks*>(mHooksFileName, mHooksFuncName, "Pythia8::UserHooks*", "pythia8_user_hooks");
181 if (!hooks) {
182 LOG(fatal) << "Failed to init \'Pythia8\': problem with user hooks configuration ";
183 return false;
184 }
185 setUserHooks(hooks);
186 }
187
188#if PYTHIA_VERSION_INTEGER < 8300
197 mPythia.readString("HadronLevel:Decay off");
198#endif
199 if (mPythia.settings.mode("Beams:frameType") == 4) {
200 // Hook for POWHEG
201 // Read in key POWHEG merging settings
202 int vetoMode = mPythia.settings.mode("POWHEG:veto");
203 int MPIvetoMode = mPythia.settings.mode("POWHEG:MPIveto");
204 bool loadHooks = (vetoMode > 0 || MPIvetoMode > 0);
205 // Add in user hooks for shower vetoing
206 std::shared_ptr<Pythia8::PowhegHooks> powhegHooks;
207 if (loadHooks) {
208 // Set ISR and FSR to start at the kinematical limit
209 if (vetoMode > 0) {
210 mPythia.readString("SpaceShower:pTmaxMatch = 2");
211 mPythia.readString("TimeShower:pTmaxMatch = 2");
212 }
213 // Set MPI to start at the kinematical limit
214 if (MPIvetoMode > 0) {
215 mPythia.readString("MultipartonInteractions:pTmaxMatch = 2");
216 }
217 powhegHooks = std::make_shared<Pythia8::PowhegHooks>();
218 mPythia.setUserHooksPtr((Pythia8::UserHooksPtr)powhegHooks);
219 }
220 }
222 mPythia.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440);
224 if (!mPythia.init()) {
225 LOG(fatal) << "Failed to init \'Pythia8\': init returned with error";
226 return false;
227 }
228
230
231 mIsInitialized = true;
232
234 return true;
235}
236
237/*****************************************************************/
238void GeneratorPythia8::setUserHooks(Pythia8::UserHooks* hooks)
239{
240#if PYTHIA_VERSION_INTEGER < 8300
241 mPythia.setUserHooksPtr(hooks);
242#else
243 mPythia.setUserHooksPtr(std::shared_ptr<Pythia8::UserHooks>(hooks));
244#endif
245}
246
247/*****************************************************************/
248
249Bool_t
251{
253 if (!mPythia.next()) {
254 return false;
255 }
256
257#if PYTHIA_VERSION_INTEGER < 8300
272 auto nParticles = mPythia.event.size();
273 for (int iparticle = 0; iparticle < nParticles; iparticle++) {
274 auto& aParticle = mPythia.event[iparticle];
275 aParticle.xProd(0.);
276 aParticle.yProd(0.);
277 aParticle.zProd(0.);
278 aParticle.tProd(0.);
279 }
280
282 if (!mPythia.moreDecays())
283 return false;
284#endif
285
287 return true;
288}
289
290/*****************************************************************/
292 const std::vector<int>& old2New,
293 size_t index,
294 std::vector<bool>& done,
295 GetRelatives getter,
296 SetRelatives setter,
297 FirstLastRelative firstLast,
298 const std::string& what,
299 const std::string& ind)
300{
301 // New index of this particle, or -1 if not kept. Index old2New directly:
302 // it is event-sized, and this is a recursive function called once per
303 // particle, so wrapping it in a by-value-capturing lambda copied the whole
304 // vector on every call -- O(N^2) in the event multiplicity, which dominated
305 // high-multiplicity PbPb generation.
306 int newIdx = old2New[index];
307 int hepmc = event[index].statusHepMC();
308
309 LOG(debug) << ind
310 << index << " -> "
311 << newIdx << " (" << hepmc << ") ";
312 if (done[index]) {
313 LOG(debug) << ind << " already done";
314 return;
315 }
316
317 // Our list of new relatives
318 using IdList = std::pair<int, int>;
319 constexpr int invalid = 0xFFFFFFF;
320 IdList newRelatives = std::make_pair(invalid, -invalid);
321
322 // Utility to add id
323 auto addId = [](IdList& l, size_t id) {
324 l.first = std::min(int(id), l.first);
325 l.second = std::max(int(id), l.second);
326 };
327
328 // Get particle and relatives
329 auto& particle = event[index];
330 auto relatives = getter(particle);
331
332 LOG(debug) << ind << " Check " << what << "s ["
333 << std::setw(3) << firstLast(particle).first << ","
334 << std::setw(3) << firstLast(particle).second << "] "
335 << relatives.size();
336
337 for (auto relativeIdx : relatives) {
338 int newRelative = old2New[relativeIdx];
339 if (newRelative >= 0) {
340 // If this relative is to be kept, then append to list of new
341 // relatives.
342 LOG(debug) << ind << " "
343 << what << " "
344 << relativeIdx << " -> "
345 << newRelative << " to be kept" << std::endl;
346 addId(newRelatives, newRelative);
347 continue;
348 }
349 LOG(debug) << ind << " "
350 << what << " "
351 << relativeIdx << " not to be kept "
352 << (done[relativeIdx] ? "already done" : "to be done")
353 << std::endl;
354
355 // Below is code for when the relative is not to be kept
356 auto& relative = event[relativeIdx];
357 if (not done[relativeIdx]) {
358 // IF the relative hasn't been processed yet, do so now
360 old2New, // Map from old to new
361 relativeIdx, // current particle index
362 done, // cache flag
363 getter, // get mother relatives
364 setter, // set mother relatives
365 firstLast, // get first and last
366 what, // what we're looking at
367 ind + " "); // Logging indent
368 }
369
370 // If this relative was already done, then get its relatives and
371 // add them to the list of new relatives.
372 auto grandRelatives = firstLast(relative);
373 int grandRelative1 = grandRelatives.first;
374 int grandRelative2 = grandRelatives.second;
375 assert(grandRelative1 != invalid);
376 assert(grandRelative2 != -invalid);
377 if (grandRelative1 > 0) {
378 addId(newRelatives, grandRelative1);
379 }
380 if (grandRelative2 > 0) {
381 addId(newRelatives, grandRelative2);
382 }
383 LOG(debug) << ind << " "
384 << what << " "
385 << relativeIdx << " gave new relatives "
386 << grandRelative1 << " -> " << grandRelative2;
387 }
388 LOG(debug) << ind << " Got "
389 << (newRelatives.second - newRelatives.first + 1) << " new "
390 << what << "s ";
391
392 if (newRelatives.first != invalid) {
393 // If the first relative is not invalid, then the second isn't
394 // either (possibly the same though).
395 int newRelative1 = newRelatives.first;
396 int newRelative2 = newRelatives.second;
397 setter(particle, newRelative1, newRelative2);
398 LOG(debug) << ind << " " << what << "s: "
399 << firstLast(particle).first << " ("
400 << newRelative1 << "),"
401 << firstLast(particle).second << " ("
402 << newRelative2 << ")";
403
404 } else {
405 setter(particle, 0, 0);
406 }
407 done[index] = true;
408}
409
410/*****************************************************************/
412{
413 // Mapping from old to new index.
414 std::vector<int> old2new(event.size(), -1);
415
416 // Particle 0 is a system particle, and we will skip that in the
417 // following.
418 size_t newId = 0;
419
420 // Loop over particles and store those we need
421 for (size_t i = 1; i < event.size(); i++) {
422 auto& particle = event[i];
423 if (select(particle)) {
424 ++newId;
425 old2new[i] = newId;
426 }
427 }
428
429 // First loop, investigate mothers - from the bottom
430 auto getMothers = [](const Pythia8::Particle& particle) { return particle.motherList(); };
431 auto setMothers = [](Pythia8::Particle& particle, int m1, int m2) { particle.mothers(m1, m2); };
432 auto firstLastMothers = [](const Pythia8::Particle& particle) { return std::make_pair(particle.mother1(), particle.mother2()); };
433
434 std::vector<bool> motherDone(event.size(), false);
435 for (size_t i = 1; i < event.size(); ++i) {
437 old2new, // Map from old to new
438 i, // current particle index
439 motherDone, // cache flag
440 getMothers, // get mother relatives
441 setMothers, // set mother relatives
442 firstLastMothers, // get first and last
443 "mother"); // what we're looking at
444 }
445
446 // Second loop, investigate daughters - from the top
447 auto getDaughters = [](const Pythia8::Particle& particle) {
448 // In case of |status|==13 (diffractive), we cannot use
449 // Pythia8::Particle::daughterList as it will give more than
450 // just the immediate daughters. In that cae, we do it
451 // ourselves.
452 if (std::abs(particle.status()) == 13) {
453 int d1 = particle.daughter1();
454 int d2 = particle.daughter2();
455 if (d1 == 0 and d2 == 0) {
456 return std::vector<int>();
457 }
458 if (d2 == 0) {
459 return std::vector<int>{d1};
460 }
461 if (d2 > d1) {
462 std::vector<int> ret(d2-d1+1);
463 std::iota(ret.begin(), ret.end(), d1);
464 return ret;
465 }
466 return std::vector<int>{d2,d1};
467 }
468 return particle.daughterList(); };
469 auto setDaughters = [](Pythia8::Particle& particle, int d1, int d2) { particle.daughters(d1, d2); };
470 auto firstLastDaughters = [](const Pythia8::Particle& particle) { return std::make_pair(particle.daughter1(), particle.daughter2()); };
471
472 std::vector<bool> daughterDone(event.size(), false);
473 for (size_t i = event.size() - 1; i > 0; --i) {
475 old2new, // Map from old to new
476 i, // current particle index
477 daughterDone, // cache flag
478 getDaughters, // get mother relatives
479 setDaughters, // set mother relatives
480 firstLastDaughters, // get first and last
481 "daughter"); // what we're looking at
482 }
483
484 // Make a pruned event
485 Pythia8::Event pruned;
486 pruned.init("Pruned event", &mPythia.particleData);
487 pruned.reset();
488
489 for (size_t i = 1; i < event.size(); i++) {
490 int newIdx = old2new[i];
491 if (newIdx < 0) {
492 continue;
493 }
494
495 auto particle = event[i];
496 int realIdx = pruned.append(particle);
497 assert(realIdx == newIdx);
498 }
499
500 // We may have that two or more mothers share some daughters, but
501 // that one or more mothers have more daughters than the other
502 // mothers, and hence not all daughters point back to all mothers.
503 // This can happen, for example, if a beam particle radiates
504 // on-shell particles before an interaction with any daughters
505 // from the other mothers. Thus, we need to take care of that or
506 // the event record will be corrupted.
507 //
508 // What we do is that for all particles, we look up the daughters.
509 // Then for each daughter, we check the mothers of those
510 // daughters. If this list of mothers include other mothers than
511 // the currently investigated mother, we must change the mothers
512 // of the currently investigated daughters.
513 using IdList = std::pair<int, int>;
514 // Utility to add id
515 auto addId = [](IdList& l, size_t id) {
516 l.first = std::min(int(id), l.first);
517 l.second = std::max(int(id), l.second);
518 };
519 constexpr int invalid = 0xFFFFFFF;
520
521 std::vector<bool> shareDone(pruned.size(), false);
522 for (size_t i = 1; i < pruned.size(); i++) {
523 if (shareDone[i]) {
524 continue;
525 }
526
527 auto& particle = pruned[i];
528 auto daughters = particle.daughterList();
529 IdList allDaughters = std::make_pair(invalid, -invalid);
530 IdList allMothers = std::make_pair(invalid, -invalid);
531 addId(allMothers, i);
532 for (auto daughterIdx : daughters) {
533 // Add this daughter to set of all daughters
534 addId(allDaughters, daughterIdx);
535 auto& daughter = pruned[daughterIdx];
536 auto otherMothers = daughter.motherList();
537 for (auto otherMotherIdx : otherMothers) {
538 // Add this mother to set of all mothers. That is, take all
539 // mothers of the current daughter of the current particle
540 // and store that. In this way, we register mothers that
541 // share a daughter with the current particle.
542 addId(allMothers, otherMotherIdx);
543 // We also need to take all the daughters of this shared
544 // mother and reister those.
545 auto& otherMother = pruned[otherMotherIdx];
546 int otherDaughter1 = otherMother.daughter1();
547 int otherDaughter2 = otherMother.daughter2();
548 if (otherDaughter1 > 0) {
549 addId(allDaughters, otherDaughter1);
550 }
551 if (otherDaughter2 > 0) {
552 addId(allDaughters, otherDaughter2);
553 }
554 }
555 // At this point, we have added all mothers of current
556 // daughter, and all daughters of those mothers.
557 }
558 // At this point, we have all mothers that share daughters with
559 // the current particle, and we have all of the daughters
560 // too.
561 //
562 // We can now update the daughter information on all mothers
563 int minDaughter = allDaughters.first;
564 int maxDaughter = allDaughters.second;
565 int minMother = allMothers.first;
566 int maxMother = allMothers.second;
567 if (minMother != invalid) {
568 // If first mother isn't invalid, then second isn't either
569 for (size_t motherIdx = minMother; motherIdx <= maxMother; //
570 motherIdx++) {
571 shareDone[motherIdx] = true;
572 if (minDaughter == invalid) {
573 pruned[motherIdx].daughters(0, 0);
574 } else {
575 pruned[motherIdx].daughters(minDaughter, maxDaughter);
576 }
577 }
578 }
579 if (minDaughter != invalid) {
580 // If least mother isn't invalid, then largest mother will not
581 // be invalid either.
582 for (size_t daughterIdx = minDaughter; daughterIdx <= maxDaughter; //
583 daughterIdx++) {
584 if (minMother == invalid) {
585 pruned[daughterIdx].mothers(0, 0);
586 } else {
587 pruned[daughterIdx].mothers(minMother, maxMother);
588 }
589 }
590 }
591 }
592 if (mGenConfig.verbose) {
593 LOG(info) << "Pythia event was pruned from " << event.size()
594 << " to " << pruned.size() << " particles";
595 }
596 // Assign our pruned event to the event passed in
597 event = pruned;
598}
599
600/*****************************************************************/
602{
603 mUserFilterFcn = [](Pythia8::Particle const&) -> bool { return true; };
604
605 std::string filter = mGenConfig.particleFilter;
606 if (filter.size() > 0) {
607 LOG(info) << "Initializing the callback for user-based particle pruning " << filter;
608 auto expandedFileName = o2::utils::expandShellVarsInFileName(filter);
609 if (std::filesystem::exists(expandedFileName)) {
610 // if the filter is in a file we will compile the hook on the fly
611 mUserFilterFcn = o2::conf::GetFromMacro<UserFilterFcn>(expandedFileName, "filterPythia()", "o2::eventgen::GeneratorPythia8::UserFilterFcn", "o2mc_pythia8_userfilter_hook");
612 LOG(info) << "Hook initialized from file " << expandedFileName;
613 } else {
614 // if it's not a file we interpret it as a C++ lambda string and JIT it directly;
615 LOG(error) << "Did not find a file " << expandedFileName << " ; Will not execute hook";
616 }
617 mApplyPruning = true;
618 }
619}
620
621/*****************************************************************/
622
623Bool_t
625{
628 // The right moment to filter out unwanted stuff (like parton-level
629 // event information) Here, we aim to filter out everything before
630 // hadronization with the motivation to reduce the size of the MC
631 // event record in the AOD.
632
633 std::function<bool(const Pythia8::Particle&)> partonSelect = [](const Pythia8::Particle&) { return true; };
634 bool includeParton = mGenConfig.includePartonEvent;
635 if (not includeParton) {
636
637 // Select pythia particles
638 partonSelect = [](const Pythia8::Particle& particle) {
639 switch (particle.statusHepMC()) {
640 case 1: // Final st
641 case 2: // Decayed
642 case 4: // Beam
643 return true;
644 }
645 // For example to keep diffractive particles
646 // if (particle.id() == 9902210) return true;
647 return false;
648 };
649 mApplyPruning = true;
650 }
651
652 if (mApplyPruning) {
653 auto finalSelect = [partonSelect, this](const Pythia8::Particle& p) { return partonSelect(p) && mUserFilterFcn(p); };
654 pruneEvent(event, finalSelect);
655 }
656
657 /* loop over particles */
658 auto nParticles = event.size();
659 for (Int_t iparticle = 1; iparticle < nParticles; iparticle++) {
660 // first particle is system
661 auto particle = event[iparticle];
662 auto pdg = particle.id();
663 auto st = o2::mcgenstatus::MCGenStatusEncoding(particle.statusHepMC(), //
664 particle.status()) //
666 mParticles.push_back(TParticle(pdg, // Particle type
667 st, // status
668 particle.mother1() - 1, // first mother
669 particle.mother2() - 1, // second mother
670 particle.daughter1() - 1, // first daughter
671 particle.daughter2() - 1, // second daughter
672 particle.px(), // X-momentum
673 particle.py(), // Y-momentum
674 particle.pz(), // Z-momentum
675 particle.e(), // Energy
676 particle.xProd(), // Production X
677 particle.yProd(), // Production Y
678 particle.zProd(), // Production Z
679 particle.tProd())); // Production t
680 mParticles.back().SetBit(ParticleStatus::kToBeDone, //
681 particle.statusHepMC() == 1);
682 }
683
685 return kTRUE;
686}
687
688/*****************************************************************/
689
691{
694
695 eventHeader->putInfo<std::string>(Key::generator, "pythia8");
696 eventHeader->putInfo<int>(Key::generatorVersion, PYTHIA_VERSION_INTEGER);
697 eventHeader->putInfo<std::string>(Key::processName, mPythia.info.name());
698 eventHeader->putInfo<int>(Key::processCode, mPythia.info.code());
699 eventHeader->putInfo<float>(Key::weight, mPythia.info.weight());
700
701 auto& info = mPythia.info;
702
703 eventHeader->putInfo<int>(Key::acceptedEvents, info.nAccepted());
704 eventHeader->putInfo<int>(Key::attemptedEvents, info.nTried());
705
706 // Set PDF information
707 eventHeader->putInfo<int>(Key::pdfParton1Id, info.id1pdf());
708 eventHeader->putInfo<int>(Key::pdfParton2Id, info.id2pdf());
709 eventHeader->putInfo<float>(Key::pdfX1, info.x1pdf());
710 eventHeader->putInfo<float>(Key::pdfX2, info.x2pdf());
711 eventHeader->putInfo<float>(Key::pdfScale, info.QFac());
712 eventHeader->putInfo<float>(Key::pdfXF1, info.pdf1());
713 eventHeader->putInfo<float>(Key::pdfXF2, info.pdf2());
714
715 // Set cross section
716 eventHeader->putInfo<float>(Key::xSection, info.sigmaGen() * 1e9);
717 eventHeader->putInfo<float>(Key::xSectionError, info.sigmaErr() * 1e9);
718
719 // Set event scale and nMPI
720 eventHeader->putInfo<float>(Key::eventScale, info.QRen());
721 eventHeader->putInfo<int>(Key::mpi, info.nMPI());
722
723 // Set weights (overrides cross-section for each weight)
724 size_t iw = 0;
725 auto xsecErr = info.weightContainerPtr->getTotalXsecErr();
726 for (auto w : info.weightContainerPtr->getTotalXsec()) {
727 std::string post = (iw == 0 ? "" : "_" + std::to_string(iw));
728 eventHeader->putInfo<float>(Key::weight + post, info.weightValueByIndex(iw));
729 eventHeader->putInfo<float>(Key::xSection + post, w * 1e9);
730 eventHeader->putInfo<float>(Key::xSectionError + post, xsecErr[iw] * 1e9);
731 iw++;
732 }
733
734#if PYTHIA_VERSION_INTEGER < 8300
735 auto hiinfo = mPythia.info.hiinfo;
736#else
737 auto hiinfo = mPythia.info.hiInfo;
738#endif
739
740 if (hiinfo) {
742 eventHeader->SetB(hiinfo->b());
743 eventHeader->putInfo<double>(Key::impactParameter, hiinfo->b());
745#if PYTHIA_VERSION_INTEGER >= 8310
746 eventHeader->putInfo<double>(Key::planeAngle, hiinfo->phi());
747#endif
748 auto bImp = hiinfo->b();
750 int nColl, nPart;
751 int nPartProtonProj, nPartNeutronProj, nPartProtonTarg, nPartNeutronTarg;
752 int nRemnProtonProj, nRemnNeutronProj, nRemnProtonTarg, nRemnNeutronTarg;
753 int nFreeNeutronProj, nFreeProtonProj, nFreeNeutronTarg, nFreeProtonTarg;
754 getNcoll(nColl);
755 getNpart(nPart);
756 getNpart(nPartProtonProj, nPartNeutronProj, nPartProtonTarg, nPartNeutronTarg);
757 getNremn(nRemnProtonProj, nRemnNeutronProj, nRemnProtonTarg, nRemnNeutronTarg);
758 getNfreeSpec(nFreeNeutronProj, nFreeProtonProj, nFreeNeutronTarg, nFreeProtonTarg);
759 eventHeader->putInfo<int>(Key::nColl, nColl);
760 // These are all non-HepMC3 fields - of limited use
761 eventHeader->putInfo<int>("Npart", nPart);
762 eventHeader->putInfo<int>("Npart_proj_p", nPartProtonProj);
763 eventHeader->putInfo<int>("Npart_proj_n", nPartNeutronProj);
764 eventHeader->putInfo<int>("Npart_targ_p", nPartProtonTarg);
765 eventHeader->putInfo<int>("Npart_targ_n", nPartNeutronTarg);
766 eventHeader->putInfo<int>("Nremn_proj_p", nRemnProtonProj);
767 eventHeader->putInfo<int>("Nremn_proj_n", nRemnNeutronProj);
768 eventHeader->putInfo<int>("Nremn_targ_p", nRemnProtonTarg);
769 eventHeader->putInfo<int>("Nremn_targ_n", nRemnNeutronTarg);
770 eventHeader->putInfo<int>("Nfree_proj_n", nFreeNeutronProj);
771 eventHeader->putInfo<int>("Nfree_proj_p", nFreeProtonProj);
772 eventHeader->putInfo<int>("Nfree_targ_n", nFreeNeutronTarg);
773 eventHeader->putInfo<int>("Nfree_targ_p", nFreeProtonTarg);
774
775 // --- HepMC3 conforming information ---
776 // This is how the Pythia authors define Ncoll
777 // eventHeader->putInfo<int>(Key::nColl,
778 // hiinfo->nAbsProj() + hiinfo->nDiffProj() +
779 // hiinfo->nAbsTarg() + hiinfo->nDiffTarg() -
780 // hiiinfo->nCollND() - hiinfo->nCollDD());
781 eventHeader->putInfo<int>(Key::nPartProjectile,
782 hiinfo->nAbsProj() + hiinfo->nDiffProj());
783 eventHeader->putInfo<int>(Key::nPartTarget,
784 hiinfo->nAbsTarg() + hiinfo->nDiffTarg());
785#if PYTHIA_VERSION_INTEGER >= 8313
786 eventHeader->putInfo<int>(Key::nCollHard, hiinfo->nCollND());
787#else
788 eventHeader->putInfo<int>(Key::nCollHard, hiinfo->nCollNDTot());
789#endif
790 }
791}
792
793/*****************************************************************/
794
795void GeneratorPythia8::selectFromAncestor(int ancestor, Pythia8::Event& inputEvent, Pythia8::Event& outputEvent)
796{
797
802 // recursive selection via lambda function
803 std::set<int> selected;
804 std::function<void(int)> select;
805 select = [&](int i) {
806 selected.insert(i);
807 auto dl = inputEvent[i].daughterList();
808 for (auto j : dl) {
809 select(j);
810 }
811 };
812 select(ancestor);
813
814 // map selected particle index to output index
815 std::map<int, int> indexMap;
816 int index = outputEvent.size();
817 for (auto i : selected) {
818 indexMap[i] = index++;
819 }
820
821 // adjust mother/daughter indices and append to output event
822 for (auto i : selected) {
823 auto p = mPythia.event[i];
824 auto m1 = indexMap[p.mother1()];
825 auto m2 = indexMap[p.mother2()];
826 auto d1 = indexMap[p.daughter1()];
827 auto d2 = indexMap[p.daughter2()];
828 p.mothers(m1, m2);
829 p.daughters(d1, d2);
830
831 outputEvent.append(p);
832 }
833}
834
835/*****************************************************************/
836
837void GeneratorPythia8::getNcoll(const Pythia8::Info& info, int& nColl)
838{
839
842#if PYTHIA_VERSION_INTEGER < 8300
843 auto hiinfo = info.hiinfo;
844#else
845 auto hiinfo = info.hiInfo;
846#endif
847 nColl = 0;
848 if (!hiinfo) {
849 LOG(warn) << "No heavy-ion information from Pythia";
850 return;
851 }
852
853 // This is how the Pythia authors define Ncoll
854 nColl = (hiinfo->nAbsProj() + hiinfo->nDiffProj() +
855 hiinfo->nAbsTarg() + hiinfo->nDiffTarg() -
856 hiinfo->nCollND() - hiinfo->nCollDD());
857
858 if (not hiinfo->subCollisionsPtr()) {
859#if PYTHIA_VERSION_INTEGER < 8310
860 LOG(fatal) << "No sub-collision pointer from Pythia";
861#endif
862 return;
863 }
864
865 // loop over sub-collisions
866 auto scptr = hiinfo->subCollisionsPtr();
867 for (auto sc : *scptr) {
868
869 // wounded nucleon flag in projectile/target
870 auto pW = sc.proj->status() == Pythia8::Nucleon::ABS; // according to C.Bierlich this should be == Nucleon::ABS
871 auto tW = sc.targ->status() == Pythia8::Nucleon::ABS;
872
873 // increase number of collisions if both are wounded
874 if (pW && tW) {
875 nColl++;
876 }
877 }
878}
879
880/*****************************************************************/
881
882void GeneratorPythia8::getNpart(const Pythia8::Info& info, int& nPart)
883{
884
887#if PYTHIA_VERSION_INTEGER < 8300
888 auto hiinfo = info.hiinfo;
889#else
890 auto hiinfo = info.hiInfo;
891#endif
892 nPart = 0;
893 if (not hiinfo) {
894 return;
895 }
896
897 // This is how the Pythia authors calculate Npart
898 nPart = (hiinfo->nAbsProj() + hiinfo->nDiffProj() +
899 hiinfo->nAbsTarg() + hiinfo->nDiffTarg());
900 if (not hiinfo->subCollisionsPtr()) {
901 return;
902 }
903
904 int nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg;
905 getNpart(info, nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg);
906 nPart = nProtonProj + nNeutronProj + nProtonTarg + nNeutronTarg;
907}
908
909/*****************************************************************/
910
911void GeneratorPythia8::getNpart(const Pythia8::Info& info, int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg)
912{
913
916#if PYTHIA_VERSION_INTEGER < 8300
917 auto hiinfo = info.hiinfo;
918#else
919 auto hiinfo = info.hiInfo;
920#endif
921
922 nProtonProj = nNeutronProj = nProtonTarg = nNeutronTarg = 0;
923 if (!hiinfo) {
924 return;
925 }
926
927 nProtonProj = hiinfo->nAbsProj() + hiinfo->nDiffProj();
928 nProtonTarg = hiinfo->nAbsTarg() + hiinfo->nDiffTarg();
929 if (not hiinfo->subCollisionsPtr()) {
930#if PYTHIA_VERSION_INTEGER < 8310
931 LOG(fatal) << "No sub-collision pointer from Pythia";
932#endif
933 return;
934 }
935
936 // keep track of wounded nucleons
937 std::vector<Pythia8::Nucleon*> projW;
938 std::vector<Pythia8::Nucleon*> targW;
939
940 // loop over sub-collisions
941 auto scptr = hiinfo->subCollisionsPtr();
942 for (auto sc : *scptr) {
943
944 // wounded nucleon flag in projectile/target
945 auto pW = sc.proj->status() == Pythia8::Nucleon::ABS || sc.proj->status() == Pythia8::Nucleon::DIFF; // according to C.Bierlich this should be == Nucleon::ABS || Nucleon::DIFF
946 auto tW = sc.targ->status() == Pythia8::Nucleon::ABS || sc.targ->status() == Pythia8::Nucleon::DIFF;
947
948 // increase number of wounded projectile nucleons if not yet in the wounded vector
949 if (pW && std::find(projW.begin(), projW.end(), sc.proj) == projW.end()) {
950 projW.push_back(sc.proj);
951 if (sc.proj->id() == 2212) {
952 nProtonProj++;
953 } else if (sc.proj->id() == 2112) {
954 nNeutronProj++;
955 }
956 }
957
958 // increase number of wounded target nucleons if not yet in the wounded vector
959 if (tW && std::find(targW.begin(), targW.end(), sc.targ) == targW.end()) {
960 targW.push_back(sc.targ);
961 if (sc.targ->id() == 2212) {
962 nProtonTarg++;
963 } else if (sc.targ->id() == 2112) {
964 nNeutronTarg++;
965 }
966 }
967 }
968}
969
970/*****************************************************************/
971
972void GeneratorPythia8::getNremn(const Pythia8::Event& event, int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg)
973{
974
977 // reset
978 nProtonProj = nNeutronProj = nProtonTarg = nNeutronTarg = 0;
979 auto nNucRem = 0;
980
981 // particle loop
982 auto nparticles = event.size();
983 for (int ipa = 0; ipa < nparticles; ++ipa) {
984 const auto particle = event[ipa];
985 auto pdg = particle.id();
986
987 // nuclear remnants have pdg code = ±10LZZZAAA9
988 if (pdg < 1000000000) {
989 continue; // must be nucleus
990 }
991 if (pdg % 10 != 9) {
992 continue; // first digit must be 9
993 }
994 nNucRem++;
995
996 // extract A, Z and L from pdg code
997 pdg /= 10;
998 auto A = pdg % 1000;
999 pdg /= 1000;
1000 auto Z = pdg % 1000;
1001 pdg /= 1000;
1002 auto L = pdg % 10;
1003
1004 if (particle.pz() > 0.) {
1005 nProtonProj = Z;
1006 nNeutronProj = A - Z;
1007 }
1008 if (particle.pz() < 0.) {
1009 nProtonTarg = Z;
1010 nNeutronTarg = A - Z;
1011 }
1012
1013 } // end of particle loop
1014
1015 if (nNucRem > 2) {
1016 LOG(warning) << " GeneratorPythia8: found more than two nuclear remnants (weird)";
1017 }
1018}
1019/*****************************************************************/
1020
1021/*****************************************************************/
1022
1023void GeneratorPythia8::getNfreeSpec(const Pythia8::Info& info, int& nFreenProj, int& nFreepProj, int& nFreenTarg, int& nFreepTarg)
1024{
1027#if PYTHIA_VERSION_INTEGER < 8300
1028 auto hiinfo = info.hiinfo;
1029#else
1030 auto hiinfo = info.hiInfo;
1031#endif
1032
1033 if (!hiinfo) {
1034 return;
1035 }
1036
1037 double b = hiinfo->b();
1038
1039 static o2::zdc::FragmentParam frag; // data-driven model to get free spectators given impact parameter
1040
1041 TF1 const& fneutrons = frag.getfNeutrons();
1042 TF1 const& fsigman = frag.getsigmaNeutrons();
1043 TF1 const& fprotons = frag.getfProtons();
1044 TF1 const& fsigmap = frag.getsigmaProtons();
1045
1046 // Calculating no. of free spectators from parametrization
1047 int nneu[2] = {0, 0};
1048 for (int i = 0; i < 2; i++) {
1049 float nave = fneutrons.Eval(b);
1050 float sigman = fsigman.Eval(b);
1051 float nfree = gRandom->Gaus(nave, 0.68 * sigman * nave);
1052 nneu[i] = (int)nfree;
1053 if (nave < 0 || nneu[i] < 0) {
1054 nneu[i] = 0;
1055 }
1056 if (nneu[i] > 126) {
1057 nneu[i] = 126;
1058 }
1059 }
1060 //
1061 int npro[2] = {0, 0};
1062 for (int i = 0; i < 2; i++) {
1063 float pave = fprotons.Eval(b);
1064 float sigmap = fsigman.Eval(b);
1065 float pfree = gRandom->Gaus(pave, 0.68 * sigmap * pave) / 0.7;
1066 npro[i] = (int)pfree;
1067 if (pave < 0 || npro[i] < 0) {
1068 npro[i] = 0;
1069 }
1070 if (npro[i] > 82) {
1071 npro[i] = 82;
1072 }
1073 }
1074
1075 nFreenProj = nneu[0];
1076 nFreenTarg = nneu[1];
1077 nFreepProj = npro[0];
1078 nFreepTarg = npro[1];
1079 /*****************************************************************/
1080}
1081
1082} /* namespace eventgen */
1083} /* namespace o2 */
o2::monitoring::tags::Key Key
std::ostringstream debug
int32_t i
bool done
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered disabling the noisy pad check for that pad Treat signal above this minimum as saturated Smoothing factor for the exponential Highly Ionising Particle tail filter Number of timebins to use for the averaging Add additional custom track errors when crossing relative as fraction of Do N fit passes in final fit of< 0:reject at max -n hits") AddOptionRTC(rejectIFCLowRadiusCluster, uint8_t, 1, "", 0, "Reject clusters that get the IFC mask error during refit") AddOptionRTC(dEdxTruncLow, uint8_t, 2, "", 0, "Low truncation threshold, fraction of 128") AddOptionRTC(dEdxTruncHigh, uint8_t, 77, "", 0, "High truncation threshold, fraction of 128") AddOptionRTC(extrapolationTracking, int8_t, 1, "", 0, "Enable Extrapolation Tracking(prolong tracks to adjacent sectors to find short segments)") AddOptionRTC(disableRefitAttachment, uint8_t, 0, "", 0, "Bitmask to disable certain attachment steps during refit(1:attachment, 2:propagation, 4:loop following, 8:mirroring)") AddOptionRTC(rejectionStrategy, uint8_t, o2::gpu::GPUSettings::RejectionStrategyA, "", 0, "Enable rejection of TPC clusters for compression(0=no, 1=strategy A, 2=strategy B)") AddOptionRTC(mergeLoopersAfterburner, uint8_t, 1, "", 0, "Run afterburner for additional looper merging") AddOptionRTC(compressionTypeMask, uint8_t, o2::gpu::GPUSettings::CompressionFull, "", 0, "TPC Compression mode bits(1=truncate charge/width LSB, 2=differences, 4=track-model)") AddOptionRTC(compressionSortOrder, uint8_t, o2::gpu::GPUSettings::SortTime, "", 0, "Sort order of TPC compression(0=time, 1=pad, 2=Z-time-pad, 3=Z-pad-time, 4=no sorting(use incoming order))") AddOptionRTC(sigBitsCharge, uint8_t, 4, "", 0, "Number of significant bits for TPC cluster charge in compression mode 1") AddOptionRTC(sigBitsWidth, uint8_t, 3, "", 0, "Number of significant bits for TPC cluster width in compression mode 1") AddOptionRTC(dropLoopers, uint8_t, 0, "", 0, "Drop looping tracks starting from second loop") AddOptionRTC(mergerInterpolateErrors, uint8_t, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") AddOptionRTC(mergerInterpolateRejectAlsoOnCurrentPosition, uint8_t, 1, "", 0, "When using mergerInterpolateErrors, reject based on chi2 twice computed with interpolated and current track position starting from NDF > mergerNonInterpolateRejectMinNDF Merge tracks accross the central electrode Enable PID response Add sub threshold clusters in TPC dEdx computation OR mask of TPC flags that will reject the cluster in alternative dEdx Reject edge clusters based on uncorrected track Y during track fit Increase decrease removal by when on this number of pads from the edge h configStandalone Reconstruction rec_trd Max chi2 for TRD tracklets to be matched to a track Chi2 cut for strict matching mode Number of sigmas for ITS TPC track time error estimate Addition to search road around track prolongation along Y in cm Optimal tracklet rphi resolution in maximum chi2 per attached tracklet for TRD tracks take into account the full likelihood of the slope including exponential tails Tracks with less attached tracklets are discarded after the tracking Post trigger Pile up integration time in BCs fits all iterations but the last one in projections Use material lookup table for TPC refit Apply workarounds to avoid cyclic merge should not be needed tpc dyn configStandalone Processing proc_rtc bool
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered disabling the noisy pad check for that pad Treat signal above this minimum as saturated Smoothing factor for the exponential Highly Ionising Particle tail filter Number of timebins to use for the averaging Add additional custom track errors when crossing relative as fraction of Do N fit passes in final fit of< 0:reject at max -n hits") AddOptionRTC(rejectIFCLowRadiusCluster, uint8_t, 1, "", 0, "Reject clusters that get the IFC mask error during refit") AddOptionRTC(dEdxTruncLow, uint8_t, 2, "", 0, "Low truncation threshold, fraction of 128") AddOptionRTC(dEdxTruncHigh, uint8_t, 77, "", 0, "High truncation threshold, fraction of 128") AddOptionRTC(extrapolationTracking, int8_t, 1, "", 0, "Enable Extrapolation Tracking(prolong tracks to adjacent sectors to find short segments)") AddOptionRTC(disableRefitAttachment, uint8_t, 0, "", 0, "Bitmask to disable certain attachment steps during refit(1:attachment, 2:propagation, 4:loop following, 8:mirroring)") AddOptionRTC(rejectionStrategy, uint8_t, o2::gpu::GPUSettings::RejectionStrategyA, "", 0, "Enable rejection of TPC clusters for compression(0=no, 1=strategy A, 2=strategy B)") AddOptionRTC(mergeLoopersAfterburner, uint8_t, 1, "", 0, "Run afterburner for additional looper merging") AddOptionRTC(compressionTypeMask, uint8_t, o2::gpu::GPUSettings::CompressionFull, "", 0, "TPC Compression mode bits(1=truncate charge/width LSB, 2=differences, 4=track-model)") AddOptionRTC(compressionSortOrder, uint8_t, o2::gpu::GPUSettings::SortTime, "", 0, "Sort order of TPC compression(0=time, 1=pad, 2=Z-time-pad, 3=Z-pad-time, 4=no sorting(use incoming order))") AddOptionRTC(sigBitsCharge, uint8_t, 4, "", 0, "Number of significant bits for TPC cluster charge in compression mode 1") AddOptionRTC(sigBitsWidth, uint8_t, 3, "", 0, "Number of significant bits for TPC cluster width in compression mode 1") AddOptionRTC(dropLoopers, uint8_t, 0, "", 0, "Drop looping tracks starting from second loop") AddOptionRTC(mergerInterpolateErrors, uint8_t, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") AddOptionRTC(mergerInterpolateRejectAlsoOnCurrentPosition, uint8_t, 1, "", 0, "When using mergerInterpolateErrors, reject based on chi2 twice computed with interpolated and current track position starting from NDF > mergerNonInterpolateRejectMinNDF Merge tracks accross the central electrode Enable PID response Add sub threshold clusters in TPC dEdx computation OR mask of TPC flags that will reject the cluster in alternative dEdx Reject edge clusters based on uncorrected track Y during track fit Increase decrease removal by when on this number of pads from the edge h configStandalone Reconstruction rec_trd Max chi2 for TRD tracklets to be matched to a track Chi2 cut for strict matching mode Number of sigmas for ITS TPC track time error estimate Addition to search road around track prolongation along Y in cm Optimal tracklet rphi resolution in maximum chi2 per attached tracklet for TRD tracks take into account the full likelihood of the slope including exponential tails Tracks with less attached tracklets are discarded after the tracking Post trigger Pile up integration time in BCs and
char error[CURL_ERROR_SIZE]
@ kToBeDone
uint32_t j
Definition RawData.h:0
benchmark::State & st
Definition A.h:16
void SetB(Double_t b)
void putInfo(std::string const &key, T const &value)
void setHooksFuncName(std::string val)
void selectFromAncestor(int ancestor, Pythia8::Event &inputEvent, Pythia8::Event &outputEvent)
void pruneEvent(Pythia8::Event &event, Select select)
static std::atomic< int > Pythia8InstanceCounter
void setHooksFileName(std::string val)
void investigateRelatives(Pythia8::Event &event, const std::vector< int > &old2New, size_t index, std::vector< bool > &done, GetRelatives getter, SetRelatives setter, FirstLastRelative firstLast, const std::string &what, const std::string &ind="")
void getNremn(int &nProtonProj, int &nNeutronProj, int &nProtonTarg, int &nNeutronTarg)
void seedGenerator()
performs seeding of the random state of Pythia (called from Init)
void setUserHooks(Pythia8::UserHooks *hooks)
void updateHeader(o2::dataformats::MCEventHeader *eventHeader) override
void getNfreeSpec(int &nFreenProj, int &nFreepProj, int &nFreenTarg, int &nFreepTarg)
void setConfig(std::string val)
std::string mInterfaceName
Definition Generator.h:141
std::vector< TParticle > mParticles
Definition Generator.h:161
Bool_t Init() override
TF1 const & getfProtons() const
TF1 const & getfNeutrons() const
TF1 const & getsigmaNeutrons() const
TF1 const & getsigmaProtons() const
struct _cl_event * event
Definition glcorearb.h:2982
GLuint index
Definition glcorearb.h:781
GLuint const GLchar * name
Definition glcorearb.h:781
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition glcorearb.h:1308
GLubyte GLubyte GLubyte GLubyte w
Definition glcorearb.h:852
GLuint id
Definition glcorearb.h:650
std::vector< InputSpec > select(char const *matcher="")
std::string expandShellVarsInFileName(std::string const &input)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"