StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
PartonVertex.cc
1 // PartonVertex.cc is a part of the PYTHIA event generator.
2 // Copyright (C) 2020 Torbjorn Sjostrand.
3 // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
4 // Please respect the MCnet Guidelines, see GUIDELINES for details.
5 
6 // Function definitions (not found in the header) for the PartonVertex class.
7 
8 #include "Pythia8/PartonVertex.h"
9 
10 namespace Pythia8 {
11 
12 //==========================================================================
13 
14 // The PartonVertex class.
15 
16 //--------------------------------------------------------------------------
17 
18 // Find relevant settings.
19 
20 void PartonVertex::init() {
21 
22  doVertex = flag("PartonVertex:setVertex");
23  modeVertex = mode("PartonVertex:modeVertex");
24  epsPhi = parm("PartonVertex:phiAsym");
25  epsRat = sqrt( (1. + epsPhi) / (1. - epsPhi) );
26  rProton = parm("PartonVertex:ProtonRadius");
27  rProton2 = rProton * rProton;
28  pTmin = parm("PartonVertex:pTmin");
29  widthEmission = parm("PartonVertex:EmissionWidth");
30 
31 }
32 
33 //--------------------------------------------------------------------------
34 
35 // Set vertices for a beam particle and the beam remnants.
36 
37 void PartonVertex::vertexBeam( int iBeam, vector<int>& iRemn,
38  vector<int>& iInit, Event& event) {
39 
40  // Set vertex for incoming beam particle itself.
41  double xBeam = (iBeam == 0) ? bHalf : -bHalf;
42  Vec4 vBeam( xBeam, 0., 0., 0.);
43  event[iBeam + 1].vProd( FM2MM * vBeam );
44 
45  // Variables for further use.
46  vector<Vec4> vNow;
47  Vec4 vSum;
48  vector<double> wtNow;
49  double wtSum = 0.;
50  double x, y, r, phi, sthe, xSgn, xWt;
51  pair<double,double> xy;
52 
53  // Loop over all remnants and set their location relative to the beam.
54  for (int i = 0; i < int(iRemn.size()); ++i) {
55 
56  // Sample according to sphere.
57  if (modeVertex < 2) {
58  r = rProton * pow(rndmPtr->flat(), 1./3.);
59  phi = 2. * M_PI * rndmPtr->flat();
60  sthe = sqrtpos( 1. - pow2(2. * rndmPtr->flat() - 1.) );
61  x = r * sthe * cos(phi);
62  y = r * sthe * sin(phi);
63 
64  // Sample according to Gaussian.
65  } else {
66  xy = rndmPtr->gauss2();
67  x = xy.first * rProton / sqrt(3.);
68  y = xy.second * rProton / sqrt(3.);
69  }
70 
71  // Save. Calculate energy-weighted center and displacement weight.
72  vNow.push_back( Vec4( x, y, 0., 0.) );
73  vSum += event[iRemn[i]].e() * vNow[i];
74  xSgn = (iBeam == 0) ? x : -x;
75  xWt = 1. / ( 1. + (bNow / rProton) * exp(xSgn / rProton) );
76  wtNow.push_back( xWt );
77  wtSum += event[iRemn[i]].e() * xWt;
78  }
79 
80  // Add initiator energy-weighted center relative to proton center.
81  for (int i = 0; i < int(iInit.size()); ++i)
82  vSum += event[iInit[i]].e() * (MM2FM * event[iInit[i]].vProd() - vBeam);
83 
84  // Distribute recoil among remnants to ensure that center is centered.
85  // (But scale down if suspiciously large shift.)
86  Vec4 vShift;
87  for (int i = 0; i < int(iRemn.size()); ++i) {
88  vShift = vSum * wtNow[i] / wtSum;
89  if (vShift.pT2() > rProton2) vShift *= rProton / vShift.pT();
90  event[iRemn[i]].vProd( FM2MM * (vNow[i] - vShift + vBeam) );
91  }
92 
93 }
94 
95 //--------------------------------------------------------------------------
96 
97 // Select vertex for an MPI.
98 
99 void PartonVertex::vertexMPI( int iBeg, int nAdd, double bNowIn,
100  Event& event) {
101 
102  // Convert the impact parameter to physical units. Prepare selection.
103  bNow = bNowIn * rProton;
104  bHalf = 0.5 * bNow;
105  if (modeVertex < 2) {
106  if (bHalf > 0.95 * rProton) {
107  infoPtr->errorMsg("Warning in PartonVertex::vertexMPI: large b value");
108  bHalf = 0.95 * rProton;
109  }
110  xMax = rProton - bHalf;
111  yMax = sqrt( rProton2 - bHalf * bHalf);
112  zWtMax = yMax * yMax;
113  }
114  double x = 0.;
115  double y = 0.;
116 
117  // Sample x and y inside a box, and then require it to be within sphere.
118  if (modeVertex < 2) {
119  bool accept = false;
120  while (!accept) {
121  x = (2. * rndmPtr->flat() - 1.) * xMax;
122  y = (2. * rndmPtr->flat() - 1.) * yMax;
123  double rA2 = pow2(x - bHalf) + y * y;
124  double rB2 = pow2(x + bHalf) + y * y;
125  if ( max( rA2, rB2) < rProton2 ) accept = true;
126  if (accept && sqrtpos(rProton2 - rA2) * sqrtpos(rProton2 - rB2)
127  < rndmPtr->flat() * zWtMax) accept = false;
128  }
129 
130  // Sample x and y according to two-dimensional Gaussian.
131  } else {
132  bool accept = false;
133  while (!accept) {
134  pair<double,double> xy = rndmPtr->gauss2();
135  x = xy.first * rProton / sqrt(6.);
136  y = xy.second * rProton / sqrt(6.);
137  if (modeVertex == 2) accept = true;
138  // Option with elliptic shape.
139  else if (modeVertex == 3) { x *= epsRat; y /= epsRat; accept = true;}
140  // Option with azimuthal distribution 1 + epsilon * cos(2 * phi).
141  else if ( 1. + epsPhi * (x*x - y*y)/(x*x + y*y)
142  > rndmPtr->flat() * (1. + abs(epsPhi)) ) accept = true;
143  }
144  }
145 
146  // Set production vertices.
147  for (int iNow = iBeg; iNow < iBeg + nAdd; ++iNow)
148  event[iNow].vProd( x * FM2MM, y * FM2MM, 0., 0.);
149 
150 }
151 
152 //--------------------------------------------------------------------------
153 
154 // Select vertex for an FSR branching.
155 
156 void PartonVertex::vertexFSR( int iNow, Event& event) {
157 
158  // Start from known vertex, or mother one.
159  int iMo = event[iNow].mother1();
160  Vec4 vStart = event[iNow].hasVertex() ? event[iNow].vProd()
161  : event[iMo].vProd();
162 
163  // Add Gaussian smearing.
164  double pT = max( event[iNow].pT(), pTmin);
165  pair<double, double> xy = rndmPtr->gauss2();
166  Vec4 vSmear = (widthEmission / pT) * Vec4( xy.first, xy.second, 0., 0.);
167  event[iNow].vProd( vStart + vSmear * FM2MM);
168 
169 }
170 
171 //--------------------------------------------------------------------------
172 
173 // Select vertex for an ISR branching.
174 
175 void PartonVertex::vertexISR( int iNow, Event& event) {
176 
177  // Start from known vertex or mother/daughter one.
178  int iMoDa = event[iNow].mother1();
179  if (iMoDa == 0) iMoDa = event[iNow].daughter1();
180  Vec4 vStart = event[iNow].vProd();
181  if (!event[iNow].hasVertex() && iMoDa != 0) vStart = event[iMoDa].vProd();
182 
183  // Add Gaussian smearing.
184  double pT = max( event[iNow].pT(), pTmin);
185  pair<double, double> xy = rndmPtr->gauss2();
186  Vec4 vSmear = (widthEmission / pT) * Vec4( xy.first, xy.second, 0., 0.);
187  event[iNow].vProd( vStart + vSmear * FM2MM);
188 
189 }
190 
191 //--------------------------------------------------------------------------
192 
193 // Propagate parton vertex information to hadrons.
194 // Still to be improved for closed gluon loops and junction topologies.
195 
196 void PartonVertex::vertexHadrons( int nBefFrag, Event& event) {
197 
198  // Identify known cases and else return.
199  int iFirst = event[nBefFrag].mother1();
200  int iLast = event[nBefFrag].mother2();
201  vector<int> iNotG;
202  for (int i = iFirst; i <= iLast; ++i)
203  if (!event[i].isGluon()) iNotG.push_back(i);
204  if ( iNotG.size() == 2 && event[iFirst].col() * event[iLast].col() == 0
205  && event[iFirst].acol() * event[iLast].acol() == 0) {
206  } else if (iNotG.size() == 0 || iNotG.size() == 3) {
207  } else {
208  infoPtr->errorMsg("Error in PartonVertex::vertexHadrons: "
209  "unknown colour topology not handled");
210  return;
211  }
212 
213  // Use middle of endpoints for collapse to single hadron.
214  if (event[iFirst].daughter2() == event[iFirst].daughter1()) {
215  Vec4 vShift = 0.5 * (event[iFirst].vProd() + event[iLast].vProd());
216  event[nBefFrag].vProdAdd( vShift);
217  return;
218  }
219 
220  // Simple qqbar strings or closed gluon loops.
221  // Warning: for the latter still to transfer info on first breakup,
222  // so not correct as is, but still good enough for smearing purposes?
223  if (iNotG.size() == 2 || iNotG.size() == 0) {
224 
225  // Initial values and variables.
226  int iBeg = iFirst;
227  int iEnd = iBeg + 1;
228  double eBeg = event[iBeg].e();
229  double eEnd = (iEnd < iLast && event[iEnd].isGluon() ? 0.5 : 1.)
230  * event[iEnd].e();
231  double ePartonSum = eBeg + eEnd;
232  double eHadronSum = 0.;
233 
234  // Loop over new primary hadrons. Midpoint energy of new hadron.
235  for (int i = nBefFrag; i < event.size(); ++i) {
236  eHadronSum += 0.5 * event[i].e();
237 
238  // Step up to parton pair that spans hadron midpoint energy.
239  while (eHadronSum > ePartonSum && iEnd < iLast) {
240  eHadronSum -= ePartonSum;
241  ++iBeg;
242  ++iEnd;
243  eBeg = eEnd;
244  eEnd = (event[iEnd].isGluon() ? 0.5 : 1.) * event[iEnd].e();
245  ePartonSum = eBeg + eEnd;
246  }
247 
248  // Add weighted average of parton vertices to hadron vertex.
249  double eFrac = clamp( eHadronSum / ePartonSum, 0., 1.);
250  Vec4 vShift = (1. - eFrac) * event[iBeg].vProd()
251  + eFrac * event[iEnd].vProd();
252  event[i].vProdAdd( vShift);
253  eHadronSum += 0.5 * event[i].e();
254  }
255 
256  // Done for simple cases.
257  return;
258  }
259 
260  // Junction systems: identify order of hadrons produced.
261  int iOrdSys[3] = {5, 5, 5};
262  for (int k = 0; k < 3; ++k) {
263  int col = max( event[iNotG[k]].col(), event[iNotG[k]].acol());
264  for (int i = 0; i < event.sizeJunction(); ++i)
265  for (int j = 0; j < 3; ++j)
266  if (event.endColJunction( i, j) == col) {
267  int statusLeg = event.statusJunction( i, j);
268  if (statusLeg == 85) iOrdSys[0] = k;
269  else if (statusLeg == 86) iOrdSys[1] = k;
270  else iOrdSys[2] = k;
271  }
272  }
273 
274  // Repair cases where one colour end is not identified.
275  if (iOrdSys[0] + iOrdSys[1] +iOrdSys[2] == 3) ;
276  else if (iOrdSys[0] == 5 && iOrdSys[1] + iOrdSys[2] < 4)
277  iOrdSys[0] = 3 - iOrdSys[1] - iOrdSys[2];
278  else if (iOrdSys[1] == 5 && iOrdSys[0] + iOrdSys[2] < 4)
279  iOrdSys[1] = 3 - iOrdSys[0] - iOrdSys[2];
280  else if (iOrdSys[2] == 5 && iOrdSys[0] + iOrdSys[1] < 4)
281  iOrdSys[2] = 3 - iOrdSys[0] - iOrdSys[1];
282  else {
283  infoPtr->errorMsg("Warning in PartonVertex::vertexHadrons: "
284  "unidentified junction topology not handled");
285  return;
286  }
287 
288  // Initial values for the two lowest-energy legs.
289  int nDone = nBefFrag;
290  for (int leg = 0; leg < 2; ++leg) {
291  int nStop = (iOrdSys[leg] == 0) ? iFirst : iNotG[iOrdSys[leg] - 1] + 1;
292  int iBeg = iNotG[iOrdSys[leg]];
293  int iEnd = max(iBeg - 1, nStop);
294  double eBeg = event[iBeg].e();
295  double eEnd = (event[iEnd].isGluon() ? 0.5 : 1.) * event[iEnd].e();
296  double ePartonSum = eBeg + eEnd;
297  double eHadronSum = 0.;
298  int statusNow = (leg == 0) ? 85 : 86;
299 
300  // Loop over primary hadrons in two lowest-energy legs.
301  for (int i = nDone; i < event.size(); ++i) {
302  if (event[i].status() != statusNow) {
303  nDone = i;
304  break;
305  }
306  eHadronSum += 0.5 * event[i].e();
307 
308  // Step up to parton pair that spans hadron midpoint energy.
309  while (eHadronSum > ePartonSum && iEnd > nStop) {
310  eHadronSum -= ePartonSum;
311  --iBeg;
312  --iEnd;
313  eBeg = eEnd;
314  eEnd = (event[iEnd].isGluon() ? 0.5 : 1.) * event[iEnd].e();
315  ePartonSum = eBeg + eEnd;
316  }
317 
318  // If only one parton left then set entirely by it, else weight.
319  if (eHadronSum > ePartonSum || iBeg == nStop) {
320  event[i].vProdAdd( event[nStop].vProd() );
321  } else {
322  double eFrac = clamp( eHadronSum / ePartonSum, 0., 1.);
323  Vec4 vShift = (1. - eFrac) * event[iBeg].vProd()
324  + eFrac * event[iEnd].vProd();
325  event[i].vProdAdd( vShift);
326  }
327  eHadronSum += 0.5 * event[i].e();
328  }
329  }
330 
331  // Initial values for last leg.
332  int nStop = (iOrdSys[2] == 0) ? iFirst : iNotG[iOrdSys[2] - 1] + 1;
333  int iBeg = iNotG[iOrdSys[2]];
334  int iEnd = max(iBeg - 1, nStop);
335  double eBeg = event[iBeg].e();
336  double eEnd = (event[iEnd].isGluon() ? 0.5 : 1.) * event[iEnd].e();
337  double ePartonSum = eBeg + eEnd;
338  double eHadronSum = 0.;
339 
340  // Loop over primary hadrons in last leg.
341  for (int i = nDone; i < event.size(); ++i) {
342  eHadronSum += 0.5 * event[i].e();
343 
344  // Step up to parton pair that spans hadron midpoint energy.
345  while (eHadronSum > ePartonSum && iEnd > nStop) {
346  eHadronSum -= ePartonSum;
347  --iBeg;
348  --iEnd;
349  eBeg = eEnd;
350  eEnd = (event[iEnd].isGluon() ? 0.5 : 1.) * event[iEnd].e();
351  ePartonSum = eBeg + eEnd;
352  }
353 
354  // If only one parton left then set entirely by it, else weight.
355  // Warning: could do better for junction baryon and hadron after it.
356  if (eHadronSum > ePartonSum) {
357  event[i].vProdAdd( event[nStop].vProd() );
358  } else {
359  double eFrac = clamp( eHadronSum / ePartonSum, 0., 1.);
360  Vec4 vShift = (1. - eFrac) * event[iBeg].vProd()
361  + eFrac * event[iEnd].vProd();
362  event[i].vProdAdd( vShift);
363  }
364  eHadronSum += 0.5 * event[i].e();
365  }
366 
367 }
368 
369 
370 //==========================================================================
371 
372 } // end namespace Pythia8
Definition: AgUStep.h:26