StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
StringFragmentation.cc
1 // StringFragmentation.cc is a part of the PYTHIA event generator.
2 // Copyright (C) 2018 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 StringEnd and
7 // StringFragmentation classes.
8 
9 #include "Pythia8/StringFragmentation.h"
10 
11 namespace Pythia8 {
12 
13 //==========================================================================
14 
15 // The StringEnd class.
16 
17 //--------------------------------------------------------------------------
18 
19 // Constants: could be changed here if desired, but normally should not.
20 
21 // Avoid unphysical solutions to equation system.
22 const double StringEnd::TINY = 1e-6;
23 
24 // Assume two (eX, eY) regions are related if pT2 differs by less.
25 const double StringEnd::PT2SAME = 0.01;
26 
27 // Fictitious typical mass and pT used to look ahead for approximately where
28 // the next hadron will be produced, to quantify string close-packing there.
29 const double StringEnd::MEANMMIN = 0.2;
30 const double StringEnd::MEANM = 0.8;
31 const double StringEnd::MEANPT = 0.4;
32 
33 //--------------------------------------------------------------------------
34 
35 // Set up initial endpoint values from input.
36 
37 void StringEnd::setUp(bool fromPosIn, int iEndIn, int idOldIn, int iMaxIn,
38  double pxIn, double pyIn, double GammaIn, double xPosIn, double xNegIn) {
39 
40  // Simple transcription from input.
41  fromPos = fromPosIn;
42  iEnd = iEndIn;
43  iMax = iMaxIn;
44  flavOld = FlavContainer(idOldIn);
45  pxOld = pxIn;
46  pyOld = pyIn;
47  GammaOld = GammaIn;
48  iPosOld = (fromPos) ? 0 : iMax;
49  iNegOld = (fromPos) ? iMax : 0;
50  xPosOld = xPosIn;
51  xNegOld = xNegIn;
52 
53 }
54 
55 //--------------------------------------------------------------------------
56 
57 // Fragment off one hadron from the string system, in flavour and pT.
58 
59 void StringEnd::newHadron(double nNSP) {
60 
61  // In case we are using the thermal model or Gaussian with
62  // mT2 suppression we have to pick the pT first.
63  if (thermalModel || mT2suppression) {
64 
65  // Pick its transverse momentum.
66  pair<double, double> pxy = pTSelPtr->pxy(flavNew.id, nNSP);
67  pxNew = pxy.first;
68  pyNew = pxy.second;
69  pxHad = pxOld + pxNew;
70  pyHad = pyOld + pyNew;
71  double pT2Had = pow2(pxHad) + pow2(pyHad);
72 
73  // Pick new flavour and form a new hadron.
74  do {
75  flavNew = flavSelPtr->pick( flavOld, sqrt(pT2Had), nNSP);
76  idHad = flavSelPtr->getHadronID( flavOld, flavNew);
77  } while (idHad == 0);
78 
79  // Get its mass and thereby define its transverse mass.
80  mHad = flavSelPtr->getHadronMassWin(idHad);
81  mT2Had = pow2(mHad) + pow2(pxHad) + pow2(pyHad);
82  }
83 
84  // In case of the Gaussian without mT2 suppression we pick
85  // the new flavour first to make the width flavour dependent.
86  else {
87 
88  // Pick new flavour and form a new hadron.
89  do {
90  flavNew = flavSelPtr->pick( flavOld);
91  idHad = flavSelPtr->combine( flavOld, flavNew);
92  } while (idHad == 0);
93 
94  // Pick its transverse momentum.
95  pair<double, double> pxy = pTSelPtr->pxy(flavNew.id, nNSP);
96  pxNew = pxy.first;
97  pyNew = pxy.second;
98  pxHad = pxOld + pxNew;
99  pyHad = pyOld + pyNew;
100 
101  // Pick its mass and thereby define its transverse mass.
102  mHad = particleDataPtr->mSel(idHad);
103  mT2Had = pow2(mHad) + pow2(pxHad) + pow2(pyHad);
104  }
105 
106 }
107 
108 //--------------------------------------------------------------------------
109 
110 // Fragment off one hadron from the string system, in momentum space,
111 // by taking steps from positive end.
112 
113 Vec4 StringEnd::kinematicsHadron( StringSystem& system,
114  vector<StringVertex>& stringVertices, bool useInputZ, double zHadIn) {
115 
116  // Pick fragmentation step z and calculate new Gamma.
117  if (useInputZ) zHad = zHadIn;
118  else zHad = zSelPtr->zFrag( flavOld.id, flavNew.id, mT2Had);
119  GammaNew = (1. - zHad) * (GammaOld + mT2Had / zHad);
120 
121  // Set up references that are direction-neutral;
122  // ...Dir for direction of iteration and ...Inv for its inverse.
123  int& iDirOld = (fromPos) ? iPosOld : iNegOld;
124  int& iInvOld = (fromPos) ? iNegOld : iPosOld;
125  int& iDirNew = (fromPos) ? iPosNew : iNegNew;
126  int& iInvNew = (fromPos) ? iNegNew : iPosNew;
127  double& xDirOld = (fromPos) ? xPosOld : xNegOld;
128  double& xInvOld = (fromPos) ? xNegOld : xPosOld;
129  double& xDirNew = (fromPos) ? xPosNew : xNegNew;
130  double& xInvNew = (fromPos) ? xNegNew : xPosNew;
131  double& xDirHad = (fromPos) ? xPosHad : xNegHad;
132  double& xInvHad = (fromPos) ? xNegHad : xPosHad;
133 
134  // Start search for new breakup in the old region.
135  iDirNew = iDirOld;
136  iInvNew = iInvOld;
137  Vec4 pTNew;
138 
139  // Each step corresponds to trying a new string region.
140  for (int iStep = 0; ; ++iStep) {
141 
142  // Referance to current string region.
143  StringRegion& region = system.region( iPosNew, iNegNew);
144 
145  // Now begin special section for rapid processing of low region.
146  if (iStep == 0 && iPosOld + iNegOld == iMax) {
147 
148  // A first step within a low region is easy.
149  if (mT2Had < zHad * xDirOld * (1. - xInvOld) * region.w2) {
150 
151  // Translate into x coordinates.
152  xDirHad = zHad * xDirOld;
153  xInvHad = mT2Had / (xDirHad * region.w2);
154  xDirNew = xDirOld - xDirHad;
155  xInvNew = xInvOld + xInvHad;
156 
157  // Store breakup vertex information from the fragmentation process.
158  stringVertices.push_back( StringVertex( fromPos, iPosNew, iNegNew,
159  xPosNew, xNegNew) );
160 
161  // Find and return four-momentum of the produced particle.
162  return region.pHad( xPosHad, xNegHad, pxHad, pyHad);
163 
164  // A first step out of a low region also OK, if there are more regions.
165  // Negative energy signals failure, i.e. in last region.
166  } else {
167  --iInvNew;
168  if (iInvNew < 0) return Vec4(0., 0., 0., -1.);
169 
170  // Momentum taken by stepping out of region. Continue to next region.
171  xInvHad = 1. - xInvOld;
172  xDirHad = 0.;
173  pSoFar = region.pHad( xPosHad, xNegHad, pxOld, pyOld);
174  continue;
175  }
176 
177  // Else, for first step, take into account starting pT.
178  } else if (iStep == 0) {
179  pSoFar = region.pHad( 0., 0., pxOld, pyOld);
180  pTNew = region.pHad( 0., 0., pxNew, pyNew);
181  }
182 
183 
184  // Now begin normal treatment of nontrivial regions.
185  // Set up four-vectors in a region not visited before.
186  if (!region.isSetUp) region.setUp(
187  system.regionLowPos(iPosNew).pPos,
188  system.regionLowNeg(iNegNew).pNeg, true);
189 
190  // If new region is vanishingly small, continue immediately to next.
191  // Negative energy signals failure to do this, i.e. moved too low.
192  if (region.isEmpty) {
193  xDirHad = (iDirNew == iDirOld) ? xDirOld : 1.;
194  xInvHad = 0.;
195  pSoFar += region.pHad( xPosHad, xNegHad, 0., 0.);
196  ++iDirNew;
197  if (iDirNew + iInvNew > iMax) return Vec4(0., 0., 0., -1.);
198  continue;
199  }
200 
201  // Reexpress pTNew w.r.t. base vectors in new region, if possible.
202  // Recall minus sign from normalization e_x^2 = e_y^2 = -1.
203  double pxNewTemp = -pTNew * region.eX;
204  double pyNewTemp = -pTNew * region.eY;
205  if (abs( pxNewTemp * pxNewTemp + pyNewTemp * pyNewTemp
206  - pxNew * pxNew - pyNew * pyNew) < PT2SAME) {
207  pxNew = pxNewTemp;
208  pyNew = pyNewTemp;
209  }
210 
211  // Four-momentum taken so far, including new pT.
212  Vec4 pTemp = pSoFar + region.pHad( 0., 0., pxNew, pyNew);
213 
214  // Derive coefficients for m2 expression.
215  // cM2 * x+ + cM3 * x- + cM4 * x+ * x- = m^2 - cM1;
216  double cM1 = pTemp.m2Calc();
217  double cM2 = 2. * (pTemp * region.pPos);
218  double cM3 = 2. * (pTemp * region.pNeg);
219  double cM4 = region.w2;
220  if (!fromPos) swap( cM2, cM3);
221 
222  // Derive coefficients for Gamma expression.
223  // cGam2 * x+ + cGam3 * x- + cGam4 * x+ * x- = Gamma_new - cGam1;
224  double cGam1 = 0.;
225  double cGam2 = 0.;
226  double cGam3 = 0.;
227  double cGam4 = 0.;
228  for (int iInv = iInvNew; iInv <= iMax - iDirNew; ++iInv) {
229  double xInv = 1.;
230  if (iInv == iInvNew) xInv = (iInvNew == iInvOld) ? xInvOld : 0.;
231  for (int iDir = iDirNew; iDir <= iMax - iInv; ++iDir) {
232  double xDir = (iDir == iDirOld) ? xDirOld : 1.;
233  int iPos = (fromPos) ? iDir : iInv;
234  int iNeg = (fromPos) ? iInv : iDir;
235  StringRegion& regionGam = system.region( iPos, iNeg);
236  if (!regionGam.isSetUp) regionGam.setUp(
237  system.regionLowPos(iPos).pPos,
238  system.regionLowNeg(iNeg).pNeg, true);
239  double w2 = regionGam.w2;
240  cGam1 += xDir * xInv * w2;
241  if (iDir == iDirNew) cGam2 -= xInv * w2;
242  if (iInv == iInvNew) cGam3 += xDir * w2;
243  if (iDir == iDirNew && iInv == iInvNew) cGam4 -= w2;
244  }
245  }
246 
247  // Solve (m2, Gamma) equation system => r2 * x-^2 + r1 * x- + r0 = 0.
248  double cM0 = pow2(mHad) - cM1;
249  double cGam0 = GammaNew - cGam1;
250  double r2 = cM3 * cGam4 - cM4 * cGam3;
251  double r1 = cM4 * cGam0 - cM0 * cGam4 + cM3 * cGam2 - cM2 * cGam3;
252  double r0 = cM2 * cGam0 - cM0 * cGam2;
253  double root = sqrtpos( r1*r1 - 4. * r2 * r0 );
254  if (abs(r2) < TINY || root < TINY) return Vec4(0., 0., 0., -1.);
255  xInvHad = 0.5 * (root / abs(r2) - r1 / r2);
256  if (abs(cM2 + cM4 * xInvHad) < TINY) return Vec4(0., 0., 0., -1.);
257  xDirHad = (cM0 - cM3 * xInvHad) / (cM2 + cM4 * xInvHad);
258 
259  // Define position of new trial vertex.
260  xDirNew = (iDirNew == iDirOld) ? xDirOld - xDirHad : 1. - xDirHad;
261  xInvNew = (iInvNew == iInvOld) ? xInvOld + xInvHad : xInvHad;
262 
263  // Step up to new region if new x- > 1.
264  if (xInvNew > 1.) {
265  xInvHad = (iInvNew == iInvOld) ? 1. - xInvOld : 1.;
266  xDirHad = 0.;
267  pSoFar += region.pHad( xPosHad, xNegHad, 0., 0.);
268  --iInvNew;
269  if (iInvNew < 0) return Vec4(0., 0., 0., -1.);
270  continue;
271 
272  // Step down to new region if new x+ < 0.
273  } else if (xDirNew < 0.) {
274  xDirHad = (iDirNew == iDirOld) ? xDirOld : 1.;
275  xInvHad = 0.;
276  pSoFar += region.pHad( xPosHad, xNegHad, 0., 0.);
277  ++iDirNew;
278  if (iDirNew + iInvNew > iMax) return Vec4(0., 0., 0., -1.);
279  continue;
280  }
281 
282  // Store breakup vertex information from the fragmentation process.
283  stringVertices.push_back( StringVertex( fromPos, iPosNew, iNegNew,
284  xPosNew, xNegNew) );
285 
286  // Else we have found the correct region, and can return four-momentum.
287  if (useInputZ) return Vec4( 0., 0., 0., 0.);
288  else return pSoFar + region.pHad( xPosHad, xNegHad, pxNew, pyNew);
289 
290  // End of "infinite" loop of stepping to new region.
291  }
292 
293 }
294 
295 //--------------------------------------------------------------------------
296 
297 // Generate momentum for some possible next hadron, based on mean values
298 // to get an estimate for rapidity and pT.
299 
300 Vec4 StringEnd::kinematicsHadronTmp( StringSystem system, Vec4 pRem,
301  double phi, double mult) {
302 
303  // Now estimate the energy the next hadron will take.
304  double mRem = pRem.mCalc();
305  double meanM = (mRem > 0.0) ? max( MEANMMIN, min( MEANM, mRem) ) : MEANM;
306  double meanMT2 = pow2(meanM) + pow2(MEANPT);
307  double GammaNow = (1.0 + aLund) / bLund;
308  // Modify Gamma value in case of ealier fails.
309  if (mult > 0.0) GammaNow *= mult;
310  double tmp = ( GammaNow + meanMT2 - GammaOld ) / GammaOld;
311  double zPlus = (-0.5 * tmp + sqrt(0.25 * pow2(tmp) + meanMT2 / GammaOld));
312  double zMinus = (-0.5 * tmp - sqrt(0.25 * pow2(tmp) + meanMT2 / GammaOld));
313  // Special case of first hadron.
314  if (GammaOld < 1e-10) {
315  zPlus = pow(1.0 + meanMT2 / GammaNow, -1.0);
316  zMinus = -1.0;
317  }
318  bool zPlusOk = (zPlus < 1.0) && (zPlus > 0.0);
319  bool zMinusOk = (zMinus < 1.0) && (zMinus > 0.0);
320  // Negative energy signals failure.
321  if ( (!zPlusOk) && (!zMinusOk) ) return Vec4(0., 0., 0., -1.);
322  double zHadTmp = (zPlusOk ? zPlus : zMinus);
323 
324  double pxHadTmp = cos(phi) * MEANPT;
325  double pyHadTmp = sin(phi) * MEANPT;
326 
327  // First make a copy of all variables to not overwrite anything.
328  int iPosOldTmp = iPosOld, iNegOldTmp = iNegOld;
329  int iPosNewTmp = iPosNew, iNegNewTmp = iNegNew;
330  double xPosOldTmp = xPosOld, xNegOldTmp = xNegOld;
331  double xPosNewTmp = xPosNew, xNegNewTmp = xNegNew;
332  double xPosHadTmp = xPosHad, xNegHadTmp = xNegHad;
333  double pxNewTmp = pxNew, pxOldTmp = pxOld;
334  double pyNewTmp = pyNew, pyOldTmp = pyOld;
335 
336  Vec4 pSoFarTmp = pSoFar;
337 
338  // Set up references that are direction-neutral;
339  // ...Dir for direction of iteration and ...Inv for its inverse.
340  int& iDirOld = (fromPos) ? iPosOldTmp : iNegOldTmp;
341  int& iInvOld = (fromPos) ? iNegOldTmp : iPosOldTmp;
342  int& iDirNew = (fromPos) ? iPosNewTmp : iNegNewTmp;
343  int& iInvNew = (fromPos) ? iNegNewTmp : iPosNewTmp;
344  double& xDirOld = (fromPos) ? xPosOldTmp : xNegOldTmp;
345  double& xInvOld = (fromPos) ? xNegOldTmp : xPosOldTmp;
346  double& xDirNew = (fromPos) ? xPosNewTmp : xNegNewTmp;
347  double& xInvNew = (fromPos) ? xNegNewTmp : xPosNewTmp;
348  double& xDirHad = (fromPos) ? xPosHadTmp : xNegHadTmp;
349  double& xInvHad = (fromPos) ? xNegHadTmp : xPosHadTmp;
350 
351  // Start search for new breakup in the old region.
352  iDirNew = iDirOld;
353  iInvNew = iInvOld;
354  Vec4 pTNew;
355 
356  // Each step corresponds to trying a new string region.
357  for (int iStep = 0; ; ++iStep) {
358 
359  // Referance to current string region.
360  StringRegion region = system.region( iPosNewTmp, iNegNewTmp);
361 
362  // Now begin special section for rapid processing of low region.
363  if (iStep == 0 && iPosOldTmp + iNegOldTmp == iMax) {
364 
365  // A first step within a low region is easy. Make sure we use this
366  // region in case it's the last one.
367  if ( (meanMT2 < zHadTmp * xDirOld * (1. - xInvOld) * region.w2)
368  || (iInvNew - 1 < 0) ) {
369 
370  if (iInvNew - 1 < 0)
371  zHadTmp = meanMT2 / xDirOld / (1. - xInvOld) / region.w2;
372 
373  // Translate into x coordinates.
374  xDirHad = zHad * xDirOld;
375  xInvHad = meanMT2 / (xDirHad * region.w2);
376  xDirNew = xDirOld - xDirHad;
377  xInvNew = xInvOld + xInvHad;
378 
379  // Find and return four-momentum of the produced particle.
380  return region.pHad( xPosHadTmp, xNegHadTmp, pxHadTmp, pyHadTmp);
381 
382  // A first step out of a low region also OK, if there are more regions.
383  // Negative energy signals failure, i.e. in last region.
384  } else {
385  --iInvNew;
386  // Should be covered by the above check.
387  if (iInvNew < 0) return Vec4(0., 0., 0., -1.);
388 
389  // Momentum taken by stepping out of region. Continue to next region.
390  xInvHad = 1. - xInvOld;
391  xDirHad = 0.;
392  pSoFarTmp = region.pHad( xPosHadTmp, xNegHadTmp, pxOldTmp, pyOldTmp);
393  continue;
394  }
395 
396  // Else, for first step, take into account starting pT.
397  } else if (iStep == 0) {
398  pSoFarTmp = region.pHad( 0., 0., pxOldTmp, pyOldTmp);
399  pTNew = region.pHad( 0., 0., pxNewTmp, pyNewTmp);
400  }
401 
402  // Now begin normal treatment of nontrivial regions.
403  // Set up four-vectors in a region not visited before.
404  if (!region.isSetUp) region.setUp(
405  system.regionLowPos(iPosNewTmp).pPos,
406  system.regionLowNeg(iNegNewTmp).pNeg, true);
407 
408  // If new region is vanishingly small, continue immediately to next.
409  // Negative energy signals failure to do this, i.e. moved too low.
410  if (region.isEmpty) {
411  xDirHad = (iDirNew == iDirOld) ? xDirOld : 1.;
412  xInvHad = 0.;
413  pSoFarTmp += region.pHad( xPosHadTmp, xNegHadTmp, 0., 0.);
414  ++iDirNew;
415  if (iDirNew + iInvNew > iMax) return Vec4(0., 0., 0., -1.);
416  continue;
417  }
418 
419  // Reexpress pTNew w.r.t. base vectors in new region, if possible.
420  // Recall minus sign from normalization e_x^2 = e_y^2 = -1.
421  double pxNewRegNow = -pTNew * region.eX;
422  double pyNewRegNow = -pTNew * region.eY;
423  if (abs( pxNewRegNow * pxNewRegNow + pyNewRegNow * pyNewRegNow
424  - pxNewTmp * pxNewTmp - pyNewTmp * pyNewTmp) < PT2SAME) {
425  pxNewTmp = pxNewRegNow;
426  pyNewTmp = pyNewRegNow;
427  }
428 
429  // Four-momentum taken so far, including new pT.
430  Vec4 pTemp = pSoFarTmp + region.pHad( 0., 0., pxNewTmp, pyNewTmp);
431 
432  // Derive coefficients for m2 expression.
433  // cM2 * x+ + cM3 * x- + cM4 * x+ * x- = m^2 - cM1;
434  double cM1 = pTemp.m2Calc();
435  double cM2 = 2. * (pTemp * region.pPos);
436  double cM3 = 2. * (pTemp * region.pNeg);
437  double cM4 = region.w2;
438  if (!fromPos) swap( cM2, cM3);
439 
440  // Derive coefficients for Gamma expression.
441  // cGam2 * x+ + cGam3 * x- + cGam4 * x+ * x- = Gamma_new - cGam1;
442  double cGam1 = 0.;
443  double cGam2 = 0.;
444  double cGam3 = 0.;
445  double cGam4 = 0.;
446  for (int iInv = iInvNew; iInv <= iMax - iDirNew; ++iInv) {
447  double xInv = 1.;
448  if (iInv == iInvNew) xInv = (iInvNew == iInvOld) ? xInvOld : 0.;
449  for (int iDir = iDirNew; iDir <= iMax - iInv; ++iDir) {
450  double xDir = (iDir == iDirOld) ? xDirOld : 1.;
451  int iPos = (fromPos) ? iDir : iInv;
452  int iNeg = (fromPos) ? iInv : iDir;
453  StringRegion regionGam = system.region( iPos, iNeg);
454  if (!regionGam.isSetUp) regionGam.setUp(
455  system.regionLowPos(iPos).pPos,
456  system.regionLowNeg(iNeg).pNeg, true);
457  double w2 = regionGam.w2;
458  cGam1 += xDir * xInv * w2;
459  if (iDir == iDirNew) cGam2 -= xInv * w2;
460  if (iInv == iInvNew) cGam3 += xDir * w2;
461  if (iDir == iDirNew && iInv == iInvNew) cGam4 -= w2;
462  }
463  }
464 
465  // Solve (m2, Gamma) equation system => r2 * x-^2 + r1 * x- + r0 = 0.
466  double cM0 = pow2(meanM) - cM1;
467  double cGam0 = GammaNow - cGam1;
468  double r2 = cM3 * cGam4 - cM4 * cGam3;
469  double r1 = cM4 * cGam0 - cM0 * cGam4 + cM3 * cGam2 - cM2 * cGam3;
470  double r0 = cM2 * cGam0 - cM0 * cGam2;
471  double root = sqrtpos( r1*r1 - 4. * r2 * r0 );
472  if (abs(r2) < TINY || root < TINY) return Vec4(0., 0., 0., -1.);
473  xInvHad = 0.5 * (root / abs(r2) - r1 / r2);
474  xDirHad = (cM0 - cM3 * xInvHad) / (cM2 + cM4 * xInvHad);
475 
476  // Define position of new trial vertex.
477  xDirNew = (iDirNew == iDirOld) ? xDirOld - xDirHad : 1. - xDirHad;
478  xInvNew = (iInvNew == iInvOld) ? xInvOld + xInvHad : xInvHad;
479 
480  // Step up to new region if new x- > 1.
481  if (xInvNew > 1.) {
482  xInvHad = (iInvNew == iInvOld) ? 1. - xInvOld : 1.;
483  xDirHad = 0.;
484  pSoFarTmp += region.pHad( xPosHadTmp, xNegHadTmp, 0., 0.);
485  --iInvNew;
486  if (iInvNew < 0) return Vec4(0., 0., 0., -1.);
487  continue;
488 
489  // Step down to new region if new x+ < 0.
490  } else if (xDirNew < 0.) {
491  xDirHad = (iDirNew == iDirOld) ? xDirOld : 1.;
492  xInvHad = 0.;
493  pSoFarTmp += region.pHad( xPosHadTmp, xNegHadTmp, 0., 0.);
494  ++iDirNew;
495  if (iDirNew + iInvNew > iMax) return Vec4(0., 0., 0., -1.);
496  continue;
497  }
498 
499  // Else we have found the correct region, and can return four-momentum.
500  return pSoFarTmp + region.pHad( xPosHadTmp, xNegHadTmp, pxNewTmp,
501  pyNewTmp);
502 
503  // End of "infinite" loop of stepping to new region.
504  }
505 
506 }
507 
508 //--------------------------------------------------------------------------
509 
510 // Update string end information after a hadron has been removed.
511 
512 void StringEnd::update() {
513 
514  flavOld.anti(flavNew);
515  iPosOld = iPosNew;
516  iNegOld = iNegNew;
517  pxOld = -pxNew;
518  pyOld = -pyNew;
519  GammaOld = GammaNew;
520  xPosOld = xPosNew;
521  xNegOld = xNegNew;
522 
523 }
524 
525 //==========================================================================
526 
527 // The StringFragmentation class.
528 
529 //--------------------------------------------------------------------------
530 
531 // Constants: could be changed here if desired, but normally should not.
532 // These are of technical nature, as described for each.
533 
534 // Maximum number of tries to (flavour-, energy) join the two string ends.
535 const int StringFragmentation::NTRYFLAV = 10;
536 const int StringFragmentation::NTRYJOIN = 30;
537 
538 // The last few times gradually increase the stop mass to make it easier.
539 const int StringFragmentation::NSTOPMASS = 15;
540 const double StringFragmentation::FACSTOPMASS = 1.05;
541 
542 // For closed string, pick a Gamma by taking a step with fictitious mass.
543 const double StringFragmentation::CLOSEDM2MAX = 25.;
544 const double StringFragmentation::CLOSEDM2FRAC = 0.1;
545 
546 // Do not allow too large argument to exp function.
547 const double StringFragmentation::EXPMAX = 50.;
548 
549 // Matching criterion that p+ and p- not the same (can happen in gg loop).
550 const double StringFragmentation::MATCHPOSNEG = 1e-4;
551 
552 // For pull on junction, do not trace too far down each leg.
553 const double StringFragmentation::EJNWEIGHTMAX = 10.;
554 
555 // Iterate junction rest frame boost until convergence or too many tries.
556 const double StringFragmentation::CONVJNREST = 1e-5;
557 const int StringFragmentation::NTRYJNREST = 20;
558 
559 // Fail and try again when two legs combined to diquark (3 loops).
560 const int StringFragmentation::NTRYJNMATCH = 20;
561 const double StringFragmentation::EEXTRAJNMATCH = 0.5;
562 const double StringFragmentation::MDIQUARKMIN = -2.0;
563 
564 // Consider junction-leg parton as massless if m2 tiny.
565 const double StringFragmentation::M2MAXJRF = 1e-4;
566 
567 // Protect against numerical precision giving zero or negative m2.
568 const double StringFragmentation::M2MINJRF = 1e-4;
569 
570 // Iterate junction rest frame equation until convergence or too many tries.
571 const double StringFragmentation::CONVJRFEQ = 1e-12;
572 const int StringFragmentation::NTRYJRFEQ = 40;
573 
574 // Check that breakup vertex does not have negative tau^2 or t within roundoff.
575 const double StringFragmentation::CHECKPOS = 1e-10;
576 
577 //--------------------------------------------------------------------------
578 
579 // Initialize and save pointers.
580 
581 void StringFragmentation::init(Info* infoPtrIn, Settings& settings,
582  ParticleData* particleDataPtrIn, Rndm* rndmPtrIn, StringFlav* flavSelPtrIn,
583  StringPT* pTSelPtrIn, StringZ* zSelPtrIn, FlavourRope* flavRopePtrIn,
584  UserHooks* userHooksPtrIn) {
585 
586  // Save pointers.
587  infoPtr = infoPtrIn;
588  particleDataPtr = particleDataPtrIn;
589  rndmPtr = rndmPtrIn;
590  flavSelPtr = flavSelPtrIn;
591  pTSelPtr = pTSelPtrIn;
592  zSelPtr = zSelPtrIn;
593  flavRopePtr = flavRopePtrIn;
594  userHooksPtr = userHooksPtrIn;
595 
596  // Initialize the StringFragmentation class.
597  stopMass = zSelPtr->stopMass();
598  stopNewFlav = zSelPtr->stopNewFlav();
599  stopSmear = zSelPtr->stopSmear();
600  eNormJunction = settings.parm("StringFragmentation:eNormJunction");
601  eBothLeftJunction
602  = settings.parm("StringFragmentation:eBothLeftJunction");
603  eMaxLeftJunction
604  = settings.parm("StringFragmentation:eMaxLeftJunction");
605  eMinLeftJunction
606  = settings.parm("StringFragmentation:eMinLeftJunction");
607 
608  // Calculation and definition of hadron space-time production vertices.
609  hadronVertex = settings.mode("HadronVertex:mode");
610  setVertices = settings.flag("Fragmentation:setVertices");
611  kappaVtx = settings.parm("HadronVertex:kappa");
612  smearOn = settings.flag("HadronVertex:smearOn");
613  xySmear = settings.parm("HadronVertex:xySmear");
614  constantTau = settings.flag("HadronVertex:constantTau");
615 
616  // Flavour Rope treatment.
617  doFlavRope = settings.flag("Ropewalk:RopeHadronization")
618  && settings.flag("Ropewalk:doFlavour");
619 
620  // Sanity check of flavour rope and vertex information.
621  if (doFlavRope) {
622  // Flavour ropes requires vertex information, unless an effective
623  // string tension is supplied by hand or Buffon treatment.
624  if (!settings.flag("PartonVertex:setVertex") &&
625  (!settings.flag("Ropewalk:setFixedKappa") &&
626  !settings.flag("Ropewalk:doBuffon")) ) {
627  infoPtr->errorMsg("Error in StringFragmentation::init: "
628  "failed initialization of flavour ropes");
629  }
630  }
631 
632  // Joining of nearby partons along the string.
633  mJoin = settings.parm("FragmentationSystems:mJoin");
634 
635  // Initialize the b parameter of the z spectrum, used when joining jets.
636  bLund = zSelPtr->bAreaLund();
637 
638  // Charm and bottom quark masses used for space-time offset.
639  mc = particleDataPtr->m0(4);
640  mb = particleDataPtr->m0(5);
641 
642  // MPI pT0, used for calculating effective number of strings.
643  pT20 = pow2(settings.parm("MultipartonInteractions:pT0Ref"));
644 
645  // Initialize the hadrons instance of an event record.
646  hadrons.init( "(string fragmentation)", particleDataPtr);
647 
648  // Send on pointers to the two StringEnd instances.
649  posEnd.init( particleDataPtr, flavSelPtr, pTSelPtr, zSelPtr, settings);
650  negEnd.init( particleDataPtr, flavSelPtr, pTSelPtr, zSelPtr, settings);
651 
652  // Check for number of nearby string pieces (nNSP) or not.
653  closePacking = settings.flag("StringPT:closePacking");
654 
655 }
656 
657 //--------------------------------------------------------------------------
658 
659 // Perform the fragmentation.
660 
661 bool StringFragmentation::fragment( int iSub, ColConfig& colConfig,
662  Event& event) {
663 
664  // Find partons and their total four-momentum.
665  iParton = colConfig[iSub].iParton;
666  iPos = iParton[0];
667  if (iPos < 0) iPos = iParton[1];
668  int idPos = event[iPos].id();
669  iNeg = iParton.back();
670  int idNeg = event[iNeg].id();
671  pSum = colConfig[iSub].pSum;
672 
673  // Rapidity pairs [yMin, yMax] of string piece ends.
674  vector< vector< pair<double,double> > > rapPairs = colConfig.rapPairs;
675 
676  // Reset the local event record and vertex arrays.
677  hadrons.clear();
678  stringVertices.clear();
679  legMinVertices.clear();
680  legMidVertices.clear();
681  posEnd.hadSoFar = 0;
682  negEnd.hadSoFar = 0;
683 
684  // For closed gluon string: pick first breakup region.
685  isClosed = colConfig[iSub].isClosed;
686  if (isClosed) iParton = findFirstRegion(iSub, colConfig, event);
687 
688  // For junction topology: fragment off two of the string legs.
689  // Then iParton overwritten to remaining leg + leftover diquark.
690  pJunctionHadrons = 0.;
691  hasJunction = colConfig[iSub].hasJunction;
692  if (hasJunction && !fragmentToJunction(event)) return false;
693  int junctionHadrons = hadrons.size();
694  if (hasJunction) {
695  idPos = event[ iParton[0] ].id();
696  idNeg = event.back().id();
697  pSum -= pJunctionHadrons;
698  }
699 
700  // Set a pointer to the event in Flavour Ropes.
701  if (doFlavRope) flavRopePtr->setEventPtr(event);
702 
703  // Set up kinematics of string evolution ( = motion).
704  system.setUp(iParton, event);
705  stopMassNow = stopMass;
706  int nExtraJoin = 0;
707 
708  // Fallback loop, when joining in the middle fails. Bailout if stuck.
709  for ( int iTry = 0; ; ++iTry) {
710  if (iTry > NTRYJOIN) {
711  infoPtr->errorMsg("Error in StringFragmentation::fragment: "
712  "stuck in joining");
713  if (hasJunction) ++nExtraJoin;
714  if (nExtraJoin > 0) event.popBack(nExtraJoin);
715  return false;
716  }
717 
718  // After several failed tries join some (extra) nearby partons.
719  if (iTry == NTRYJOIN / 3) nExtraJoin = extraJoin( 2., event);
720  if (iTry == 2 * NTRYJOIN / 3) nExtraJoin += extraJoin( 4., event);
721 
722  // After several failed tries gradually allow larger stop mass.
723  if (iTry > NTRYJOIN - NSTOPMASS) stopMassNow *= FACSTOPMASS;
724 
725  // Set up flavours of two string ends, and reset other info.
726  setStartEnds(idPos, idNeg, system);
727  pRem = pSum;
728 
729  // Begin fragmentation loop, interleaved from the two ends.
730  bool fromPos;
731 
732  // Variables used to help identifying baryons from junction splittings.
733  bool usedPosJun = false, usedNegJun = false;
734 
735  // Keep track of the momentum of hadrons taken from left and right.
736  Vec4 hadMomPos, hadMomNeg;
737 
738  for ( ; ; ) {
739 
740  // Take a step either from the positive or the negative end.
741  fromPos = (rndmPtr->flat() < 0.5);
742  StringEnd& nowEnd = (fromPos) ? posEnd : negEnd;
743 
744  // Check how many nearby string pieces there are for the next hadron.
745  double nNSP = (closePacking) ? nearStringPieces(nowEnd, rapPairs) : 0.;
746 
747  // The FlavourRope treatment changes the fragmentation parameters.
748  if (doFlavRope) {
749  if (!flavRopePtr->doChangeFragPar(flavSelPtr, zSelPtr, pTSelPtr,
750  (fromPos ? hadMomPos.m2Calc() : hadMomNeg.m2Calc()), iParton,
751  (fromPos ? idPos : idNeg)) )
752  infoPtr->errorMsg("Error in StringFragmentation::fragment: "
753  "FlavourRope failed to change fragmentation parameters.");
754  }
755 
756  // Possibility for a user to change the fragmentation parameters.
757  if ( (userHooksPtr != 0) && userHooksPtr->canChangeFragPar() ) {
758  if ( !userHooksPtr->doChangeFragPar( flavSelPtr, zSelPtr, pTSelPtr,
759  (fromPos ? idPos : idNeg),
760  (fromPos ? hadMomPos.m2Calc() : hadMomNeg.m2Calc()), iParton) )
761  infoPtr->errorMsg("Error in StringFragmentation::fragment: "
762  "failed to change hadronisation parameters.");
763  }
764 
765  // Construct trial hadron and check that energy remains.
766  nowEnd.newHadron(nNSP);
767 
768  if ( energyUsedUp(fromPos) ) break;
769 
770  // Construct kinematics of the new hadron and store it.
771  Vec4 pHad = nowEnd.kinematicsHadron(system, stringVertices);
772  int statusHad = (fromPos) ? 83 : 84;
773  nowEnd.hadSoFar += 1;
774 
775  // Change status code if hadron from junction.
776  if (abs(nowEnd.idHad) > 1000 && abs(nowEnd.idHad) < 10000) {
777  if (fromPos && event[iPos].statusAbs() == 74 && !usedPosJun) {
778  statusHad = 87;
779  usedPosJun = true;
780  }
781  if (!fromPos && event[iNeg].statusAbs() == 74 && !usedNegJun) {
782  statusHad = 88;
783  usedNegJun = true;
784  }
785  if (!fromPos && hasJunction && !usedNegJun) {
786  statusHad = 88;
787  usedNegJun = true;
788  }
789  }
790 
791  // Possibility for a user to veto the hadron production.
792  if ( (userHooksPtr != 0) && userHooksPtr->canChangeFragPar() ) {
793  // Provide full particle info for veto decision.
794  if ( userHooksPtr->doVetoFragmentation( Particle( nowEnd.idHad,
795  statusHad, iPos, iNeg, 0, 0, 0, 0, pHad, nowEnd.mHad) ) )
796  continue;
797  }
798 
799  // Bookkeeping of momentum taken away.
800  if (fromPos) hadMomPos += pHad;
801  else hadMomNeg += pHad;
802 
803  // Append produced hadron.
804  hadrons.append( nowEnd.idHad, statusHad, iPos, iNeg,
805  0, 0, 0, 0, pHad, nowEnd.mHad);
806  if (pHad.e() < 0.) break;
807 
808  // Update string end and remaining momentum.
809  nowEnd.update();
810  pRem -= pHad;
811 
812  // End of fragmentation loop.
813  }
814 
815  // Check how many nearby string pieces there are for the last hadron.
816  double nNSP = (closePacking) ? nearStringPieces(
817  ((rndmPtr->flat() < 0.5) ? posEnd : negEnd), rapPairs) : 0.;
818 
819  // When done, join in the middle. If this works, then really done.
820  if ( finalTwo(fromPos, event, usedPosJun, usedNegJun, nNSP) ) break;
821 
822  // Else remove produced particles (except from first two junction legs)
823  // and start all over.
824  int newHadrons = hadrons.size() - junctionHadrons;
825  hadrons.popBack(newHadrons);
826  stringVertices.clear();
827  posEnd.hadSoFar = 0;
828  negEnd.hadSoFar = 0;
829  }
830 
831  // Junctions & extra joins: remove fictitious end, restore original partons.
832  if (hasJunction) ++nExtraJoin;
833  if (nExtraJoin > 0) {
834  event.popBack(nExtraJoin);
835  iParton = colConfig[iSub].iParton;
836  }
837 
838  // Store the hadrons in the normal event record, ordered from one end.
839  store(event);
840 
841  // Store hadron production space-time vertices.
842  if (setVertices) setHadronVertices( event);
843 
844  // Done.
845  return true;
846 
847 }
848 
849 //--------------------------------------------------------------------------
850 
851 // Find region where to put first string break for closed gluon loop.
852 
853 vector<int> StringFragmentation::findFirstRegion(int iSub,
854  ColConfig& colConfig, Event& event) {
855 
856  // Partons and their total four-momentum.
857  vector<int> iPartonIn = colConfig[iSub].iParton;
858 
859  // Evaluate mass-squared for all adjacent gluon pairs.
860  vector<double> m2Pair;
861  double m2Sum = 0.;
862  int size = iPartonIn.size();
863  for (int i = 0; i < size; ++i) {
864  double m2Now = 0.5 * event[ iPartonIn[i] ].p()
865  * event[ iPartonIn[(i + 1)%size] ].p();
866  m2Pair.push_back(m2Now);
867  m2Sum += m2Now;
868  }
869 
870  // Pick breakup region with probability proportional to mass-squared.
871  double m2Reg = m2Sum * rndmPtr->flat();
872  int iReg = -1;
873  do m2Reg -= m2Pair[++iReg];
874  while (m2Reg > 0. && iReg < size - 1);
875 
876  // Create reordered parton list, with breakup string region duplicated.
877  vector<int> iPartonOut;
878  for (int i = 0; i < size + 2; ++i)
879  iPartonOut.push_back( iPartonIn[(i + iReg)%size] );
880 
881  // Done.
882  return iPartonOut;
883 
884 }
885 
886 //--------------------------------------------------------------------------
887 
888 // Set flavours and momentum position for initial string endpoints.
889 
890 void StringFragmentation::setStartEnds(int idPos, int idNeg,
891 StringSystem systemNow, int legNow) {
892 
893  // Variables characterizing string endpoints: defaults for open string.
894  double px = 0.;
895  double py = 0.;
896  double Gamma = 0.;
897  double xPosFromPos = 1.;
898  double xNegFromPos = 0.;
899  double xPosFromNeg = 0.;
900  double xNegFromNeg = 1.;
901 
902  // For closed gluon loop need to pick an initial flavour.
903  if (isClosed) {
904  do {
905  int idTry = flavSelPtr->pickLightQ();
906  FlavContainer flavTry(idTry, 1);
907  flavTry = flavSelPtr->pick( flavTry);
908  flavTry = flavSelPtr->pick( flavTry);
909  idPos = flavTry.id;
910  idNeg = -idPos;
911  } while (idPos == 0);
912 
913  // Also need pT and breakup vertex position in region.
914  pair<double, double> pxy = pTSelPtr->pxy(idPos);
915  px = pxy.first;
916  py = pxy.second;
917  double m2Region = systemNow.regionLowPos(0).w2;
918  double m2Temp = min( CLOSEDM2MAX, CLOSEDM2FRAC * m2Region);
919  do {
920  double zTemp = zSelPtr->zFrag( idPos, idNeg, m2Temp);
921  xPosFromPos = 1. - zTemp;
922  xNegFromPos = m2Temp / (zTemp * m2Region);
923  } while (xNegFromPos > 1.);
924  Gamma = xPosFromPos * xNegFromPos * m2Region;
925  xPosFromNeg = xPosFromPos;
926  xNegFromNeg = xNegFromPos;
927  }
928 
929  // Initialize two string endpoints.
930  posEnd.setUp( true, iPos, idPos, systemNow.iMax, px, py,
931  Gamma, xPosFromPos, xNegFromPos);
932  negEnd.setUp( false, iNeg, idNeg, systemNow.iMax, -px, -py,
933  Gamma, xPosFromNeg, xNegFromNeg);
934 
935  // Store breakup vertex information from the first and last points.
936  if (setVertices) {
937  if (legNow == legMin) legMinVertices.push_back(
938  StringVertex( true, 0, systemNow.iMax, xPosFromPos, xNegFromPos) );
939  else if (legNow == legMid) legMidVertices.push_back(
940  StringVertex( true, 0, systemNow.iMax, xPosFromPos, xNegFromPos) );
941  else {
942  stringVertices.push_back(
943  StringVertex (true, 0, systemNow.iMax, xPosFromPos, xNegFromPos) );
944  stringVertices.push_back(
945  StringVertex( false, systemNow.iMax, 0, xPosFromNeg, xNegFromNeg) );
946  }
947  }
948  // For closed gluon loop can allow popcorn on one side but not both.
949  if (isClosed) {
950  flavSelPtr->assignPopQ(posEnd.flavOld);
951  flavSelPtr->assignPopQ(negEnd.flavOld);
952  if (rndmPtr->flat() < 0.5) posEnd.flavOld.nPop = 0;
953  else negEnd.flavOld.nPop = 0;
954  posEnd.flavOld.rank = 1;
955  negEnd.flavOld.rank = 1;
956  }
957 
958  // Done.
959 
960 }
961 
962 //--------------------------------------------------------------------------
963 
964 // Check remaining energy-momentum whether it is OK to continue.
965 
966 bool StringFragmentation::energyUsedUp(bool fromPos) {
967 
968  // If remaining negative energy then abort right away.
969  if (pRem.e() < 0.) return true;
970 
971  // Calculate W2_minimum and done if remaining W2 is below it.
972  double wMin = stopMassNow
973  + particleDataPtr->constituentMass(posEnd.flavOld.id)
974  + particleDataPtr->constituentMass(negEnd.flavOld.id);
975  if (fromPos) wMin += stopNewFlav
976  * particleDataPtr->constituentMass(posEnd.flavNew.id);
977  else wMin += stopNewFlav
978  * particleDataPtr->constituentMass(negEnd.flavNew.id);
979  wMin *= 1. + (2. * rndmPtr->flat() - 1.) * stopSmear;
980  w2Rem = pRem.m2Calc();
981  if (w2Rem < pow2(wMin)) return true;
982 
983  // Else still enough energy left to continue iteration.
984  return false;
985 
986 }
987 
988 
989 //--------------------------------------------------------------------------
990 
991 // Store hadron production vertices in the event record.
992 
993 void StringFragmentation::setHadronVertices( Event& event) {
994 
995  // Order breakup points from one end to the other.
996  int vertexSize = stringVertices.size();
997  vector<StringVertex> orderedVertices;
998  for (int i = 0; i < vertexSize; ++i) if (stringVertices[i].fromPos)
999  orderedVertices.push_back( stringVertices[i] );
1000  for (int i = vertexSize - 1; i >= 0; --i) if (!stringVertices[i].fromPos)
1001  orderedVertices.push_back( stringVertices[i] );
1002 
1003  // Obtain space-time picture for breakup points.
1004  vector<Vec4> longitudinal;
1005  int finalSpacePos = 0;
1006  int finalVertexPos = 0;
1007  vector<int> iPartonIn = (hasJunction) ? iPartonMax : iParton;
1008  int id1 = event[ iPartonIn.front() ].idAbs();
1009  int id2 = (hasJunction) ? idDiquark : event[ iPartonIn.back() ].idAbs();
1010  int iHadJunc = (hasJunction)
1011  ? legMinVertices.size() + legMidVertices.size() - 2 : 0;
1012 
1013  // Loop over ordered vertices to obtain longitudinal space-time locations.
1014  for (int i = 0; i < vertexSize; ++i) {
1015  int iPosIn = orderedVertices[i].iRegPos;
1016  int iNegIn = orderedVertices[i].iRegNeg;
1017  double xPosIn = orderedVertices[i].xRegPos;
1018  double xNegIn = orderedVertices[i].xRegNeg;
1019 
1020  // Special case for complicated solutions in finalTwo: calculated after.
1021  if (iPosIn == -1 && iNegIn == -1) {
1022  longitudinal.push_back( Vec4( 0., 0., 0., 0.) );
1023  finalSpacePos = i + 1;
1024  finalVertexPos = i;
1025 
1026  // The normal cases.
1027  } else {
1028  StringRegion currentRegion = system.region( iPosIn, iNegIn);
1029  Vec4 gluonOffset = currentRegion.gluonOffset( iPartonIn, event, iPosIn,
1030  iNegIn) / kappaVtx;
1031  Vec4 noOffset = (xPosIn * currentRegion.pPos +
1032  xNegIn * currentRegion.pNeg) / kappaVtx;
1033  Vec4 pRegion = (currentRegion.pPos + currentRegion.pNeg) / kappaVtx;
1034 
1035  // Correction added to the space-time location of breakup points
1036  // if negative squared invariant time.
1037  if (noOffset.m2Calc() < 0.) {
1038  double cPlus = (-pRegion * noOffset + sqrt( pow2(pRegion*noOffset)
1039  - pRegion.m2Calc() * noOffset.m2Calc())) / pRegion.m2Calc();
1040  Vec4 pCorrection = noOffset + cPlus * pRegion;
1041  Vec4 fracCorrection;
1042  bool betterFrac = false;
1043  if (xPosIn < 0. || xPosIn > 1. || xNegIn < 0. || xNegIn > 1.) {
1044  xPosIn = max( 0., min( 1., xPosIn) );
1045  xNegIn = max( 0., min( 1., xNegIn) );
1046  fracCorrection = (xPosIn * currentRegion.pPos
1047  + xNegIn * currentRegion.pNeg) / kappaVtx;
1048  betterFrac = abs(noOffset.e() - pCorrection.e())
1049  > abs(noOffset.e() - fracCorrection.e());
1050  }
1051  noOffset = (betterFrac) ? fracCorrection : pCorrection;
1052  }
1053  // Store vertex and check positivity.
1054  Vec4 fromBreaks = noOffset + gluonOffset;
1055  longitudinal.push_back(fromBreaks);
1056  if (fromBreaks.m2Calc() < -CHECKPOS * max(1., pow2(fromBreaks.e())))
1057  infoPtr->errorMsg("Warning in StringFragmentation::setVertices: "
1058  "negative tau^2 from breaks");
1059  }
1060  }
1061 
1062  // Breakup longitudinal space--time location for the special finalTwo case.
1063  if (finalSpacePos != 0) {
1064  double xPosIn = orderedVertices[finalVertexPos].xRegPos;
1065  double xNegIn = orderedVertices[finalVertexPos].xRegNeg;
1066  Vec4 v1 = longitudinal[finalSpacePos - 1];
1067  Vec4 v2 = longitudinal[finalSpacePos + 1];
1068  Vec4 vl = v1 - v2;
1069  Vec4 vk = pPosFinalReg + pNegFinalReg;
1070  double r = 0.;
1071  if (vl.m2Calc() > 0.) {
1072  Vec4 va = ( 0.5 * (v1 + v2) + xPosIn * pPosFinalReg +
1073  xNegIn * pNegFinalReg ) / kappaVtx;
1074  r = (va * vk - sqrt(pow2(va * vk) - vk.m2Calc() * va.m2Calc()) )
1075  / vk.m2Calc();
1076  } else r = 0.5 * sqrt(-vl.m2Calc() / vk.m2Calc());
1077  longitudinal[finalSpacePos] = ( 0.5 * (v1 + v2) + (xPosIn - r)
1078  * pPosFinalReg + (xNegIn - r) * pNegFinalReg ) / kappaVtx;
1079  }
1080 
1081  // Longitudinal offset of breakup points for massive quarks.
1082  for (int i = 0; i < vertexSize; ++i) {
1083  int iPosIn = orderedVertices[i].iRegPos;
1084  int iNegIn = orderedVertices[i].iRegNeg;
1085  if (iPosIn != -1) {
1086  StringRegion currentRegion = system.region( iPosIn, iNegIn);
1087  if ( currentRegion.massiveOffset( iPosIn, iNegIn, system.iMax,
1088  id1, id2, mc, mb) ) {
1089  Vec4 v1, v2;
1090 
1091  // Initial endpoint correction.
1092  if (i == 0 && (id1 == 4 || id1 == 5)) {
1093  int iPosIn2 = orderedVertices[i + 1].iRegPos;
1094  int iNegIn2 = orderedVertices[i + 1].iRegNeg;
1095  v2 = longitudinal[i + 1];
1096  double mHad = event[event.size() + iHadJunc - hadrons.size()].m();
1097  double pPosMass = particleDataPtr->m0(id1);
1098  if (iPosIn == iPosIn2 && iNegIn == iNegIn2) {
1099  v1 = longitudinal[i];
1100  longitudinal[i] = v1 + (pPosMass / mHad) * (v2 - v1);
1101  if (longitudinal[i].m2Calc()
1102  < -CHECKPOS * max(1., pow2(longitudinal[i].e())))
1103  infoPtr->errorMsg("Warning in StringFragmentation::setVertices: "
1104  "negative tau^2 for endpoint massive correction");
1105  } else {
1106  StringRegion region2 = system.region( iPosIn2, iNegIn2);
1107  Vec4 gluonOffset = currentRegion.gluonOffset( iPartonIn, event,
1108  iPosIn, iNegIn);
1109  v1 = (region2.pPos + gluonOffset) / kappaVtx;
1110  longitudinal[i] = v1 + (pPosMass / mHad) * (v2 - v1);
1111  if (longitudinal[i].m2Calc()
1112  < -CHECKPOS * max(1., pow2(longitudinal[i].e())))
1113  infoPtr->errorMsg("Warning in StringFragmentation::setVertices: "
1114  "negative tau^2 for endpoint massive correction");
1115  continue;
1116  }
1117  }
1118 
1119  // Final endpoint correction.
1120  if (i == vertexSize - 1 && (id2 == 4 || id2 == 5) && !hasJunction) {
1121  int iPosIn2 = orderedVertices[i - 1].iRegPos;
1122  int iNegIn2 = orderedVertices[i - 1].iRegNeg;
1123  double mHad = event[i - 1 + event.size() + iHadJunc
1124  - hadrons.size()].m();
1125  double pNegMass = particleDataPtr->m0(id2);
1126  if (iPosIn == iPosIn2 && iNegIn == iNegIn2) {
1127  v1 = longitudinal[i];
1128  v2 = longitudinal[i - 1] + currentRegion.massOffset / kappaVtx;
1129  longitudinal[i] = v1 + (pNegMass / mHad) * (v2 - v1);
1130  if (longitudinal[i].m2Calc()
1131  < -CHECKPOS * max(1., pow2(longitudinal[i].e())))
1132  infoPtr->errorMsg("Warning in StringFragmentation::setVertices: "
1133  "negative tau^2 for endpoint massive correction");
1134 
1135  } else {
1136  StringRegion region2 = system.region( iPosIn2, iNegIn2);
1137  Vec4 gluonOffset = currentRegion.gluonOffset( iPartonIn, event,
1138  iPosIn, iNegIn);
1139  v1 = (region2.pNeg + gluonOffset) / kappaVtx;
1140  v2 = longitudinal[i - 1];
1141  longitudinal[i] = v1 + (pNegMass / mHad) * (v2 - v1);
1142  if (longitudinal[i].m2Calc()
1143  < -CHECKPOS * max(1., pow2(longitudinal[i].e())))
1144  infoPtr->errorMsg("Warning in StringFragmentation::setVertices: "
1145  "negative tau^2 for endpoint massive correction");
1146  continue;
1147  }
1148  }
1149 
1150  // Add mass offset for all breakup points.
1151  Vec4 massOffset = currentRegion.massOffset / kappaVtx;
1152  Vec4 position = longitudinal[i] - massOffset;
1153  if (position.m2Calc() < 0.) {
1154  double cMinus = 0.;
1155  if (position.m2Calc() > -CHECKPOS * max(1., pow2(position.e())))
1156  position.e( position.pAbs() );
1157  else {
1158  if (massOffset.m2Calc() > 1e-6)
1159  cMinus = (longitudinal[i] * massOffset
1160  - sqrt(pow2(longitudinal[i] * massOffset)
1161  - longitudinal[i].m2Calc() * massOffset.m2Calc()))
1162  / massOffset.m2Calc();
1163  else cMinus = 0.5 * longitudinal[i].m2Calc()
1164  / (longitudinal[i] * massOffset);
1165  position = longitudinal[i] - cMinus * massOffset;
1166  }
1167  }
1168  longitudinal[i] = position;
1169  }
1170  }
1171  }
1172 
1173  // Smearing in transverse space.
1174  vector<Vec4> spaceTime;
1175  for (int i = 0; i < vertexSize; ++i) {
1176  Vec4 positionTot = longitudinal[i];
1177  if (smearOn) {
1178 
1179  if (!isClosed && (i == 0 || i == vertexSize -1)) {
1180  spaceTime.push_back(positionTot);
1181  continue;
1182  }
1183  Vec4 eX, eY;
1184  int iPosIn = orderedVertices[i].iRegPos;
1185  int iNegIn = orderedVertices[i].iRegNeg;
1186 
1187  // Find two spacelike transverse four-vector directions.
1188  if (iPosIn == -1 && iNegIn == -1) {
1189  eX = eXFinalReg;
1190  eY = eYFinalReg;
1191  } else{
1192  StringRegion currentRegion = system.region(iPosIn, iNegIn);
1193  eX = currentRegion.eX;
1194  eY = currentRegion.eY;
1195  }
1196 
1197  // Smearing calculated randomly following a gaussian.
1198  for (int iTry = 0; ; ++iTry) {
1199  double transX = rndmPtr -> gauss();
1200  double transY = rndmPtr -> gauss();
1201  Vec4 transversePos = xySmear * (transX * eX + transY * eY) / sqrt(2.);
1202  positionTot = transversePos + longitudinal[i];
1203 
1204  // Keep proper or actual time constant when including the smearing.
1205  if (constantTau) {
1206  double newtime = sqrt(longitudinal[i].m2Calc()
1207  + positionTot.pAbs2());
1208  positionTot.e(newtime);
1209  break;
1210  } else {
1211  if (positionTot.m2Calc() >= 0.) break;
1212  if (iTry == 100) {
1213  positionTot = longitudinal[i];
1214  break;
1215  }
1216  }
1217  }
1218  }
1219  spaceTime.push_back(positionTot);
1220  }
1221 
1222  // Space-time location of the breakup points in two initial junction legs.
1223  vector<Vec4> legMinSpaceTime, legMidSpaceTime;
1224  if (hasJunction) {
1225  int hadSoFar = 0;
1226  // Loop over the two lowest-energy legs.
1227  for (int legLoop = 0; legLoop < 2; ++legLoop) {
1228  vector<StringVertex> legVertices = (legLoop == 0) ? legMinVertices
1229  : legMidVertices;
1230  StringSystem& systemNow = (legLoop == 0) ? systemMin : systemMid;
1231  vector<int> iPartonNow = (legLoop == 0) ? iPartonMinLeg : iPartonMidLeg;
1232  vector<Vec4> longitudinalPos;
1233  if (int(legVertices.size()) < 2) continue;
1234 
1235  // Loop over all breakup points of the specific leg.
1236  for (int i = 0; i < int(legVertices.size()); i++) {
1237 
1238  // Obtain longitudinal space-time location of breakup vertices.
1239  int iPosIn = legVertices[i].iRegPos;
1240  int iNegIn = legVertices[i].iRegNeg;
1241  double xPosIn = legVertices[i].xRegPos;
1242  double xNegIn = legVertices[i].xRegNeg;
1243  StringRegion currentRegion = systemNow.region( iPosIn, iNegIn);
1244  Vec4 gluonOffset = currentRegion.gluonOffsetJRF( iPartonNow, event,
1245  iPosIn, iNegIn, MtoJRF) / kappaVtx;
1246  Vec4 pRegion = (currentRegion.pPos + currentRegion.pNeg) / kappaVtx;
1247  Vec4 noOffset = (xPosIn * currentRegion.pPos
1248  + xNegIn * currentRegion.pNeg) / kappaVtx;
1249 
1250  // Correction added to the space-time location of breakup points
1251  // if negative squared invariant time.
1252  if (noOffset.m2Calc() < 0.) {
1253  double cPlus = (-pRegion * noOffset + sqrt( pow2(pRegion * noOffset)
1254  - pRegion.m2Calc() * noOffset.m2Calc())) / pRegion.m2Calc();
1255  Vec4 pCorrection = noOffset + cPlus * pRegion;
1256  Vec4 fracCorrection;
1257  bool betterFrac = false;
1258  if (xPosIn < 0. || xPosIn > 1. || xNegIn < 0. || xNegIn > 1.) {
1259  xPosIn = max( 0., min( 1., xPosIn) );
1260  xNegIn = max( 0., min( 1., xNegIn) );
1261  fracCorrection = (xPosIn * currentRegion.pPos
1262  + xNegIn * currentRegion.pNeg) / kappaVtx;
1263  betterFrac = abs(noOffset.e() - pCorrection.e())
1264  > abs(noOffset.e() - fracCorrection.e());
1265  }
1266  noOffset = (betterFrac) ? fracCorrection : pCorrection;
1267  }
1268  // Store vertex and check positivity.
1269  Vec4 fromBreaks = noOffset + gluonOffset;
1270  longitudinalPos.push_back(fromBreaks);
1271  if (fromBreaks.m2Calc() < -CHECKPOS * max(1., pow2(fromBreaks.e())))
1272  infoPtr->errorMsg("Warning in StringFragmentation::setVertices: "
1273  "negative tau^2 from breaks");
1274  }
1275 
1276  // Longitudinal offset of breakup points for massive quarks.
1277  for (int i = 0; i < int(legVertices.size()); ++i) {
1278  int iPosIn = legVertices[i].iRegPos;
1279  int iNegIn = legVertices[i].iRegNeg;
1280  StringRegion currentRegion = systemNow.region( iPosIn, iNegIn);
1281  int id = event[ iPartonNow.front() ].idAbs();
1282 
1283  if ( currentRegion.massiveOffset( iPosIn, iNegIn, systemNow.iMax,
1284  id, 2, mc, mb) ) {
1285  Vec4 v1, v2;
1286  // Initial endpoint correction.
1287  if (i == 0 && (id == 4 || id == 5)) {
1288  int iPosIn2 = legVertices[i + 1].iRegPos;
1289  int iNegIn2 = legVertices[i + 1].iRegNeg;
1290  v2 = longitudinalPos[i + 1];
1291  double mHad = event[hadSoFar + event.size() - hadrons.size()].m();
1292  double pPosMass = particleDataPtr->m0(id);
1293  if (iPosIn == iPosIn2 && iNegIn == iNegIn2) {
1294  v1 = longitudinalPos[i];
1295  longitudinalPos[i] = v1 + (pPosMass / mHad) * (v2 - v1);
1296  if (longitudinalPos[i].m2Calc()
1297  < -CHECKPOS * max(1., pow2(longitudinalPos[i].e())))
1298  infoPtr->errorMsg("Warning in StringFragmentation::setVertices"
1299  ": negative tau^2 for endpoint massive correction");
1300  } else {
1301  StringRegion region2 = systemNow.region( iPosIn2, iNegIn2);
1302  Vec4 gluonOffset = currentRegion.gluonOffsetJRF( iPartonNow,
1303  event, iPosIn2, iNegIn2, MtoJRF);
1304  v1 = (region2.pPos + gluonOffset) / kappaVtx;
1305  longitudinalPos[i] = v1 + (pPosMass / mHad) * (v2 - v1);
1306  if (longitudinalPos[i].m2Calc()
1307  < -CHECKPOS * max(1., pow2(longitudinalPos[i].e())))
1308  infoPtr->errorMsg("Warning in StringFragmentation::setVertices"
1309  ": negative tau^2 for endpoint massive correction");
1310  continue;
1311  }
1312  }
1313 
1314  // Add mass offset for all breakup points.
1315  Vec4 massOffset = currentRegion.massOffset / kappaVtx;
1316  Vec4 position = longitudinalPos[i] - massOffset;
1317 
1318  // Correction for non-physical situations.
1319  if (position.m2Calc() < 0.) {
1320  double cMinus = 0.;
1321  if (position.m2Calc() > -CHECKPOS * max(1., pow2(position.e())))
1322  position.e( position.pAbs() );
1323  else {
1324  if (massOffset.m2Calc() > 1e-6)
1325  cMinus = (longitudinalPos[i] * massOffset
1326  - sqrt(pow2(longitudinalPos[i] * massOffset)
1327  - longitudinalPos[i].m2Calc() * massOffset.m2Calc()))
1328  / massOffset.m2Calc();
1329  else cMinus = 0.5 * longitudinalPos[i].m2Calc()
1330  / (longitudinalPos[i] * massOffset);
1331  position = longitudinalPos[i] - cMinus * massOffset;
1332  }
1333  }
1334  longitudinalPos[i] = position;
1335  }
1336  }
1337 
1338  for (int i = 0; i < int(legVertices.size()); ++i) {
1339  Vec4 positionTot = longitudinalPos[i];
1340 
1341  // Smearing in transverse space.
1342  if (smearOn) {
1343  int iPosIn = legVertices[i].iRegPos;
1344  int iNegIn = legVertices[i].iRegNeg;
1345  StringRegion currentRegion = systemNow.region( iPosIn, iNegIn);
1346  Vec4 eX = currentRegion.eX;
1347  Vec4 eY = currentRegion.eY;
1348  for (int iTry = 0; ; ++iTry) {
1349  double transX = rndmPtr->gauss();
1350  double transY = rndmPtr->gauss();
1351  Vec4 transversePos = xySmear * (transX * eX + transY * eY)
1352  / sqrt(2.);
1353  positionTot = transversePos + longitudinalPos[i];
1354 
1355  // Keep proper or actual time constant when including the smearing.
1356  if (constantTau) {
1357  double newtime = sqrt( longitudinalPos[i].m2Calc()
1358  + positionTot.pAbs2());
1359  positionTot.e(newtime);
1360  break;
1361  } else {
1362  if (positionTot.m2Calc() >= 0.) break;
1363  if (iTry == 100) {
1364  positionTot = longitudinalPos[i];
1365  break;
1366  }
1367  }
1368  }
1369  }
1370 
1371  // Boost from the rest frame of the junction to the original frame.
1372  positionTot.rotbst(MfromJRF);
1373 
1374  // Recalculate time to compensate for numerical precision loss
1375  // in iterative calculation of MfromJRF. Store final result.
1376  if (positionTot.m2Calc() < 0.)
1377  positionTot.e( positionTot.pAbs() );
1378  if (legLoop == 0) legMinSpaceTime.push_back(positionTot);
1379  else legMidSpaceTime.push_back(positionTot);
1380 
1381  // End of loops over all breakup point of two lowest-energy legs.
1382  }
1383  hadSoFar = hadSoFar + legVertices.size() - 1;
1384  }
1385  }
1386 
1387  // Calculate hadron production points of the two initial junction legs.
1388  if (hasJunction) {
1389  int hadSoFar = 0;
1390 
1391  // Loop over the two lowest-energy legs.
1392  for (int legLoop = 0; legLoop < 2; legLoop++) {
1393  vector<Vec4>& finalLocation = (legLoop == 0) ? legMinSpaceTime
1394  : legMidSpaceTime;
1395  vector<int> iPartonNow = (legLoop == 0) ? iPartonMinLeg : iPartonMidLeg;
1396  int id = event[iPartonNow.front()].idAbs();
1397 
1398  // Calculate hadron production points from breakup vertices
1399  // using one of the three definitions.
1400  for (int i = 0; i < int(finalLocation.size()) - 1; ++i) {
1401  Vec4 middlePoint = 0.5 * (finalLocation[i] + finalLocation[i + 1]);
1402  int iHad = i + hadSoFar + event.size() - hadrons.size();
1403  Vec4 pHad = event[iHad].p();
1404  Vec4 prodPoints = Vec4( 0., 0., 0., 0.);
1405 
1406  // Reduced oscillation period if system contains massive quarks.
1407  double mHad = event[iHad].m();
1408  double redOsc = (i == 0 && (id == 4 || id == 5))
1409  ? 1. - pow2(particleDataPtr->m0(id) / mHad) : 1.;
1410 
1411  // Hadron production points calculation depending on definition.
1412  if (hadronVertex == 0) prodPoints = middlePoint;
1413  else if (hadronVertex == 1)
1414  prodPoints = middlePoint + 0.5 * redOsc * pHad / kappaVtx;
1415  else {
1416  prodPoints = middlePoint - 0.5 * redOsc * pHad / kappaVtx;
1417  if (prodPoints.m2Calc() < 0.) {
1418  double midpProd = redOsc * middlePoint * pHad;
1419  double tau0fac = 2. * (midpProd - sqrt( pow2(midpProd)
1420  - middlePoint.m2Calc() * pow2(redOsc * mHad)))
1421  / pow2(redOsc * mHad);
1422  prodPoints = middlePoint - 0.5 * tau0fac * redOsc * pHad
1423  / kappaVtx;
1424  }
1425  }
1426  event[iHad].vProd( event[iHad].vProd() + prodPoints * FM2MM );
1427  }
1428  // End of the two legs loop. Number of hadrons with stored vertices.
1429  hadSoFar = hadSoFar + finalLocation.size() - 1;
1430  }
1431  }
1432 
1433  // Normal string system or last leg: calculate hadron production points
1434  // from breakup vertices using one of the three definitions.
1435  for (int i = 0; i < int(spaceTime.size()) - 1; ++i) {
1436  Vec4 middlePoint = 0.5 * (spaceTime[i] + spaceTime[i + 1]);
1437  int iHad = i + iHadJunc + event.size() - hadrons.size();
1438  Vec4 pHad = event[iHad].p();
1439  Vec4 prodPoints = Vec4( 0., 0., 0., 0.);
1440 
1441  // Reduced oscillation period if system contains massive quarks.
1442  double redOsc = 1.;
1443  double mHad = event[iHad].m();
1444  if (i == 0 && (id1 == 4 || id1 == 5))
1445  redOsc = 1. - pow2( particleDataPtr->m0(id1) / mHad);
1446  if (i == int(spaceTime.size()) - 1 && (id2 == 4 || id2 == 5))
1447  redOsc = 1. - pow2( particleDataPtr->m0(id2) / mHad);
1448 
1449  // Hadron production points calculation depending on definition.
1450  if (hadronVertex == 0) prodPoints = middlePoint;
1451  else if (hadronVertex == 1)
1452  prodPoints = middlePoint + 0.5 * redOsc * pHad / kappaVtx;
1453  else {
1454  prodPoints = middlePoint - 0.5 * redOsc * pHad / kappaVtx;
1455  if (prodPoints.m2Calc() < 0.) {
1456  double midpProd = redOsc * middlePoint * pHad;
1457  double tau0fac = 2. * ( midpProd - sqrt( pow2(midpProd)
1458  - middlePoint.m2Calc() * pow2(redOsc * mHad))) / pow2(redOsc * mHad);
1459  prodPoints = middlePoint - 0.5 * tau0fac * redOsc * pHad / kappaVtx;
1460  }
1461  }
1462  event[iHad].vProd( event[iHad].vProd() + prodPoints * FM2MM );
1463  }
1464 
1465 }
1466 
1467 //--------------------------------------------------------------------------
1468 
1469 // Produce the final two partons to complete the system.
1470 
1471 bool StringFragmentation::finalTwo(bool fromPos, Event& event,
1472  bool usedPosJun, bool usedNegJun, double nNSP) {
1473 
1474  // Check whether we went too far in p+-.
1475  if (pRem.e() < 0. || w2Rem < 0. || (hadrons.size() > 0
1476  && hadrons.back().e() < 0.) ) return false;
1477  if ( posEnd.iPosOld > negEnd.iPosOld || negEnd.iNegOld > posEnd.iNegOld)
1478  return false;
1479  if ( posEnd.iPosOld == negEnd.iPosOld && posEnd.xPosOld < negEnd.xPosOld)
1480  return false;
1481  if ( posEnd.iNegOld == negEnd.iNegOld && posEnd.xNegOld > negEnd.xNegOld)
1482  return false;
1483 
1484  // Impossible to join two diquarks.
1485  FlavContainer flav1 = (fromPos) ? posEnd.flavNew.anti() : posEnd.flavOld;
1486  FlavContainer flav2 = (fromPos) ? negEnd.flavOld : negEnd.flavNew.anti();
1487  if (flav1.isDiquark() && flav2.isDiquark()) return false;
1488 
1489  // Construct preliminary hadron pT as if no region changes.
1490  double pHadPrelim[2] = { 0.0, 0.0 };
1491  if (fromPos) {
1492  pHadPrelim[0] = negEnd.pxOld-posEnd.pxNew;
1493  pHadPrelim[1] = negEnd.pyOld-posEnd.pyNew;
1494  } else {
1495  pHadPrelim[0] = posEnd.pxOld-negEnd.pxNew;
1496  pHadPrelim[1] = posEnd.pyOld-negEnd.pyNew;
1497  }
1498  double pThadPrelim = sqrt( pow2(pHadPrelim[0]) + pow2(pHadPrelim[1]) );
1499 
1500  // Construct the final hadron from the leftover flavours. Break if stuck.
1501  int idHad;
1502  for (int iTry = 0; iTry < NTRYFLAV; ++iTry) {
1503  idHad = flavSelPtr->getHadronID( flav1, flav2, pThadPrelim, nNSP, true);
1504  if (idHad != 0) break;
1505  }
1506  if (idHad == 0) return false;
1507 
1508  // Store the final particle and its new pT, and construct its mass.
1509  if (fromPos) {
1510  negEnd.idHad = idHad;
1511  negEnd.pxNew = -posEnd.pxNew;
1512  negEnd.pyNew = -posEnd.pyNew;
1513  negEnd.mHad = flavSelPtr->getHadronMassWin(idHad);
1514  } else {
1515  posEnd.idHad = idHad;
1516  posEnd.pxNew = -negEnd.pxNew;
1517  posEnd.pyNew = -negEnd.pyNew;
1518  posEnd.mHad = flavSelPtr->getHadronMassWin(idHad);
1519  }
1520 
1521  // String region in which to do the joining.
1522  StringRegion region = finalRegion();
1523  if (region.isEmpty) return false;
1524 
1525  // Project remaining momentum along longitudinal and transverse directions.
1526  region.project( pRem);
1527  double pxRem = region.px() - posEnd.pxOld - negEnd.pxOld;
1528  double pyRem = region.py() - posEnd.pyOld - negEnd.pyOld;
1529  double xPosRem = region.xPos();
1530  double xNegRem = region.xNeg();
1531 
1532  // Share extra pT kick evenly between final two hadrons.
1533  posEnd.pxOld += 0.5 * pxRem;
1534  posEnd.pyOld += 0.5 * pyRem;
1535  negEnd.pxOld += 0.5 * pxRem;
1536  negEnd.pyOld += 0.5 * pyRem;
1537 
1538  // Construct new pT and mT of the final two particles.
1539  posEnd.pxHad = posEnd.pxOld + posEnd.pxNew;
1540  posEnd.pyHad = posEnd.pyOld + posEnd.pyNew;
1541  posEnd.mT2Had = pow2(posEnd.mHad) + pow2(posEnd.pxHad)
1542  + pow2(posEnd.pyHad);
1543  negEnd.pxHad = negEnd.pxOld + negEnd.pxNew;
1544  negEnd.pyHad = negEnd.pyOld + negEnd.pyNew;
1545  negEnd.mT2Had = pow2(negEnd.mHad) + pow2(negEnd.pxHad)
1546  + pow2(negEnd.pyHad);
1547 
1548  // Construct remaining system transverse mass.
1549  double wT2Rem = w2Rem + pow2( posEnd.pxHad + negEnd.pxHad)
1550  + pow2( posEnd.pyHad + negEnd.pyHad);
1551 
1552  // Check that kinematics possible.
1553  if ( sqrt(wT2Rem) < sqrt(posEnd.mT2Had) + sqrt(negEnd.mT2Had) )
1554  return false;
1555  double lambda2 = pow2( wT2Rem - posEnd.mT2Had - negEnd.mT2Had)
1556  - 4. * posEnd.mT2Had * negEnd.mT2Had;
1557  if (lambda2 <= 0.) return false;
1558 
1559  // Construct kinematics, as viewed in the transverse rest frame.
1560  double lambda = sqrt(lambda2);
1561  double probReverse = 1. / (1. + exp( min( EXPMAX, bLund * lambda) ) );
1562  double xpzPos = 0.5 * lambda/ wT2Rem;
1563  if (probReverse > rndmPtr->flat()) xpzPos = -xpzPos;
1564  double xmDiff = (posEnd.mT2Had - negEnd.mT2Had) / wT2Rem;
1565  double xePos = 0.5 * (1. + xmDiff);
1566  double xeNeg = 0.5 * (1. - xmDiff );
1567 
1568  // Translate this into kinematics in the string frame.
1569  Vec4 pHadPos = region.pHad( (xePos + xpzPos) * xPosRem,
1570  (xePos - xpzPos) * xNegRem, posEnd.pxHad, posEnd.pyHad);
1571  Vec4 pHadNeg = region.pHad( (xeNeg - xpzPos) * xPosRem,
1572  (xeNeg + xpzPos) * xNegRem, negEnd.pxHad, negEnd.pyHad);
1573 
1574  // Project pHadPos momentum fraction on the positive region
1575  // to obtain breakup vertices with respect to that region.
1576  if (setVertices) {
1577  StringRegion posRegion = system.region( posEnd.iPosOld, posEnd.iNegOld);
1578  posRegion.project(pHadPos);
1579  double xFromPosPos = posEnd.xPosOld - posRegion.xPos();
1580  double xFromPosNeg = posEnd.xNegOld + posRegion.xNeg();
1581 
1582  // Same, but projecting pHadNeg fractions on the negative region.
1583  StringRegion negRegion = system.region( negEnd.iPosOld, negEnd.iNegOld);
1584  negRegion.project(pHadNeg);
1585  double xFromNegPos = negEnd.xPosOld + negRegion.xPos();
1586  double xFromNegNeg = negEnd.xNegOld - negRegion.xNeg();
1587 
1588  // Store energy-momentum coordinates for the final breakup vertex.
1589  // If projections give valid results, store them as breakup fractions.
1590  if (xFromPosPos > 0. && xFromPosPos < 1. && xFromPosNeg > 0.
1591  && xFromPosNeg < 1.) stringVertices.push_back( StringVertex(
1592  fromPos, posEnd.iPosOld, posEnd.iNegOld, xFromPosPos, xFromPosNeg) );
1593  else if (xFromNegPos > 0. && xFromNegPos < 1. && xFromNegNeg > 0.
1594  && xFromNegNeg < 1.) stringVertices.push_back( StringVertex(
1595  fromPos, negEnd.iPosOld, negEnd.iNegOld, xFromNegPos, xFromNegNeg) );
1596 
1597  // If above procedures do not work, calculate a new zHad and use
1598  // the kinematicsHadron method, first from the positive end.
1599  else {
1600  double gammaPosOld = posEnd.GammaOld;
1601  double gammaNegOld = negEnd.GammaOld;
1602  double zNewReg = 0.;
1603  if (posEnd.hadSoFar == 0) zNewReg = wT2Rem / (wT2Rem + gammaNegOld);
1604  else zNewReg = 0.5 * ( sqrt( pow2(wT2Rem + gammaNegOld - gammaPosOld)
1605  + 4. * wT2Rem * gammaPosOld) - (wT2Rem + gammaNegOld - gammaPosOld) )
1606  / gammaPosOld;
1607  double zHad = (xePos + xpzPos) * zNewReg;
1608  Vec4 proof = posEnd.kinematicsHadron(system, stringVertices, true, zHad);
1609 
1610  // Try negative-end kinematicsHadron method if positive-end one failed.
1611  if (proof.e() < -1e-8) {
1612  if (negEnd.hadSoFar == 0) zNewReg = wT2Rem / (wT2Rem + gammaPosOld);
1613  else zNewReg = 0.5 * ( sqrt( pow2(wT2Rem + gammaPosOld - gammaNegOld)
1614  + 4. * wT2Rem * gammaNegOld) - (wT2Rem + gammaPosOld - gammaNegOld) )
1615  / gammaNegOld;
1616  zHad = (xeNeg + xpzPos) * zNewReg;
1617  proof = negEnd.kinematicsHadron( system, stringVertices, true, zHad);
1618 
1619  // As last resort use the final region created by the finalTwo method.
1620  if (proof.e() < -1.) {
1621  pPosFinalReg = region.pPos;
1622  pNegFinalReg = region.pNeg;
1623  eXFinalReg = region.eX;
1624  eYFinalReg = region.eY;
1625  stringVertices.push_back( StringVertex( true, -1, -1,
1626  1. - (xePos + xpzPos) * xPosRem, (xePos - xpzPos) * xNegRem) );
1627  }
1628  }
1629  }
1630  }
1631 
1632  // Update status codes for junction baryons.
1633  int statusHadPos = 83;
1634  int statusHadNeg = 84;
1635  if (fromPos) {
1636  if (abs(posEnd.idHad) > 1000 && abs(posEnd.idHad) < 10000) {
1637  if (event[iPos].statusAbs() == 74 && !usedPosJun) {
1638  statusHadPos = 87;
1639  usedPosJun = true;
1640  }
1641  }
1642  if (abs(idHad) > 1000 && abs(idHad) < 10000) {
1643  if ((!usedNegJun && (event[iNeg].statusAbs() == 74 || hasJunction))
1644  || (!usedPosJun && event[iPos].statusAbs() == 74)) {
1645  statusHadNeg = 88;
1646  }
1647  }
1648  } else {
1649  if (abs(negEnd.idHad) > 1000 && abs(negEnd.idHad) < 10000) {
1650  if (!usedNegJun && (event[iNeg].statusAbs() == 74 || hasJunction)) {
1651  statusHadNeg = 88;
1652  usedNegJun = true;
1653  }
1654  }
1655  if (abs(idHad) > 1000 && abs(idHad) < 10000) {
1656  if ((!usedNegJun && (event[iNeg].statusAbs() == 74 || hasJunction))
1657  || (!usedPosJun && event[iPos].statusAbs() == 74)) {
1658  statusHadPos = 87;
1659  }
1660  }
1661  }
1662 
1663  // Add produced particles to the event record.
1664  hadrons.append( posEnd.idHad, statusHadPos, posEnd.iEnd, negEnd.iEnd,
1665  0, 0, 0, 0, pHadPos, posEnd.mHad);
1666  hadrons.append( negEnd.idHad, statusHadNeg, posEnd.iEnd, negEnd.iEnd,
1667  0, 0, 0, 0, pHadNeg, negEnd.mHad);
1668 
1669  // It worked.
1670  return true;
1671 
1672 }
1673 
1674 //--------------------------------------------------------------------------
1675 
1676 // Construct a special joining region for the final two hadrons.
1677 
1678 StringRegion StringFragmentation::finalRegion() {
1679 
1680  // Simple case when both string ends are in the same region.
1681  if (posEnd.iPosOld == negEnd.iPosOld && posEnd.iNegOld == negEnd.iNegOld)
1682  return system.region( posEnd.iPosOld, posEnd.iNegOld);
1683 
1684  // Start out with empty region. (Empty used for error returns.)
1685  StringRegion region;
1686 
1687  // Add up all remaining p+.
1688  Vec4 pPosJoin;
1689  if ( posEnd.iPosOld == negEnd.iPosOld) {
1690  double xPosJoin = posEnd.xPosOld - negEnd.xPosOld;
1691  if (xPosJoin < 0.) return region;
1692  pPosJoin = system.regionLowPos(posEnd.iPosOld).pHad( xPosJoin, 0., 0., 0.);
1693  } else {
1694  for (int iPosNow = posEnd.iPosOld; iPosNow <= negEnd.iPosOld; ++iPosNow) {
1695  if (iPosNow == posEnd.iPosOld) pPosJoin
1696  += system.regionLowPos(iPosNow).pHad( posEnd.xPosOld, 0., 0., 0.);
1697  else if (iPosNow == negEnd.iPosOld) pPosJoin
1698  += system.regionLowPos(iPosNow).pHad( 1. - negEnd.xPosOld, 0., 0., 0.);
1699  else pPosJoin += system.regionLowPos(iPosNow).pHad( 1., 0., 0., 0.);
1700  }
1701  }
1702 
1703  // Add up all remaining p-.
1704  Vec4 pNegJoin;
1705  if ( negEnd.iNegOld == posEnd.iNegOld) {
1706  double xNegJoin = negEnd.xNegOld - posEnd.xNegOld;
1707  if (xNegJoin < 0.) return region;
1708  pNegJoin = system.regionLowNeg(negEnd.iNegOld).pHad( 0., xNegJoin, 0., 0.);
1709  } else {
1710  for (int iNegNow = negEnd.iNegOld; iNegNow <= posEnd.iNegOld; ++iNegNow) {
1711  if (iNegNow == negEnd.iNegOld) pNegJoin
1712  += system.regionLowNeg(iNegNow).pHad( 0., negEnd.xNegOld, 0., 0.);
1713  else if (iNegNow == posEnd.iNegOld) pNegJoin
1714  += system.regionLowNeg(iNegNow).pHad( 0., 1. - posEnd.xNegOld, 0., 0.);
1715  else pNegJoin += system.regionLowNeg(iNegNow).pHad( 0., 1., 0., 0.);
1716  }
1717  }
1718 
1719  // For a closed gluon loop pPosJoin == pNegJoin and the above does not work.
1720  // So reshuffle; "perfect" for g g systems, OK in general.
1721  Vec4 pTest = pPosJoin - pNegJoin;
1722  if ( abs(pTest.px()) + abs(pTest.py()) + abs(pTest.pz()) + abs(pTest.e())
1723  < MATCHPOSNEG * (pPosJoin.e() + pNegJoin.e()) ) {
1724  Vec4 delta
1725  = system.regionLowPos(posEnd.iPosOld + 1).pHad( 1., 0., 0., 0.)
1726  - system.regionLowNeg(negEnd.iNegOld + 1).pHad( 0., 1., 0., 0.);
1727  // If reshuffle did not help then pick random axis to break tie.
1728  // (Needed for low-mass q-g-qbar with q-qbar perfectly parallel.)
1729  if ( abs(delta.px()) + abs(delta.py()) + abs(delta.pz()) + abs(delta.e())
1730  < MATCHPOSNEG * (pPosJoin.e() + pNegJoin.e()) ) {
1731  double cthe = 2. * rndmPtr->flat() - 1.;
1732  double sthe = sqrtpos(1. - cthe * cthe);
1733  double phi = 2. * M_PI * rndmPtr->flat();
1734  delta = 0.5 * min( pPosJoin.e(), pNegJoin.e())
1735  * Vec4( sthe * sin(phi), sthe * cos(phi), cthe, 0.);
1736  infoPtr->errorMsg("Warning in StringFragmentation::finalRegion: "
1737  "random axis needed to break tie");
1738  }
1739  pPosJoin -= delta;
1740  pNegJoin += delta;
1741  }
1742 
1743  // Construct a new region from remaining p+ and p-.
1744  region.setUp( pPosJoin, pNegJoin);
1745  if (region.isEmpty) return region;
1746 
1747  // Project the existing pTold vectors onto the new directions.
1748  Vec4 pTposOld = system.region( posEnd.iPosOld, posEnd.iNegOld).pHad(
1749  0., 0., posEnd.pxOld, posEnd.pyOld);
1750  region.project( pTposOld);
1751  posEnd.pxOld = region.px();
1752  posEnd.pyOld = region.py();
1753  Vec4 pTnegOld = system.region( negEnd.iPosOld, negEnd.iNegOld).pHad(
1754  0., 0., negEnd.pxOld, negEnd.pyOld);
1755  region.project( pTnegOld);
1756  negEnd.pxOld = region.px();
1757  negEnd.pyOld = region.py();
1758 
1759  // Done.
1760  return region;
1761 
1762 }
1763 
1764 //--------------------------------------------------------------------------
1765 
1766 // Store the hadrons in the normal event record, ordered from one end.
1767 
1768 void StringFragmentation::store(Event& event) {
1769 
1770  // Starting position.
1771  int iFirst = event.size();
1772 
1773  // Copy straight over from first two junction legs.
1774  if (hasJunction) {
1775  for (int i = 0; i < hadrons.size(); ++i)
1776  if (hadrons[i].status() == 85 || hadrons[i].status() == 86)
1777  event.append( hadrons[i] );
1778  }
1779 
1780  // Loop downwards, copying all from the positive end.
1781  for (int i = 0; i < hadrons.size(); ++i)
1782  if (hadrons[i].status() == 83 || hadrons[i].status() == 87)
1783  event.append( hadrons[i] );
1784 
1785  // Loop upwards, copying all from the negative end.
1786  for (int i = hadrons.size() - 1; i >= 0 ; --i)
1787  if (hadrons[i].status() == 84 || hadrons[i].status() == 88)
1788  event.append( hadrons[i] );
1789 
1790  int iLast = event.size() - 1;
1791 
1792  // Set decay vertex when this is displaced.
1793  if (event[posEnd.iEnd].hasVertex()) {
1794  Vec4 vDec = event[posEnd.iEnd].vDec();
1795  for (int i = iFirst; i <= iLast; ++i) event[i].vProd( vDec );
1796  }
1797 
1798  // Set lifetime of hadrons.
1799  for (int i = iFirst; i <= iLast; ++i)
1800  event[i].tau( event[i].tau0() * rndmPtr->exp() );
1801 
1802  // Mark original partons as hadronized and set their daughter range.
1803  for (int i = 0; i < int(iParton.size()); ++i)
1804  if (iParton[i] >= 0) {
1805  event[ iParton[i] ].statusNeg();
1806  event[ iParton[i] ].daughters(iFirst, iLast);
1807  }
1808 
1809 }
1810 
1811 //--------------------------------------------------------------------------
1812 
1813 // Fragment off two of the string legs in to a junction.
1814 
1815 bool StringFragmentation::fragmentToJunction(Event& event) {
1816 
1817  // Identify range of partons on the three legs.
1818  // (Each leg begins with an iParton[i] = -(10 + 10*junctionNumber + leg),
1819  // and partons then appear ordered from the junction outwards.)
1820  int legBeg[3] = { 0, 0, 0};
1821  int legEnd[3] = { 0, 0, 0};
1822  int leg = -1;
1823  // PS (4/10/2011) Protect against invalid systems
1824  if (iParton[0] > 0) {
1825  infoPtr->errorMsg("Error in StringFragmentation::fragment"
1826  "ToJunction: iParton[0] not a valid junctionNumber");
1827  return false;
1828  }
1829  for (int i = 0; i < int(iParton.size()); ++i) {
1830  if (iParton[i] < 0) {
1831  if (leg == 2) {
1832  infoPtr->errorMsg("Error in StringFragmentation::fragment"
1833  "ToJunction: unprocessed multi-junction system");
1834  return false;
1835  }
1836  legBeg[++leg] = i + 1;
1837  }
1838  else legEnd[leg] = i;
1839  }
1840 
1841  // Iterate from system rest frame towards the junction rest frame (JRF).
1842  RotBstMatrix Mstep;
1843  MtoJRF.reset();
1844  MtoJRF.bstback(pSum);
1845  Vec4 pWTinJRF[3];
1846  int iter = 0;
1847  double errInCM = 0.;
1848 
1849  do {
1850  ++iter;
1851  // Find weighted sum of momenta on the three sides of the junction.
1852  for (leg = 0; leg < 3; ++ leg) {
1853  pWTinJRF[leg] = 0.;
1854  double eWeight = 0.;
1855  for (int i = legBeg[leg]; i <= legEnd[leg]; ++i) {
1856  Vec4 pTemp = event[ iParton[i] ].p();
1857  pTemp.rotbst(MtoJRF);
1858  pWTinJRF[leg] += pTemp * exp(-eWeight);
1859  eWeight += pTemp.e() / eNormJunction;
1860  if (eWeight > EJNWEIGHTMAX) break;
1861  }
1862  }
1863 
1864  // Store original deviation from 120 degree topology.
1865  if (iter == 1) errInCM = pow2(costheta(pWTinJRF[0], pWTinJRF[1]) + 0.5)
1866  + pow2(costheta(pWTinJRF[0], pWTinJRF[2]) + 0.5)
1867  + pow2(costheta(pWTinJRF[1], pWTinJRF[2]) + 0.5);
1868 
1869  // Check numerical instabilities in boost, use rest frame if it fails.
1870  if ( (pWTinJRF[0] + pWTinJRF[1]).m2Calc() < M2MINJRF
1871  || (pWTinJRF[0] + pWTinJRF[2]).m2Calc() < M2MINJRF
1872  || (pWTinJRF[1] + pWTinJRF[2]).m2Calc() < M2MINJRF ) {
1873  infoPtr->errorMsg("Warning in StringFragmentation::fragmentTo"
1874  "Junction: Negative invariant masses in junction rest frame");
1875  MtoJRF.reset();
1876  MtoJRF.bstback(pSum);
1877  break;
1878  }
1879  // Find new JRF from the set of weighted momenta.
1880  Mstep = junctionRestFrame( pWTinJRF[0], pWTinJRF[1], pWTinJRF[2]);
1881  // Fortran code will not take full step after the first few
1882  // iterations. How implement this in terms of an M matrix??
1883  MtoJRF.rotbst( Mstep );
1884  } while (iter < 3 || (Mstep.deviation() > CONVJNREST && iter < NTRYJNREST) );
1885 
1886  // If final deviation from 120 degrees is bigger than in CM then revert.
1887  double errInJRF = pow2(costheta(pWTinJRF[0], pWTinJRF[1]) + 0.5)
1888  + pow2(costheta(pWTinJRF[0], pWTinJRF[2]) + 0.5)
1889  + pow2(costheta(pWTinJRF[1], pWTinJRF[2]) + 0.5);
1890  if (errInJRF > errInCM + CONVJNREST) {
1891  infoPtr->errorMsg("Warning in StringFragmentation::fragmentTo"
1892  "Junction: bad convergence junction rest frame");
1893  MtoJRF.reset();
1894  MtoJRF.bstback(pSum);
1895  }
1896 
1897  // Opposite operation: boost from JRF to original system.
1898  MfromJRF = MtoJRF;
1899  MfromJRF.invert();
1900 
1901  // Sum leg four-momenta in original frame and in JRF.
1902  Vec4 pInLeg[3], pInJRF[3];
1903  for (leg = 0; leg < 3; ++leg) {
1904  pInLeg[leg] = 0.;
1905  for (int i = legBeg[leg]; i <= legEnd[leg]; ++i)
1906  pInLeg[leg] += event[ iParton[i] ].p();
1907  pInJRF[leg] = pInLeg[leg];
1908  pInJRF[leg].rotbst(MtoJRF);
1909  }
1910 
1911  // Pick the two legs with lowest energy in JRF.
1912  legMin = (pInJRF[0].e() < pInJRF[1].e()) ? 0 : 1;
1913  int legMax = 1 - legMin;
1914  if (pInJRF[2].e() < min(pInJRF[0].e(), pInJRF[1].e()) ) legMin = 2;
1915  else if (pInJRF[2].e() > max(pInJRF[0].e(), pInJRF[1].e()) ) legMax = 2;
1916  legMid = 3 - legMin - legMax;
1917 
1918  // Save info on which status codes belong with the three legs.
1919  int iJunction = (-iParton[0]) / 10 - 1;
1920  event.statusJunction( iJunction, legMin, 85);
1921  event.statusJunction( iJunction, legMid, 86);
1922  event.statusJunction( iJunction, legMax, 83);
1923 
1924  // Temporarily copy the partons on the low-energy legs, into the JRF,
1925  // in reverse order, so (anti)quark leg end first.
1926  vector<int> iPartonMin;
1927  iPartonMinLeg.clear();
1928  for (int i = legEnd[legMin]; i >= legBeg[legMin]; --i) {
1929  if (setVertices) iPartonMinLeg.push_back( iParton[i] );
1930  int iNew = event.append( event[ iParton[i] ] );
1931  event[iNew].rotbst(MtoJRF);
1932  iPartonMin.push_back( iNew );
1933  }
1934  vector<int> iPartonMid;
1935  iPartonMidLeg.clear();
1936  for (int i = legEnd[legMid]; i >= legBeg[legMid]; --i) {
1937  if (setVertices) iPartonMidLeg.push_back( iParton[i] );
1938  int iNew = event.append( event[ iParton[i] ] );
1939  event[iNew].rotbst(MtoJRF);
1940  iPartonMid.push_back( iNew );
1941  }
1942 
1943  // Find final weighted sum of momenta on each of the two legs.
1944  double eWeight = 0.;
1945  pWTinJRF[legMin] = 0.;
1946  for (int i = iPartonMin.size() - 1; i >= 0; --i) {
1947  pWTinJRF[legMin] += event[ iPartonMin[i] ].p() * exp(-eWeight);
1948  eWeight += event[ iPartonMin[i] ].e() / eNormJunction;
1949  if (eWeight > EJNWEIGHTMAX) break;
1950  }
1951  eWeight = 0.;
1952  pWTinJRF[legMid] = 0.;
1953  for (int i = iPartonMid.size() - 1; i >= 0; --i) {
1954  pWTinJRF[legMid] += event[ iPartonMid[i] ].p() * exp(-eWeight);
1955  eWeight += event[ iPartonMid[i] ].e() / eNormJunction;
1956  if (eWeight > EJNWEIGHTMAX) break;
1957  }
1958 
1959  // Define fictitious opposing partons in JRF and store as string ends.
1960  Vec4 pOppose = pWTinJRF[legMin];
1961  pOppose.flip3();
1962  int idOppose = (rndmPtr->flat() > 0.5) ? 2 : 1;
1963  if (event[ iPartonMin[0] ].col() > 0) idOppose = -idOppose;
1964  int iOppose = event.append( idOppose, 77, 0, 0, 0, 0, 0, 0,
1965  pOppose, 0.);
1966  iPartonMin.push_back( iOppose);
1967  pOppose = pWTinJRF[legMid];
1968  pOppose.flip3();
1969  idOppose = (rndmPtr->flat() > 0.5) ? 2 : 1;
1970  if (event[ iPartonMid[0] ].col() > 0) idOppose = -idOppose;
1971  iOppose = event.append( idOppose, 77, 0, 0, 0, 0, 0, 0,
1972  pOppose, 0.);
1973  iPartonMid.push_back( iOppose);
1974 
1975  // Set up kinematics of string evolution in low-energy temporary systems.
1976  systemMin.setUp(iPartonMin, event);
1977  systemMid.setUp(iPartonMid, event);
1978 
1979  // Outer fallback loop, when too little energy left for third leg.
1980  int idMin = 0;
1981  int idMid = 0;
1982  Vec4 pDiquark;
1983  for ( int iTryOuter = 0; ; ++iTryOuter) {
1984 
1985  // Middle fallback loop, when much unused energy in leg remnants.
1986  double eLeftMin = 0.;
1987  double eLeftMid = 0.;
1988  for ( int iTryMiddle = 0; ; ++iTryMiddle) {
1989 
1990  // Loop over the two lowest-energy legs.
1991  for (int legLoop = 0; legLoop < 2; ++ legLoop) {
1992  int legNow = (legLoop == 0) ? legMin : legMid;
1993 
1994  // Read in properties specific to this leg.
1995  StringSystem& systemNow = (legLoop == 0) ? systemMin : systemMid;
1996  int idPos = (legLoop == 0) ? event[ iPartonMin[0] ].id()
1997  : event[ iPartonMid[0] ].id();
1998  idOppose = (legLoop == 0) ? event[ iPartonMin.back() ].id()
1999  : event[ iPartonMid.back() ].id();
2000  double eInJRF = pInJRF[legNow].e();
2001  int statusHad = (legLoop == 0) ? 85 : 86;
2002 
2003  // Inner fallback loop, when a diquark comes in to junction.
2004  double eUsed = 0.;
2005  vector<StringVertex> junctionVertices;
2006  for ( int iTryInner = 0; ; ++iTryInner) {
2007 
2008  if (iTryInner > 2 * NTRYJNMATCH) {
2009  infoPtr->errorMsg("Error in StringFragmentation::fragment"
2010  "ToJunction: caught in junction flavour loop");
2011  event.popBack( iPartonMin.size() + iPartonMid.size() );
2012  return false;
2013  }
2014 
2015  bool needBaryon = (abs(idPos) > 10 && iTryInner > NTRYJNMATCH);
2016  double eExtra = (iTryInner > NTRYJNMATCH) ? EEXTRAJNMATCH : 0.;
2017 
2018  // Set up two string ends, and begin fragmentation loop.
2019  setStartEnds(idPos, idOppose, systemNow, legNow);
2020  eUsed = 0.;
2021  int nHadrons = 0;
2022  bool noNegE = true;
2023 
2024  // Keep track of hadron momentum.
2025  Vec4 hadMom;
2026  for ( ; ; ++nHadrons) {
2027 
2028  // The FlavourRope treatment changes the fragmentation parameters.
2029  if (doFlavRope) {
2030  if (!flavRopePtr->doChangeFragPar(flavSelPtr, zSelPtr, pTSelPtr,
2031  hadMom.m2Calc(), (legLoop == 0 ? iPartonMin : iPartonMid ),
2032  idPos )) infoPtr->errorMsg("Error in StringFragmentation::"
2033  "fragmentToJunction: FlavourRope failed to change "
2034  "fragmentation parameters.");
2035  }
2036 
2037  // Possibility for a user to change the fragmentation parameters.
2038  if ( (userHooksPtr != 0) && userHooksPtr->canChangeFragPar() ) {
2039  if ( !userHooksPtr->doChangeFragPar( flavSelPtr, zSelPtr,
2040  pTSelPtr, idPos, hadMom.m2Calc(),
2041  (legLoop == 0 ? iPartonMin : iPartonMid )) )
2042  infoPtr->errorMsg("Error in StringFragmentation::fragment"
2043  "ToJunction: failed to change hadronisation parameters.");
2044  }
2045 
2046  // Construct trial hadron from positive end.
2047  posEnd.newHadron();
2048  Vec4 pHad = posEnd.kinematicsHadron(systemNow, junctionVertices);
2049 
2050  // Negative energy signals failure in construction.
2051  if (pHad.e() < 0. ) { noNegE = false; break; }
2052 
2053  // Break if passed system midpoint ( = junction) in energy.
2054  // Exceptions: small systems, and/or with diquark end.
2055  bool delayedBreak = false;
2056  if (eUsed + pHad.e() + eExtra > eInJRF) {
2057  if (nHadrons > 0 || !needBaryon) {
2058  junctionVertices.pop_back();
2059  break;
2060  }
2061  delayedBreak = true;
2062  }
2063 
2064  // Else construct kinematics of the new hadron and store it.
2065  hadrons.append( posEnd.idHad, statusHad, iPos, iNeg,
2066  0, 0, 0, 0, pHad, posEnd.mHad);
2067 
2068  // Update hadron, string end and remaining momentum.
2069  hadMom += pHad;
2070  posEnd.update();
2071  eUsed += pHad.e();
2072 
2073  // Delayed break in small systems, and/or with diquark end.
2074  if (delayedBreak) {
2075  ++nHadrons;
2076  break;
2077  }
2078  }
2079 
2080  // Possible to produce zero hadrons if the endpoint is not a diquark.
2081  if (iTryInner > NTRYJNMATCH && !noNegE && nHadrons == 0 &&
2082  abs(idPos) < 10) break;
2083 
2084  // End of fragmentation loop. Inner loopback if ends on a diquark.
2085  if ( noNegE && abs(posEnd.flavOld.id) < 10 ) break;
2086  hadrons.popBack(nHadrons);
2087  junctionVertices.clear();
2088  if (legNow == legMin) legMinVertices.clear();
2089  else legMidVertices.clear();
2090  }
2091 
2092  // End of one-leg fragmentation. Store end quark and remnant energy.
2093  if (legNow == legMin) {
2094  idMin = posEnd.flavOld.id;
2095  eLeftMin = eInJRF - eUsed;
2096  } else {
2097  idMid = posEnd.flavOld.id;
2098  eLeftMid = eInJRF - eUsed;
2099  }
2100 
2101  // Store breakup vertices in two different vectors depending on leg.
2102  if (setVertices) {
2103  for (int i = 0; i < int(junctionVertices.size()); ++i) {
2104  if (legNow == legMin)
2105  legMinVertices.push_back( junctionVertices[i]);
2106  else legMidVertices.push_back( junctionVertices[i]);
2107  }
2108  }
2109  }
2110 
2111  // End of both-leg fragmentation.
2112  // Middle loopback if too much energy left.
2113  double eTrial = eBothLeftJunction + rndmPtr->flat() * eMaxLeftJunction;
2114  if (iTryMiddle > NTRYJNMATCH
2115  || ( min( eLeftMin, eLeftMid) < eBothLeftJunction
2116  && max( eLeftMin, eLeftMid) < eTrial ) ) break;
2117  hadrons.clear();
2118  legMinVertices.clear();
2119  legMidVertices.clear();
2120  }
2121 
2122  // Boost hadrons away from the JRF to the original frame.
2123  for (int i = 0; i < hadrons.size(); ++i) {
2124  hadrons[i].rotbst(MfromJRF);
2125 
2126  // Recalculate energy to compensate for numerical precision loss
2127  // in iterative calculation of MfromJRF.
2128  hadrons[i].e( hadrons[i].eCalc() );
2129  pJunctionHadrons += hadrons[i].p();
2130  }
2131 
2132  // Outer loopback if combined diquark mass too negative
2133  // or too little energy left in third leg.
2134  pDiquark = pInLeg[legMin] + pInLeg[legMid] - pJunctionHadrons;
2135  double m2Left = m2( pInLeg[legMax], pDiquark);
2136  if (iTryOuter > NTRYJNMATCH || (pDiquark.mCalc() > MDIQUARKMIN
2137  && m2Left > eMinLeftJunction * pInLeg[legMax].e()) ) break;
2138  hadrons.clear();
2139  legMinVertices.clear();
2140  legMidVertices.clear();
2141  pJunctionHadrons = 0.;
2142  }
2143 
2144  // Now found solution; no more loopback. Remove temporary parton copies.
2145  event.popBack( iPartonMin.size() + iPartonMid.size() );
2146 
2147  // Construct and store an effective diquark string end from the
2148  // two remnant quark ends, for temporary usage.
2149  idDiquark = flavSelPtr->makeDiquark( idMin, idMid);
2150  int iDiquark = event.append( idDiquark, 78, 0, 0, 0, 0, 0, 0,
2151  pDiquark, pDiquark.mCalc());
2152 
2153  // Find the partons on the last leg, again in reverse order.
2154  iPartonMax.clear();
2155  for (int i = legEnd[legMax]; i >= legBeg[legMax]; --i)
2156  iPartonMax.push_back( iParton[i] );
2157  iPartonMax.push_back( iDiquark );
2158 
2159  // Recluster gluons nearby to diquark end when taken too much energy.
2160  int iPsize = iPartonMax.size();
2161  double m0Diquark = event[iDiquark].m0();
2162  while (iPsize > 2) {
2163  Vec4 pGluNear = event[ iPartonMax[iPsize - 2] ].p();
2164  if ( (pDiquark + 0.5 * pGluNear).mCalc() > m0Diquark + mJoin ) break;
2165  pDiquark += pGluNear;
2166  event[iDiquark].p( pDiquark );
2167  event[iDiquark].m( pDiquark.mCalc() );
2168  iPartonMax.pop_back();
2169  --iPsize;
2170  iPartonMax[iPsize - 1] = iDiquark;
2171  }
2172 
2173  // Modify parton list to remaining leg + remnant of the first two.
2174  iParton = iPartonMax;
2175 
2176  // Done.
2177  return true;
2178 }
2179 
2180 //--------------------------------------------------------------------------
2181 
2182 // Find the boost matrix to the rest frame of a junction,
2183 // given the three respective endpoint four-momenta.
2184 
2185 RotBstMatrix StringFragmentation::junctionRestFrame(Vec4& p0, Vec4& p1,
2186  Vec4& p2) {
2187 
2188  // Calculate masses and other invariants.
2189  Vec4 pSumJun = p0 + p1 + p2;
2190  double sHat = pSumJun.m2Calc();
2191  double pp[3][3];
2192  pp[0][0] = p0.m2Calc();
2193  pp[1][1] = p1.m2Calc();
2194  pp[2][2] = p2.m2Calc();
2195  pp[0][1] = pp[1][0] = p0 * p1;
2196  pp[0][2] = pp[2][0] = p0 * p2;
2197  pp[1][2] = pp[2][1] = p1 * p2;
2198 
2199  // Requirement (eiMax)_j = pi*pj/mj < (eiMax)_k = pi*pk/mk, used below,
2200  // here rewritten as pi*pj * mk < pi*pk * mj and squared.
2201  double eMax01 = pow2(pp[0][1]) * pp[2][2];
2202  double eMax02 = pow2(pp[0][2]) * pp[1][1];
2203  double eMax12 = pow2(pp[1][2]) * pp[0][0];
2204 
2205  // Initially pick i to be the most massive parton. but allow other tries.
2206  int i = (pp[1][1] > pp[0][0]) ? 1 : 0;
2207  if (pp[2][2] > max(pp[0][0], pp[1][1])) i = 2;
2208  int j, k;
2209  double ei = 0.;
2210  double ej = 0.;
2211  double ek = 0.;
2212  for (int iTry = 0; iTry < 3; ++iTry) {
2213 
2214  // Pick j to give minimal eiMax, and k the third vector.
2215  if (i == 0) j = (eMax02 < eMax01) ? 2 : 1;
2216  else if (i == 1) j = (eMax12 < eMax01) ? 2 : 0;
2217  else j = (eMax12 < eMax02) ? 1 : 0;
2218  k = 3 - i - j;
2219 
2220  // Alternative names according to i, j, k conventions.
2221  double m2i = pp[i][i];
2222  double m2j = pp[j][j];
2223  double m2k = pp[k][k];
2224  double pipj = pp[i][j];
2225  double pipk = pp[i][k];
2226  double pjpk = pp[j][k];
2227 
2228  // Trivial to find new parton energies if all three partons are massless.
2229  if (m2i < M2MAXJRF) {
2230  ei = sqrt( 2. * pipk * pipj / (3. * pjpk) );
2231  ej = sqrt( 2. * pjpk * pipj / (3. * pipk) );
2232  ek = sqrt( 2. * pipk * pjpk / (3. * pipj) );
2233 
2234  // Else find three-momentum range for parton i and values at extremes.
2235  } else {
2236  // Minimum when i is at rest.
2237  double piMin = 0.;
2238  double eiMin = sqrt(m2i);
2239  double ejMin = pipj / eiMin;
2240  double ekMin = pipk / eiMin;
2241  double pjMin = sqrtpos( ejMin*ejMin - m2j );
2242  double pkMin = sqrtpos( ekMin*ekMin - m2k );
2243  double fMin = ejMin * ekMin + 0.5 * pjMin * pkMin - pjpk;
2244  // Maximum estimated when j + k is at rest, alternatively j at rest.
2245  double eiMax = (pipj + pipk)
2246  / sqrt( max( M2MINJRF, m2j + m2k + 2. * pjpk) );
2247  if (m2j > M2MAXJRF) eiMax = min( eiMax, pipj / sqrt(m2j) );
2248  double piMax = sqrtpos( eiMax*eiMax - m2i );
2249  double temp = eiMax*eiMax - 0.25 *piMax*piMax;
2250  double pjMax = (eiMax * sqrtpos( pipj*pipj - m2j * temp )
2251  - 0.5 * piMax * pipj) / temp;
2252  double pkMax = (eiMax * sqrtpos( pipk*pipk - m2k * temp )
2253  - 0.5 * piMax * pipk) / temp;
2254  double ejMax = sqrt(pjMax*pjMax + m2j);
2255  double ekMax = sqrt(pkMax*pkMax + m2k);
2256  double fMax = ejMax * ekMax + 0.5 * pjMax * pkMax - pjpk;
2257 
2258  // If unexpected values at upper endpoint then pick another parton.
2259  if (fMax > 0.) {
2260  int iPrel = (i + 1)%3;
2261  if (pp[iPrel][iPrel] > M2MAXJRF) {i = iPrel; continue;}
2262  ++iTry;
2263  iPrel = (i + 2)%3;
2264  if (iTry < 3 && pp[iPrel][iPrel] > M2MAXJRF) {i = iPrel; continue;}
2265  }
2266 
2267  // Start binary + linear search to find solution inside range.
2268  int iterMin = 0;
2269  int iterMax = 0;
2270  double pi = 0.5 * (piMin + piMax);
2271  for (int iter = 0; iter < NTRYJRFEQ; ++iter) {
2272 
2273  // Derive momentum of other two partons and distance to root.
2274  ei = sqrt(pi*pi + m2i);
2275  temp = ei*ei - 0.25 * pi*pi;
2276  double pj = (ei * sqrtpos( pipj*pipj - m2j * temp )
2277  - 0.5 * pi * pipj) / temp;
2278  double pk = (ei * sqrtpos( pipk*pipk - m2k * temp )
2279  - 0.5 * pi * pipk) / temp;
2280  ej = sqrt(pj*pj + m2j);
2281  ek = sqrt(pk*pk + m2k);
2282  double fNow = ej * ek + 0.5 * pj * pk - pjpk;
2283 
2284  // Replace lower or upper bound by new value.
2285  if (fNow > 0.) { ++iterMin; piMin = pi; fMin = fNow;}
2286  else {++iterMax; piMax = pi; fMax = fNow;}
2287 
2288  // Pick next i momentum to explore, hopefully closer to root.
2289  if (2 * iter < NTRYJRFEQ
2290  && (iterMin < 2 || iterMax < 2 || 4 * iter < NTRYJRFEQ))
2291  { pi = 0.5 * (piMin + piMax); continue;}
2292  if (fMin < 0. || fMax > 0. || abs(fNow) < CONVJRFEQ * sHat) break;
2293  pi = piMin + (piMax - piMin) * fMin / (fMin - fMax);
2294  }
2295 
2296  // If arrived here then either succeeded or exhausted possibilities.
2297  } break;
2298  }
2299 
2300  // Now we know the energies in the junction rest frame.
2301  double eNew[3] = { 0., 0., 0.};
2302  eNew[i] = ei;
2303  eNew[j] = ej;
2304  eNew[k] = ek;
2305 
2306  // Boost (copy of) partons to their rest frame.
2307  RotBstMatrix Mmove;
2308  Vec4 p0cm = p0;
2309  Vec4 p1cm = p1;
2310  Vec4 p2cm = p2;
2311  Mmove.bstback(pSumJun);
2312  p0cm.rotbst(Mmove);
2313  p1cm.rotbst(Mmove);
2314  p2cm.rotbst(Mmove);
2315 
2316  // Construct difference vectors and the boost to junction rest frame.
2317  Vec4 pDir01 = p0cm / p0cm.e() - p1cm / p1cm.e();
2318  Vec4 pDir02 = p0cm / p0cm.e() - p2cm / p2cm.e();
2319  double pDiff01 = pDir01.pAbs2();
2320  double pDiff02 = pDir02.pAbs2();
2321  double pDiff0102 = dot3(pDir01, pDir02);
2322  double eDiff01 = eNew[0] / p0cm.e() - eNew[1] / p1cm.e();
2323  double eDiff02 = eNew[0] / p0cm.e() - eNew[2] / p2cm.e();
2324  double denom = pDiff01 * pDiff02 - pDiff0102*pDiff0102;
2325  double coef01 = (eDiff01 * pDiff02 - eDiff02 * pDiff0102) / denom;
2326  double coef02 = (eDiff02 * pDiff01 - eDiff01 * pDiff0102) / denom;
2327  Vec4 vJunction = coef01 * pDir01 + coef02 * pDir02;
2328  vJunction.e( sqrt(1. + vJunction.pAbs2()) );
2329 
2330  // Add two boosts, giving final result.
2331  Mmove.bst(vJunction);
2332  return Mmove;
2333 
2334 }
2335 
2336 //--------------------------------------------------------------------------
2337 
2338 // When string fragmentation has failed several times,
2339 // try to join some more nearby partons.
2340 
2341 int StringFragmentation::extraJoin(double facExtra, Event& event) {
2342 
2343  // Keep on looping while pairs found below joining threshold.
2344  int nJoin = 0;
2345  int iPsize = iParton.size();
2346  while (iPsize > 2) {
2347 
2348  // Look for the pair of neighbour partons (along string) with
2349  // the smallest invariant mass (subtracting quark masses).
2350  int iJoinMin = -1;
2351  double mJoinMin = 2. * facExtra * mJoin;
2352  for (int i = 0; i < iPsize - 1; ++i) {
2353  Particle& parton1 = event[ iParton[i] ];
2354  Particle& parton2 = event[ iParton[i + 1] ];
2355  Vec4 pSumNow;
2356  pSumNow += (parton2.isGluon()) ? 0.5 * parton1.p() : parton1.p();
2357  pSumNow += (parton2.isGluon()) ? 0.5 * parton2.p() : parton2.p();
2358  double mJoinNow = pSumNow.mCalc();
2359  if (!parton1.isGluon()) mJoinNow -= parton1.m0();
2360  if (!parton2.isGluon()) mJoinNow -= parton2.m0();
2361  if (mJoinNow < mJoinMin) { iJoinMin = i; mJoinMin = mJoinNow; }
2362  }
2363 
2364  // Decide whether to join, if not finished.
2365  if (iJoinMin < 0 || mJoinMin > facExtra * mJoin) return nJoin;
2366  ++nJoin;
2367 
2368  // Create new joined parton.
2369  int iJoin1 = iParton[iJoinMin];
2370  int iJoin2 = iParton[iJoinMin + 1];
2371  int idNew = (event[iJoin1].isGluon()) ? event[iJoin2].id()
2372  : event[iJoin1].id();
2373  int colNew = event[iJoin1].col();
2374  int acolNew = event[iJoin2].acol();
2375  if (colNew == acolNew) {
2376  colNew = event[iJoin2].col();
2377  acolNew = event[iJoin1].acol();
2378  }
2379  Vec4 pNew = event[iJoin1].p() + event[iJoin2].p();
2380 
2381  // Append joined parton to event record and reduce parton list.
2382  int iNew = event.append( idNew, 73, min(iJoin1, iJoin2),
2383  max(iJoin1, iJoin2), 0, 0, colNew, acolNew, pNew, pNew.mCalc() );
2384  iParton[iJoinMin] = iNew;
2385  for (int i = iJoinMin + 1; i < iPsize - 1; ++i)
2386  iParton[i] = iParton[i + 1];
2387  iParton.pop_back();
2388  --iPsize;
2389 
2390  // Done.
2391  }
2392  return nJoin;
2393 }
2394 
2395 //--------------------------------------------------------------------------
2396 
2397 // When the next hadron is generated the temperature can be dependent on the
2398 // number of nearby string pieces with respect to the string piece the hadron
2399 // will be produced on.
2400 
2401 double StringFragmentation::nearStringPieces(StringEnd end,
2402  vector< vector< pair<double,double> > >& rapPairs) {
2403 
2404  // No modification for junctions.
2405  if (hasJunction) return 1;
2406 
2407  // Get temporary hadron momentum.
2408  double phi = 2.0 * M_PI * rndmPtr->flat();
2409  double mult = -1.0;
2410  int nTryMax = 100;
2411  double multStep = 5.0 / ((double)nTryMax/2);
2412  double multNow = 1.0 + multStep;
2413  Vec4 pHad = Vec4(0., 0., 0., -1.);
2414  for (int i = 1; i <= nTryMax; i++) {
2415  pHad = end.kinematicsHadronTmp(system, pRem, phi, mult);
2416  // If valid momentum found, done.
2417  if (pHad.e() > 0.0) break;
2418  // Set mult as multiplicative factor. Alternate between adding and
2419  // subtracting multStep.
2420  mult = 1.0;
2421  if (i%2 == 0) {
2422  mult *= multNow;
2423  multNow += multStep;
2424  } else mult /= multNow;
2425  }
2426 
2427  // In case of failure, use remnant momentum.
2428  if (pHad.e() < 0.0) pHad = pRem;
2429 
2430  // Now loop through the list of rapidity pairs and count strings
2431  // sitting at the hadron rapidity.
2432  Particle hadron = Particle();
2433  hadron.p(pHad); hadron.m(pHad.mCalc());
2434  double yHad = hadron.y();
2435  int nString = -1;
2436  for (int iSub = 0; iSub < int(rapPairs.size()); iSub++) {
2437  vector< pair<double,double> > pairNow = rapPairs[iSub];
2438  for (int iPair = 0; iPair < int(pairNow.size()); iPair++) {
2439  double y1 = pairNow[iPair].first;
2440  double y2 = pairNow[iPair].second;
2441  if ( (y1 < yHad) && (yHad < y2) ) nString++;
2442  }
2443  }
2444  // Effective number of strings takes pT into account.
2445  double pT2Had = pHad.pT2();
2446  double nStringEff = double(nString) / (1.0 + pT2Had / pT20);
2447 
2448  return nStringEff + 1.0;
2449 }
2450 
2451 //==========================================================================
2452 
2453 } // end namespace Pythia8
Definition: AgUStep.h:26