Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:06:36

0001 // ColourReconnectionHooks.h is a part of the PYTHIA event generator.
0002 // Copyright (C) 2024 Torbjorn Sjostrand.
0003 // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
0004 // Please respect the MCnet Guidelines, see GUIDELINES for details.
0005 
0006 // This file contains two UserHooks that, along with the internal models,
0007 // implement all the models used for the top mass study in
0008 // S. Argyropoulos and T. Sjostrand,
0009 // arXiv:1407.6653 [hep-ph] (LU TP 14-23, DESY 14-134, MCnet-14-15)
0010 
0011 // MBReconUserHooks: can be used for all kinds of events, not only top ones.
0012 // TopReconUserHooks: models intended specifically for top decay products,
0013 // whereas the underlying event is handled by the default model.
0014 
0015 // Warning: some small modifications have been made when collecting
0016 // the models, but nothing intended to change the behaviour.
0017 // Note: the move model is also available with ColourReconnection:mode = 2,
0018 // while the ColourReconnection:mode = 1 model has not been used here.
0019 // Note: the new models tend to be slower than the default CR scenario,
0020 // since they have to probe many more reconnection possibilities.
0021 
0022 #ifndef Pythia8_ColourReconnectionHooks_H
0023 #define Pythia8_ColourReconnectionHooks_H
0024 
0025 // Includes
0026 #include "Pythia8/Pythia.h"
0027 namespace Pythia8 {
0028 
0029 //==========================================================================
0030 
0031 // Class for colour reconnection models of general validity.
0032 
0033 class MBReconUserHooks : public UserHooks {
0034 
0035 public:
0036 
0037   // Constructor and destructor.
0038   // mode = 0: no reconnection (dummy option, does nothing);
0039   //      = 1: swap gluons to minimize lambda.
0040   //      = 2: move gluons to minimize lambda.
0041   // flip = 0: no flip between quark-antiquark ends.
0042   //      = 1: flip between quark-antiquark ends, excluding junction systems.
0043   //      = 2: flip between quark-antiquark ends, including junction systems.
0044   // dLamCut: smallest -delta-lambda value for which to swap/mode (positive).
0045   // fracGluon: the fraction of gluons that will be studied for reconnection.
0046   // m2Ref   : squared reference mass scale for lambda measure calculation.
0047   MBReconUserHooks(int modeIn = 0, int flipIn = 0, double dLamCutIn = 0.,
0048     double fracGluonIn = 1.) : mode(modeIn), flip(flipIn), dLamCut(dLamCutIn),
0049     fracGluon(fracGluonIn) { m2Ref = 1.; dLamCut = max(0., dLamCut); }
0050   ~MBReconUserHooks() {}
0051 
0052   // Allow colour reconnection after resonance decays (early or late)...
0053   virtual bool canReconnectResonanceSystems() {return true;}
0054 
0055   // ...which gives access to the event, for modification.
0056   virtual bool doReconnectResonanceSystems( int, Event& event) {
0057 
0058     // Return without action for relevant mode numbers.
0059     if (mode <= 0 || mode > 2) return true;
0060 
0061     // Double diffraction not yet implemented, so return without action.
0062     // (But works for internal move implementation.)
0063     if (infoPtr->isDiffractiveA() && infoPtr->isDiffractiveB())
0064       return true;
0065 
0066     // Initial setup: relevant gluons and coloured partons.
0067     if (!setupConfig( event)) return false;
0068 
0069     // Done if not enough gluons.
0070     if ( (mode == 1 && nGlu < 2) || (mode == 2 && nGlu < 1) ) return true;
0071 
0072     // Colour reconnect. Return if failed.
0073     bool hasRec = (mode == 1) ? doReconnectSwap( event)
0074                               : doReconnectMove( event);
0075     if (!hasRec) return false;
0076 
0077     // Colour flip afterburner.
0078     if (flip > 0) return doReconnectFlip( event);
0079     return true;
0080 
0081   }
0082 
0083   // Return number of reconnections for current event.
0084   //int numberReconnections() {return nRec;}
0085   //double dLambdaReconnections() {return -dLamTot;}
0086 
0087 private:
0088 
0089   // Mode. Number of reconnections. lambda measure reference scale.
0090   int    mode, flip, nRec, nGlu, nAllCol, nCol;
0091   double dLamCut, fracGluon, m2Ref, dLamTot;
0092 
0093   // Array of (indices of) final gluons.
0094   vector<int> iGlu;
0095 
0096   // Array of (indices of) all final coloured particles.
0097   vector<int> iToAllCol, iAllCol;
0098 
0099   // Maps telling where all colours and anticolours are stored.
0100   map<int, int> colMap, acolMap;
0101 
0102   // Array of all lambda distances between coloured partons.
0103   vector<double> lambdaij;
0104 
0105   // Function to return lambda value from array.
0106   double lambda12( int i, int j) {
0107     int iAC = iToAllCol[i]; int jAC = iToAllCol[j];
0108     return lambdaij[nAllCol * min( iAC, jAC) + max( iAC, jAC)];
0109   }
0110 
0111   // Function to return lambda(i,j) + lambda(i,k) - lambda(j,k).
0112   double lambda123( int i, int j, int k) {
0113     int iAC = iToAllCol[i]; int jAC = iToAllCol[j]; int kAC = iToAllCol[k];
0114     return lambdaij[nAllCol * min( iAC, jAC) + max( iAC, jAC)]
0115          + lambdaij[nAllCol * min( iAC, kAC) + max( iAC, kAC)]
0116          - lambdaij[nAllCol * min( jAC, kAC) + max( jAC, kAC)];
0117   }
0118 
0119   // Small nested class for the effect of a potential gluon swap or move.
0120   class InfoSwapMove{
0121     public:
0122     InfoSwapMove(int i1in = 0, int i2in = 0) : i1(i1in), i2(i2in) {}
0123     InfoSwapMove(int i1in, int i2in, int iCol1in, int iAcol1in, int iCol2in,
0124       int iAcol2in, int dLamIn) : i1(i1in), i2(i2in), iCol1(iCol1in),
0125       iAcol1(iAcol1in), iCol2(iCol2in), iAcol2(iAcol2in), dLam(dLamIn) {}
0126     ~InfoSwapMove() {}
0127     int i1, i2, col1, acol1, iCol1, iAcol1, col2, acol2, iCol2, iAcol2;
0128     double lamNow, dLam;
0129   };
0130 
0131   // Vector of (1) gluon-pair swap or (2) gluon move properties.
0132   vector<InfoSwapMove> infoSM;
0133 
0134   //----------------------------------------------------------------------
0135 
0136   // Initial setup: relevant gluons and coloured partons.
0137 
0138   inline bool setupConfig(Event& event) {
0139 
0140     // Reset arrays to prepare for the new event analysis.
0141     iGlu.clear();
0142     iToAllCol.resize( event.size() );
0143     iAllCol.clear();
0144     colMap.clear();
0145     acolMap.clear();
0146     infoSM.clear();
0147     nRec = 0;
0148     dLamTot = 0.;
0149 
0150     // Loop over all final particles. Store (fraction of) gluons.
0151     for (int i = 3; i < event.size(); ++i) if (event[i].isFinal()) {
0152       if (event[i].id() == 21 && rndmPtr->flat() < fracGluon)
0153         iGlu.push_back(i);
0154 
0155       // Store location of all colour and anticolour particles and indices.
0156       if (event[i].col() > 0 || event[i].acol() > 0) {
0157         iToAllCol[i] = iAllCol.size();
0158         iAllCol.push_back(i);
0159         if (event[i].col() > 0) colMap[event[i].col()] = i;
0160         if (event[i].acol() > 0) acolMap[event[i].acol()] = i;
0161       }
0162     }
0163 
0164     // Erase (anti)colours for (anti)junctions and skip adjacent gluons.
0165     for (int iJun = 0; iJun < event.sizeJunction(); ++iJun) {
0166       if (event.kindJunction(iJun) == 1) {
0167         for (int j = 0; j < 3; ++j) {
0168           int jCol = event.colJunction( iJun, j);
0169           for (map<int, int>::iterator colM = colMap.begin();
0170             colM != colMap.end(); ++colM)
0171           if (colM->first == jCol) {
0172             colMap.erase( colM);
0173             break;
0174           }
0175           for (int iG = 0; iG < int(iGlu.size()); ++iG)
0176           if (event[iGlu[iG]].col() == jCol) {
0177             iGlu.erase(iGlu.begin() + iG);
0178             break;
0179           }
0180         }
0181       } else if (event.kindJunction(iJun) == 2) {
0182         for (int j = 0; j < 3; ++j) {
0183           int jCol = event.colJunction( iJun, j);
0184           for (map<int, int>::iterator acolM = acolMap.begin();
0185             acolM != acolMap.end(); ++acolM)
0186           if (acolM->first == jCol) {
0187             acolMap.erase( acolM);
0188             break;
0189           }
0190           for (int iG = 0; iG < int(iGlu.size()); ++iG)
0191           if (event[iGlu[iG]].acol() == jCol) {
0192             iGlu.erase(iGlu.begin() + iG);
0193             break;
0194           }
0195         }
0196       }
0197     }
0198 
0199     // Error checks.
0200     nGlu = iGlu.size();
0201     nCol = colMap.size();
0202     if (int(acolMap.size()) != nCol) {
0203       loggerPtr->ERROR_MSG("map sizes do not match");
0204       return false;
0205     }
0206     map<int, int>::iterator colM = colMap.begin();
0207     map<int, int>::iterator acolM = acolMap.begin();
0208     for (int iCol = 0; iCol < nCol; ++iCol) {
0209       if (colM->first != acolM->first) {
0210         loggerPtr->ERROR_MSG("map elements do not match");
0211         return false;
0212       }
0213       ++colM;
0214       ++acolM;
0215     }
0216 
0217     // Calculate and tabulate lambda between any pair of coloured partons.
0218     nAllCol = iAllCol.size();
0219     lambdaij.resize( pow2(nAllCol) );
0220     int i, j;
0221     for (int iAC = 0; iAC < nAllCol - 1; ++iAC) {
0222       i = iAllCol[iAC];
0223       for (int jAC = iAC + 1; jAC < nAllCol; ++jAC) {
0224         j = iAllCol[jAC];
0225         lambdaij[nAllCol * iAC + jAC]
0226           = log1p(m2( event[i], event[j]) / m2Ref);
0227       }
0228     }
0229 
0230     // Done.
0231     return true;
0232 
0233   }
0234 
0235   //----------------------------------------------------------------------
0236 
0237   // Swap gluons by lambda measure.
0238 
0239   inline bool doReconnectSwap(Event& event) {
0240 
0241     // Set up initial possible gluon swap pairs with lambda gains/losses.
0242     for (int iG1 = 0; iG1 < nGlu - 1; ++iG1) {
0243       int i1 = iGlu[iG1];
0244       for (int iG2 = iG1 + 1; iG2 < nGlu; ++iG2) {
0245         int i2 = iGlu[iG2];
0246         InfoSwapMove tmpSM( i1, i2);
0247         calcLamSwap( tmpSM, event);
0248         infoSM.push_back( tmpSM);
0249       }
0250     }
0251     int nPair = infoSM.size();
0252 
0253     // Keep on looping over swaps until no further negative dLambda.
0254     for ( int iSwap = 0; iSwap < nGlu; ++iSwap) {
0255       int    iPairMin = -1;
0256       double dLamMin  = 1e4;
0257 
0258       // Find lowest dLambda.
0259       for (int iPair = 0; iPair < nPair; ++iPair)
0260       if (infoSM[iPair].dLam < dLamMin) {
0261         iPairMin = iPair;
0262         dLamMin  = infoSM[iPair].dLam;
0263       }
0264 
0265       // Break if no shift below upper limit found.
0266       if (dLamMin > -dLamCut) break;
0267       ++nRec;
0268       dLamTot += dLamMin;
0269       int i1min = infoSM[iPairMin].i1;
0270       int i2min = infoSM[iPairMin].i2;
0271 
0272       // Swap the colours in the event record.
0273       int col1  = event[i1min].col();
0274       int acol1 = event[i1min].acol();
0275       int col2  = event[i2min].col();
0276       int acol2 = event[i2min].acol();
0277       event[i1min].cols( col2, acol2);
0278       event[i2min].cols( col1, acol1);
0279 
0280       // Swap the indices in the colour maps.
0281       colMap[col1]   = i2min;
0282       acolMap[acol1] = i2min;
0283       colMap[col2]   = i1min;
0284       acolMap[acol2] = i1min;
0285 
0286       // Remove already swapped pair from further consideration.
0287       infoSM[iPairMin] = infoSM.back();
0288       infoSM.pop_back();
0289       --nPair;
0290 
0291       // Update all pairs that have been affected.
0292       for (int iPair = 0; iPair < nPair; ++iPair) {
0293         InfoSwapMove& tmpSM = infoSM[iPair];
0294         if ( tmpSM.i1     == i1min || tmpSM.i1     == i2min
0295           || tmpSM.i2     == i1min || tmpSM.i2     == i2min
0296           || tmpSM.iCol1  == i1min || tmpSM.iCol1  == i2min
0297           || tmpSM.iAcol1 == i1min || tmpSM.iAcol1 == i2min
0298           || tmpSM.iCol2  == i1min || tmpSM.iCol2  == i2min
0299           || tmpSM.iAcol2 == i1min || tmpSM.iAcol2 == i2min)
0300           calcLamSwap( tmpSM, event);
0301       }
0302     }
0303 
0304     // Done.
0305     return true;
0306 
0307   }
0308 
0309   //----------------------------------------------------------------------
0310 
0311   // Calculate pair swap properties.
0312 
0313   inline void calcLamSwap( InfoSwapMove& tmpSM, Event& event) {
0314 
0315     // Colour line tracing to neighbours.
0316     tmpSM.col1   = event[tmpSM.i1].col();
0317     tmpSM.acol1  = event[tmpSM.i1].acol();
0318     tmpSM.iCol1  = acolMap[tmpSM.col1];
0319     tmpSM.iAcol1 = colMap[tmpSM.acol1];
0320     tmpSM.col2   = event[tmpSM.i2].col();
0321     tmpSM.acol2  = event[tmpSM.i2].acol();
0322     tmpSM.iCol2  = acolMap[tmpSM.col2];
0323     tmpSM.iAcol2 = colMap[tmpSM.acol2];
0324 
0325     // Lambda swap properties.
0326     double lam1c = lambda12( tmpSM.i1, tmpSM.iCol1);
0327     double lam1a = lambda12( tmpSM.i1, tmpSM.iAcol1);
0328     double lam2c = lambda12( tmpSM.i2, tmpSM.iCol2);
0329     double lam2a = lambda12( tmpSM.i2, tmpSM.iAcol2);
0330     double lam3c = lambda12( tmpSM.i1, tmpSM.iCol2);
0331     double lam3a = lambda12( tmpSM.i1, tmpSM.iAcol2);
0332     double lam4c = lambda12( tmpSM.i2, tmpSM.iCol1);
0333     double lam4a = lambda12( tmpSM.i2, tmpSM.iAcol1);
0334     if (tmpSM.col1 == tmpSM.acol2 && tmpSM.acol1 == tmpSM.col2)
0335        tmpSM.dLam = 2e4;
0336     else if (tmpSM.col1 == tmpSM.acol2)
0337        tmpSM.dLam = (lam3c + lam4a) - (lam1a + lam2c);
0338     else if (tmpSM.acol1 == tmpSM.col2)
0339        tmpSM.dLam = (lam3a + lam4c) - (lam1c + lam2a);
0340     else tmpSM.dLam = (lam3c + lam3a + lam4c + lam4a)
0341                    - (lam1c + lam1a + lam2c + lam2a);
0342 
0343   // Done.
0344   }
0345 
0346   //----------------------------------------------------------------------
0347 
0348   // Move gluons by lambda measure.
0349 
0350   inline bool doReconnectMove(Event& event) {
0351 
0352     // Temporary variables.
0353     int    iNow, colNow, acolNow, iColNow, iAcolNow, col2Now;
0354     double lamNow;
0355 
0356     // Set up initial possible gluon moves with lambda gains/losses.
0357     for (int iG = 0; iG < nGlu; ++iG) {
0358 
0359       // Gluon and its neighbours.
0360       iNow     = iGlu[iG];
0361       colNow   = event[iNow].col();
0362       acolNow  = event[iNow].acol();
0363       iColNow  = acolMap[colNow];
0364       iAcolNow = colMap[acolNow];
0365 
0366       // Addition to Lambda of gluon in current position.
0367       lamNow   = lambda123( iNow, iColNow, iAcolNow);
0368 
0369       // Loop over all colour lines where gluon could be inserted.
0370       for (map<int, int>::iterator colM = colMap.begin();
0371       colM != colMap.end(); ++colM) {
0372         col2Now = colM->first;
0373 
0374         // New container for gluon and colour line information.
0375         InfoSwapMove tmpSM( iNow);
0376         tmpSM.col1   = colNow;
0377         tmpSM.acol1  = acolNow;
0378         tmpSM.iCol1  = iColNow;
0379         tmpSM.iAcol1 = iAcolNow;
0380         tmpSM.lamNow = lamNow;
0381         tmpSM.col2   = col2Now;
0382         tmpSM.iCol2  = colMap[col2Now];
0383         tmpSM.iAcol2 = acolMap[col2Now];
0384 
0385         // Addition to Lambda if gluon inserted on line.
0386         tmpSM.dLam = (tmpSM.iCol2 == tmpSM.i1 || tmpSM.iAcol2 == tmpSM.i1
0387           || tmpSM.iCol1 == tmpSM.iAcol1) ? 2e4
0388           : lambda123( tmpSM.i1, tmpSM.iCol2, tmpSM.iAcol2) - tmpSM.lamNow;
0389         infoSM.push_back( tmpSM);
0390       }
0391     }
0392     int nPair = infoSM.size();
0393 
0394     // Keep on looping over moves until no further negative dLambda.
0395     for ( int iMove = 0; iMove < nGlu; ++iMove) {
0396       int    iPairMin = -1;
0397       double dLamMin  = 1e4;
0398 
0399       // Find lowest dLambda.
0400       for (int iPair = 0; iPair < nPair; ++iPair)
0401       if (infoSM[iPair].dLam < dLamMin) {
0402         iPairMin = iPair;
0403         dLamMin  = infoSM[iPair].dLam;
0404       }
0405 
0406       // Break if no shift below upper limit found.
0407       if (dLamMin > -dLamCut) break;
0408       ++nRec;
0409       dLamTot += dLamMin;
0410 
0411       // Partons and colours involved in move.
0412       InfoSwapMove& selSM = infoSM[iPairMin];
0413       int i1Sel     = selSM.i1;
0414       int iCol1Sel  = selSM.iCol1;
0415       int iAcol1Sel = selSM.iAcol1;
0416       int iCol2Sel  = selSM.iCol2;
0417       int iAcol2Sel = selSM.iAcol2;
0418       int iCol2Mod[3]  = { iAcol1Sel , i1Sel     , iCol2Sel    };
0419       int col2Mod[3]   = { selSM.col1, selSM.col2, selSM.acol1};
0420 
0421       // Remove gluon from old colour line and insert on new colour line.
0422       for (int i = 0; i < 3; ++i) {
0423         event[ iCol2Mod[i] ].col( col2Mod[i] );
0424         colMap[ col2Mod[i] ] = iCol2Mod[i];
0425       }
0426 
0427       // Update info for partons with new colors.
0428       int  i1Now    = 0;
0429       bool doUpdate = false;
0430       for (int iPair = 0; iPair < nPair; ++iPair) {
0431         InfoSwapMove& tmpSM = infoSM[iPair];
0432         if (tmpSM.i1 != i1Now) {
0433           i1Now = tmpSM.i1;
0434           doUpdate = false;
0435           if (i1Now == i1Sel || i1Now == iCol1Sel || i1Now == iAcol1Sel
0436             || i1Now == iCol2Sel || i1Now == iAcol2Sel) {
0437             colNow   = event[i1Now].col();
0438             acolNow  = event[i1Now].acol();
0439             iColNow  = acolMap[colNow];
0440             iAcolNow = colMap[acolNow];
0441             lamNow   = lambda123( i1Now, iColNow, iAcolNow);
0442             doUpdate = true;
0443           }
0444         }
0445         if (doUpdate) {
0446           tmpSM.col1   = colNow;
0447           tmpSM.acol1  = acolNow;
0448           tmpSM.iCol1  = iColNow;
0449           tmpSM.iAcol1 = iAcolNow;
0450           tmpSM.lamNow = lamNow;
0451         }
0452       }
0453 
0454       // Update info on dLambda for affected particles and colour lines.
0455       for (int iPair = 0; iPair < nPair; ++iPair) {
0456         InfoSwapMove& tmpSM = infoSM[iPair];
0457         int iMod = -1;
0458         for (int i = 0; i < 3; ++i) if (tmpSM.col2 == col2Mod[i]) iMod = i;
0459         if (iMod > -1) tmpSM.iCol2 = iCol2Mod[iMod];
0460         if (tmpSM.i1 == i1Sel || tmpSM.i1 == iCol1Sel || tmpSM.i1 == iAcol1Sel
0461           || tmpSM.i1 == iCol2Sel || tmpSM.i1 == iAcol2Sel || iMod > -1)
0462           tmpSM.dLam = (tmpSM.iCol2 == tmpSM.i1 || tmpSM.iAcol2 == tmpSM.i1
0463             || tmpSM.iCol1 == tmpSM.iAcol1) ? 2e4
0464             : lambda123( tmpSM.i1, tmpSM.iCol2, tmpSM.iAcol2) - tmpSM.lamNow;
0465       }
0466 
0467     // End of loop over gluon shifting.
0468     }
0469 
0470     // Done.
0471     return true;
0472 
0473   }
0474 
0475   //----------------------------------------------------------------------
0476 
0477   // Flip colour chains by lambda measure.
0478 
0479   inline bool doReconnectFlip(Event& event) {
0480 
0481     // Array with colour lines, and where each line begins and ends.
0482     vector<int> iTmp, iVec, iBeg, iEnd;
0483 
0484     // Grab all colour ends.
0485     for (int i = 3; i < event.size(); ++i)
0486     if (event[i].isFinal() && event[i].col() > 0 && event[i].acol() == 0) {
0487       iTmp.clear();
0488       iTmp.push_back( i);
0489 
0490       // Step through colour neighbours to catch system.
0491       int iNow = i;
0492       map<int, int>::iterator acolM = acolMap.find( event[iNow].col() );
0493       bool foundEnd = false;
0494       while (acolM != acolMap.end()) {
0495         iNow = acolM->second;
0496         iTmp.push_back( iNow);
0497         if (event[iNow].col() == 0) {
0498           foundEnd = true;
0499           break;
0500         }
0501         acolM = acolMap.find( event[iNow].col() );
0502       }
0503 
0504       // Store acceptable system, optionally including junction legs.
0505       if (foundEnd || flip == 2) {
0506         iBeg.push_back( iVec.size());
0507         for (int j = 0; j < int(iTmp.size()); ++j) iVec.push_back( iTmp[j]);
0508         iEnd.push_back( iVec.size());
0509       }
0510     }
0511 
0512     // Optionally search for antijunction legs: grab all anticolour ends.
0513     if (flip == 2) for (int i = 3; i < event.size(); ++i)
0514     if (event[i].isFinal() && event[i].acol() > 0 && event[i].col() == 0) {
0515       iTmp.clear();
0516       iTmp.push_back( i);
0517 
0518       // Step through anticolour neighbours to catch system.
0519       int iNow = i;
0520       map<int, int>::iterator colM = colMap.find( event[iNow].acol() );
0521       bool foundEnd = false;
0522       while (colM != colMap.end()) {
0523         iNow = colM->second;
0524         iTmp.push_back( iNow);
0525         if (event[iNow].acol() == 0) {
0526           foundEnd = true;
0527           break;
0528         }
0529         colM = colMap.find( event[iNow].acol() );
0530       }
0531 
0532       // Store acceptable system, but do not doublecount q - (n g) - qbar.
0533       if (!foundEnd) {
0534         iBeg.push_back( iVec.size());
0535         for (int j = 0; j < int(iTmp.size()); ++j) iVec.push_back( iTmp[j]);
0536         iEnd.push_back( iVec.size());
0537       }
0538     }
0539 
0540 
0541     // Variables for minimum search.
0542     int nSys = iBeg.size();
0543     int i1c, i1a, i2c, i2a, i1cMin, i1aMin, i2cMin, i2aMin, iSMin;
0544     double dLam, dLamMin;
0545     vector<InfoSwapMove> flipMin;
0546 
0547     // Loop through all system pairs.
0548     for (int iSys1 = 0; iSys1 < nSys - 1; ++iSys1) if (iBeg[iSys1] >= 0)
0549     for (int iSys2 = iSys1 + 1; iSys2 < nSys; ++iSys2) if (iBeg[iSys2] >= 0) {
0550       i1cMin = 0;
0551       i1aMin = 0;
0552       i2cMin = 0;
0553       i2aMin = 0;
0554       dLamMin = 1e4;
0555 
0556       // Loop through all possible flip locations for a pair.
0557       for (int j1 = iBeg[iSys1]; j1 < iEnd[iSys1] - 1; ++j1)
0558       for (int j2 = iBeg[iSys2]; j2 < iEnd[iSys2] - 1; ++j2) {
0559         i1c = iVec[j1];
0560         i1a = iVec[j1 + 1];
0561         i2c = iVec[j2];
0562         i2a = iVec[j2 + 1];
0563         dLam = lambda12( i1c, i2a) + lambda12( i2c, i1a)
0564              - lambda12( i1c, i1a) - lambda12( i2c, i2a);
0565         if (dLam < dLamMin) {
0566           i1cMin = i1c;
0567           i1aMin = i1a;
0568           i2cMin = i2c;
0569           i2aMin = i2a;
0570           dLamMin = dLam;
0571         }
0572       }
0573 
0574       // Store possible flips if low enough dLamMin.
0575       if (dLamMin < -dLamCut) flipMin.push_back( InfoSwapMove(
0576         iSys1, iSys2, i1cMin, i1aMin, i2cMin, i2aMin, dLamMin) );
0577     }
0578     int flipSize = flipMin.size();
0579 
0580     // Search for lowest possible flip among unused systems.
0581     for (int iFlip = 0; iFlip < min( nSys / 2, flipSize); ++iFlip) {
0582       iSMin = -1;
0583       dLamMin  = 1e4;
0584       for (int iSys12 = 0; iSys12 < flipSize; ++iSys12)
0585       if (flipMin[iSys12].i1 >= 0 && flipMin[iSys12].dLam < dLamMin) {
0586         iSMin   = iSys12;
0587         dLamMin = flipMin[iSys12].dLam;
0588       }
0589 
0590       // Do flip. Mark flipped systems.
0591       if (iSMin >= 0) {
0592         InfoSwapMove& flipNow = flipMin[iSMin];
0593         int iS1 = flipNow.i1;
0594         int iS2 = flipNow.i2;
0595         event[ flipNow.iAcol1 ].acol( event[flipNow.iCol2].col() );
0596         event[ flipNow.iAcol2 ].acol( event[flipNow.iCol1].col() );
0597         ++nRec;
0598         dLamTot += flipNow.dLam;
0599         for (int iSys12 = 0; iSys12 < flipSize; ++iSys12)
0600         if ( flipMin[iSys12].i1 == iS1 || flipMin[iSys12].i1 == iS2
0601           || flipMin[iSys12].i2 == iS1 || flipMin[iSys12].i2 == iS2)
0602           flipMin[iSys12].i1 = -1;
0603       }
0604       else break;
0605     }
0606 
0607     // Done.
0608     return true;
0609 
0610   }
0611 
0612 };
0613 
0614 //==========================================================================
0615 
0616 
0617 // Class for colour reconnection models specifically aimed at top decays.
0618 
0619 class TopReconUserHooks : public UserHooks {
0620 
0621 public:
0622 
0623   // Constructor and destructor.
0624   // mode = 0: no reconnection of tops (dummy option, does nothing);
0625   //      = 1: reconnect with random background gluon;
0626   //      = 2: reconnect with nearest (smallest-mass) background gluon;
0627   //      = 3: reconnect with furthest (largest-mass) background gluon;
0628   //      = 4: reconnect with smallest (with sign) lambda measure shift;
0629   //      = 5: reconnect only if reduced lamda, and then to most reduction.
0630   // strength: fraction of top gluons that is to be colour reconnected.
0631   // nList: list first nList parton classifications.
0632   // pTolerance: acceptable total momentum error (in reconstruction check).
0633   // m2Ref: squared reference mass scale for lambda measure calculation.
0634   // Possible variants for the future: swap with nearest in angle, not mass,
0635   // and/or only allow a background gluon to swap colours once.
0636 
0637   TopReconUserHooks(int modeIn = 0, double strengthIn = 1.) : mode(modeIn),
0638     strength(strengthIn) { iList = 0; nList = 0; pTolerance = 0.01;
0639     m2Ref = 1.;}
0640   ~TopReconUserHooks() {}
0641 
0642   // Allow colour reconnection after resonance decays (early or late)...
0643   virtual bool canReconnectResonanceSystems() {return true;}
0644 
0645   // ...which gives access to the event, for modification.
0646   virtual bool doReconnectResonanceSystems( int, Event& event) {
0647 
0648     // Return without action for relevant mode numbers.
0649     if (mode <= 0 || mode > 5) return true;
0650 
0651     // Classify coloured final partons.
0652     classifyFinalPartons(event);
0653 
0654     // Check that classification worked as expected.
0655     if (!checkClassification(event)) return false;
0656 
0657     // List first few classifications, along with the event.
0658     if (iList++ < nList) {
0659       listClassification();
0660       event.list();
0661     }
0662 
0663     // Perform reconnection for t and tbar in random order.
0664     bool tqrkFirst = (rndmPtr->flat() < 0.5);
0665     doReconnect( tqrkFirst, event);
0666     doReconnect(!tqrkFirst, event);
0667 
0668     // Done.
0669     return true;
0670   }
0671 
0672   // Return number of reconnections for current event.
0673   //int numberReconnections() {return nRec;}
0674 
0675 private:
0676 
0677   // Mode. Counters for how many events to list. Allowed momentum error.
0678   int    mode, iList, nList, nRec;
0679   double strength, pTolerance, m2Ref;
0680 
0681   // Arrays of (indices of) final partons from different sources.
0682   // So far geared towards t -> b W decays only.
0683   vector<int> iBqrk, iWpos, iTqrk, iBbar, iWneg, iTbar, iRest;
0684 
0685   // Maps telling where all colours and anticolours are stored.
0686   map<int, int> colMap, acolMap;
0687 
0688   //----------------------------------------------------------------------
0689 
0690   // Classify all coloured partons at the end of showers by origin.
0691   // Note: for now only t -> b W is fully classified.
0692 
0693   inline bool classifyFinalPartons(Event& event) {
0694 
0695     // Reset arrays to prepare for the new event analysis.
0696     iBqrk.clear();
0697     iWpos.clear();
0698     iTqrk.clear();
0699     iBbar.clear();
0700     iWneg.clear();
0701     iTbar.clear();
0702     iRest.clear();
0703     colMap.clear();
0704     acolMap.clear();
0705     nRec = 0;
0706 
0707     // Loop over all final particles. Tag coloured ones.
0708     for (int i = 3; i < event.size(); ++i) if (event[i].isFinal()) {
0709       bool hasCol = (event[i].colType() != 0);
0710 
0711       // Set up to find where each parton comes from.
0712       bool fsrFromT = false;
0713       bool fromTqrk = false;
0714       bool fromBqrk = false;
0715       bool fromWpos = false;
0716       bool fromTbar = false;
0717       bool fromBbar = false;
0718       bool fromWneg = false;
0719 
0720       // Identify current particle.
0721       int iNow  = i;
0722       int idOld = 0;
0723       do {
0724         int idNow = event[iNow].id();
0725 
0726         // Exclude FSR of gluons/photons from the t quark proper.
0727         if (abs(idNow) == 6 && (idOld == 21 || idOld == 22)) fsrFromT = true;
0728 
0729         // Check if current particle matches any of the categories.
0730         else if (idNow ==   6) fromTqrk = true;
0731         else if (idNow ==   5) fromBqrk = true;
0732         else if (idNow ==  24) fromWpos = true;
0733         else if (idNow ==  -6) fromTbar = true;
0734         else if (idNow ==  -5) fromBbar = true;
0735         else if (idNow == -24) fromWneg = true;
0736 
0737         // Step up through the history to the very top.
0738         iNow  = event[iNow].mother1();
0739         idOld = idNow;
0740       } while (iNow > 2 && !fsrFromT);
0741 
0742       // Bookkeep where the parton comes from. Note that b quarks also
0743       // can appear in W decays, so order of checks is relevant.
0744       if      (fromTqrk && fromWpos && hasCol) iWpos.push_back(i);
0745       else if (fromTqrk && fromBqrk && hasCol) iBqrk.push_back(i);
0746       else if (fromTqrk)                       iTqrk.push_back(i);
0747       else if (fromTbar && fromWneg && hasCol) iWneg.push_back(i);
0748       else if (fromTbar && fromBbar && hasCol) iBbar.push_back(i);
0749       else if (fromTbar)                       iTbar.push_back(i);
0750       else if (hasCol)                         iRest.push_back(i);
0751 
0752       // Store location of all colour and anticolour indices.
0753       if (hasCol && (mode == 4 || mode == 5)) {
0754         if (event[i].col() > 0) colMap[event[i].col()] = i;
0755         if (event[i].acol() > 0) acolMap[event[i].acol()] = i;
0756       }
0757     }
0758 
0759     // So far method always returns true.
0760     return true;
0761 
0762   }
0763 
0764   //----------------------------------------------------------------------
0765 
0766   // Check that classification worked by summing up partons to t/tbar.
0767 
0768   inline bool checkClassification(Event& event) {
0769 
0770     // Find final copy of t and tbar quarks.
0771     int iTqrkLoc = 0;
0772     int iTbarLoc = 0;
0773     for (int i = 3; i < event.size(); ++i) {
0774       if(event[i].id() ==  6) iTqrkLoc = i;
0775       if(event[i].id() == -6) iTbarLoc = i;
0776     }
0777 
0778     // Four-momentum of t minus all its decay products.
0779     Vec4 tqrkDiff = event[iTqrkLoc].p();
0780     for (int i = 0; i < int(iBqrk.size()); ++i)
0781       tqrkDiff -= event[iBqrk[i]].p();
0782     for (int i = 0; i < int(iWpos.size()); ++i)
0783       tqrkDiff -= event[iWpos[i]].p();
0784     for (int i = 0; i < int(iTqrk.size()); ++i)
0785       tqrkDiff -= event[iTqrk[i]].p();
0786 
0787     // Four-momentum of tbar minus all its decay products.
0788     Vec4 tbarDiff = event[iTbarLoc].p();
0789     for (int i = 0; i < int(iBbar.size()); ++i)
0790       tbarDiff -= event[iBbar[i]].p();
0791     for (int i = 0; i < int(iWneg.size()); ++i)
0792       tbarDiff -= event[iWneg[i]].p();
0793     for (int i = 0; i < int(iTbar.size()); ++i)
0794       tbarDiff -= event[iTbar[i]].p();
0795 
0796     // Print difference vectors and event if sum deviation is too big.
0797     double totErr = abs(tqrkDiff.px()) + abs(tqrkDiff.py())
0798       + abs(tqrkDiff.pz()) + abs(tqrkDiff.e()) + abs(tbarDiff.px())
0799       + abs(tbarDiff.py()) + abs(tbarDiff.pz()) + abs(tqrkDiff.e());
0800     if (totErr > pTolerance) {
0801       loggerPtr->ERROR_MSG("Error in t/tbar daughter search");
0802       cout << "\n Error in t/tbar daughter search: \n t    difference "
0803            << tqrkDiff << " tbar difference "<< tbarDiff;
0804       listClassification();
0805       event.list();
0806     }
0807 
0808     // Done.
0809     return (totErr < pTolerance);
0810   }
0811 
0812   //----------------------------------------------------------------------
0813 
0814   // Print how final-state (mainly coloured) particles were classified.
0815 
0816   inline void listClassification() {
0817 
0818     cout << "\n Final-state coloured partons classified by source: ";
0819     cout << "\n From Bqrk:";
0820     for (int i = 0; i < int(iBqrk.size()); ++i) cout << "  " << iBqrk[i];
0821     cout << "\n From Wpos:";
0822     for (int i = 0; i < int(iWpos.size()); ++i) cout << "  " << iWpos[i];
0823     cout << "\n From Tqrk:";
0824     for (int i = 0; i < int(iTqrk.size()); ++i) cout << "  " << iTqrk[i];
0825     cout << "\n From Bbar:";
0826     for (int i = 0; i < int(iBbar.size()); ++i) cout << "  " << iBbar[i];
0827     cout << "\n From Wneg:";
0828     for (int i = 0; i < int(iWneg.size()); ++i) cout << "  " << iWneg[i];
0829     cout << "\n From Tbar:";
0830     for (int i = 0; i < int(iTbar.size()); ++i) cout << "  " << iTbar[i];
0831     cout << "\n From Rest:";
0832     for (int i = 0; i < int(iRest.size()); ++i) {
0833       cout << "  " << iRest[i];
0834       if (i%20 == 19 && i + 1 != int(iRest.size())) cout << "\n           ";
0835     }
0836     cout << endl;
0837   }
0838 
0839   //----------------------------------------------------------------------
0840 
0841   // Reconnect gluons either from t or from tbar quark.
0842 
0843   inline bool doReconnect(bool doTqrk, Event& event) {
0844 
0845     // Gather coloured decay products either of t or of tbar.
0846     vector<int> iTdec;
0847     if (doTqrk) {
0848       for (int i = 0; i < int(iBqrk.size()); ++i) iTdec.push_back(iBqrk[i]);
0849       for (int i = 0; i < int(iWpos.size()); ++i) iTdec.push_back(iWpos[i]);
0850     } else {
0851       for (int i = 0; i < int(iBbar.size()); ++i) iTdec.push_back(iBbar[i]);
0852       for (int i = 0; i < int(iWneg.size()); ++i) iTdec.push_back(iWneg[i]);
0853     }
0854 
0855     // Extract the gluons from the t quark decay.
0856     vector<int> iGT;
0857     for (int i = 0; i < int(iTdec.size()); ++i) {
0858       int colNow  = event[iTdec[i]].col();
0859       int acolNow = event[iTdec[i]].acol();
0860       if (colNow > 0 && acolNow > 0) iGT.push_back(iTdec[i]);
0861     }
0862     int nGT    = iGT.size();
0863 
0864     // Randomize their stored order.
0865     if (nGT > 1) for (int i = 0; i < nGT; ++i) {
0866       int j = min( int(nGT * rndmPtr->flat()), nGT - 1 );
0867       swap( iGT[i], iGT[j]);
0868     }
0869 
0870     // Also extract the rest of the gluons in the event.
0871     vector<int> iGR;
0872     for (int i = 0; i < int(iRest.size()); ++i) {
0873       int colNow = event[iRest[i]].col();
0874       int acolNow = event[iRest[i]].acol();
0875       if (colNow > 0 && acolNow > 0) iGR.push_back(iRest[i]);
0876     }
0877     int nGR    = iGR.size();
0878     int iR, colT, acolT, iColT, iAcolT, colR, acolR, iColR, iAcolR;
0879     double mTR2, mTR2now, dLam = 0.0, lamT, lamNow, lamRec;
0880 
0881     // Loop through all top gluons; study fraction given by strength.
0882     if (nGT > 0 && nGR > 0)
0883     for (int iT = 0; iT < nGT; ++iT) {
0884       if (strength < rndmPtr->flat()) continue;
0885 
0886       // Pick random gluon from rest of event.
0887       if (mode == 1) iR = min( int(nGR * rndmPtr->flat()), nGR - 1 );
0888 
0889       // Find gluon from rest with lowest or highest invariant mass.
0890       else if (mode < 4) {
0891         iR   = 0;
0892         mTR2 = m2( event[iGT[iT]], event[iGR[iR]]);
0893         for (int ii = 1; ii < nGR; ++ii) {
0894           mTR2now = m2( event[iGT[iT]], event[iGR[ii]]);
0895           if (mode == 2 && mTR2now < mTR2) {iR = ii; mTR2 = mTR2now;}
0896           if (mode == 3 && mTR2now > mTR2) {iR = ii; mTR2 = mTR2now;}
0897         }
0898 
0899       // Find gluon from rest with smallest lambda value shift.
0900       } else {
0901         iR     = -1;
0902         dLam   = 1e10;
0903         colT   = event[iGT[iT]].col();
0904         acolT  = event[iGT[iT]].acol();
0905         iColT  = acolMap[colT];
0906         iAcolT = colMap[acolT];
0907         lamT   = log1p(m2( event[iGT[iT]], event[iColT]) / m2Ref)
0908                + log1p(m2( event[iGT[iT]], event[iAcolT]) / m2Ref);
0909         for (int ii = 0; ii < nGR; ++ii) {
0910           colR   = event[iGR[ii]].col();
0911           acolR  = event[iGR[ii]].acol();
0912           iColR  = acolMap[colR];
0913           iAcolR = colMap[acolR];
0914           lamNow = lamT
0915                  + log1p(m2( event[iGR[ii]], event[iColR]) / m2Ref)
0916                  + log1p(m2( event[iGR[ii]], event[iAcolR]) / m2Ref);
0917           lamRec = log1p(m2( event[iGT[iT]], event[iColR]) / m2Ref)
0918                  + log1p(m2( event[iGT[iT]], event[iAcolR]) / m2Ref)
0919                  + log1p(m2( event[iGR[ii]], event[iColT]) / m2Ref)
0920                  + log1p(m2( event[iGR[ii]], event[iAcolT]) / m2Ref);
0921           if (lamRec - lamNow < dLam) {iR = ii; dLam = lamRec - lamNow;}
0922         }
0923         if (mode == 5 && dLam > 0.) continue;
0924       }
0925 
0926       // Swap top and rest gluon colour and anticolour.
0927       ++nRec;
0928       swapCols( iGT[iT], iGR[iR], event);
0929     }
0930 
0931     // Done.
0932     return true;
0933 
0934   }
0935 
0936   //----------------------------------------------------------------------
0937 
0938   // Swap colours and/or anticolours in the event listing.
0939 
0940   inline void swapCols( int i, int j, Event& event) {
0941 
0942     // Swap the colours in the event record.
0943     int coli  = event[i].col();
0944     int acoli = event[i].acol();
0945     int colj  = event[j].col();
0946     int acolj = event[j].acol();
0947     event[i].cols( colj, acolj);
0948     event[j].cols( coli, acoli);
0949 
0950     // Swap the indices in the colour maps.
0951     colMap[coli]   = j;
0952     acolMap[acoli] = j;
0953     colMap[colj]   = i;
0954     acolMap[acolj] = i;
0955 
0956   }
0957 
0958 };
0959 
0960 //==========================================================================
0961 
0962 } // end namespace Pythia8
0963 
0964 #endif // end Pythia8_ColourReconnectionHooks_H