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 LOG(info) << "Instance \'Pythia8\' generator with following parameters";
70 LOG(info) << "config: " << config.config;
71 LOG(info) << "hooksFileName: " << config.hooksFileName;
72 LOG(info) << "hooksFuncName: " << config.hooksFuncName;
73
74 mGenConfig = config;
75
79}
80
81/*****************************************************************/
82
83GeneratorPythia8::GeneratorPythia8(const Char_t* name, const Char_t* title) : Generator(name, title)
84{
87 mInterface = reinterpret_cast<void*>(&mPythia);
88 mInterfaceName = "pythia8";
89}
90
92{
93 // check first of all if Init not yet called and seed not <0
94 if (mIsInitialized) {
95 return false;
96 }
97 if (seed < 0) {
98 return false;
99 }
100 // sets the initial seed and applies the correct Pythia
101 // range
102 mInitialRNGSeed = seed % (MAX_SEED + 1);
103 LOG(info) << "GeneratorPythia8: Setting initial seed to " << mInitialRNGSeed;
104 return true;
105}
106
107/*****************************************************************/
109{
114
117
118 auto seed = mInitialRNGSeed;
119 if (seed == -1) {
120 // Will use the mInitialRNGSeed if it was set.
121 // Otherwise will seed the generator with the state of
122 // TRandom::GetSeed. This is the seed that is influenced from
123 // SimConfig --seed command line options options.
124 seed = gRandom->TRandom::GetSeed(); // this uses the "original" seed
125 // we advance the seed by one so that the next Pythia8 generator gets a different value
126 if (mThisPythia8InstanceID > 0) {
127 gRandom->Rndm();
128 LOG(info) << "Multiple Pythia8 generator instances detected .. automatically adjusting seed further to avoid overlap ";
129 seed = seed ^ gRandom->GetSeed(); // this uses the "current" seed
130 }
131 // apply max seed cuttof
132 seed = seed % (MAX_SEED + 1);
133 LOG(info) << "GeneratorPythia8: Using random seed from gRandom % 900000001: " << seed;
134 }
135 mPythia.readString("Random:setSeed on");
136 mPythia.readString("Random:seed " + std::to_string(seed));
137}
138
139/*****************************************************************/
140
142{
147
153
155 if (!mConfig.empty()) {
156 std::stringstream ss(mConfig);
157 std::string config;
158 while (getline(ss, config, ' ')) {
159 TString expandedConfig = config;
160 gSystem->ExpandPathName(expandedConfig);
161 config = expandedConfig.Data();
162 LOG(info) << "Reading configuration from file: " << config;
163 if (!mPythia.readFile(config, true)) {
164 LOG(fatal) << "Failed to init \'Pythia8\': problems with configuration file "
165 << config;
166 return false;
167 }
168 }
169 }
170
172 if (!mHooksFileName.empty()) {
173 LOG(info) << "Applying \'Pythia8\' user hooks: " << mHooksFileName << " -> " << mHooksFuncName;
174 auto hooks = o2::conf::GetFromMacro<Pythia8::UserHooks*>(mHooksFileName, mHooksFuncName, "Pythia8::UserHooks*", "pythia8_user_hooks");
175 if (!hooks) {
176 LOG(fatal) << "Failed to init \'Pythia8\': problem with user hooks configuration ";
177 return false;
178 }
179 setUserHooks(hooks);
180 }
181
182#if PYTHIA_VERSION_INTEGER < 8300
191 mPythia.readString("HadronLevel:Decay off");
192#endif
193 if (mPythia.settings.mode("Beams:frameType") == 4) {
194 // Hook for POWHEG
195 // Read in key POWHEG merging settings
196 int vetoMode = mPythia.settings.mode("POWHEG:veto");
197 int MPIvetoMode = mPythia.settings.mode("POWHEG:MPIveto");
198 bool loadHooks = (vetoMode > 0 || MPIvetoMode > 0);
199 // Add in user hooks for shower vetoing
200 std::shared_ptr<Pythia8::PowhegHooks> powhegHooks;
201 if (loadHooks) {
202 // Set ISR and FSR to start at the kinematical limit
203 if (vetoMode > 0) {
204 mPythia.readString("SpaceShower:pTmaxMatch = 2");
205 mPythia.readString("TimeShower:pTmaxMatch = 2");
206 }
207 // Set MPI to start at the kinematical limit
208 if (MPIvetoMode > 0) {
209 mPythia.readString("MultipartonInteractions:pTmaxMatch = 2");
210 }
211 powhegHooks = std::make_shared<Pythia8::PowhegHooks>();
212 mPythia.setUserHooksPtr((Pythia8::UserHooksPtr)powhegHooks);
213 }
214 }
216 mPythia.particleData.addParticle(1000100200, "20Ne", 6, 30, 0, 19.992440);
218 if (!mPythia.init()) {
219 LOG(fatal) << "Failed to init \'Pythia8\': init returned with error";
220 return false;
221 }
222
224
225 mIsInitialized = true;
226
228 return true;
229}
230
231/*****************************************************************/
232void GeneratorPythia8::setUserHooks(Pythia8::UserHooks* hooks)
233{
234#if PYTHIA_VERSION_INTEGER < 8300
235 mPythia.setUserHooksPtr(hooks);
236#else
237 mPythia.setUserHooksPtr(std::shared_ptr<Pythia8::UserHooks>(hooks));
238#endif
239}
240
241/*****************************************************************/
242
243Bool_t
245{
247 if (!mPythia.next()) {
248 return false;
249 }
250
251#if PYTHIA_VERSION_INTEGER < 8300
266 auto nParticles = mPythia.event.size();
267 for (int iparticle = 0; iparticle < nParticles; iparticle++) {
268 auto& aParticle = mPythia.event[iparticle];
269 aParticle.xProd(0.);
270 aParticle.yProd(0.);
271 aParticle.zProd(0.);
272 aParticle.tProd(0.);
273 }
274
276 if (!mPythia.moreDecays())
277 return false;
278#endif
279
281 return true;
282}
283
284/*****************************************************************/
286 const std::vector<int>& old2New,
287 size_t index,
288 std::vector<bool>& done,
289 GetRelatives getter,
290 SetRelatives setter,
291 FirstLastRelative firstLast,
292 const std::string& what,
293 const std::string& ind)
294{
295 // New index of this particle, or -1 if not kept. Index old2New directly:
296 // it is event-sized, and this is a recursive function called once per
297 // particle, so wrapping it in a by-value-capturing lambda copied the whole
298 // vector on every call -- O(N^2) in the event multiplicity, which dominated
299 // high-multiplicity PbPb generation.
300 int newIdx = old2New[index];
301 int hepmc = event[index].statusHepMC();
302
303 LOG(debug) << ind
304 << index << " -> "
305 << newIdx << " (" << hepmc << ") ";
306 if (done[index]) {
307 LOG(debug) << ind << " already done";
308 return;
309 }
310
311 // Our list of new relatives
312 using IdList = std::pair<int, int>;
313 constexpr int invalid = 0xFFFFFFF;
314 IdList newRelatives = std::make_pair(invalid, -invalid);
315
316 // Utility to add id
317 auto addId = [](IdList& l, size_t id) {
318 l.first = std::min(int(id), l.first);
319 l.second = std::max(int(id), l.second);
320 };
321
322 // Get particle and relatives
323 auto& particle = event[index];
324 auto relatives = getter(particle);
325
326 LOG(debug) << ind << " Check " << what << "s ["
327 << std::setw(3) << firstLast(particle).first << ","
328 << std::setw(3) << firstLast(particle).second << "] "
329 << relatives.size();
330
331 for (auto relativeIdx : relatives) {
332 int newRelative = old2New[relativeIdx];
333 if (newRelative >= 0) {
334 // If this relative is to be kept, then append to list of new
335 // relatives.
336 LOG(debug) << ind << " "
337 << what << " "
338 << relativeIdx << " -> "
339 << newRelative << " to be kept" << std::endl;
340 addId(newRelatives, newRelative);
341 continue;
342 }
343 LOG(debug) << ind << " "
344 << what << " "
345 << relativeIdx << " not to be kept "
346 << (done[relativeIdx] ? "already done" : "to be done")
347 << std::endl;
348
349 // Below is code for when the relative is not to be kept
350 auto& relative = event[relativeIdx];
351 if (not done[relativeIdx]) {
352 // IF the relative hasn't been processed yet, do so now
354 old2New, // Map from old to new
355 relativeIdx, // current particle index
356 done, // cache flag
357 getter, // get mother relatives
358 setter, // set mother relatives
359 firstLast, // get first and last
360 what, // what we're looking at
361 ind + " "); // Logging indent
362 }
363
364 // If this relative was already done, then get its relatives and
365 // add them to the list of new relatives.
366 auto grandRelatives = firstLast(relative);
367 int grandRelative1 = grandRelatives.first;
368 int grandRelative2 = grandRelatives.second;
369 assert(grandRelative1 != invalid);
370 assert(grandRelative2 != -invalid);
371 if (grandRelative1 > 0) {
372 addId(newRelatives, grandRelative1);
373 }
374 if (grandRelative2 > 0) {
375 addId(newRelatives, grandRelative2);
376 }
377 LOG(debug) << ind << " "
378 << what << " "
379 << relativeIdx << " gave new relatives "
380 << grandRelative1 << " -> " << grandRelative2;
381 }
382 LOG(debug) << ind << " Got "
383 << (newRelatives.second - newRelatives.first + 1) << " new "
384 << what << "s ";
385
386 if (newRelatives.first != invalid) {
387 // If the first relative is not invalid, then the second isn't
388 // either (possibly the same though).
389 int newRelative1 = newRelatives.first;
390 int newRelative2 = newRelatives.second;
391 setter(particle, newRelative1, newRelative2);
392 LOG(debug) << ind << " " << what << "s: "
393 << firstLast(particle).first << " ("
394 << newRelative1 << "),"
395 << firstLast(particle).second << " ("
396 << newRelative2 << ")";
397
398 } else {
399 setter(particle, 0, 0);
400 }
401 done[index] = true;
402}
403
404/*****************************************************************/
406{
407 // Mapping from old to new index.
408 std::vector<int> old2new(event.size(), -1);
409
410 // Particle 0 is a system particle, and we will skip that in the
411 // following.
412 size_t newId = 0;
413
414 // Loop over particles and store those we need
415 for (size_t i = 1; i < event.size(); i++) {
416 auto& particle = event[i];
417 if (select(particle)) {
418 ++newId;
419 old2new[i] = newId;
420 }
421 }
422
423 // First loop, investigate mothers - from the bottom
424 auto getMothers = [](const Pythia8::Particle& particle) { return particle.motherList(); };
425 auto setMothers = [](Pythia8::Particle& particle, int m1, int m2) { particle.mothers(m1, m2); };
426 auto firstLastMothers = [](const Pythia8::Particle& particle) { return std::make_pair(particle.mother1(), particle.mother2()); };
427
428 std::vector<bool> motherDone(event.size(), false);
429 for (size_t i = 1; i < event.size(); ++i) {
431 old2new, // Map from old to new
432 i, // current particle index
433 motherDone, // cache flag
434 getMothers, // get mother relatives
435 setMothers, // set mother relatives
436 firstLastMothers, // get first and last
437 "mother"); // what we're looking at
438 }
439
440 // Second loop, investigate daughters - from the top
441 auto getDaughters = [](const Pythia8::Particle& particle) {
442 // In case of |status|==13 (diffractive), we cannot use
443 // Pythia8::Particle::daughterList as it will give more than
444 // just the immediate daughters. In that cae, we do it
445 // ourselves.
446 if (std::abs(particle.status()) == 13) {
447 int d1 = particle.daughter1();
448 int d2 = particle.daughter2();
449 if (d1 == 0 and d2 == 0) {
450 return std::vector<int>();
451 }
452 if (d2 == 0) {
453 return std::vector<int>{d1};
454 }
455 if (d2 > d1) {
456 std::vector<int> ret(d2-d1+1);
457 std::iota(ret.begin(), ret.end(), d1);
458 return ret;
459 }
460 return std::vector<int>{d2,d1};
461 }
462 return particle.daughterList(); };
463 auto setDaughters = [](Pythia8::Particle& particle, int d1, int d2) { particle.daughters(d1, d2); };
464 auto firstLastDaughters = [](const Pythia8::Particle& particle) { return std::make_pair(particle.daughter1(), particle.daughter2()); };
465
466 std::vector<bool> daughterDone(event.size(), false);
467 for (size_t i = event.size() - 1; i > 0; --i) {
469 old2new, // Map from old to new
470 i, // current particle index
471 daughterDone, // cache flag
472 getDaughters, // get mother relatives
473 setDaughters, // set mother relatives
474 firstLastDaughters, // get first and last
475 "daughter"); // what we're looking at
476 }
477
478 // Make a pruned event
479 Pythia8::Event pruned;
480 pruned.init("Pruned event", &mPythia.particleData);
481 pruned.reset();
482
483 for (size_t i = 1; i < event.size(); i++) {
484 int newIdx = old2new[i];
485 if (newIdx < 0) {
486 continue;
487 }
488
489 auto particle = event[i];
490 int realIdx = pruned.append(particle);
491 assert(realIdx == newIdx);
492 }
493
494 // We may have that two or more mothers share some daughters, but
495 // that one or more mothers have more daughters than the other
496 // mothers, and hence not all daughters point back to all mothers.
497 // This can happen, for example, if a beam particle radiates
498 // on-shell particles before an interaction with any daughters
499 // from the other mothers. Thus, we need to take care of that or
500 // the event record will be corrupted.
501 //
502 // What we do is that for all particles, we look up the daughters.
503 // Then for each daughter, we check the mothers of those
504 // daughters. If this list of mothers include other mothers than
505 // the currently investigated mother, we must change the mothers
506 // of the currently investigated daughters.
507 using IdList = std::pair<int, int>;
508 // Utility to add id
509 auto addId = [](IdList& l, size_t id) {
510 l.first = std::min(int(id), l.first);
511 l.second = std::max(int(id), l.second);
512 };
513 constexpr int invalid = 0xFFFFFFF;
514
515 std::vector<bool> shareDone(pruned.size(), false);
516 for (size_t i = 1; i < pruned.size(); i++) {
517 if (shareDone[i]) {
518 continue;
519 }
520
521 auto& particle = pruned[i];
522 auto daughters = particle.daughterList();
523 IdList allDaughters = std::make_pair(invalid, -invalid);
524 IdList allMothers = std::make_pair(invalid, -invalid);
525 addId(allMothers, i);
526 for (auto daughterIdx : daughters) {
527 // Add this daughter to set of all daughters
528 addId(allDaughters, daughterIdx);
529 auto& daughter = pruned[daughterIdx];
530 auto otherMothers = daughter.motherList();
531 for (auto otherMotherIdx : otherMothers) {
532 // Add this mother to set of all mothers. That is, take all
533 // mothers of the current daughter of the current particle
534 // and store that. In this way, we register mothers that
535 // share a daughter with the current particle.
536 addId(allMothers, otherMotherIdx);
537 // We also need to take all the daughters of this shared
538 // mother and reister those.
539 auto& otherMother = pruned[otherMotherIdx];
540 int otherDaughter1 = otherMother.daughter1();
541 int otherDaughter2 = otherMother.daughter2();
542 if (otherDaughter1 > 0) {
543 addId(allDaughters, otherDaughter1);
544 }
545 if (otherDaughter2 > 0) {
546 addId(allDaughters, otherDaughter2);
547 }
548 }
549 // At this point, we have added all mothers of current
550 // daughter, and all daughters of those mothers.
551 }
552 // At this point, we have all mothers that share daughters with
553 // the current particle, and we have all of the daughters
554 // too.
555 //
556 // We can now update the daughter information on all mothers
557 int minDaughter = allDaughters.first;
558 int maxDaughter = allDaughters.second;
559 int minMother = allMothers.first;
560 int maxMother = allMothers.second;
561 if (minMother != invalid) {
562 // If first mother isn't invalid, then second isn't either
563 for (size_t motherIdx = minMother; motherIdx <= maxMother; //
564 motherIdx++) {
565 shareDone[motherIdx] = true;
566 if (minDaughter == invalid) {
567 pruned[motherIdx].daughters(0, 0);
568 } else {
569 pruned[motherIdx].daughters(minDaughter, maxDaughter);
570 }
571 }
572 }
573 if (minDaughter != invalid) {
574 // If least mother isn't invalid, then largest mother will not
575 // be invalid either.
576 for (size_t daughterIdx = minDaughter; daughterIdx <= maxDaughter; //
577 daughterIdx++) {
578 if (minMother == invalid) {
579 pruned[daughterIdx].mothers(0, 0);
580 } else {
581 pruned[daughterIdx].mothers(minMother, maxMother);
582 }
583 }
584 }
585 }
586 if (mGenConfig.verbose) {
587 LOG(info) << "Pythia event was pruned from " << event.size()
588 << " to " << pruned.size() << " particles";
589 }
590 // Assign our pruned event to the event passed in
591 event = pruned;
592}
593
594/*****************************************************************/
596{
597 mUserFilterFcn = [](Pythia8::Particle const&) -> bool { return true; };
598
599 std::string filter = mGenConfig.particleFilter;
600 if (filter.size() > 0) {
601 LOG(info) << "Initializing the callback for user-based particle pruning " << filter;
602 auto expandedFileName = o2::utils::expandShellVarsInFileName(filter);
603 if (std::filesystem::exists(expandedFileName)) {
604 // if the filter is in a file we will compile the hook on the fly
605 mUserFilterFcn = o2::conf::GetFromMacro<UserFilterFcn>(expandedFileName, "filterPythia()", "o2::eventgen::GeneratorPythia8::UserFilterFcn", "o2mc_pythia8_userfilter_hook");
606 LOG(info) << "Hook initialized from file " << expandedFileName;
607 } else {
608 // if it's not a file we interpret it as a C++ lambda string and JIT it directly;
609 LOG(error) << "Did not find a file " << expandedFileName << " ; Will not execute hook";
610 }
611 mApplyPruning = true;
612 }
613}
614
615/*****************************************************************/
616
617Bool_t
619{
622 // The right moment to filter out unwanted stuff (like parton-level
623 // event information) Here, we aim to filter out everything before
624 // hadronization with the motivation to reduce the size of the MC
625 // event record in the AOD.
626
627 std::function<bool(const Pythia8::Particle&)> partonSelect = [](const Pythia8::Particle&) { return true; };
628 bool includeParton = mGenConfig.includePartonEvent;
629 if (not includeParton) {
630
631 // Select pythia particles
632 partonSelect = [](const Pythia8::Particle& particle) {
633 switch (particle.statusHepMC()) {
634 case 1: // Final st
635 case 2: // Decayed
636 case 4: // Beam
637 return true;
638 }
639 // For example to keep diffractive particles
640 // if (particle.id() == 9902210) return true;
641 return false;
642 };
643 mApplyPruning = true;
644 }
645
646 if (mApplyPruning) {
647 auto finalSelect = [partonSelect, this](const Pythia8::Particle& p) { return partonSelect(p) && mUserFilterFcn(p); };
648 pruneEvent(event, finalSelect);
649 }
650
651 /* loop over particles */
652 auto nParticles = event.size();
653 for (Int_t iparticle = 1; iparticle < nParticles; iparticle++) {
654 // first particle is system
655 auto particle = event[iparticle];
656 auto pdg = particle.id();
657 auto st = o2::mcgenstatus::MCGenStatusEncoding(particle.statusHepMC(), //
658 particle.status()) //
660 mParticles.push_back(TParticle(pdg, // Particle type
661 st, // status
662 particle.mother1() - 1, // first mother
663 particle.mother2() - 1, // second mother
664 particle.daughter1() - 1, // first daughter
665 particle.daughter2() - 1, // second daughter
666 particle.px(), // X-momentum
667 particle.py(), // Y-momentum
668 particle.pz(), // Z-momentum
669 particle.e(), // Energy
670 particle.xProd(), // Production X
671 particle.yProd(), // Production Y
672 particle.zProd(), // Production Z
673 particle.tProd())); // Production t
674 mParticles.back().SetBit(ParticleStatus::kToBeDone, //
675 particle.statusHepMC() == 1);
676 }
677
679 return kTRUE;
680}
681
682/*****************************************************************/
683
685{
688
689 eventHeader->putInfo<std::string>(Key::generator, "pythia8");
690 eventHeader->putInfo<int>(Key::generatorVersion, PYTHIA_VERSION_INTEGER);
691 eventHeader->putInfo<std::string>(Key::processName, mPythia.info.name());
692 eventHeader->putInfo<int>(Key::processCode, mPythia.info.code());
693 eventHeader->putInfo<float>(Key::weight, mPythia.info.weight());
694
695 auto& info = mPythia.info;
696
697 eventHeader->putInfo<int>(Key::acceptedEvents, info.nAccepted());
698 eventHeader->putInfo<int>(Key::attemptedEvents, info.nTried());
699
700 // Set PDF information
701 eventHeader->putInfo<int>(Key::pdfParton1Id, info.id1pdf());
702 eventHeader->putInfo<int>(Key::pdfParton2Id, info.id2pdf());
703 eventHeader->putInfo<float>(Key::pdfX1, info.x1pdf());
704 eventHeader->putInfo<float>(Key::pdfX2, info.x2pdf());
705 eventHeader->putInfo<float>(Key::pdfScale, info.QFac());
706 eventHeader->putInfo<float>(Key::pdfXF1, info.pdf1());
707 eventHeader->putInfo<float>(Key::pdfXF2, info.pdf2());
708
709 // Set cross section
710 eventHeader->putInfo<float>(Key::xSection, info.sigmaGen() * 1e9);
711 eventHeader->putInfo<float>(Key::xSectionError, info.sigmaErr() * 1e9);
712
713 // Set event scale and nMPI
714 eventHeader->putInfo<float>(Key::eventScale, info.QRen());
715 eventHeader->putInfo<int>(Key::mpi, info.nMPI());
716
717 // Set weights (overrides cross-section for each weight)
718 size_t iw = 0;
719 auto xsecErr = info.weightContainerPtr->getTotalXsecErr();
720 for (auto w : info.weightContainerPtr->getTotalXsec()) {
721 std::string post = (iw == 0 ? "" : "_" + std::to_string(iw));
722 eventHeader->putInfo<float>(Key::weight + post, info.weightValueByIndex(iw));
723 eventHeader->putInfo<float>(Key::xSection + post, w * 1e9);
724 eventHeader->putInfo<float>(Key::xSectionError + post, xsecErr[iw] * 1e9);
725 iw++;
726 }
727
728#if PYTHIA_VERSION_INTEGER < 8300
729 auto hiinfo = mPythia.info.hiinfo;
730#else
731 auto hiinfo = mPythia.info.hiInfo;
732#endif
733
734 if (hiinfo) {
736 eventHeader->SetB(hiinfo->b());
737 eventHeader->putInfo<double>(Key::impactParameter, hiinfo->b());
739#if PYTHIA_VERSION_INTEGER >= 8310
740 eventHeader->putInfo<double>(Key::planeAngle, hiinfo->phi());
741#endif
742 auto bImp = hiinfo->b();
744 int nColl, nPart;
745 int nPartProtonProj, nPartNeutronProj, nPartProtonTarg, nPartNeutronTarg;
746 int nRemnProtonProj, nRemnNeutronProj, nRemnProtonTarg, nRemnNeutronTarg;
747 int nFreeNeutronProj, nFreeProtonProj, nFreeNeutronTarg, nFreeProtonTarg;
748 getNcoll(nColl);
749 getNpart(nPart);
750 getNpart(nPartProtonProj, nPartNeutronProj, nPartProtonTarg, nPartNeutronTarg);
751 getNremn(nRemnProtonProj, nRemnNeutronProj, nRemnProtonTarg, nRemnNeutronTarg);
752 getNfreeSpec(nFreeNeutronProj, nFreeProtonProj, nFreeNeutronTarg, nFreeProtonTarg);
753 eventHeader->putInfo<int>(Key::nColl, nColl);
754 // These are all non-HepMC3 fields - of limited use
755 eventHeader->putInfo<int>("Npart", nPart);
756 eventHeader->putInfo<int>("Npart_proj_p", nPartProtonProj);
757 eventHeader->putInfo<int>("Npart_proj_n", nPartNeutronProj);
758 eventHeader->putInfo<int>("Npart_targ_p", nPartProtonTarg);
759 eventHeader->putInfo<int>("Npart_targ_n", nPartNeutronTarg);
760 eventHeader->putInfo<int>("Nremn_proj_p", nRemnProtonProj);
761 eventHeader->putInfo<int>("Nremn_proj_n", nRemnNeutronProj);
762 eventHeader->putInfo<int>("Nremn_targ_p", nRemnProtonTarg);
763 eventHeader->putInfo<int>("Nremn_targ_n", nRemnNeutronTarg);
764 eventHeader->putInfo<int>("Nfree_proj_n", nFreeNeutronProj);
765 eventHeader->putInfo<int>("Nfree_proj_p", nFreeProtonProj);
766 eventHeader->putInfo<int>("Nfree_targ_n", nFreeNeutronTarg);
767 eventHeader->putInfo<int>("Nfree_targ_p", nFreeProtonTarg);
768
769 // --- HepMC3 conforming information ---
770 // This is how the Pythia authors define Ncoll
771 // eventHeader->putInfo<int>(Key::nColl,
772 // hiinfo->nAbsProj() + hiinfo->nDiffProj() +
773 // hiinfo->nAbsTarg() + hiinfo->nDiffTarg() -
774 // hiiinfo->nCollND() - hiinfo->nCollDD());
775 eventHeader->putInfo<int>(Key::nPartProjectile,
776 hiinfo->nAbsProj() + hiinfo->nDiffProj());
777 eventHeader->putInfo<int>(Key::nPartTarget,
778 hiinfo->nAbsTarg() + hiinfo->nDiffTarg());
779#if PYTHIA_VERSION_INTEGER >= 8313
780 eventHeader->putInfo<int>(Key::nCollHard, hiinfo->nCollND());
781#else
782 eventHeader->putInfo<int>(Key::nCollHard, hiinfo->nCollNDTot());
783#endif
784 }
785}
786
787/*****************************************************************/
788
789void GeneratorPythia8::selectFromAncestor(int ancestor, Pythia8::Event& inputEvent, Pythia8::Event& outputEvent)
790{
791
796 // recursive selection via lambda function
797 std::set<int> selected;
798 std::function<void(int)> select;
799 select = [&](int i) {
800 selected.insert(i);
801 auto dl = inputEvent[i].daughterList();
802 for (auto j : dl) {
803 select(j);
804 }
805 };
806 select(ancestor);
807
808 // map selected particle index to output index
809 std::map<int, int> indexMap;
810 int index = outputEvent.size();
811 for (auto i : selected) {
812 indexMap[i] = index++;
813 }
814
815 // adjust mother/daughter indices and append to output event
816 for (auto i : selected) {
817 auto p = mPythia.event[i];
818 auto m1 = indexMap[p.mother1()];
819 auto m2 = indexMap[p.mother2()];
820 auto d1 = indexMap[p.daughter1()];
821 auto d2 = indexMap[p.daughter2()];
822 p.mothers(m1, m2);
823 p.daughters(d1, d2);
824
825 outputEvent.append(p);
826 }
827}
828
829/*****************************************************************/
830
831void GeneratorPythia8::getNcoll(const Pythia8::Info& info, int& nColl)
832{
833
836#if PYTHIA_VERSION_INTEGER < 8300
837 auto hiinfo = info.hiinfo;
838#else
839 auto hiinfo = info.hiInfo;
840#endif
841 nColl = 0;
842 if (!hiinfo) {
843 LOG(warn) << "No heavy-ion information from Pythia";
844 return;
845 }
846
847 // This is how the Pythia authors define Ncoll
848 nColl = (hiinfo->nAbsProj() + hiinfo->nDiffProj() +
849 hiinfo->nAbsTarg() + hiinfo->nDiffTarg() -
850 hiinfo->nCollND() - hiinfo->nCollDD());
851
852 if (not hiinfo->subCollisionsPtr()) {
853#if PYTHIA_VERSION_INTEGER < 8310
854 LOG(fatal) << "No sub-collision pointer from Pythia";
855#endif
856 return;
857 }
858
859 // loop over sub-collisions
860 auto scptr = hiinfo->subCollisionsPtr();
861 for (auto sc : *scptr) {
862
863 // wounded nucleon flag in projectile/target
864 auto pW = sc.proj->status() == Pythia8::Nucleon::ABS; // according to C.Bierlich this should be == Nucleon::ABS
865 auto tW = sc.targ->status() == Pythia8::Nucleon::ABS;
866
867 // increase number of collisions if both are wounded
868 if (pW && tW) {
869 nColl++;
870 }
871 }
872}
873
874/*****************************************************************/
875
876void GeneratorPythia8::getNpart(const Pythia8::Info& info, int& nPart)
877{
878
881#if PYTHIA_VERSION_INTEGER < 8300
882 auto hiinfo = info.hiinfo;
883#else
884 auto hiinfo = info.hiInfo;
885#endif
886 nPart = 0;
887 if (not hiinfo) {
888 return;
889 }
890
891 // This is how the Pythia authors calculate Npart
892 nPart = (hiinfo->nAbsProj() + hiinfo->nDiffProj() +
893 hiinfo->nAbsTarg() + hiinfo->nDiffTarg());
894 if (not hiinfo->subCollisionsPtr()) {
895 return;
896 }
897
898 int nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg;
899 getNpart(info, nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg);
900 nPart = nProtonProj + nNeutronProj + nProtonTarg + nNeutronTarg;
901}
902
903/*****************************************************************/
904
905void GeneratorPythia8::getNpart(const Pythia8::Info& info, int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg)
906{
907
910#if PYTHIA_VERSION_INTEGER < 8300
911 auto hiinfo = info.hiinfo;
912#else
913 auto hiinfo = info.hiInfo;
914#endif
915
916 nProtonProj = nNeutronProj = nProtonTarg = nNeutronTarg = 0;
917 if (!hiinfo) {
918 return;
919 }
920
921 nProtonProj = hiinfo->nAbsProj() + hiinfo->nDiffProj();
922 nProtonTarg = hiinfo->nAbsTarg() + hiinfo->nDiffTarg();
923 if (not hiinfo->subCollisionsPtr()) {
924#if PYTHIA_VERSION_INTEGER < 8310
925 LOG(fatal) << "No sub-collision pointer from Pythia";
926#endif
927 return;
928 }
929
930 // keep track of wounded nucleons
931 std::vector<Pythia8::Nucleon*> projW;
932 std::vector<Pythia8::Nucleon*> targW;
933
934 // loop over sub-collisions
935 auto scptr = hiinfo->subCollisionsPtr();
936 for (auto sc : *scptr) {
937
938 // wounded nucleon flag in projectile/target
939 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
940 auto tW = sc.targ->status() == Pythia8::Nucleon::ABS || sc.targ->status() == Pythia8::Nucleon::DIFF;
941
942 // increase number of wounded projectile nucleons if not yet in the wounded vector
943 if (pW && std::find(projW.begin(), projW.end(), sc.proj) == projW.end()) {
944 projW.push_back(sc.proj);
945 if (sc.proj->id() == 2212) {
946 nProtonProj++;
947 } else if (sc.proj->id() == 2112) {
948 nNeutronProj++;
949 }
950 }
951
952 // increase number of wounded target nucleons if not yet in the wounded vector
953 if (tW && std::find(targW.begin(), targW.end(), sc.targ) == targW.end()) {
954 targW.push_back(sc.targ);
955 if (sc.targ->id() == 2212) {
956 nProtonTarg++;
957 } else if (sc.targ->id() == 2112) {
958 nNeutronTarg++;
959 }
960 }
961 }
962}
963
964/*****************************************************************/
965
966void GeneratorPythia8::getNremn(const Pythia8::Event& event, int& nProtonProj, int& nNeutronProj, int& nProtonTarg, int& nNeutronTarg)
967{
968
971 // reset
972 nProtonProj = nNeutronProj = nProtonTarg = nNeutronTarg = 0;
973 auto nNucRem = 0;
974
975 // particle loop
976 auto nparticles = event.size();
977 for (int ipa = 0; ipa < nparticles; ++ipa) {
978 const auto particle = event[ipa];
979 auto pdg = particle.id();
980
981 // nuclear remnants have pdg code = ±10LZZZAAA9
982 if (pdg < 1000000000) {
983 continue; // must be nucleus
984 }
985 if (pdg % 10 != 9) {
986 continue; // first digit must be 9
987 }
988 nNucRem++;
989
990 // extract A, Z and L from pdg code
991 pdg /= 10;
992 auto A = pdg % 1000;
993 pdg /= 1000;
994 auto Z = pdg % 1000;
995 pdg /= 1000;
996 auto L = pdg % 10;
997
998 if (particle.pz() > 0.) {
999 nProtonProj = Z;
1000 nNeutronProj = A - Z;
1001 }
1002 if (particle.pz() < 0.) {
1003 nProtonTarg = Z;
1004 nNeutronTarg = A - Z;
1005 }
1006
1007 } // end of particle loop
1008
1009 if (nNucRem > 2) {
1010 LOG(warning) << " GeneratorPythia8: found more than two nuclear remnants (weird)";
1011 }
1012}
1013/*****************************************************************/
1014
1015/*****************************************************************/
1016
1017void GeneratorPythia8::getNfreeSpec(const Pythia8::Info& info, int& nFreenProj, int& nFreepProj, int& nFreenTarg, int& nFreepTarg)
1018{
1021#if PYTHIA_VERSION_INTEGER < 8300
1022 auto hiinfo = info.hiinfo;
1023#else
1024 auto hiinfo = info.hiInfo;
1025#endif
1026
1027 if (!hiinfo) {
1028 return;
1029 }
1030
1031 double b = hiinfo->b();
1032
1033 static o2::zdc::FragmentParam frag; // data-driven model to get free spectators given impact parameter
1034
1035 TF1 const& fneutrons = frag.getfNeutrons();
1036 TF1 const& fsigman = frag.getsigmaNeutrons();
1037 TF1 const& fprotons = frag.getfProtons();
1038 TF1 const& fsigmap = frag.getsigmaProtons();
1039
1040 // Calculating no. of free spectators from parametrization
1041 int nneu[2] = {0, 0};
1042 for (int i = 0; i < 2; i++) {
1043 float nave = fneutrons.Eval(b);
1044 float sigman = fsigman.Eval(b);
1045 float nfree = gRandom->Gaus(nave, 0.68 * sigman * nave);
1046 nneu[i] = (int)nfree;
1047 if (nave < 0 || nneu[i] < 0) {
1048 nneu[i] = 0;
1049 }
1050 if (nneu[i] > 126) {
1051 nneu[i] = 126;
1052 }
1053 }
1054 //
1055 int npro[2] = {0, 0};
1056 for (int i = 0; i < 2; i++) {
1057 float pave = fprotons.Eval(b);
1058 float sigmap = fsigman.Eval(b);
1059 float pfree = gRandom->Gaus(pave, 0.68 * sigmap * pave) / 0.7;
1060 npro[i] = (int)pfree;
1061 if (pave < 0 || npro[i] < 0) {
1062 npro[i] = 0;
1063 }
1064 if (npro[i] > 82) {
1065 npro[i] = 82;
1066 }
1067 }
1068
1069 nFreenProj = nneu[0];
1070 nFreenTarg = nneu[1];
1071 nFreepProj = npro[0];
1072 nFreepTarg = npro[1];
1073 /*****************************************************************/
1074}
1075
1076} /* namespace eventgen */
1077} /* 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:127
std::vector< TParticle > mParticles
Definition Generator.h:147
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"