ATLAS Offline Software
Pythia8ForDecays.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 // Abused from Pythia6.cc in Geant4 extended examples
6 
7 // My own class definition
9 
10 // Helper functions for Pythia8 and Pythia8 classes
11 #include "Pythia8_i/Pythia8_i.h"
12 
13 // Pythia8 RHadrons code that we call into
14 #include "Pythia8/RHadrons.h"
15 
16 // HepMC for translation into format Pythia likes
17 #include "AtlasHepMC/GenEvent.h"
18 #include "AtlasHepMC/GenParticle.h"
19 
20 // G4 classes for translation into G4 format
21 #include "G4ParticleDefinition.hh"
22 #include "G4ParticleTable.hh"
23 #include "G4ThreeVector.hh"
24 #include "TLorentzVector.h"
25 
26 static inline unsigned short int nth_digit(const int& val,const unsigned short& n) { return (std::abs(val)/(int(std::pow(10,n-1))))%10;}
27 
28 // STL includes
29 #include <cstdlib>
30 #include <cstring>
31 #include <cmath>
32 #include <fstream>
33 
34 
36  {
37  // Pythia instance where RHadrons can decay
38  std::string docstring = Pythia8_i::xmlpath();
39  m_pythia = std::make_unique<Pythia8::Pythia>(docstring);
40  m_pythia->readString("SLHA:file = SLHA_INPUT.DAT");
41  m_pythia->readString("ProcessLevel:all = off");
42  m_pythia->readString("Init:showChangedSettings = off");
43  m_pythia->readString("RHadrons:allow = on");
44  m_pythia->readString("RHadrons:allowDecay = on");
45  m_pythia->readString("RHadrons:probGluinoball = 0.1");
46  m_pythia->readString("PartonLevel:FSR = off");
47  m_pythia->readString("Init:showAllParticleData = on");
48 
49  // Process the file of commands left for us by the python layer
50  std::string line;
51  std::ifstream command_stream ("PYTHIA8_COMMANDS.TXT");
52  while(getline(command_stream,line)){
53  // Leaving it to the top-level to get this file right
54  m_pythia->readString(line);
55  }
56  command_stream.close();
57 
58  m_pythia->init();
59 
60 }
61 
62 G4ParticleDefinition* Pythia8ForDecays::GetParticleDefinition(const int pdgEncoding) const
63 {
64  G4ParticleTable* particleTable = G4ParticleTable::GetParticleTable();
65  G4ParticleDefinition* particleDefinition(nullptr);
66  if (pdgEncoding != 0) particleDefinition = particleTable->FindParticle(pdgEncoding);
67  return particleDefinition;
68 }
69 
70 
71 std::pair<int,int> Pythia8ForDecays::fromIdWithSquark( int idRHad) const
72 {
73 
74  // Find squark flavour content.
75  int idRSb = m_pythia->settings.mode("RHadrons:idSbottom");
76  int idRSt = m_pythia->settings.mode("RHadrons:idStop");
77  int idLight = (abs(idRHad) - 1000000) / 10;
78  int idSq = (idLight < 100) ? idLight/10 : idLight/100;
79  int id1 = (idSq == 6) ? idRSt : idRSb;
80  if (idRHad < 0) id1 = -id1;
81 
82  // Find light (di)quark flavour content.
83  int id2 = (idLight < 100) ? idLight%10 : idLight%100;
84  if (id2 > 10) id2 = 100 * id2 + abs(idRHad)%10;
85  if ((id2 < 10 && idRHad > 0) || (id2 > 10 && idRHad < 0)) id2 = -id2;
86 
87  // Done.
88  return std::make_pair( id1, id2);
89 
90 }
91 
92 // TODO: Would be nice for this to be a public function in Pythia8::RHadrons.hh
93 std::pair<int,int> Pythia8ForDecays::fromIdWithGluino( int idRHad, Pythia8::Rndm* rndmPtr) const
94 {
95  // Find light flavour content of R-hadron.
96  int idLight = (abs(idRHad) - 1000000) / 10;
97  int id1, id2, idTmp, idA, idB, idC;
98  double diquarkSpin1RH = 0.5;
99 
100  // Gluinoballs: split g into d dbar or u ubar.
101  if (idLight < 100) {
102  id1 = (rndmPtr->flat() < 0.5) ? 1 : 2;
103  id2 = -id1;
104 
105  // Gluino-meson: split into q + qbar.
106  } else if (idLight < 1000) {
107  id1 = (idLight / 10) % 10;
108  id2 = -(idLight % 10);
109  // Flip signs when first quark of down-type.
110  if (id1%2 == 1) {
111  idTmp = id1;
112  id1 = -id2;
113  id2 = -idTmp;
114  }
115 
116  // Gluino-baryon: split to q + qq (diquark).
117  // Pick diquark at random, except if c or b involved.
118  } else {
119  idA = (idLight / 100) % 10;
120  idB = (idLight / 10) % 10;
121  idC = idLight % 10;
122  double rndmQ = 3. * rndmPtr->flat();
123  if (idA > 3) rndmQ = 0.5;
124  if (rndmQ < 1.) {
125  id1 = idA;
126  id2 = 1000 * idB + 100 * idC + 3;
127  if (idB != idC && rndmPtr->flat() > diquarkSpin1RH) id2 -= 2;
128  } else if (rndmQ < 2.) {
129  id1 = idB;
130  id2 = 1000 * idA + 100 * idC + 3;
131  if (idA != idC && rndmPtr->flat() > diquarkSpin1RH) id2 -= 2;
132  } else {
133  id1 = idC;
134  id2 = 1000 * idA + 100 * idB +3;
135  if (idA != idB && rndmPtr->flat() > diquarkSpin1RH) id2 -= 2;
136  }
137  }
138  // Flip signs for anti-R-hadron.
139  if (idRHad < 0) {
140  idTmp = id1;
141  id1 = -id2;
142  id2 = -idTmp;
143  }
144 
145  // Done.
146  return std::make_pair( id1, id2);
147 
148 }
149 
152 void Pythia8ForDecays::fillParticle(const G4Track& aTrack, Pythia8::Event& event) const
153 {
154  // Reset event record to allow for new event.
155  event.reset();
156 
157  // Select particle mass; where relevant according to Breit-Wigner.
158  double mm = aTrack.GetDynamicParticle()->GetMass();
159 
160  // Store the particle in the event record.
161  event.append( aTrack.GetDefinition()->GetPDGEncoding(), 1, 0, 0, aTrack.GetMomentum().x()/CLHEP::GeV, aTrack.GetMomentum().y()/CLHEP::GeV,
162  aTrack.GetMomentum().z()/CLHEP::GeV, aTrack.GetDynamicParticle()->GetTotalEnergy()/CLHEP::GeV, mm/CLHEP::GeV);
163  // Note: this function returns an int, but we don't need or use its output
164 }
165 
166 bool Pythia8ForDecays::isGluinoRHadron(int pdgId) const{
167  // Checking what kind of RHadron this is based on the digits in its PDGID
168  const unsigned short digitValue_q1 = nth_digit(pdgId,4);
169  const unsigned short digitValue_l = nth_digit(pdgId,5);
170 
171  // Gluino R-Hadrons have the form 109xxxx or 1009xxx
172  if (digitValue_l == 9 || (digitValue_l==0 && digitValue_q1 == 9) ){
173  // This is a gluino R-Hadron
174  return true;
175  }
176 
177  // Special case : R-gluinoball
178  if (pdgId==1000993) return true;
179 
180  // This is not a gluino R-Hadron (probably a squark R-Hadron)
181  return false;
182 
183 }
184 
185 void Pythia8ForDecays::Py1ent(const G4Track& aTrack, std::vector<G4DynamicParticle*> & particles)
186 {
187 
188  // Get members from Pythia8 instance where RHadrons can decay
189  Pythia8::Event& event = m_pythia->event;
190  Pythia8::ParticleData& pdt = m_pythia->particleData;
191 
192  // Use pythiaDecay information to fill event with the input particle
193  fillParticle(aTrack, event);
194 
195  // Copy and paste of RHadron decay code
196  int iRNow = 1;
197  int idRHad = event[iRNow].id();
198  double mRHad = event[iRNow].m();
199  int iR0 = 0;
200  int iR2 = 0;
201 
202  bool isTriplet = !isGluinoRHadron(idRHad);
203 
204  // Find flavour content of squark or gluino R-hadron.
205  std::pair<int,int> idPair = (isTriplet) ? fromIdWithSquark( idRHad) : fromIdWithGluino( idRHad, &(m_pythia->rndm));
206  int id1 = idPair.first;
207  int id2 = idPair.second;
208 
209  // Sharing of momentum: the squark/gluino should be restored
210  // to original mass, but error if negative-mass spectators.
211  int idRSb = m_pythia->settings.mode("RHadrons:idSbottom");
212  int idRSt = m_pythia->settings.mode("RHadrons:idStop");
213  int idRGo = m_pythia->settings.mode("RHadrons:idGluino");
214  int idLight = (abs(idRHad) - 1000000) / 10;
215  int idSq = (idLight < 100) ? idLight/10 : idLight/100;
216  int idRSq = (idSq == 6) ? idRSt : idRSb;
217 
218  // Handling R-Hadrons with anti-squarks
219  idRSq = idRSq * std::copysign(1, idRHad);
220 
221  int idRBef = isTriplet ? idRSq : idRGo;
222 
223  // Mass of the underlying sparticle
224  double mRBef = pdt.mSel(idRBef);
225 
226  // Fraction of the RHadron mass given by the sparticle
227  double fracR = mRBef / mRHad;
228  int counter=0;
229  while (fracR>=1.){
230  if (counter==10){
231  G4cout << "Needed more than 10 attempts with constituent " << idRBef << " mass (" << mRBef << " above R-Hadron " << idRHad << " mass " << mRHad << G4endl;
232  } else if (counter>100){
233  G4cout << "Pythia8ForDecays::Py1ent ERROR Failed >100 times. Constituent " << idRBef << " mass (" << mRBef << " above R-Hadron " << idRHad << " mass " << mRHad << G4endl;
234  return;
235  }
236  mRBef = pdt.mSel(idRBef);
237  fracR = mRBef / mRHad;
238  counter++;
239  }
240 
241  // Squark case
242  if(isTriplet){
243  const int col = (pdt.colType(idRBef) != 0) ? event.nextColTag() : 0; // NB There should be no way that this can be zero (see discussion on ATLASSIM-6687), but leaving check in there just in case something changes in the future.
244  int tmpSparticleColor = id1>0 ? col : 0;
245  int tmpSparticleAnticolor = id1>0 ? 0 : col;
246 
247  // Store the constituents of a squark R-hadron.
248 
249  // Sparticle
250  // (id, status, mother1, mother2, daughter1, daughter2, col, acol, px, py, pz, e, m=0., scaleIn=0., polIn=9.)
251  iR0 = event.append( id1, 106, iRNow, 0, 0, 0, tmpSparticleColor, tmpSparticleAnticolor, fracR * event[iRNow].p(), fracR * mRHad, 0.);
252  // Spectator quark
253  iR2 = event.append( id2, 106, iRNow, 0, 0, 0, tmpSparticleAnticolor, tmpSparticleColor, (1. - fracR) * event[iRNow].p(), (1. - fracR) * mRHad, 0.);
254  }
255  // Gluino case
256  else{
257  double mOffsetCloudRH = 0.2; // could be read from internal data?
258  double m1Eff = pdt.constituentMass(id1) + mOffsetCloudRH;
259  double m2Eff = pdt.constituentMass(id2) + mOffsetCloudRH;
260  double frac1 = (1. - fracR) * m1Eff / ( m1Eff + m2Eff);
261  double frac2 = (1. - fracR) * m2Eff / ( m1Eff + m2Eff);
262 
263  // Two new colours needed in the breakups.
264  int col1 = event.nextColTag();
265  int col2 = event.nextColTag();
266 
267  // Store the constituents of a gluino R-hadron.
268  iR0 = event.append( idRBef, 106, iRNow, 0, 0, 0, col2, col1, fracR * event[iRNow].p(), fracR * mRHad, 0.);
269  event.append( id1, 106, iRNow, 0, 0, 0, col1, 0, frac1 * event[iRNow].p(), frac1 * mRHad, 0.);
270  iR2 = event.append( id2, 106, iRNow, 0, 0, 0, 0, col2, frac2 * event[iRNow].p(), frac2 * mRHad, 0.);
271  }
272 
273  // Mark R-hadron as decayed and update history.
274  event[iRNow].statusNeg();
275  event[iRNow].daughters( iR0, iR2);
276 
277  // Generate events. Quit if failure.
278  if (!m_pythia->next()) {
279  m_pythia->forceRHadronDecays();
280  }
281 
283  // Add the particles from the Pythia event into the GEANT particle vector
285  particles.clear();
286 
287  for(int i=0; i<m_pythia->event.size(); i++){
288  if ( m_pythia->event[i].status()<0 ) continue; // stable only
289  G4ThreeVector momentum( m_pythia->event[i].px() , m_pythia->event[i].py() , m_pythia->event[i].pz() );
290  momentum*=1000.0;//GeV to MeV
291  const G4ParticleDefinition * particleDefinition = GetParticleDefinition( m_pythia->event[i].id() );
292 
293  if (!particleDefinition){
294  G4cout << "I don't know a definition for pdgid "<<m_pythia->event[i].id()<<"! Skipping it..." << G4endl;
295  continue;
296  }
297 
298  G4DynamicParticle* dynamicParticle = new G4DynamicParticle(particleDefinition, momentum);
299  particles.push_back( dynamicParticle );
300  }
301 
302 }
303 
GeV
#define GeV
Definition: PhysicsAnalysis/TauID/TauAnalysisTools/Root/HelperFunctions.cxx:17
checkFileSG.line
line
Definition: checkFileSG.py:75
GenEvent.h
Pythia8_i::xmlpath
static std::string xmlpath()
Definition: Pythia8_i.cxx:693
conifer::pow
constexpr int pow(int x)
Definition: conifer.h:20
JiveXML::Event
struct Event_t Event
Definition: ONCRPCServer.h:65
Pythia8ForDecays::isGluinoRHadron
bool isGluinoRHadron(int pdgId) const
Definition: Pythia8ForDecays.cxx:166
Pythia8_i.h
GenParticle.h
Pythia8ForDecays::fromIdWithGluino
std::pair< int, int > fromIdWithGluino(int idRHad, Pythia8::Rndm *rndmPtr) const
Get the quarks from a gluino R-hadron. From Pythia8 code.
Definition: Pythia8ForDecays.cxx:93
python.utils.AtlRunQueryDQUtils.p
p
Definition: AtlRunQueryDQUtils.py:210
ParticleGun_EoverP_Config.momentum
momentum
Definition: ParticleGun_EoverP_Config.py:63
id2
HWIdentifier id2
Definition: LArRodBlockPhysicsV0.cxx:562
event
POOL::TEvent event(POOL::TEvent::kClassAccess)
lumiFormat.i
int i
Definition: lumiFormat.py:85
beamspotman.n
n
Definition: beamspotman.py:731
Pythia8ForDecays::Py1ent
void Py1ent(const G4Track &, std::vector< G4DynamicParticle * > &)
Function that decays the RHadron; returns products in G4 format.
Definition: Pythia8ForDecays.cxx:185
Pythia8ForDecays::m_pythia
std::unique_ptr< Pythia8::Pythia > m_pythia
The instance of Pythia8 that will do the work.
Definition: Pythia8ForDecays.h:46
Pythia8ForDecays::fillParticle
void fillParticle(const G4Track &, Pythia8::Event &event) const
Fill a Pythia8 event with the information from a G4Track.
Definition: Pythia8ForDecays.cxx:152
Pythia8ForDecays::GetParticleDefinition
G4ParticleDefinition * GetParticleDefinition(const int) const
Helper for getting G4ParticleDefinition from PDG ID.
Definition: Pythia8ForDecays.cxx:62
query_example.col
col
Definition: query_example.py:7
python.SystemOfUnits.mm
int mm
Definition: SystemOfUnits.py:83
Pythia8_RapidityOrderMPI.val
val
Definition: Pythia8_RapidityOrderMPI.py:14
Pythia8ForDecays.h
LArG4FSStartPointFilter.particles
list particles
Definition: LArG4FSStartPointFilter.py:84
Pythia8ForDecays::fromIdWithSquark
std::pair< int, int > fromIdWithSquark(int idRHad) const
Definition: Pythia8ForDecays.cxx:71
Pythia8ForDecays::Pythia8ForDecays
Pythia8ForDecays()
Definition: Pythia8ForDecays.cxx:35
test_pyathena.counter
counter
Definition: test_pyathena.py:15