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
@ kToBeDone
uint32_t j
Definition RawData.h:0
benchmark::State & st
Definition A.h:16
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:131
std::vector< TParticle > mParticles
Definition Generator.h:151
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"