StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
ColourReconnectionHooks.h
1 // ColourReconnectionHooks.h is a part of the PYTHIA event generator.
2 // Copyright (C) 2020 Torbjorn Sjostrand.
3 // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
4 // Please respect the MCnet Guidelines, see GUIDELINES for details.
5 
6 // This file contains two UserHooks that, along with the internal models,
7 // implement all the models used for the top mass study in
8 // S. Argyropoulos and T. Sjostrand,
9 // arXiv:1407.6653 [hep-ph] (LU TP 14-23, DESY 14-134, MCnet-14-15)
10 
11 // MBReconUserHooks: can be used for all kinds of events, not only top ones.
12 // TopReconUserHooks: models intended specifically for top decay products,
13 // whereas the underlying event is handled by the default model.
14 
15 // Warning: some small modifications have been made when collecting
16 // the models, but nothing intended to change the behaviour.
17 // Note: the move model is also available with ColourReconnection:mode = 2,
18 // while the ColourReconnection:mode = 1 model has not been used here.
19 // Note: the new models tend to be slower than the default CR scenario,
20 // since they have to probe many more reconnection possibilities.
21 
22 #ifndef Pythia8_ColourReconnectionHooks_H
23 #define Pythia8_ColourReconnectionHooks_H
24 
25 // Includes
26 #include "Pythia8/Pythia.h"
27 namespace Pythia8 {
28 
29 //==========================================================================
30 
31 // Class for colour reconnection models of general validity.
32 
33 class MBReconUserHooks : public UserHooks {
34 
35 public:
36 
37  // Constructor and destructor.
38  // mode = 0: no reconnection (dummy option, does nothing);
39  // = 1: swap gluons to minimize lambda.
40  // = 2: move gluons to minimize lambda.
41  // flip = 0: no flip between quark-antiquark ends.
42  // = 1: flip between quark-antiquark ends, excluding junction systems.
43  // = 2: flip between quark-antiquark ends, including junction systems.
44  // dLamCut: smallest -delta-lambda value for which to swap/mode (positive).
45  // fracGluon: the fraction of gluons that will be studied for reconnection.
46  // m2Ref : squared reference mass scale for lambda measure calculation.
47  MBReconUserHooks(int modeIn = 0, int flipIn = 0, double dLamCutIn = 0.,
48  double fracGluonIn = 1.) : mode(modeIn), flip(flipIn), dLamCut(dLamCutIn),
49  fracGluon(fracGluonIn) { m2Ref = 1.; dLamCut = max(0., dLamCut); }
50  ~MBReconUserHooks() {}
51 
52  // Allow colour reconnection after resonance decays (early or late)...
53  virtual bool canReconnectResonanceSystems() {return true;}
54 
55  // ...which gives access to the event, for modification.
56  virtual bool doReconnectResonanceSystems( int, Event& event) {
57 
58  // Return without action for relevant mode numbers.
59  if (mode <= 0 || mode > 2) return true;
60 
61  // Double diffraction not yet implemented, so return without action.
62  // (But works for internal move implementation.)
63  if (infoPtr->isDiffractiveA() && infoPtr->isDiffractiveB())
64  return true;
65 
66  // Initial setup: relevant gluons and coloured partons.
67  if (!setupConfig( event)) return false;
68 
69  // Done if not enough gluons.
70  if ( (mode == 1 && nGlu < 2) || (mode == 2 && nGlu < 1) ) return true;
71 
72  // Colour reconnect. Return if failed.
73  bool hasRec = (mode == 1) ? doReconnectSwap( event)
74  : doReconnectMove( event);
75  if (!hasRec) return false;
76 
77  // Colour flip afterburner.
78  if (flip > 0) return doReconnectFlip( event);
79  return true;
80 
81  }
82 
83  // Return number of reconnections for current event.
84  //int numberReconnections() {return nRec;}
85  //double dLambdaReconnections() {return -dLamTot;}
86 
87 private:
88 
89  // Mode. Number of reconnections. lambda measure reference scale.
90  int mode, flip, nRec, nGlu, nAllCol, nCol;
91  double dLamCut, fracGluon, m2Ref, dLamTot;
92 
93  // Array of (indices of) final gluons.
94  vector<int> iGlu;
95 
96  // Array of (indices of) all final coloured particles.
97  vector<int> iToAllCol, iAllCol;
98 
99  // Maps telling where all colours and anticolours are stored.
100  map<int, int> colMap, acolMap;
101 
102  // Array of all lambda distances between coloured partons.
103  vector<double> lambdaij;
104 
105  // Function to return lambda value from array.
106  double lambda12( int i, int j) {
107  int iAC = iToAllCol[i]; int jAC = iToAllCol[j];
108  return lambdaij[nAllCol * min( iAC, jAC) + max( iAC, jAC)];
109  }
110 
111  // Function to return lambda(i,j) + lambda(i,k) - lambda(j,k).
112  double lambda123( int i, int j, int k) {
113  int iAC = iToAllCol[i]; int jAC = iToAllCol[j]; int kAC = iToAllCol[k];
114  return lambdaij[nAllCol * min( iAC, jAC) + max( iAC, jAC)]
115  + lambdaij[nAllCol * min( iAC, kAC) + max( iAC, kAC)]
116  - lambdaij[nAllCol * min( jAC, kAC) + max( jAC, kAC)];
117  }
118 
119  // Small nested class for the effect of a potential gluon swap or move.
120  class InfoSwapMove{
121  public:
122  InfoSwapMove(int i1in = 0, int i2in = 0) : i1(i1in), i2(i2in) {}
123  InfoSwapMove(int i1in, int i2in, int iCol1in, int iAcol1in, int iCol2in,
124  int iAcol2in, int dLamIn) : i1(i1in), i2(i2in), iCol1(iCol1in),
125  iAcol1(iAcol1in), iCol2(iCol2in), iAcol2(iAcol2in), dLam(dLamIn) {}
126  ~InfoSwapMove() {}
127  int i1, i2, col1, acol1, iCol1, iAcol1, col2, acol2, iCol2, iAcol2;
128  double lamNow, dLam;
129  };
130 
131  // Vector of (1) gluon-pair swap or (2) gluon move properties.
132  vector<InfoSwapMove> infoSM;
133 
134  //----------------------------------------------------------------------
135 
136  // Initial setup: relevant gluons and coloured partons.
137 
138  inline bool setupConfig(Event& event) {
139 
140  // Reset arrays to prepare for the new event analysis.
141  iGlu.clear();
142  iToAllCol.resize( event.size() );
143  iAllCol.clear();
144  colMap.clear();
145  acolMap.clear();
146  infoSM.clear();
147  nRec = 0;
148  dLamTot = 0.;
149 
150  // Loop over all final particles. Store (fraction of) gluons.
151  for (int i = 3; i < event.size(); ++i) if (event[i].isFinal()) {
152  if (event[i].id() == 21 && rndmPtr->flat() < fracGluon)
153  iGlu.push_back(i);
154 
155  // Store location of all colour and anticolour particles and indices.
156  if (event[i].col() > 0 || event[i].acol() > 0) {
157  iToAllCol[i] = iAllCol.size();
158  iAllCol.push_back(i);
159  if (event[i].col() > 0) colMap[event[i].col()] = i;
160  if (event[i].acol() > 0) acolMap[event[i].acol()] = i;
161  }
162  }
163 
164  // Erase (anti)colours for (anti)junctions and skip adjacent gluons.
165  for (int iJun = 0; iJun < event.sizeJunction(); ++iJun) {
166  if (event.kindJunction(iJun) == 1) {
167  for (int j = 0; j < 3; ++j) {
168  int jCol = event.colJunction( iJun, j);
169  for (map<int, int>::iterator colM = colMap.begin();
170  colM != colMap.end(); ++colM)
171  if (colM->first == jCol) {
172  colMap.erase( colM);
173  break;
174  }
175  for (int iG = 0; iG < int(iGlu.size()); ++iG)
176  if (event[iGlu[iG]].col() == jCol) {
177  iGlu.erase(iGlu.begin() + iG);
178  break;
179  }
180  }
181  } else if (event.kindJunction(iJun) == 2) {
182  for (int j = 0; j < 3; ++j) {
183  int jCol = event.colJunction( iJun, j);
184  for (map<int, int>::iterator acolM = acolMap.begin();
185  acolM != acolMap.end(); ++acolM)
186  if (acolM->first == jCol) {
187  acolMap.erase( acolM);
188  break;
189  }
190  for (int iG = 0; iG < int(iGlu.size()); ++iG)
191  if (event[iGlu[iG]].acol() == jCol) {
192  iGlu.erase(iGlu.begin() + iG);
193  break;
194  }
195  }
196  }
197  }
198 
199  // Error checks.
200  nGlu = iGlu.size();
201  nCol = colMap.size();
202  if (int(acolMap.size()) != nCol) {
203  infoPtr->errorMsg("Error in MBReconUserHooks: map sizes do not match");
204  return false;
205  }
206  map<int, int>::iterator colM = colMap.begin();
207  map<int, int>::iterator acolM = acolMap.begin();
208  for (int iCol = 0; iCol < nCol; ++iCol) {
209  if (colM->first != acolM->first) {
210  infoPtr->errorMsg("Error in MBReconUserHooks: map elements"
211  " do not match");
212  return false;
213  }
214  ++colM;
215  ++acolM;
216  }
217 
218  // Calculate and tabulate lambda between any pair of coloured partons.
219  nAllCol = iAllCol.size();
220  lambdaij.resize( pow2(nAllCol) );
221  int i, j;
222  for (int iAC = 0; iAC < nAllCol - 1; ++iAC) {
223  i = iAllCol[iAC];
224  for (int jAC = iAC + 1; jAC < nAllCol; ++jAC) {
225  j = iAllCol[jAC];
226  lambdaij[nAllCol * iAC + jAC]
227  = log(1. + m2( event[i], event[j]) / m2Ref);
228  }
229  }
230 
231  // Done.
232  return true;
233 
234  }
235 
236  //----------------------------------------------------------------------
237 
238  // Swap gluons by lambda measure.
239 
240  inline bool doReconnectSwap(Event& event) {
241 
242  // Set up initial possible gluon swap pairs with lambda gains/losses.
243  for (int iG1 = 0; iG1 < nGlu - 1; ++iG1) {
244  int i1 = iGlu[iG1];
245  for (int iG2 = iG1 + 1; iG2 < nGlu; ++iG2) {
246  int i2 = iGlu[iG2];
247  InfoSwapMove tmpSM( i1, i2);
248  calcLamSwap( tmpSM, event);
249  infoSM.push_back( tmpSM);
250  }
251  }
252  int nPair = infoSM.size();
253 
254  // Keep on looping over swaps until no further negative dLambda.
255  for ( int iSwap = 0; iSwap < nGlu; ++iSwap) {
256  int iPairMin = -1;
257  double dLamMin = 1e4;
258 
259  // Find lowest dLambda.
260  for (int iPair = 0; iPair < nPair; ++iPair)
261  if (infoSM[iPair].dLam < dLamMin) {
262  iPairMin = iPair;
263  dLamMin = infoSM[iPair].dLam;
264  }
265 
266  // Break if no shift below upper limit found.
267  if (dLamMin > -dLamCut) break;
268  ++nRec;
269  dLamTot += dLamMin;
270  int i1min = infoSM[iPairMin].i1;
271  int i2min = infoSM[iPairMin].i2;
272 
273  // Swap the colours in the event record.
274  int col1 = event[i1min].col();
275  int acol1 = event[i1min].acol();
276  int col2 = event[i2min].col();
277  int acol2 = event[i2min].acol();
278  event[i1min].cols( col2, acol2);
279  event[i2min].cols( col1, acol1);
280 
281  // Swap the indices in the colour maps.
282  colMap[col1] = i2min;
283  acolMap[acol1] = i2min;
284  colMap[col2] = i1min;
285  acolMap[acol2] = i1min;
286 
287  // Remove already swapped pair from further consideration.
288  infoSM[iPairMin] = infoSM.back();
289  infoSM.pop_back();
290  --nPair;
291 
292  // Update all pairs that have been affected.
293  for (int iPair = 0; iPair < nPair; ++iPair) {
294  InfoSwapMove& tmpSM = infoSM[iPair];
295  if ( tmpSM.i1 == i1min || tmpSM.i1 == i2min
296  || tmpSM.i2 == i1min || tmpSM.i2 == i2min
297  || tmpSM.iCol1 == i1min || tmpSM.iCol1 == i2min
298  || tmpSM.iAcol1 == i1min || tmpSM.iAcol1 == i2min
299  || tmpSM.iCol2 == i1min || tmpSM.iCol2 == i2min
300  || tmpSM.iAcol2 == i1min || tmpSM.iAcol2 == i2min)
301  calcLamSwap( tmpSM, event);
302  }
303  }
304 
305  // Done.
306  return true;
307 
308  }
309 
310  //----------------------------------------------------------------------
311 
312  // Calculate pair swap properties.
313 
314  inline void calcLamSwap( InfoSwapMove& tmpSM, Event& event) {
315 
316  // Colour line tracing to neighbours.
317  tmpSM.col1 = event[tmpSM.i1].col();
318  tmpSM.acol1 = event[tmpSM.i1].acol();
319  tmpSM.iCol1 = acolMap[tmpSM.col1];
320  tmpSM.iAcol1 = colMap[tmpSM.acol1];
321  tmpSM.col2 = event[tmpSM.i2].col();
322  tmpSM.acol2 = event[tmpSM.i2].acol();
323  tmpSM.iCol2 = acolMap[tmpSM.col2];
324  tmpSM.iAcol2 = colMap[tmpSM.acol2];
325 
326  // Lambda swap properties.
327  double lam1c = lambda12( tmpSM.i1, tmpSM.iCol1);
328  double lam1a = lambda12( tmpSM.i1, tmpSM.iAcol1);
329  double lam2c = lambda12( tmpSM.i2, tmpSM.iCol2);
330  double lam2a = lambda12( tmpSM.i2, tmpSM.iAcol2);
331  double lam3c = lambda12( tmpSM.i1, tmpSM.iCol2);
332  double lam3a = lambda12( tmpSM.i1, tmpSM.iAcol2);
333  double lam4c = lambda12( tmpSM.i2, tmpSM.iCol1);
334  double lam4a = lambda12( tmpSM.i2, tmpSM.iAcol1);
335  if (tmpSM.col1 == tmpSM.acol2 && tmpSM.acol1 == tmpSM.col2)
336  tmpSM.dLam = 2e4;
337  else if (tmpSM.col1 == tmpSM.acol2)
338  tmpSM.dLam = (lam3c + lam4a) - (lam1a + lam2c);
339  else if (tmpSM.acol1 == tmpSM.col2)
340  tmpSM.dLam = (lam3a + lam4c) - (lam1c + lam2a);
341  else tmpSM.dLam = (lam3c + lam3a + lam4c + lam4a)
342  - (lam1c + lam1a + lam2c + lam2a);
343 
344  // Done.
345  }
346 
347  //----------------------------------------------------------------------
348 
349  // Move gluons by lambda measure.
350 
351  inline bool doReconnectMove(Event& event) {
352 
353  // Temporary variables.
354  int iNow, colNow, acolNow, iColNow, iAcolNow, col2Now;
355  double lamNow;
356 
357  // Set up initial possible gluon moves with lambda gains/losses.
358  for (int iG = 0; iG < nGlu; ++iG) {
359 
360  // Gluon and its neighbours.
361  iNow = iGlu[iG];
362  colNow = event[iNow].col();
363  acolNow = event[iNow].acol();
364  iColNow = acolMap[colNow];
365  iAcolNow = colMap[acolNow];
366 
367  // Addition to Lambda of gluon in current position.
368  lamNow = lambda123( iNow, iColNow, iAcolNow);
369 
370  // Loop over all colour lines where gluon could be inserted.
371  for (map<int, int>::iterator colM = colMap.begin();
372  colM != colMap.end(); ++colM) {
373  col2Now = colM->first;
374 
375  // New container for gluon and colour line information.
376  InfoSwapMove tmpSM( iNow);
377  tmpSM.col1 = colNow;
378  tmpSM.acol1 = acolNow;
379  tmpSM.iCol1 = iColNow;
380  tmpSM.iAcol1 = iAcolNow;
381  tmpSM.lamNow = lamNow;
382  tmpSM.col2 = col2Now;
383  tmpSM.iCol2 = colMap[col2Now];
384  tmpSM.iAcol2 = acolMap[col2Now];
385 
386  // Addition to Lambda if gluon inserted on line.
387  tmpSM.dLam = (tmpSM.iCol2 == tmpSM.i1 || tmpSM.iAcol2 == tmpSM.i1
388  || tmpSM.iCol1 == tmpSM.iAcol1) ? 2e4
389  : lambda123( tmpSM.i1, tmpSM.iCol2, tmpSM.iAcol2) - tmpSM.lamNow;
390  infoSM.push_back( tmpSM);
391  }
392  }
393  int nPair = infoSM.size();
394 
395  // Keep on looping over moves until no further negative dLambda.
396  for ( int iMove = 0; iMove < nGlu; ++iMove) {
397  int iPairMin = -1;
398  double dLamMin = 1e4;
399 
400  // Find lowest dLambda.
401  for (int iPair = 0; iPair < nPair; ++iPair)
402  if (infoSM[iPair].dLam < dLamMin) {
403  iPairMin = iPair;
404  dLamMin = infoSM[iPair].dLam;
405  }
406 
407  // Break if no shift below upper limit found.
408  if (dLamMin > -dLamCut) break;
409  ++nRec;
410  dLamTot += dLamMin;
411 
412  // Partons and colours involved in move.
413  InfoSwapMove& selSM = infoSM[iPairMin];
414  int i1Sel = selSM.i1;
415  int iCol1Sel = selSM.iCol1;
416  int iAcol1Sel = selSM.iAcol1;
417  int iCol2Sel = selSM.iCol2;
418  int iAcol2Sel = selSM.iAcol2;
419  int iCol2Mod[3] = { iAcol1Sel , i1Sel , iCol2Sel };
420  int col2Mod[3] = { selSM.col1, selSM.col2, selSM.acol1};
421 
422  // Remove gluon from old colour line and insert on new colour line.
423  for (int i = 0; i < 3; ++i) {
424  event[ iCol2Mod[i] ].col( col2Mod[i] );
425  colMap[ col2Mod[i] ] = iCol2Mod[i];
426  }
427 
428  // Update info for partons with new colors.
429  int i1Now = 0;
430  bool doUpdate = false;
431  for (int iPair = 0; iPair < nPair; ++iPair) {
432  InfoSwapMove& tmpSM = infoSM[iPair];
433  if (tmpSM.i1 != i1Now) {
434  i1Now = tmpSM.i1;
435  doUpdate = false;
436  if (i1Now == i1Sel || i1Now == iCol1Sel || i1Now == iAcol1Sel
437  || i1Now == iCol2Sel || i1Now == iAcol2Sel) {
438  colNow = event[i1Now].col();
439  acolNow = event[i1Now].acol();
440  iColNow = acolMap[colNow];
441  iAcolNow = colMap[acolNow];
442  lamNow = lambda123( i1Now, iColNow, iAcolNow);
443  doUpdate = true;
444  }
445  }
446  if (doUpdate) {
447  tmpSM.col1 = colNow;
448  tmpSM.acol1 = acolNow;
449  tmpSM.iCol1 = iColNow;
450  tmpSM.iAcol1 = iAcolNow;
451  tmpSM.lamNow = lamNow;
452  }
453  }
454 
455  // Update info on dLambda for affected particles and colour lines.
456  for (int iPair = 0; iPair < nPair; ++iPair) {
457  InfoSwapMove& tmpSM = infoSM[iPair];
458  int iMod = -1;
459  for (int i = 0; i < 3; ++i) if (tmpSM.col2 == col2Mod[i]) iMod = i;
460  if (iMod > -1) tmpSM.iCol2 = iCol2Mod[iMod];
461  if (tmpSM.i1 == i1Sel || tmpSM.i1 == iCol1Sel || tmpSM.i1 == iAcol1Sel
462  || tmpSM.i1 == iCol2Sel || tmpSM.i1 == iAcol2Sel || iMod > -1)
463  tmpSM.dLam = (tmpSM.iCol2 == tmpSM.i1 || tmpSM.iAcol2 == tmpSM.i1
464  || tmpSM.iCol1 == tmpSM.iAcol1) ? 2e4
465  : lambda123( tmpSM.i1, tmpSM.iCol2, tmpSM.iAcol2) - tmpSM.lamNow;
466  }
467 
468  // End of loop over gluon shifting.
469  }
470 
471  // Done.
472  return true;
473 
474  }
475 
476  //----------------------------------------------------------------------
477 
478  // Flip colour chains by lambda measure.
479 
480  inline bool doReconnectFlip(Event& event) {
481 
482  // Array with colour lines, and where each line begins and ends.
483  vector<int> iTmp, iVec, iBeg, iEnd;
484 
485  // Grab all colour ends.
486  for (int i = 3; i < event.size(); ++i)
487  if (event[i].isFinal() && event[i].col() > 0 && event[i].acol() == 0) {
488  iTmp.clear();
489  iTmp.push_back( i);
490 
491  // Step through colour neighbours to catch system.
492  int iNow = i;
493  map<int, int>::iterator acolM = acolMap.find( event[iNow].col() );
494  bool foundEnd = false;
495  while (acolM != acolMap.end()) {
496  iNow = acolM->second;
497  iTmp.push_back( iNow);
498  if (event[iNow].col() == 0) {
499  foundEnd = true;
500  break;
501  }
502  acolM = acolMap.find( event[iNow].col() );
503  }
504 
505  // Store acceptable system, optionally including junction legs.
506  if (foundEnd || flip == 2) {
507  iBeg.push_back( iVec.size());
508  for (int j = 0; j < int(iTmp.size()); ++j) iVec.push_back( iTmp[j]);
509  iEnd.push_back( iVec.size());
510  }
511  }
512 
513  // Optionally search for antijunction legs: grab all anticolour ends.
514  if (flip == 2) for (int i = 3; i < event.size(); ++i)
515  if (event[i].isFinal() && event[i].acol() > 0 && event[i].col() == 0) {
516  iTmp.clear();
517  iTmp.push_back( i);
518 
519  // Step through anticolour neighbours to catch system.
520  int iNow = i;
521  map<int, int>::iterator colM = colMap.find( event[iNow].acol() );
522  bool foundEnd = false;
523  while (colM != colMap.end()) {
524  iNow = colM->second;
525  iTmp.push_back( iNow);
526  if (event[iNow].acol() == 0) {
527  foundEnd = true;
528  break;
529  }
530  colM = colMap.find( event[iNow].acol() );
531  }
532 
533  // Store acceptable system, but do not doublecount q - (n g) - qbar.
534  if (!foundEnd) {
535  iBeg.push_back( iVec.size());
536  for (int j = 0; j < int(iTmp.size()); ++j) iVec.push_back( iTmp[j]);
537  iEnd.push_back( iVec.size());
538  }
539  }
540 
541 
542  // Variables for minimum search.
543  int nSys = iBeg.size();
544  int i1c, i1a, i2c, i2a, i1cMin, i1aMin, i2cMin, i2aMin, iSMin;
545  double dLam, dLamMin;
546  vector<InfoSwapMove> flipMin;
547 
548  // Loop through all system pairs.
549  for (int iSys1 = 0; iSys1 < nSys - 1; ++iSys1) if (iBeg[iSys1] >= 0)
550  for (int iSys2 = iSys1 + 1; iSys2 < nSys; ++iSys2) if (iBeg[iSys2] >= 0) {
551  i1cMin = 0;
552  i1aMin = 0;
553  i2cMin = 0;
554  i2aMin = 0;
555  dLamMin = 1e4;
556 
557  // Loop through all possible flip locations for a pair.
558  for (int j1 = iBeg[iSys1]; j1 < iEnd[iSys1] - 1; ++j1)
559  for (int j2 = iBeg[iSys2]; j2 < iEnd[iSys2] - 1; ++j2) {
560  i1c = iVec[j1];
561  i1a = iVec[j1 + 1];
562  i2c = iVec[j2];
563  i2a = iVec[j2 + 1];
564  dLam = lambda12( i1c, i2a) + lambda12( i2c, i1a)
565  - lambda12( i1c, i1a) - lambda12( i2c, i2a);
566  if (dLam < dLamMin) {
567  i1cMin = i1c;
568  i1aMin = i1a;
569  i2cMin = i2c;
570  i2aMin = i2a;
571  dLamMin = dLam;
572  }
573  }
574 
575  // Store possible flips if low enough dLamMin.
576  if (dLamMin < -dLamCut) flipMin.push_back( InfoSwapMove(
577  iSys1, iSys2, i1cMin, i1aMin, i2cMin, i2aMin, dLamMin) );
578  }
579  int flipSize = flipMin.size();
580 
581  // Search for lowest possible flip among unused systems.
582  for (int iFlip = 0; iFlip < min( nSys / 2, flipSize); ++iFlip) {
583  iSMin = -1;
584  dLamMin = 1e4;
585  for (int iSys12 = 0; iSys12 < flipSize; ++iSys12)
586  if (flipMin[iSys12].i1 >= 0 && flipMin[iSys12].dLam < dLamMin) {
587  iSMin = iSys12;
588  dLamMin = flipMin[iSys12].dLam;
589  }
590 
591  // Do flip. Mark flipped systems.
592  if (iSMin >= 0) {
593  InfoSwapMove& flipNow = flipMin[iSMin];
594  int iS1 = flipNow.i1;
595  int iS2 = flipNow.i2;
596  event[ flipNow.iAcol1 ].acol( event[flipNow.iCol2].col() );
597  event[ flipNow.iAcol2 ].acol( event[flipNow.iCol1].col() );
598  ++nRec;
599  dLamTot += flipNow.dLam;
600  for (int iSys12 = 0; iSys12 < flipSize; ++iSys12)
601  if ( flipMin[iSys12].i1 == iS1 || flipMin[iSys12].i1 == iS2
602  || flipMin[iSys12].i2 == iS1 || flipMin[iSys12].i2 == iS2)
603  flipMin[iSys12].i1 = -1;
604  }
605  else break;
606  }
607 
608  // Done.
609  return true;
610 
611  }
612 
613 };
614 
615 //==========================================================================
616 
617 
618 // Class for colour reconnection models specifically aimed at top decays.
619 
620 class TopReconUserHooks : public UserHooks {
621 
622 public:
623 
624  // Constructor and destructor.
625  // mode = 0: no reconnection of tops (dummy option, does nothing);
626  // = 1: reconnect with random background gluon;
627  // = 2: reconnect with nearest (smallest-mass) background gluon;
628  // = 3: reconnect with furthest (largest-mass) background gluon;
629  // = 4: reconnect with smallest (with sign) lambda measure shift;
630  // = 5: reconnect only if reduced lamda, and then to most reduction.
631  // strength: fraction of top gluons that is to be colour reconnected.
632  // nList: list first nList parton classifications.
633  // pTolerance: acceptable total momentum error (in reconstruction check).
634  // m2Ref: squared reference mass scale for lambda measure calculation.
635  // Possible variants for the future: swap with nearest in angle, not mass,
636  // and/or only allow a background gluon to swap colours once.
637 
638  TopReconUserHooks(int modeIn = 0, double strengthIn = 1.) : mode(modeIn),
639  strength(strengthIn) { iList = 0; nList = 0; pTolerance = 0.01;
640  m2Ref = 1.;}
641  ~TopReconUserHooks() {}
642 
643  // Allow colour reconnection after resonance decays (early or late)...
644  virtual bool canReconnectResonanceSystems() {return true;}
645 
646  // ...which gives access to the event, for modification.
647  virtual bool doReconnectResonanceSystems( int, Event& event) {
648 
649  // Return without action for relevant mode numbers.
650  if (mode <= 0 || mode > 5) return true;
651 
652  // Classify coloured final partons.
653  classifyFinalPartons(event);
654 
655  // Check that classification worked as expected.
656  if (!checkClassification(event)) return false;
657 
658  // List first few classifications, along with the event.
659  if (iList++ < nList) {
660  listClassification();
661  event.list();
662  }
663 
664  // Perform reconnection for t and tbar in random order.
665  bool tqrkFirst = (rndmPtr->flat() < 0.5);
666  doReconnect( tqrkFirst, event);
667  doReconnect(!tqrkFirst, event);
668 
669  // Done.
670  return true;
671  }
672 
673  // Return number of reconnections for current event.
674  //int numberReconnections() {return nRec;}
675 
676 private:
677 
678  // Mode. Counters for how many events to list. Allowed momentum error.
679  int mode, iList, nList, nRec;
680  double strength, pTolerance, m2Ref;
681 
682  // Arrays of (indices of) final partons from different sources.
683  // So far geared towards t -> b W decays only.
684  vector<int> iBqrk, iWpos, iTqrk, iBbar, iWneg, iTbar, iRest;
685 
686  // Maps telling where all colours and anticolours are stored.
687  map<int, int> colMap, acolMap;
688 
689  //----------------------------------------------------------------------
690 
691  // Classify all coloured partons at the end of showers by origin.
692  // Note: for now only t -> b W is fully classified.
693 
694  inline bool classifyFinalPartons(Event& event) {
695 
696  // Reset arrays to prepare for the new event analysis.
697  iBqrk.clear();
698  iWpos.clear();
699  iTqrk.clear();
700  iBbar.clear();
701  iWneg.clear();
702  iTbar.clear();
703  iRest.clear();
704  colMap.clear();
705  acolMap.clear();
706  nRec = 0;
707 
708  // Loop over all final particles. Tag coloured ones.
709  for (int i = 3; i < event.size(); ++i) if (event[i].isFinal()) {
710  bool hasCol = (event[i].colType() != 0);
711 
712  // Set up to find where each parton comes from.
713  bool fsrFromT = false;
714  bool fromTqrk = false;
715  bool fromBqrk = false;
716  bool fromWpos = false;
717  bool fromTbar = false;
718  bool fromBbar = false;
719  bool fromWneg = false;
720 
721  // Identify current particle.
722  int iNow = i;
723  int idOld = 0;
724  do {
725  int idNow = event[iNow].id();
726 
727  // Exclude FSR of gluons/photons from the t quark proper.
728  if (abs(idNow) == 6 && (idOld == 21 || idOld == 22)) fsrFromT = true;
729 
730  // Check if current particle matches any of the categories.
731  else if (idNow == 6) fromTqrk = true;
732  else if (idNow == 5) fromBqrk = true;
733  else if (idNow == 24) fromWpos = true;
734  else if (idNow == -6) fromTbar = true;
735  else if (idNow == -5) fromBbar = true;
736  else if (idNow == -24) fromWneg = true;
737 
738  // Step up through the history to the very top.
739  iNow = event[iNow].mother1();
740  idOld = idNow;
741  } while (iNow > 2 && !fsrFromT);
742 
743  // Bookkeep where the parton comes from. Note that b quarks also
744  // can appear in W decays, so order of checks is relevant.
745  if (fromTqrk && fromWpos && hasCol) iWpos.push_back(i);
746  else if (fromTqrk && fromBqrk && hasCol) iBqrk.push_back(i);
747  else if (fromTqrk) iTqrk.push_back(i);
748  else if (fromTbar && fromWneg && hasCol) iWneg.push_back(i);
749  else if (fromTbar && fromBbar && hasCol) iBbar.push_back(i);
750  else if (fromTbar) iTbar.push_back(i);
751  else if (hasCol) iRest.push_back(i);
752 
753  // Store location of all colour and anticolour indices.
754  if (hasCol && (mode == 4 || mode == 5)) {
755  if (event[i].col() > 0) colMap[event[i].col()] = i;
756  if (event[i].acol() > 0) acolMap[event[i].acol()] = i;
757  }
758  }
759 
760  // So far method always returns true.
761  return true;
762 
763  }
764 
765  //----------------------------------------------------------------------
766 
767  // Check that classification worked by summing up partons to t/tbar.
768 
769  inline bool checkClassification(Event& event) {
770 
771  // Find final copy of t and tbar quarks.
772  int iTqrkLoc = 0;
773  int iTbarLoc = 0;
774  for (int i = 3; i < event.size(); ++i) {
775  if(event[i].id() == 6) iTqrkLoc = i;
776  if(event[i].id() == -6) iTbarLoc = i;
777  }
778 
779  // Four-momentum of t minus all its decay products.
780  Vec4 tqrkDiff = event[iTqrkLoc].p();
781  for (int i = 0; i < int(iBqrk.size()); ++i)
782  tqrkDiff -= event[iBqrk[i]].p();
783  for (int i = 0; i < int(iWpos.size()); ++i)
784  tqrkDiff -= event[iWpos[i]].p();
785  for (int i = 0; i < int(iTqrk.size()); ++i)
786  tqrkDiff -= event[iTqrk[i]].p();
787 
788  // Four-momentum of tbar minus all its decay products.
789  Vec4 tbarDiff = event[iTbarLoc].p();
790  for (int i = 0; i < int(iBbar.size()); ++i)
791  tbarDiff -= event[iBbar[i]].p();
792  for (int i = 0; i < int(iWneg.size()); ++i)
793  tbarDiff -= event[iWneg[i]].p();
794  for (int i = 0; i < int(iTbar.size()); ++i)
795  tbarDiff -= event[iTbar[i]].p();
796 
797  // Print difference vectors and event if sum deviation is too big.
798  double totErr = abs(tqrkDiff.px()) + abs(tqrkDiff.py())
799  + abs(tqrkDiff.pz()) + abs(tqrkDiff.e()) + abs(tbarDiff.px())
800  + abs(tbarDiff.py()) + abs(tbarDiff.pz()) + abs(tqrkDiff.e());
801  if (totErr > pTolerance) {
802  infoPtr->errorMsg("Error in TopReconUserHooks::checkClassification");
803  cout << "\n Error in t/tbar daughter search: \n t difference "
804  << tqrkDiff << " tbar difference "<< tbarDiff;
805  listClassification();
806  event.list();
807  }
808 
809  // Done.
810  return (totErr < pTolerance);
811  }
812 
813  //----------------------------------------------------------------------
814 
815  // Print how final-state (mainly coloured) particles were classified.
816 
817  inline void listClassification() {
818 
819  cout << "\n Final-state coloured partons classified by source: ";
820  cout << "\n From Bqrk:";
821  for (int i = 0; i < int(iBqrk.size()); ++i) cout << " " << iBqrk[i];
822  cout << "\n From Wpos:";
823  for (int i = 0; i < int(iWpos.size()); ++i) cout << " " << iWpos[i];
824  cout << "\n From Tqrk:";
825  for (int i = 0; i < int(iTqrk.size()); ++i) cout << " " << iTqrk[i];
826  cout << "\n From Bbar:";
827  for (int i = 0; i < int(iBbar.size()); ++i) cout << " " << iBbar[i];
828  cout << "\n From Wneg:";
829  for (int i = 0; i < int(iWneg.size()); ++i) cout << " " << iWneg[i];
830  cout << "\n From Tbar:";
831  for (int i = 0; i < int(iTbar.size()); ++i) cout << " " << iTbar[i];
832  cout << "\n From Rest:";
833  for (int i = 0; i < int(iRest.size()); ++i) {
834  cout << " " << iRest[i];
835  if (i%20 == 19 && i + 1 != int(iRest.size())) cout << "\n ";
836  }
837  cout << endl;
838  }
839 
840  //----------------------------------------------------------------------
841 
842  // Reconnect gluons either from t or from tbar quark.
843 
844  inline bool doReconnect(bool doTqrk, Event& event) {
845 
846  // Gather coloured decay products either of t or of tbar.
847  vector<int> iTdec;
848  if (doTqrk) {
849  for (int i = 0; i < int(iBqrk.size()); ++i) iTdec.push_back(iBqrk[i]);
850  for (int i = 0; i < int(iWpos.size()); ++i) iTdec.push_back(iWpos[i]);
851  } else {
852  for (int i = 0; i < int(iBbar.size()); ++i) iTdec.push_back(iBbar[i]);
853  for (int i = 0; i < int(iWneg.size()); ++i) iTdec.push_back(iWneg[i]);
854  }
855 
856  // Extract the gluons from the t quark decay.
857  vector<int> iGT;
858  for (int i = 0; i < int(iTdec.size()); ++i) {
859  int colNow = event[iTdec[i]].col();
860  int acolNow = event[iTdec[i]].acol();
861  if (colNow > 0 && acolNow > 0) iGT.push_back(iTdec[i]);
862  }
863  int nGT = iGT.size();
864 
865  // Randomize their stored order.
866  if (nGT > 1) for (int i = 0; i < nGT; ++i) {
867  int j = min( int(nGT * rndmPtr->flat()), nGT - 1 );
868  swap( iGT[i], iGT[j]);
869  }
870 
871  // Also extract the rest of the gluons in the event.
872  vector<int> iGR;
873  for (int i = 0; i < int(iRest.size()); ++i) {
874  int colNow = event[iRest[i]].col();
875  int acolNow = event[iRest[i]].acol();
876  if (colNow > 0 && acolNow > 0) iGR.push_back(iRest[i]);
877  }
878  int nGR = iGR.size();
879  int iR, colT, acolT, iColT, iAcolT, colR, acolR, iColR, iAcolR;
880  double mTR2, mTR2now, dLam = 0.0, lamT, lamNow, lamRec;
881 
882  // Loop through all top gluons; study fraction given by strength.
883  if (nGT > 0 && nGR > 0)
884  for (int iT = 0; iT < nGT; ++iT) {
885  if (strength < rndmPtr->flat()) continue;
886 
887  // Pick random gluon from rest of event.
888  if (mode == 1) iR = min( int(nGR * rndmPtr->flat()), nGR - 1 );
889 
890  // Find gluon from rest with lowest or highest invariant mass.
891  else if (mode < 4) {
892  iR = 0;
893  mTR2 = m2( event[iGT[iT]], event[iGR[iR]]);
894  for (int ii = 1; ii < nGR; ++ii) {
895  mTR2now = m2( event[iGT[iT]], event[iGR[ii]]);
896  if (mode == 2 && mTR2now < mTR2) {iR = ii; mTR2 = mTR2now;}
897  if (mode == 3 && mTR2now > mTR2) {iR = ii; mTR2 = mTR2now;}
898  }
899 
900  // Find gluon from rest with smallest lambda value shift.
901  } else {
902  iR = -1;
903  dLam = 1e10;
904  colT = event[iGT[iT]].col();
905  acolT = event[iGT[iT]].acol();
906  iColT = acolMap[colT];
907  iAcolT = colMap[acolT];
908  lamT = log(1. + m2( event[iGT[iT]], event[iColT]) / m2Ref)
909  + log(1. + m2( event[iGT[iT]], event[iAcolT]) / m2Ref);
910  for (int ii = 0; ii < nGR; ++ii) {
911  colR = event[iGR[ii]].col();
912  acolR = event[iGR[ii]].acol();
913  iColR = acolMap[colR];
914  iAcolR = colMap[acolR];
915  lamNow = lamT
916  + log(1. + m2( event[iGR[ii]], event[iColR]) / m2Ref)
917  + log(1. + m2( event[iGR[ii]], event[iAcolR]) / m2Ref);
918  lamRec = log(1. + m2( event[iGT[iT]], event[iColR]) / m2Ref)
919  + log(1. + m2( event[iGT[iT]], event[iAcolR]) / m2Ref)
920  + log(1. + m2( event[iGR[ii]], event[iColT]) / m2Ref)
921  + log(1. + m2( event[iGR[ii]], event[iAcolT]) / m2Ref);
922  if (lamRec - lamNow < dLam) {iR = ii; dLam = lamRec - lamNow;}
923  }
924  if (mode == 5 && dLam > 0.) continue;
925  }
926 
927  // Swap top and rest gluon colour and anticolour.
928  ++nRec;
929  swapCols( iGT[iT], iGR[iR], event);
930  }
931 
932  // Done.
933  return true;
934 
935  }
936 
937  //----------------------------------------------------------------------
938 
939  // Swap colours and/or anticolours in the event listing.
940 
941  inline void swapCols( int i, int j, Event& event) {
942 
943  // Swap the colours in the event record.
944  int coli = event[i].col();
945  int acoli = event[i].acol();
946  int colj = event[j].col();
947  int acolj = event[j].acol();
948  event[i].cols( colj, acolj);
949  event[j].cols( coli, acoli);
950 
951  // Swap the indices in the colour maps.
952  colMap[coli] = j;
953  acolMap[acoli] = j;
954  colMap[colj] = i;
955  acolMap[acolj] = i;
956 
957  }
958 
959 };
960 
961 //==========================================================================
962 
963 } // end namespace Pythia8
964 
965 #endif // end Pythia8_ColourReconnectionHooks_H
Definition: AgUStep.h:26