// History.cc is a part of the PYTHIA event generator. // Copyright (C) 2017 Torbjorn Sjostrand. // PYTHIA is licenced under the GNU GPL version 2, see COPYING for details. // Please respect the MCnet Guidelines, see GUIDELINES for details. // This file is written by Stefan Prestel. // Function definitions (not found in the header) for the // Clustering and History classes. #include "Pythia8/History.h" namespace Pythia8 { //========================================================================== // The Clustering class. //-------------------------------------------------------------------------- // Declaration of Clustering class // This class holds information about one radiator, recoiler, // emitted system. // This class is a container class for History class use. // print for debug void Clustering::list() const { cout << " emt " << emitted << " rad " << emittor << " rec " << recoiler << " partner " << partner << " pTscale " << pTscale << endl; } //========================================================================== // The History class. // A History object represents an event in a given step in the CKKW-L // clustering procedure. It defines a tree-like recursive structure, // where the root node represents the state with n jets as given by // the matrix element generator, and is characterized by the member // variable mother being null. The leaves on the tree corresponds to a // fully clustered paths where the original n-jets has been clustered // down to the Born-level state. Also states which cannot be clustered // down to the Born-level are possible - these will be called // incomplete. The leaves are characterized by the vector of children // being empty. //-------------------------------------------------------------------------- // Number of trial emission to use for calculating the average number of // emissions const int History::NTRIAL = 1; //-------------------------------------------------------------------------- // Declaration of History class // The only constructor. Default arguments are used when creating // the initial history node. The \a depth is the maximum number of // clusterings requested. \a scalein is the scale at which the \a // statein was clustered (should be set to the merging scale for the // initial history node. \a beamAIn and beamBIn are needed to // calcutate PDF ratios, \a particleDataIn to have access to the // correct masses of particles. If \a isOrdered is true, the previous // clusterings has been ordered. \a is the PDF ratio for this // clustering (=1 for FSR clusterings). \a probin is the accumulated // probabilities for the previous clusterings, and \ mothin is the // previous history node (null for the initial node). History::History( int depth, double scalein, Event statein, Clustering c, MergingHooks* mergingHooksPtrIn, BeamParticle beamAIn, BeamParticle beamBIn, ParticleData* particleDataPtrIn, Info* infoPtrIn, PartonLevel* showersIn, CoupSM* coupSMPtrIn, bool isOrdered = true, bool isStronglyOrdered = true, bool isAllowed = true, bool isNextInInput = true, double probin = 1.0, History * mothin = 0) : state(statein), mother(mothin), selectedChild(-1), sumpath(0.0), sumGoodBranches(0.0), sumBadBranches(0.0), foundOrderedPath(false), foundStronglyOrderedPath(false), foundAllowedPath(false), foundCompletePath(false), scale(scalein), nextInInput(isNextInInput), prob(probin), clusterIn(c), iReclusteredOld(0), doInclude(true), mergingHooksPtr(mergingHooksPtrIn), beamA(beamAIn), beamB(beamBIn), particleDataPtr(particleDataPtrIn), infoPtr(infoPtrIn), showers(showersIn), coupSMPtr(coupSMPtrIn) { // Initialise beam particles setupBeams(); // Update probability with PDF ratio if (mother && mergingHooksPtr->includeRedundant()) prob *= pdfForSudakov(); // Minimal scalar sum of pT used in Herwig to choose history // Keep track of scalar PT if (mother) { double acoll = (mother->state[clusterIn.emittor].isFinal()) ? mergingHooksPtr->herwigAcollFSR() : mergingHooksPtr->herwigAcollISR(); sumScalarPT = mother->sumScalarPT + acoll*scale; } else sumScalarPT = 0.0; // Remember reclustered radiator in lower multiplicity state if ( mother ) iReclusteredOld = mother->iReclusteredNew; // Check if more steps should be taken. int nFinalP = 0, nFinalW = 0, nFinalZ = 0; int nL = 0, nA= 0, nH = 0; for ( int i = 0; i < int(state.size()); ++i ) if ( state[i].isFinal() ) { if ( state[i].colType() != 0 ) nFinalP++; if ( state[i].idAbs() == 23 ) nFinalZ++; if ( state[i].idAbs() == 24 ) nFinalW++; if ( state[i].idAbs() < 20 && state[i].idAbs() > 10) nL++; if ( state[i].idAbs() == 22) nA++; if ( state[i].idAbs() == 23 || state[i].idAbs() == 24 || state[i].idAbs() == 25) nH++; } if ( mergingHooksPtr->doWeakClustering() && nFinalP == 2 && nFinalW == 0 && nFinalZ == 0) depth = 0; // Stop clustering at 2->1 massive. // Stop clustering at 2->2 massless. bool qcd = ( nFinalP > mergingHooksPtr->hardProcess->nQuarksOut() ); // If this is not the fully clustered state, try to find possible // QCD clusterings. vector clusterings; if ( qcd && depth > 0 ) clusterings = getAllQCDClusterings(); bool dow = ( mergingHooksPtr->doWeakClustering() && nFinalP > 1 && nFinalW+nFinalZ > 0 ); // If necessary, try to find possible EW clusterings. vector clusteringsEW; // if ( depth > 0 && mergingHooksPtr->doWeakClustering() ) if ( depth > 0 && dow ) clusteringsEW = getAllEWClusterings(); if ( !clusteringsEW.empty() ) { clusterings.insert( clusterings.end(), clusteringsEW.begin(), clusteringsEW.end() ); } // If necessary, try to find possible SQCD clusterings. vector clusteringsSQCD; if ( depth > 0 && mergingHooksPtr->doSQCDClustering() ) clusteringsSQCD = getAllSQCDClusterings(); if ( !clusteringsSQCD.empty() ) clusterings.insert( clusterings.end(), clusteringsSQCD.begin(), clusteringsSQCD.end() ); // If no clusterings were found, the recursion is done and we // register this node. if ( clusterings.empty() ) { // Multiply with hard process matrix element. prob *= hardProcessME(state); registerPath( *this, isOrdered, isStronglyOrdered, isAllowed, depth == 0 ); return; } // Now we sort the possible clusterings so that we try the // smallest scale first. multimap sorted; for ( int i = 0, N = clusterings.size(); i < N; ++i ) { sorted.insert(make_pair(clusterings[i].pT(), &clusterings[i])); } for ( multimap::iterator it = sorted.begin(); it != sorted.end(); ++it ) { // If this path is not strongly ordered and we already have found an // ordered path, then we don't need to continue along this path. bool stronglyOrdered = isStronglyOrdered; if ( mergingHooksPtr->enforceStrongOrdering() && ( !stronglyOrdered || ( mother && ( it->first < mergingHooksPtr->scaleSeparationFactor()*scale ) ))) { if ( onlyStronglyOrderedPaths() ) continue; stronglyOrdered = false; } // Check if reclustering follows ordered sequence. bool ordered = isOrdered; if ( mergingHooksPtr->orderInRapidity() && mergingHooksPtr->orderHistories() ) { // Get new z value double z = getCurrentZ((*it->second).emittor, (*it->second).recoiler,(*it->second).emitted, (*it->second).flavRadBef); // Get z value of splitting that produced this state double zOld = (!mother) ? 0. : mother->getCurrentZ(clusterIn.emittor, clusterIn.recoiler,clusterIn.emitted, clusterIn.flavRadBef); // If this path is not ordered in pT and y, and we already have found // an ordered path, then we don't need to continue along this path. if ( !ordered || ( mother && (it->first < scale || it->first < pow(1. - z,2) / (z * (1. - zOld ))*scale ))) { if ( onlyOrderedPaths() ) continue; ordered = false; } } else if ( mergingHooksPtr->orderHistories() ) { // If this path is not ordered in pT and we already have found an // ordered path, then we don't need to continue along this path, unless // we have not yet found an allowed path. if ( !ordered || ( mother && (it->first < scale) ) ) { if ( onlyOrderedPaths() && onlyAllowedPaths() ) continue; ordered = false; } } // Check if reclustered state should be disallowed. bool doCut = mergingHooksPtr->canCutOnRecState() || mergingHooksPtr->allowCutOnRecState(); bool allowed = isAllowed; if ( doCut && mergingHooksPtr->doCutOnRecState(cluster(*it->second)) ) { if ( onlyAllowedPaths() ) continue; allowed = false; } // Perform the clustering and recurse and construct the next // history node. children.push_back(new History(depth - 1,it->first,cluster(*it->second), *it->second, mergingHooksPtr, beamA, beamB, particleDataPtr, infoPtr, showers, coupSMPtr, ordered, stronglyOrdered, allowed, true, prob*getProb(*it->second), this )); } } //-------------------------------------------------------------------------- // Function to project all possible paths onto only the desired paths. bool History::projectOntoDesiredHistories() { // At the moment, only trim histories. return trimHistories(); } //-------------------------------------------------------------------------- // In the initial history node, select one of the paths according to // the probabilities. This function should be called for the initial // history node. // IN trialShower* : Previously initialised trialShower object, // to perform trial showering and as // repository of pointers to initialise alphaS // PartonSystems* : PartonSystems object needed to initialise // shower objects // OUT double : (Sukadov) , (alpha_S ratios) , (PDF ratios) double History::weightTREE(PartonLevel* trial, AlphaStrong * asFSR, AlphaStrong * asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double RN) { if ( mergingHooksPtr->canCutOnRecState() && !foundAllowedPath ) { string message="Warning in History::weightTREE: No allowed history"; message+=" found. Using disallowed history."; infoPtr->errorMsg(message); } if ( mergingHooksPtr->orderHistories() && !foundOrderedPath ) { string message="Warning in History::weightTREE: No ordered history"; message+=" found. Using unordered history."; infoPtr->errorMsg(message); } if ( mergingHooksPtr->canCutOnRecState() && mergingHooksPtr->orderHistories() && !foundAllowedPath && !foundOrderedPath ) { string message="Warning in History::weightTREE: No allowed or ordered"; message+=" history found."; infoPtr->errorMsg(message); } // Read alpha_S in ME calculation and maximal scale (eCM) double asME = infoPtr->alphaS(); double aemME = infoPtr->alphaEM(); double maxScale = (foundCompletePath) ? infoPtr->eCM() : mergingHooksPtr->muFinME(); // Select a path of clusterings History * selected = select(RN); // Set scales in the states to the scales pythia would have set selected->setScalesInHistory(); // Get weight. double sudakov = 1.; double asWeight = 1.; double aemWeight = 1.; double pdfWeight = 1.; // Do trial shower, calculation of alpha_S ratios, PDF ratios sudakov = selected->weightTree( trial, asME, aemME, maxScale, selected->clusterIn.pT(), asFSR, asISR, aemFSR, aemISR, asWeight, aemWeight, pdfWeight ); // MPI no-emission probability int njetsMaxMPI = mergingHooksPtr->nMinMPI(); double mpiwt = selected->weightTreeEmissions( trial, -1, 0, njetsMaxMPI, maxScale ); // Set hard process renormalisation scale to default Pythia value. bool resetScales = mergingHooksPtr->resetHardQRen(); // For pure QCD dijet events, evaluate the coupling of the hard process at // a more reasonable pT, rather than evaluation \alpha_s at a fixed // arbitrary scale. if ( resetScales && mergingHooksPtr->getProcessString().compare("pp>jj") == 0) { // Reset to a running coupling. Here we choose FSR for simplicity. double newQ2Ren = pow2( selected->hardRenScale(selected->state) ); double runningCoupling = (*asFSR).alphaS(newQ2Ren) / asME; asWeight *= pow2(runningCoupling); } else if (mergingHooksPtr->doWeakClustering() && isQCD2to2(selected->state)) { // Reset to a running coupling. Here we choose FSR for simplicity. double newQ2Ren = pow2( selected->hardRenScale(selected->state) ); double runningCoupling = (*asFSR).alphaS(newQ2Ren) / asME; asWeight *= pow2(runningCoupling); } // For W clustering, correct the \alpha_em. if (mergingHooksPtr->doWeakClustering() && isEW2to1(selected->state)) { // Reset to a running coupling. Here we choose FSR for simplicity. double newQ2Ren = pow2( selected->hardRenScale(selected->state) ); double runningCoupling = (*aemFSR).alphaEM(newQ2Ren) / aemME; aemWeight *= runningCoupling; } // For prompt photon events, evaluate the coupling of the hard process at // a more reasonable pT, rather than evaluation \alpha_s at a fixed // arbitrary scale. if ( resetScales && mergingHooksPtr->getProcessString().compare("pp>aj") == 0) { // Reset to a running coupling. In prompt photon always ISR. double newQ2Ren = pow2( selected->hardRenScale(selected->state) ); double runningCoupling = (*asISR).alphaS( newQ2Ren + pow(mergingHooksPtr->pT0ISR(),2) ) / asME; asWeight *= runningCoupling; } // Done return (sudakov*asWeight*aemWeight*pdfWeight*mpiwt); } //-------------------------------------------------------------------------- // Function to return weight of virtual correction and subtractive events // for NL3 merging double History::weightLOOP(PartonLevel* trial, double RN ) { if ( mergingHooksPtr->canCutOnRecState() && !foundAllowedPath ) { string message="Warning in History::weightLOOP: No allowed history"; message+=" found. Using disallowed history."; infoPtr->errorMsg(message); } // Select a path of clusterings History * selected = select(RN); // Set scales in the states to the scales pythia would have set selected->setScalesInHistory(); // So far, no reweighting double wt = 1.; // Only reweighting with MPI no-emission probability double maxScale = (foundCompletePath) ? infoPtr->eCM() : mergingHooksPtr->muFinME(); int njetsMaxMPI = mergingHooksPtr->nMinMPI(); double mpiwt = selected->weightTreeEmissions( trial, -1, 0, njetsMaxMPI, maxScale ); wt = mpiwt; // Done return wt; } //-------------------------------------------------------------------------- // Function to calculate O(\alpha_s)-term of CKKWL-weight for NLO merging double History::weightFIRST(PartonLevel* trial, AlphaStrong* asFSR, AlphaStrong* asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double RN, Rndm* rndmPtr ) { // Dummy statement to avoid compiler warnings. if (false) cout << aemFSR << aemISR; // Read alpha_S in ME calculation and maximal scale (eCM) double asME = infoPtr->alphaS(); double muR = mergingHooksPtr->muRinME(); double maxScale = (foundCompletePath) ? infoPtr->eCM() : mergingHooksPtr->muFinME(); // Pick path of clusterings History * selected = select(RN); // Set scales in the states to the scales pythia would have set selected->setScalesInHistory(); double nSteps = mergingHooksPtr->getNumberOfClusteringSteps(state); // Get the lowest order k-factor and add first two terms in expansion double kFactor = asME * mergingHooksPtr->k1Factor(nSteps); // If using Bbar, which includes a tree-level part, subtract an // additional one, i.e. the O(\as^0) contribution as well double wt = 1. + kFactor; // Calculate sum of O(alpha) terms wt += selected->weightFirst(trial,asME, muR, maxScale, asFSR, asISR, rndmPtr ); // Get starting scale for trial showers. double startingScale = (selected->mother) ? state.scale() : infoPtr->eCM(); // Count emissions: New variant // Generate true average, not only one-point bool fixpdf = true; bool fixas = true; double nWeight1 = 0.; for(int i=0; i < NTRIAL; ++i) { // Get number of emissions vector unresolvedEmissionTerm = countEmissions( trial, startingScale, mergingHooksPtr->tms(), 2, asME, asFSR, asISR, 3, fixpdf, fixas ); nWeight1 += unresolvedEmissionTerm[1]; } wt += nWeight1/double(NTRIAL); // Done return wt; } //-------------------------------------------------------------------------- double History::weight_UMEPS_TREE(PartonLevel* trial, AlphaStrong * asFSR, AlphaStrong * asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double RN) { // No difference to CKKW-L. Recycle CKKW-L function. return weightTREE( trial, asFSR, asISR, aemFSR, aemISR, RN); } //-------------------------------------------------------------------------- // Function to return weight of virtual correction events for NLO merging double History::weight_UMEPS_SUBT(PartonLevel* trial, AlphaStrong * asFSR, AlphaStrong * asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double RN ) { // Read alpha_S in ME calculation and maximal scale (eCM) double asME = infoPtr->alphaS(); double aemME = infoPtr->alphaEM(); double maxScale = (foundCompletePath) ? infoPtr->eCM() : mergingHooksPtr->muFinME(); // Select a path of clusterings History * selected = select(RN); // Set scales in the states to the scales pythia would have set selected->setScalesInHistory(); // Get weight. double sudakov = 1.; double asWeight = 1.; double aemWeight = 1.; double pdfWeight = 1.; // Do trial shower, calculation of alpha_S ratios, PDF ratios sudakov = selected->weightTree(trial, asME, aemME, maxScale, selected->clusterIn.pT(), asFSR, asISR, aemFSR, aemISR, asWeight, aemWeight, pdfWeight); // MPI no-emission probability. int njetsMaxMPI = mergingHooksPtr->nMinMPI()+1; double mpiwt = selected->weightTreeEmissions( trial, -1, 0, njetsMaxMPI, maxScale ); // Set hard process renormalisation scale to default Pythia value. bool resetScales = mergingHooksPtr->resetHardQRen(); // For pure QCD dijet events, evaluate the coupling of the hard process at // a more reasonable pT, rather than evaluation \alpha_s at a fixed // arbitrary scale. if ( resetScales && mergingHooksPtr->getProcessString().compare("pp>jj") == 0) { // Reset to a running coupling. Here we choose FSR for simplicity. double newQ2Ren = pow2( selected->hardRenScale(selected->state) ); double runningCoupling = (*asFSR).alphaS(newQ2Ren) / asME; asWeight *= pow(runningCoupling,2); } // For prompt photon events, evaluate the coupling of the hard process at // a more reasonable pT, rather than evaluation \alpha_s at a fixed // arbitrary scale. if ( resetScales && mergingHooksPtr->getProcessString().compare("pp>aj") == 0) { // Reset to a running coupling. In prompt photon always ISR. double newQ2Ren = pow2( selected->hardRenScale(selected->state) ); double runningCoupling = (*asISR).alphaS( newQ2Ren + pow(mergingHooksPtr->pT0ISR(),2) ) / asME; asWeight *= runningCoupling; } // Done return (sudakov*asWeight*aemWeight*pdfWeight*mpiwt); } //-------------------------------------------------------------------------- double History::weight_UNLOPS_TREE(PartonLevel* trial, AlphaStrong * asFSR, AlphaStrong * asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double RN, int depth) { // Read alpha_S in ME calculation and maximal scale (eCM) double asME = infoPtr->alphaS(); double aemME = infoPtr->alphaEM(); double maxScale = (foundCompletePath) ? infoPtr->eCM() : mergingHooksPtr->muFinME(); // Select a path of clusterings History * selected = select(RN); // Set scales in the states to the scales pythia would have set selected->setScalesInHistory(); // Get weight. double asWeight = 1.; double aemWeight = 1.; double pdfWeight = 1.; // Do trial shower, calculation of alpha_S ratios, PDF ratios double wt = 1.; if (depth < 0) wt = selected->weightTree(trial, asME, aemME, maxScale, selected->clusterIn.pT(), asFSR, asISR, aemFSR, aemISR, asWeight, aemWeight, pdfWeight); else { wt = selected->weightTreeEmissions( trial, 1, 0, depth, maxScale ); if (wt != 0.) asWeight = selected->weightTreeALPHAS( asME, asFSR, asISR, depth); if (wt != 0.) aemWeight = selected->weightTreeALPHAEM( aemME, aemFSR, aemISR, depth); if (wt != 0.) pdfWeight = selected->weightTreePDFs( maxScale, selected->clusterIn.pT(), depth); } // MPI no-emission probability. int njetsMaxMPI = mergingHooksPtr->nMinMPI(); double mpiwt = selected->weightTreeEmissions( trial, -1, 0, njetsMaxMPI, maxScale ); // Set hard process renormalisation scale to default Pythia value. bool resetScales = mergingHooksPtr->resetHardQRen(); // For pure QCD dijet events, evaluate the coupling of the hard process at // a more reasonable pT, rather than evaluation \alpha_s at a fixed // arbitrary scale. if ( resetScales && mergingHooksPtr->getProcessString().compare("pp>jj") == 0) { // Reset to a running coupling. Here we choose FSR for simplicity. double newQ2Ren = pow2( selected->hardRenScale(selected->state) ); double runningCoupling = (*asFSR).alphaS(newQ2Ren) / asME; asWeight *= pow(runningCoupling,2); } // For prompt photon events, evaluate the coupling of the hard process at // a more reasonable pT, rather than evaluation \alpha_s at a fixed // arbitrary scale. if ( resetScales && mergingHooksPtr->getProcessString().compare("pp>aj") == 0) { // Reset to a running coupling. In prompt photon always ISR. double newQ2Ren = pow2( selected->hardRenScale(selected->state) ); double runningCoupling = (*asISR).alphaS( newQ2Ren + pow(mergingHooksPtr->pT0ISR(),2) ) / asME; asWeight *= runningCoupling; } // Done return (wt*asWeight*aemWeight*pdfWeight*mpiwt); } //-------------------------------------------------------------------------- double History::weight_UNLOPS_LOOP(PartonLevel* trial, AlphaStrong * asFSR, AlphaStrong * asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double RN, int depth) { // No difference to default NL3 if (depth < 0) return weightLOOP(trial, RN); else return weight_UNLOPS_TREE(trial, asFSR,asISR, aemFSR,aemISR, RN,depth); } //-------------------------------------------------------------------------- double History::weight_UNLOPS_SUBT(PartonLevel* trial, AlphaStrong * asFSR, AlphaStrong * asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double RN, int depth) { // Select a path of clusterings History * selected = select(RN); // Set scales in the states to the scales pythia would have set selected->setScalesInHistory(); // So far, no reweighting double wt = 1.; // Read alpha_S in ME calculation and maximal scale (eCM) double asME = infoPtr->alphaS(); double aemME = infoPtr->alphaEM(); double maxScale = (foundCompletePath) ? infoPtr->eCM() : mergingHooksPtr->muFinME(); // Only allow two clusterings if all intermediate states above the // merging scale. double nSteps = mergingHooksPtr->getNumberOfClusteringSteps(state); if ( nSteps == 2 && mergingHooksPtr->nRecluster() == 2 && ( !foundCompletePath || !selected->allIntermediateAboveRhoMS( mergingHooksPtr->tms() )) ) return 0.; // Get weights: alpha_S ratios and PDF ratios double asWeight = 1.; double aemWeight = 1.; double pdfWeight = 1.; // Do trial shower, calculation of alpha_S ratios, PDF ratios double sudakov = 1.; if (depth < 0) sudakov = selected->weightTree(trial, asME, aemME, maxScale, selected->clusterIn.pT(), asFSR, asISR, aemFSR, aemISR, asWeight, aemWeight, pdfWeight); else { sudakov = selected->weightTreeEmissions( trial, 1, 0, depth, maxScale ); if (sudakov > 0.) asWeight = selected->weightTreeALPHAS( asME, asFSR, asISR, depth); if (sudakov > 0.) aemWeight = selected->weightTreeALPHAEM( aemME, aemFSR, aemISR, depth); if (sudakov > 0.) pdfWeight = selected->weightTreePDFs( maxScale, selected->clusterIn.pT(), depth); } // MPI no-emission probability. int njetsMaxMPI = mergingHooksPtr->nMinMPI()+1; double mpiwt = selected->weightTreeEmissions( trial, -1, 0, njetsMaxMPI, maxScale ); // Set weight wt = ( mergingHooksPtr->nRecluster() == 2 ) ? 1. : asWeight*aemWeight*pdfWeight*sudakov*mpiwt; // Done return wt; } //-------------------------------------------------------------------------- double History::weight_UNLOPS_SUBTNLO(PartonLevel* trial, AlphaStrong * asFSR, AlphaStrong * asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double RN, int depth) { if (depth < 0) { // Select a path of clusterings History * selected = select(RN); // Set scales in the states to the scales pythia would have set selected->setScalesInHistory(); // So far, no reweighting double wt = 1.; // Only reweighting with MPI no-emission probability double maxScale = (foundCompletePath) ? infoPtr->eCM() : mergingHooksPtr->muFinME(); int njetsMaxMPI = mergingHooksPtr->nMinMPI()+1; double mpiwt = selected->weightTreeEmissions( trial, -1, 0, njetsMaxMPI, maxScale ); wt = mpiwt; // Done return wt; } else return weight_UNLOPS_SUBT(trial, asFSR, asISR, aemFSR, aemISR, RN, depth); } //-------------------------------------------------------------------------- // Function to calculate O(\alpha_s)-term of CKKWL-weight for NLO merging double History::weight_UNLOPS_CORRECTION( int order, PartonLevel* trial, AlphaStrong* asFSR, AlphaStrong* asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double RN, Rndm* rndmPtr ) { // Dummy statement to avoid compiler warnings. if (false) cout << aemFSR << aemISR; // Already done if no correction should be calculated if ( order < 0 ) return 0.; // Read alpha_S in ME calculation and maximal scale (eCM) double asME = infoPtr->alphaS(); //double aemME = infoPtr->alphaEM(); double muR = mergingHooksPtr->muRinME(); double maxScale = (foundCompletePath) ? infoPtr->eCM() : mergingHooksPtr->muFinME(); // Pick path of clusterings History * selected = select(RN); // Set scales in the states to the scales pythia would have set selected->setScalesInHistory(); double nSteps = mergingHooksPtr->getNumberOfClusteringSteps(state); // Get the lowest order k-factor and add first two terms in expansion double kFactor = asME * mergingHooksPtr->k1Factor(nSteps); // If using Bbar, which includes a tree-level part, subtract an // additional one, i.e. the O(\as^0) contribution as well double wt = 1.; // If only O(\alpha_s^0)-term is to be calculated, done already. if ( order == 0 ) return wt; // Start by adding the O(\alpha_s^1)-term of the k-factor. wt += kFactor; // Calculate sum of O(\alpha_s^1)-terms of the ckkw-l weight WITHOUT // the O(\alpha_s^1)-term of the last no-emission probability. bool fixpdf = true; bool fixas = true; // Get first term in expansion of alpha_s ratios. double wA = selected->weightFirstALPHAS( asME, muR, asFSR, asISR ); // Add logarithm from \alpha_s expansion to weight. wt += (fixas) ? wA : 0.; // Generate true average, not only one-point. double nWeight = 0.; for ( int i=0; i < NTRIAL; ++i ) { // Get average number of emissions. double wE = selected->weightFirstEmissions(trial,asME, maxScale, asFSR, asISR, fixpdf, fixas ); // Add average number of emissions off reconstructed states to weight. nWeight += wE; // Get first term in expansion of PDF ratios. double pscale = selected->clusterIn.pT(); double wP = selected->weightFirstPDFs(asME, maxScale, pscale, rndmPtr); // Add integral of DGLAP shifted PDF ratios from \alpha_s expansion to wt. nWeight += (fixpdf) ? wP : 0.; } wt += nWeight/double(NTRIAL); // If O(\alpha_s^1)-term + O(\alpha_s^1)-term is to be calculated, done. if ( order == 1 ) return wt; // So far, no calculation of O(\alpha_s^2)-term return 0.; } //-------------------------------------------------------------------------- // Function to set the state with complete scales for evolution. void History::getStartingConditions( const double RN, Event& outState ) { // Select the history History * selected = select(RN); // Set scales in the states to the scales pythia would have set selected->setScalesInHistory(); // Get number of clustering steps. int nSteps = mergingHooksPtr->getNumberOfClusteringSteps(state); // Update the lowest order process. if (!selected->mother) { int nFinal = 0; for(int i=0; i < int(state.size()); ++i) if ( state[i].isFinal()) nFinal++; if (nFinal <=2) state.scale(mergingHooksPtr->muF()); // Save information on last splitting, to allow the next // emission in the shower to have smaller rapidity with // respect to the last ME splitting. // For hard process, use dummy values. if (mergingHooksPtr->getNumberOfClusteringSteps(state) == 0) { infoPtr->zNowISR(0.5); infoPtr->pT2NowISR(pow(state[0].e(),2)); infoPtr->hasHistory(true); // For incomplete process, try to use real values. } else { infoPtr->zNowISR(selected->zISR()); infoPtr->pT2NowISR(pow(selected->pTISR(),2)); infoPtr->hasHistory(true); } // Set QCD 2->2 starting scale different from arbitrary scale in LHEF! // --> Set to minimal mT of partons. int nFinalCol = 0; double muf = state[0].e(); for ( int i=0; i < state.size(); ++i ) if ( state[i].isFinal() && ( state[i].colType() != 0 || state[i].id() == 22 ) ) { nFinalCol++; muf = min( muf, abs(state[i].mT()) ); } // For pure QCD dijet events (only!), set the process scale to the // transverse momentum of the outgoing partons. if ( nSteps == 0 && nFinalCol == 2 && ( mergingHooksPtr->getProcessString().compare("pp>jj") == 0 || mergingHooksPtr->getProcessString().compare("pp>aj") == 0) ) { state.scale(muf); for (int i = 3;i < state.size();++i) state[i].scale(muf); } // For weak inclusive merging, follow QCD 2->2 starting scale for dijet // events. Also, restore input input polarisations. if (nSteps == 0 && nFinalCol == 2 && mergingHooksPtr->getProcessString().find("inc") != string::npos) { state.scale(muf); for (int i = 3;i < state.size();++i) state[i].scale(muf); for ( int i=0; i < min(state.size(),outState.size()); ++i ) state[i].pol(outState[i].pol()); } } else { // Save information on last splitting, to allow the next // emission in the shower to have smaller rapidity with // respect to the last ME splitting. infoPtr->zNowISR(selected->zISR()); infoPtr->pT2NowISR(pow(selected->pTISR(),2)); infoPtr->hasHistory(true); } // Copy the output state. outState = state; // Save MPI starting scale. if (nSteps == 0) mergingHooksPtr->muMI(infoPtr->eCM()); else mergingHooksPtr->muMI(outState.scale()); // Setup the weak shower if W clustering is enabled. if (mergingHooksPtr->doWeakClustering()) setupWeakShower(0); } //-------------------------------------------------------------------------- // Function to print the history that would be chosen from the number RN. void History::printHistory( const double RN ) { History * selected = select(RN); selected->printStates(); // Done } //-------------------------------------------------------------------------- // Function to print the states in a history, starting from the hard process. void History::printStates() { if ( !mother ) { cout << scientific << setprecision(6) << "Probability=" << prob << endl; state.list(); return; } // Print. double p = (mother) ? prob/mother->prob : prob; cout << scientific << setprecision(6) << "Probability=" << p << " scale=" << clusterIn.pT() << endl; state.list(); // Recurse mother->printStates(); // Done return; } //-------------------------------------------------------------------------- // Function to set the state with complete scales for evolution. bool History::getClusteredEvent( const double RN, int nSteps, Event& outState) { // Select history History * selected = select(RN); // Set scales in the states to the scales pythia would have set // (Only needed if not done before in calculation of weights or // setting of starting conditions) selected->setScalesInHistory(); // If the history does not allow for nSteps clusterings (e.g. because the // history is incomplete), return false if (nSteps > selected->nClusterings()) return false; // Return event with nSteps-1 additional partons (i.e. recluster the last // splitting) and copy the output state outState = selected->clusteredState(nSteps-1); // Done. return true; } //-------------------------------------------------------------------------- bool History::getFirstClusteredEventAboveTMS( const double RN, int nDesired, Event& process, int& nPerformed, bool doUpdate ) { // Do reclustering (looping) steps. Remember process scale. int nTried = nDesired - 1; // Get number of clustering steps. int nSteps = select(RN)->nClusterings(); // Set scales in the states to the scales pythia would have set. select(RN)->setScalesInHistory(); // Recluster until reclustered event is above the merging scale. Event dummy = Event(); do { // Initialise temporary output of reclustering. dummy.clear(); dummy.init( "(hard process-modified)", particleDataPtr ); dummy.clear(); // Recluster once more. nTried++; // If reclustered event does not exist, exit. if ( !getClusteredEvent( RN, nSteps-nTried+1, dummy ) ) return false; if ( nTried >= nSteps ) break; // Continue loop if reclustered event has unresolved partons. } while ( mergingHooksPtr->getNumberOfClusteringSteps(dummy) > 0 && mergingHooksPtr->tmsNow( dummy) < mergingHooksPtr->tms() ); // Update the hard process. if ( doUpdate ) process = dummy; // Failed to produce output state. if ( nTried > nSteps ) return false; nPerformed = nTried; if ( doUpdate ) { // Update to the actual number of steps. mergingHooksPtr->nReclusterSave = nPerformed; // Save MPI starting scale if (mergingHooksPtr->getNumberOfClusteringSteps(state) == 0) mergingHooksPtr->muMI(infoPtr->eCM()); else mergingHooksPtr->muMI(state.scale()); } // Done return true; } //-------------------------------------------------------------------------- // Calculate and return pdf ratio. double History::getPDFratio( int side, bool forSudakov, bool useHardPDFs, int flavNum, double xNum, double muNum, int flavDen, double xDen, double muDen) { // Do nothing for e+e- beams if ( abs(flavNum) > 10 && flavNum != 21 ) return 1.0; if ( abs(flavDen) > 10 && flavDen != 21 ) return 1.0; // Now calculate PDF ratio if necessary double pdfRatio = 1.0; // Get mother and daughter pdfs double pdfNum = 0.0; double pdfDen = 0.0; // Use hard process PDFs (i.e. PDFs NOT used in ISR, FSR or MPI). if ( useHardPDFs ) { if (side == 1) { if (forSudakov) pdfNum = mother->beamA.xfHard( flavNum, xNum, muNum*muNum); else pdfNum = beamA.xfHard( flavNum, xNum, muNum*muNum); if (forSudakov) pdfDen = max(1e-10, beamA.xfHard( flavDen, xDen, muDen*muDen)); else pdfDen = max(1e-10, beamA.xfHard( flavDen, xDen, muDen*muDen)); } else { if (forSudakov) pdfNum = mother->beamB.xfHard( flavNum, xNum, muNum*muNum); else pdfNum = beamB.xfHard( flavNum, xNum, muNum*muNum); if (forSudakov) pdfDen = max(1e-10,beamB.xfHard( flavDen, xDen, muDen*muDen)); else pdfDen = max(1e-10,beamB.xfHard( flavDen, xDen, muDen*muDen)); } // Use rescaled PDFs in the presence of multiparton interactions } else { if (side == 1) { if (forSudakov) pdfNum = mother->beamA.xfISR(0, flavNum, xNum, muNum*muNum); else pdfNum = beamA.xfISR(0, flavNum, xNum, muNum*muNum); if (forSudakov) pdfDen = max(1e-10, beamA.xfISR(0, flavDen, xDen, muDen*muDen)); else pdfDen = max(1e-10, beamA.xfISR(0, flavDen, xDen, muDen*muDen)); } else { if (forSudakov) pdfNum = mother->beamB.xfISR(0, flavNum, xNum, muNum*muNum); else pdfNum = beamB.xfISR(0, flavNum, xNum, muNum*muNum); if (forSudakov) pdfDen = max(1e-10,beamB.xfISR(0, flavDen, xDen, muDen*muDen)); else pdfDen = max(1e-10,beamB.xfISR(0, flavDen, xDen, muDen*muDen)); } } // Cut out charm threshold. if ( forSudakov && abs(flavNum) ==4 && abs(flavDen) == 4 && muDen == muNum && muNum < particleDataPtr->m0(4)) pdfDen = pdfNum = 1.0; // Return ratio of pdfs if ( pdfNum > 1e-15 && pdfDen > 1e-10 ) { pdfRatio *= pdfNum / pdfDen; } else if ( pdfNum < pdfDen ) { pdfRatio = 0.; } else if ( pdfNum > pdfDen ) { pdfRatio = 1.; } // Done return pdfRatio; } //-------------------------------------------------------------------------- /*--------------- METHODS USED FOR ONLY ONE PATH OF HISTORY NODES ------- */ // Function to set all scales in the sequence of states. This is a // wrapper routine for setScales and setEventScales methods void History::setScalesInHistory() { // Find correct links from n+1 to n states (mother --> child), as // needed for enforcing ordered scale sequences vector ident; findPath(ident); // Set production scales in the states to the scales pythia would // have set and enforce ordering setScales(ident,true); // Set the overall event scales to the scale of the last branching setEventScales(); } //-------------------------------------------------------------------------- // Setup function that call the real getWeakProb. double History::getWeakProb() { vector modes, fermionLines; vector mom; return getWeakProb(modes, mom, fermionLines); } //-------------------------------------------------------------------------- // Recursive function that returns the weak probability for the given path. // mode refers to which ME correction to use, 1 = sChannel, 2 = gluon channel, // 3 = double quark t-channel, 4 is double quark u-channel. double History::getWeakProb(vector &mode, vector &mom, vector fermionLines) { // If at end, return 1. if (!mother) return 1.; // Find the transfer map given the splitting. map stateTransfer; findStateTransfer(stateTransfer); // Setup hard process. if (mode.empty()) setupWeakHard(mode,fermionLines,mom); // Update modes and fermionLines. vector modeNew = updateWeakModes(mode, stateTransfer); vector fermionLinesNew = updateWeakFermionLines(fermionLines, stateTransfer); // Get the probability if it is a weak emission. if (mother->state[clusterIn.emitted].idAbs() == 24 || mother->state[clusterIn.emitted].idAbs() == 23) return getSingleWeakProb(modeNew, mom, fermionLinesNew) * mother->getWeakProb(modeNew, mom, fermionLinesNew); else return mother->getWeakProb(modeNew, mom, fermionLinesNew); } //-------------------------------------------------------------------------- double History::getSingleWeakProb(vector &mode, vector &mom, vector fermionLines) { // Find the correct coupling coefficient. double weakCoupling = 0.0; if (mother->state[clusterIn.emitted].idAbs() == 24) { // No emissions from right handed particles. if (clusterIn.spinRadBef == 1) return 0.0; else if (clusterIn.spinRadBef == -1) weakCoupling = 4.*M_PI/ coupSMPtr->sin2thetaW() * coupSMPtr->V2CKMid(abs(clusterIn.flavRadBef), mother->state[clusterIn.emittor].idAbs()); else { infoPtr->errorMsg("Warning in History::getSingleWeakProb: " "Spin not properly configurated. Skipping history"); return 0.0; } } else if (mother->state[clusterIn.emitted].idAbs() == 23) { // No emissions from right handed particles. if (clusterIn.spinRadBef == 1) weakCoupling = 4.*M_PI*pow2(coupSMPtr->rf( abs(clusterIn.flavRadBef))) / (coupSMPtr->sin2thetaW() * coupSMPtr->cos2thetaW()) ; else if (clusterIn.spinRadBef == -1) weakCoupling = 4.*M_PI*pow2(coupSMPtr->lf( abs(clusterIn.flavRadBef))) / (coupSMPtr->sin2thetaW() * coupSMPtr->cos2thetaW()) ; else { infoPtr->errorMsg("Warning in History::getSingleWeakProb: " "Spin not properly configurated. Skipping history"); return 0.0; } } else { infoPtr->errorMsg("Warning in History::getSingleWeakProb: " "Did not emit W/Z. Skipping history."); return 0.0; } // Find and store kinematics (e.g. z, pT, k1, k3). // Store momenta. Vec4 pRadAft = mother->state[clusterIn.emittor].p(); Vec4 pEmtAft = mother->state[clusterIn.emitted].p(); Vec4 pRecAft = mother->state[clusterIn.recoiler].p(); Vec4 pSum = pRadAft + pEmtAft + pRecAft; double m2sum = pSum.m2Calc(); double Qsq = (pRadAft + pEmtAft).m2Calc(); double m2Rad0 = pRadAft.m2Calc(); double m2Emt0 = pEmtAft.m2Calc(); double lambda13 = sqrt( pow2(Qsq - m2Rad0 - m2Emt0 ) - 4. * m2Rad0*m2Emt0 ); double k1 = ( Qsq - lambda13 + (m2Emt0 - m2Rad0 ) ) / ( 2. * Qsq ); double k3 = ( Qsq - lambda13 - (m2Emt0 - m2Rad0 ) ) / ( 2. * Qsq ); double z = mother->getCurrentZ(clusterIn.emittor, clusterIn.recoiler, clusterIn.emitted, clusterIn.flavRadBef); double pT2 = pow2(clusterIn.pTscale); double x1 = 2. * pRadAft * pSum / m2sum; double x2 = 2. * pRecAft * pSum / m2sum; double x3 = 2. * pEmtAft * pSum / m2sum; // Final state clustering. if ( state[clusterIn.radBef].status() > 0) { // s-channel if (mode[clusterIn.emittor] == 1) { // Calculate variables. double eCMME = pSum.mCalc(); double r1 = mother->state[clusterIn.emittor].m() / eCMME; double r2 = mother->state[clusterIn.recoiler].m() / eCMME; double r3 = mother->state[clusterIn.emitted].m() / eCMME; double x1s = x1 * x1; double x2s = x2 * x2; double r1s = r1 * r1; double r2s = r2 * r2; double r3s = r3 * r3; double prop1 = 1. + r1s - r2s - x1; double prop2 = 1. + r2s - r1s - x2; double prop1s = prop1 * prop1; double prop2s = prop2 * prop2; double prop12 = prop1 * prop2; // Calculate Jacobian. double jac = 1./(1.-z) * 1./pT2 * (1-x2+r2-r1)*(x3 - k1*(x1+x3)) * (1.-x1+r1-r2) / x3; return jac * weakCoupling * ((2. * r3s * r3s + 2. * r3s * (x1 + x2) + x1s + x2s) / prop12 - r3s / prop1s - r3s / prop2s); } // t-channel. else { // Store momentas needed. Vec4 p1 = mother->state[clusterIn.emittor].p(); Vec4 p2 = mother->state[clusterIn.recoiler].p(); Vec4 p3 = mother->state[clusterIn.emitted].p(); Vec4 radBef = state[clusterIn.radBef].p(); Vec4 recBef = state[clusterIn.recBef].p(); Vec4 pIn1 = mom[0]; Vec4 pIn2 = mom[1]; // Check if a swap is needed. if (fermionLines[2] == clusterIn.emittor); else if (fermionLines[3] == clusterIn.emittor) swap(pIn1, pIn2); // Rescaling of incoming partons p3 and p4. double scaleFactor2 = (p1 + p2 + p3).m2Calc() / (pIn1 + pIn2).m2Calc(); double scaleFactor = sqrt(scaleFactor2); pIn1 *= scaleFactor; pIn2 *= scaleFactor; // Longitudinal boost to rest frame of incoming partons of // hard interaction. RotBstMatrix rot2to2frame; rot2to2frame.bstback(pIn1 + pIn2); pIn1.rotbst(rot2to2frame); pIn2.rotbst(rot2to2frame); p1.rotbst(rot2to2frame); p2.rotbst(rot2to2frame); p3.rotbst(rot2to2frame); recBef.rotbst(rot2to2frame); radBef.rotbst(rot2to2frame); // Further boost to rest frame of outgoing state. RotBstMatrix rot2to3frame; rot2to3frame.bstback(p1 + p2 + p3); p1.rotbst(rot2to3frame); p2.rotbst(rot2to3frame); p3.rotbst(rot2to3frame); recBef.rotbst(rot2to3frame); radBef.rotbst(rot2to3frame); // Calculate variables; double sHat = (pIn1 + pIn2).m2Calc(); double tHat = (radBef - pIn1).m2Calc(); double uHat = (recBef - pIn1).m2Calc(); double localProb = 0; double Q2 = pT2 / (z*(1.-z)); double jac = 1./(4. * M_PI) * 1./( (1.-z) * z ) * sHat / (sHat - Q2) * (1. - k1 - k3); // Calculate the ME depending on the top of process. if (mode[clusterIn.emittor] == 2) localProb = weakShowerMEs.getMEqg2qgZ( pIn1, pIn2, p2, p3, p1) / weakShowerMEs.getMEqg2qg( sHat, tHat, uHat); else if (mode[clusterIn.emittor] == 3) localProb = weakShowerMEs.getMEqq2qqZ( pIn1, pIn2, p3, p2, p1) / weakShowerMEs.getMEqq2qq( sHat, tHat, uHat, false); else if (mode[clusterIn.emittor] == 4) localProb = weakShowerMEs.getMEqq2qqZ( pIn1, pIn2, p3, p2, p1) / weakShowerMEs.getMEqq2qq( sHat, tHat, uHat, true); else { string message="Warning in History::getSingleWeakProb: Wrong"; message+=" mode setup. Setting probability for path to zero."; infoPtr->errorMsg(message); } // Split matrix element according to propagaters. localProb *= abs((-p3 + pIn1).m2Calc()) / ((p3 + p1).m2Calc() + abs((-pIn1 + p3).m2Calc())); return jac * weakCoupling * localProb; } } // Initial clustering. else { // s-channel if (mode[clusterIn.emittor] == 1) { Vec4 pIn1 = mother->state[clusterIn.emittor].p(); Vec4 pIn2 = mother->state[clusterIn.recoiler].p(); Vec4 p1 = mother->state[clusterIn.emitted].p(); Vec4 p2 = pIn1 + pIn2 -p1; double sH = (pIn1 + pIn2).m2Calc(); double tH = (p1 - pIn1).m2Calc(); double uH = (p1 - pIn2).m2Calc(); double m3s = p1.m2Calc(); double m4s = p2.m2Calc(); double jac = 1./sH * tH*uH / ( tH * (tH + uH) ); return jac * weakCoupling * ((uH*uH + tH*tH + 2 * sH * (m3s + m4s)) / (uH*tH) - m3s * m4s * (1/(tH*tH) + 1/(uH*uH))); } else { // Store momenta. Vec4 pIn1 = mother->state[clusterIn.emittor].p(); Vec4 pIn2 = mother->state[clusterIn.recoiler].p(); Vec4 p3 = mother->state[clusterIn.emitted].p(); Vec4 p1 = mom[2]; Vec4 p2 = mom[3]; // Check if radiator is from beam one or two. if (fermionLines[0] == clusterIn.emittor); else if (fermionLines[1] == clusterIn.emittor) swap(p1, p2); Vec4 pDaughter, pRecoiler; int signBeam = (pIn1.pz() > 0.) ? 1 : -1; double eCM = state[0].e(), phi = 0; // Undo the ISR boost. reverseBoostISR(pIn1, p3, pIn2, pDaughter, pRecoiler, signBeam, eCM, phi); // Scale outgoing vectors to conserve energy / momentum. //double scaleFactor2 = (pIn1 + pIn2 - p3).m2Calc() / (p1 + p2).m2Calc(); double scaleFactor2 = (pIn1 + pIn2 - p3).m2Calc() / (p1 + p2).m2Calc(); double scaleFactor = sqrt(scaleFactor2); RotBstMatrix rot2to2frame; rot2to2frame.bstback(p1 + p2); p1.rotbst(rot2to2frame); p2.rotbst(rot2to2frame); p1 *= scaleFactor; p2 *= scaleFactor; // Find 2 to 2 rest frame for incoming particles. // This is done before one of the two are made virtual (Q^2 mass). Vec4 radBef = state[clusterIn.radBef].p(); Vec4 recBef = state[clusterIn.recBef].p(); RotBstMatrix rot2to2frameInc; rot2to2frameInc.bstback(radBef + recBef); radBef.rotbst(rot2to2frameInc); recBef.rotbst(rot2to2frameInc); double sHat = (p1 + p2).m2Calc(); double tHat = (p1 - radBef).m2Calc(); double uHat = (p1 - recBef).m2Calc(); double localProb = 0; p1.rot(0., -phi); p2.rot(0., -phi); // Calculating the Jacobian double jac = z / (4.*M_PI); if (mode[clusterIn.emittor] == 2) localProb = weakShowerMEs.getMEqg2qgZ(pIn1, pIn2, p2, p3, p1) / weakShowerMEs.getMEqg2qg(sHat, tHat, uHat); else if (mode[clusterIn.emittor] == 4) localProb = weakShowerMEs.getMEqq2qqZ(pIn1, pIn2, p3, p2, p1) / weakShowerMEs.getMEqq2qq(sHat, tHat, uHat, true); else if (mode[clusterIn.emittor] == 3) localProb = weakShowerMEs.getMEqq2qqZ(pIn1, pIn2, p3, p2, p1) / weakShowerMEs.getMEqq2qq(sHat, tHat, uHat, false); else { string message="Warning in History::getSingleWeakProb: Wrong"; message+=" mode setup. Setting probability for path to zero."; infoPtr->errorMsg(message); } // Split of ME into an ISR part and FSR part. localProb *= (p3 + p1).m2Calc() / ( (p3 + p1).m2Calc() + abs((-pIn1 + p3).m2Calc()) ); return jac * weakCoupling * localProb; } } } //-------------------------------------------------------------------------- // Check if the weak recoil structure is allowed. bool History::checkWeakRecoils(map &allowedRecoils, bool isFirst) { if (!mother) return true; // Setup if first if (isFirst) { // Drell-Yan production. if (state.size() != 8) { if (state[3].isQuark() || state[3].isLepton()) allowedRecoils.insert(pair(3,4)); if (state[4].isQuark() || state[4].isLepton()) allowedRecoils.insert(pair(4,3)); } else { if (state[3].isQuark() || state[3].isLepton()) allowedRecoils.insert(pair(3,4)); if (state[4].isQuark() || state[4].isLepton()) allowedRecoils.insert(pair(4,3)); if (state[5].isQuark() || state[5].isLepton()) allowedRecoils.insert(pair(5,6)); if (state[6].isQuark() || state[6].isLepton()) allowedRecoils.insert(pair(6,5)); } } // Find the transfer map. map transfer; findStateTransfer(transfer); // Copy the new allowed recoils. map allowedRecoilsNew; for (map::iterator it = allowedRecoils.begin(); it != allowedRecoils.end(); ++it) { // Start by considering final state splittings. if (state[clusterIn.radBef].status() > 0) { // If the dipole was not connected to current splitting. if (it->first != clusterIn.radBef && it->second != clusterIn.radBef) allowedRecoilsNew.insert(pair(transfer[it->first], transfer[it->second])); // If the recoiler is splitted into two. else if (it->second == clusterIn.radBef) { // Follow fermion line. if (state[clusterIn.recBef].isQuark() || state[clusterIn.recBef].isLepton()) { if (mother->state[clusterIn.emittor].isQuark() || mother->state[clusterIn.emittor].isLepton()) allowedRecoilsNew.insert(pair(transfer[it->first], clusterIn.emittor)); else allowedRecoilsNew.insert(pair(transfer[it->first], clusterIn.emitted)); } // If no fermion line to follow, choose the largest invariant mass. else { double mEmittor = (mother->state[clusterIn.emittor].p() + mother->state[transfer[it->first]].p()).mCalc(); double mEmitted = (mother->state[clusterIn.emitted].p() + mother->state[transfer[it->first]].p()).mCalc(); if (mEmitted > mEmittor) allowedRecoilsNew.insert(pair(transfer[it->first], clusterIn.emitted)); else allowedRecoilsNew.insert(pair(transfer[it->first], clusterIn.emittor)); } } // If the radiator is splitted into two. if (mother->state[clusterIn.emittor].isQuark() || mother->state[clusterIn.emittor].isLepton()) allowedRecoilsNew.insert(pair(clusterIn.emittor, transfer[it->second])); else allowedRecoilsNew.insert(pair(clusterIn.emitted, transfer[it->second])); } // Look at initial splittings. else { // If not involved in the splitting. if (it->first != clusterIn.radBef && it->second != clusterIn.radBef) allowedRecoilsNew.insert(pair(transfer[it->first], transfer[it->second])); // If the recoiler is splitted, always choose the emittor. else if (it->second == clusterIn.radBef) allowedRecoilsNew.insert(pair(transfer[it->first], clusterIn.emittor)); // If the radiator splits into two. else { // If the the fermion line continues to be the beam particle. if (mother->state[clusterIn.emittor].isQuark() || mother->state[clusterIn.emittor].isLepton()) allowedRecoilsNew.insert(pair(clusterIn.emittor, clusterIn.recoiler)); // If the fermion line is emitted, find recoiler in final state. else allowedRecoilsNew.insert(pair(clusterIn.emittor, findISRRecoiler())); } } } // If a gluon/phton is split into a quark-antiquark pair, add two new // possible configurations. if ( (state[clusterIn.radBef].idAbs() == 22 || state[clusterIn.radBef].idAbs() == 21) && (mother->state[clusterIn.emittor].isQuark() || mother->state[clusterIn.emittor].isLepton() ) ) { // If it is a final splitting, just add the two. if (state[clusterIn.radBef].status() > 0) { allowedRecoilsNew.insert(pair(clusterIn.emittor, clusterIn.emitted)); allowedRecoilsNew.insert(pair(clusterIn.emitted, clusterIn.emittor)); } // If it is an initial splitting. else { allowedRecoilsNew.insert(pair(clusterIn.emittor, clusterIn.recoiler)); allowedRecoilsNew.insert(pair(clusterIn.emitted, findISRRecoiler())); } } // allowedRecoilsNew is now properly setup, so ready to check // if recoil works. // If weak emission, do the check. if (mother->state[clusterIn.emitted].idAbs() == 24 || mother->state[clusterIn.emitted].idAbs() == 23) if ( clusterIn.recoiler != allowedRecoilsNew[clusterIn.emittor]) return false; // check the mother. return mother->checkWeakRecoils(allowedRecoilsNew); } //-------------------------------------------------------------------------- // Find the recoiler for an ISR scattered weak particle. // Always use 1 as weak weight, even though the shower uses a slightly // different value for Z emissions. int History::findISRRecoiler() { int flavRad = mother->state[clusterIn.emitted].id(); Vec4 pRad = mother->state[clusterIn.emitted].p(); double mRad = mother->state[clusterIn.emitted].m(); int iRad = clusterIn.emitted; int iRec = 0; double ppMin = 1E20; for (int i = 0;i < mother->state.size(); ++i) { if (i == iRad) continue; if (mother->state[i].isFinal() && mother->state[i].id() == - flavRad) { double ppNow = mother->state[i].p() * pRad - mother->state[i].m() - mRad; if (ppNow < ppMin) { ppMin = ppNow; iRec = i; } } } if (iRec) return iRec; // Find nearest recoiler weak-charge-squared-weighted. for (int i = 0;i < mother->state.size(); ++i) { if (i == iRad) continue; if (mother->state[i].isFinal() && mother->state[i].idAbs() < 20) { double weakCoupNow = 1.; double ppNow = (mother->state[i].p() * pRad - mother->state[i].m() - mRad) / weakCoupNow; if (ppNow < ppMin) { ppMin = ppNow; iRec = i; } } } if (iRec) return iRec; // Find nearest recoiler in final state. for (int i = 0;i < mother->state.size(); ++i) { if (i == iRad) continue; if (mother->state[i].isFinal()) { double ppNow = mother->state[i].p() * pRad - mother->state[i].m() - mRad; if (ppNow < ppMin) { ppMin = ppNow; iRec = i; } } } if (iRec) return iRec; return 0; } //-------------------------------------------------------------------------- // Find map between indecies in the current state and the state after // the splitting. // NOT IMPLEMENTED FOR MULTIPLE W/Z/GAMMA (NEED TO HAVE A WAY TO IDENTIFY THEM) void History::findStateTransfer(map &transfer) { // No need to transfer if already at highest multiplicity. if (!mother) return; transfer.clear(); // Directly assign the 3 first particles (system, beam1, beam2); for(int i = 0;i < 3; ++i) transfer.insert(pair(i,i)); transfer.insert(pair(clusterIn.radBef, clusterIn.emittor)); transfer.insert(pair(clusterIn.recBef, clusterIn.recoiler)); // Handle all particles that are not part of the clustering. for (int i = 0; i < int(mother->state.size()); ++i) { if (clusterIn.emitted == i || clusterIn.emittor == i || clusterIn.recoiler == i) continue; for (int j = 0;j < int(state.size()); ++j) { if (mother->state[i].id() == state[j].id() && mother->state[i].colType() == state[j].colType() && mother->state[i].chargeType() == state[j].chargeType() && mother->state[i].col() == state[j].col() && mother->state[i].acol() == state[j].acol() && mother->state[i].status() == state[j].status()) { transfer.insert(pair(j,i)); break; } } } } //-------------------------------------------------------------------------- // Function to find the index (in the mother histories) of the // child history, thus providing a way access the path from both // initial history (mother == 0) and final history (all children == 0) // IN vector : The index of each child in the children vector // of the current history node will be saved in // this vector // NO OUTPUT void History::findPath(vector& out) { // If the initial and final nodes are identical, return if (!mother && int(children.size()) < 1) return; // Find the child by checking the children vector for the perfomed // clustering int iChild=-1; if ( mother ) { int size = int(mother->children.size()); // Loop through children and identify child chosen for ( int i=0; i < size; ++i) { if ( mother->children[i]->scale == scale && mother->children[i]->prob == prob && equalClustering(mother->children[i]->clusterIn,clusterIn)) { iChild = i; break; } } // Save the index of the child in the children vector and recurse if (iChild >-1) out.push_back(iChild); mother->findPath(out); } } //-------------------------------------------------------------------------- // Functions to set the parton production scales and enforce // ordering on the scales of the respective clusterings stored in // the History node: // Method will start from lowest multiplicity state and move to // higher states, setting the production scales the shower would // have used. // When arriving at the highest multiplicity, the method will switch // and go back in direction of lower states to check and enforce // ordering for unordered histories. // IN vector : Vector of positions of the chosen child // in the mother history to allow to move // in direction initial->final along path // bool : True: Move in direction low->high // multiplicity and set production scales // False: Move in direction high->low // multiplicity and check and enforce // ordering // NO OUTPUT void History::setScales( vector index, bool forward) { // First, set the scales of the hard process to the kinematial // limit (=s) if ( children.empty() && forward ) { // New "incomplete" configurations showered from mu if (!mother) { double scaleNew = 1.; if (mergingHooksPtr->incompleteScalePrescip()==0) { scaleNew = mergingHooksPtr->muF(); } else if (mergingHooksPtr->incompleteScalePrescip()==1) { Vec4 pOut; pOut.p(0.,0.,0.,0.); for(int i=0; iincompleteScalePrescip()==2) { scaleNew = state[0].e(); } scaleNew = max( mergingHooksPtr->pTcut(), scaleNew); state.scale(scaleNew); for(int i=3; i < int(state.size());++i) if (state[i].colType() != 0) state[i].scale(scaleNew); } else { // 2->2 with non-parton particles showered from eCM state.scale( state[0].e() ); // Count final partons bool isLEP = ( state[3].isLepton() && state[4].isLepton() ); int nFinal = 0; int nFinalPartons = 0; int nFinalPhotons = 0; for ( int i=0; i < int(state.size()); ++i ) { if ( state[i].isFinal() ) { nFinal++; if ( state[i].colType() != 0 ) nFinalPartons++; if ( state[i].id() == 22 ) nFinalPhotons++; } } bool isQCD = ( nFinal == 2 && nFinal == nFinalPartons ); bool isPPh = ( nFinal == 2 && nFinalPartons == 1 && nFinalPhotons == 1); // If 2->2, purely partonic, set event scale to kinematic pT if ( !isLEP && ( isQCD || isPPh ) ) { double scaleNew = hardFacScale(state); state.scale( scaleNew ); } } } // Set all particle production scales, starting from lowest // multiplicity (final) state if (mother && forward) { // When choosing splitting scale, beware of unordered splittings: double scaleNew = 1.; if (mergingHooksPtr->unorderedScalePrescip() == 0) { // Use larger scale as common splitting scale for mother and child scaleNew = max( mergingHooksPtr->pTcut(), max(scale,mother->scale)); } else if (mergingHooksPtr->unorderedScalePrescip() == 1) { // Use smaller scale as common splitting scale for mother and child if (scale < mother->scale) scaleNew *= max( mergingHooksPtr->pTcut(), min(scale,mother->scale)); else scaleNew *= max( mergingHooksPtr->pTcut(), max(scale,mother->scale)); } // Rescale the mother state partons to the clustering scales // that have been found along the path mother->state[clusterIn.emitted].scale(scaleNew); mother->state[clusterIn.emittor].scale(scaleNew); mother->state[clusterIn.recoiler].scale(scaleNew); // Find unchanged copies of partons in higher multiplicity states // and rescale those mother->scaleCopies(clusterIn.emitted, mother->state, scaleNew); mother->scaleCopies(clusterIn.emittor, mother->state, scaleNew); mother->scaleCopies(clusterIn.recoiler, mother->state, scaleNew); // Recurse mother->setScales(index,true); } // Now, check and correct ordering from the highest multiplicity // state backwards to all the clustered states if (!mother || !forward) { // Get index of child along the path int iChild = -1; if ( int(index.size()) > 0 ) { iChild = index.back(); index.pop_back(); } // Check that the reclustered scale is above the shower cut if (mother) { scale = max(mergingHooksPtr->pTcut(), scale); } // If this is NOT the 2->2 process, check and enforce ordering if (iChild != -1 && !children.empty()) { if (scale > children[iChild]->scale ) { if (mergingHooksPtr->unorderedScalePrescip() == 0) { // Use larger scale as common splitting scale for mother and child double scaleNew = max( mergingHooksPtr->pTcut(), max(scale,children[iChild]->scale)); // Enforce ordering in particle production scales for( int i = 0; i < int(children[iChild]->state.size()); ++i) if (children[iChild]->state[i].scale() == children[iChild]->scale) children[iChild]->state[i].scale(scaleNew); // Enforce ordering in saved clustering scale children[iChild]->scale = scaleNew; } else if ( mergingHooksPtr->unorderedScalePrescip() == 1) { // Use smaller scale as common splitting scale for mother & child double scaleNew = max(mergingHooksPtr->pTcut(), min(scale,children[iChild]->scale)); // Enforce ordering in particle production scales for( int i = 0; i < int(state.size()); ++i) if (state[i].scale() == scale) state[i].scale(scaleNew); // Enforce ordering in saved clustering scale scale = scaleNew; } // Just set the overall event scale to the minimal scale } else { double scalemin = state[0].e(); for( int i = 0; i < int(state.size()); ++i) if (state[i].colType() != 0) scalemin = max(mergingHooksPtr->pTcut(), min(scalemin,state[i].scale())); state.scale(scalemin); scale = max(mergingHooksPtr->pTcut(), scale); } //Recurse children[iChild]->setScales(index, false); } } } //-------------------------------------------------------------------------- // Function to find a particle in all higher multiplicity events // along the history path and set its production scale to the input // scale // IN int iPart : Parton in refEvent to be checked / rescaled // Event& refEvent : Reference event for iPart // double scale : Scale to be set as production scale for // unchanged copies of iPart in subsequent steps void History::scaleCopies(int iPart, const Event& refEvent, double rho) { // Check if any parton recently rescaled is found unchanged: // Same charge, colours in mother->state if ( mother ) { for( int i=0; i < mother->state.size(); ++i) { if ( ( mother->state[i].id() == refEvent[iPart].id() && mother->state[i].colType() == refEvent[iPart].colType() && mother->state[i].chargeType() == refEvent[iPart].chargeType() && mother->state[i].col() == refEvent[iPart].col() && mother->state[i].acol() == refEvent[iPart].acol() ) ) { // Rescale the unchanged parton mother->state[i].scale(rho); // Recurse if (mother->mother) mother->scaleCopies( iPart, refEvent, rho ); } // end if found unchanged parton case } // end loop over particle entries in event } } //-------------------------------------------------------------------------- // Functions to set the OVERALL EVENT SCALES [=state.scale()] to // the scale of the last clustering // NO INPUT // NO OUTPUT void History::setEventScales() { // Set the event scale to the scale of the last clustering, // except for the very lowest multiplicity state if (mother) { mother->state.scale(scale); // Recurse mother->setEventScales(); } } //-------------------------------------------------------------------------- // Functions to return the z value of the last ISR splitting // NO INPUT // OUTPUT double : z value of last ISR splitting in history double History::zISR() { // Do nothing for ME level state if (!mother) return 0.0; // Skip FSR splitting if (mother->state[clusterIn.emittor].isFinal()) return mother->zISR(); // Calculate z int rad = clusterIn.emittor; int rec = clusterIn.recoiler; int emt = clusterIn.emitted; double z = (mother->state[rad].p() + mother->state[rec].p() - mother->state[emt].p()).m2Calc() / (mother->state[rad].p() + mother->state[rec].p()).m2Calc(); // Recurse double znew = mother->zISR(); // Update z if (znew > 0.) z = znew; return z; } //-------------------------------------------------------------------------- // Functions to return the z value of the last FSR splitting // NO INPUT // OUTPUT double : z value of last FSR splitting in history double History::zFSR() { // Do nothing for ME level state if (!mother) return 0.0; // Skip ISR splitting if (!mother->state[clusterIn.emittor].isFinal()) return mother->zFSR(); // Calculate z int rad = clusterIn.emittor; int rec = clusterIn.recoiler; int emt = clusterIn.emitted; // Construct 2->3 variables for FSR Vec4 sum = mother->state[rad].p() + mother->state[rec].p() + mother->state[emt].p(); double m2Dip = sum.m2Calc(); double x1 = 2. * (sum * mother->state[rad].p()) / m2Dip; double x3 = 2. * (sum * mother->state[emt].p()) / m2Dip; // Calculate z of splitting for FSR double z = x1/(x1+x3); // Recurse double znew = mother->zFSR(); // Update z if (znew > 0.) z = znew; return z; } //-------------------------------------------------------------------------- // Functions to return the pT scale of the last FSR splitting // NO INPUT // OUTPUT double : pT scale of last FSR splitting in history double History::pTISR() { // Do nothing for ME level state if (!mother) return 0.0; // Skip FSR splitting if (mother->state[clusterIn.emittor].isFinal()) return mother->pTISR(); double pT = mother->state.scale(); // Recurse double pTnew = mother->pTISR(); // Update pT if (pTnew > 0.) pT = pTnew; return pT; } //-------------------------------------------------------------------------- // Functions to return the pT scale of the last FSR splitting // NO INPUT // OUTPUT double : pT scale of last FSR splitting in history double History::pTFSR() { // Do nothing for ME level state if (!mother) return 0.0; // Skip ISR splitting if (!mother->state[clusterIn.emittor].isFinal()) return mother->pTFSR(); double pT = mother->state.scale(); // Recurse double pTnew = mother->pTFSR(); // Update pT if (pTnew > 0.) pT = pTnew; return pT; } //-------------------------------------------------------------------------- // Function to return the depth of the history (i.e. the number of // reclustered splittings) // NO INPUT // OUTPUT int : Depth of history int History::nClusterings() { if (!mother) return 0; int w = mother->nClusterings(); w += 1; return w; } //-------------------------------------------------------------------------- // Functions to return the event after nSteps splittings of the 2->2 process // Example: nSteps = 1 -> return event with one additional parton // INPUT int : Number of splittings in the event, // as counted from core 2->2 process // OUTPUT Event : event with nSteps additional partons Event History::clusteredState(int nSteps) { // Save state Event outState = state; // As long as there are steps to do, recursively save state if (mother && nSteps > 0) outState = mother->clusteredState(nSteps - 1); // Done return outState; } //-------------------------------------------------------------------------- // Function to choose a path from all paths in the tree // according to their splitting probabilities // IN double : Random number // OUT History* : Leaf of history path chosen History * History::select(double rnd) { // No need to choose if no paths have been constructed. if ( goodBranches.empty() && badBranches.empty() ) return this; // Choose amongst paths allowed by projections. double sum = 0.; map selectFrom; if ( !goodBranches.empty() ) { selectFrom = goodBranches; sum = sumGoodBranches; } else { selectFrom = badBranches; sum = sumBadBranches; } if (mergingHooksPtr->pickBySumPT()) { // Find index of history with minimal sum of scalar pT int nFinal = 0; for (int i=0; i < state.size(); ++i) if (state[i].isFinal()) nFinal++; double iMin = 0.; double sumMin = (nFinal-2)*state[0].e(); for ( map::iterator it = selectFrom.begin(); it != selectFrom.end(); ++it ) { if (it->second->sumScalarPT < sumMin) { sumMin = it->second->sumScalarPT; iMin = it->first; } } // Choose history with smallest sum of scalar pT return selectFrom.lower_bound(iMin)->second; } else { // Choose history according to probability, be careful about upper bound if ( rnd != 1. ) { return selectFrom.upper_bound(sum*rnd)->second; } else { return selectFrom.lower_bound(sum*rnd)->second; } } // Done } //-------------------------------------------------------------------------- // Function to project paths onto desired paths. bool History::trimHistories() { // Do nothing if no paths have been constructed. if ( paths.empty() ) return false; // Loop through all constructed paths. Check all removal conditions. for ( map::iterator it = paths.begin(); it != paths.end(); ++it ) { // Check if history is allowed. if ( it->second->keep() && !it->second->keepHistory() ) it->second->remove(); } // Project onto desired / undesired branches. double sumold, sumnew, sumprob, mismatch; sumold = sumnew = sumprob = mismatch = 0.; // Loop through all constructed paths and store allowed paths. // Skip undesired paths. for ( map::iterator it = paths.begin(); it != paths.end(); ++it ) { // Update index sumnew = it->first; if ( it->second->keep() ) { // Fill branches with allowed paths. goodBranches.insert( make_pair( sumnew - mismatch, it->second) ); // Add probability of this path. sumGoodBranches = sumnew - mismatch; } else { // Update mismatch in probabilities resulting from not including this // path double mismatchOld = mismatch; mismatch += sumnew - sumold; // Fill branches with allowed paths. badBranches.insert( make_pair( mismatchOld + sumnew - sumold, it->second ) ); // Add probability of this path. sumBadBranches = mismatchOld + sumnew - sumold; } // remember index of this path in order to caclulate probability of // subsequent path. sumold = it->first; } // Done return !goodBranches.empty(); } //-------------------------------------------------------------------------- // Function implementing checks on a paths, deciding if the path is valid. bool History::keepHistory() { bool keepPath = true; // Tag unordered paths for removal. if ( mergingHooksPtr->getProcessString().compare("pp>jj") == 0 || mergingHooksPtr->getProcessString().compare("pp>aj") == 0 || isQCD2to2(state) ) { // Tag unordered paths for removal. Include scale of hard 2->2 process // into the ordering definition. double maxScale = hardFacScale(state); return keepPath = isOrderedPath( maxScale ); } // Set starting scale to mass of Drell-Yan for 2->1. if (isEW2to1(state)) { Vec4 pSum(0,0,0,0); for (int i = 0;i < state.size(); ++i) if (state[i].isFinal()) pSum += state[i].p(); return isOrderedPath( pSum.mCalc()); } keepPath = isOrderedPath( infoPtr->eCM() ); // More stringent criterion. //keepPath = allIntermediateAboveRhoMS( mergingHooksPtr->tms() ); //Done return keepPath; } //-------------------------------------------------------------------------- // Function to check if a path is ordered in evolution pT. bool History::isOrderedPath( double maxscale ) { double newscale = clusterIn.pT(); if ( !mother ) return true; if ( mother->state[clusterIn.emittor].idAbs() == 21 && mother->state[clusterIn.emitted].idAbs() == 5 && !mother->state[clusterIn.emittor].isFinal()) newscale=maxscale; bool ordered = mother->isOrderedPath(newscale); if ( !ordered || maxscale < newscale) return false; return ordered; } //-------------------------------------------------------------------------- // Function to check if all reconstucted states in a path pass the merging // scale cut. bool History::allIntermediateAboveRhoMS( double rhoms, bool good ) { // If one state below the merging scale has already been found, no need to // check further. if ( !good ) return false; // Check merging scale for states with more than 0 jets int nFinal = 0; for ( int i = 0; i < state.size(); ++i ) if ( state[i].isFinal() && state[i].colType() != 0 ) nFinal++; double rhoNew = (nFinal > 0 ) ? mergingHooksPtr->tmsNow( state ) : state[0].e(); // Assume state from ME generator passes merging scale cut. if ( !mother ) return good; // Recurse. return good && mother->allIntermediateAboveRhoMS( rhoms, (rhoNew > rhoms) ); } //-------------------------------------------------------------------------- // Function to check if any ordered paths were found (and kept). bool History::foundAnyOrderedPaths() { //Do nothing if no paths were found if ( paths.empty() ) return false; double maxscale = infoPtr->eCM(); // Loop through paths. Divide probability into ordered and unordered pieces. for ( map::iterator it = paths.begin(); it != paths.end(); ++it ) if ( it->second->isOrderedPath(maxscale) ) return true; // Done return false; } //-------------------------------------------------------------------------- // For a full path, find the weight calculated from the ratio of // couplings, the no-emission probabilities, and possible PDF // ratios. This function should only be called for the last history // node of a full path. // IN TimeShower : Already initialised shower object to be used as // trial shower // double : alpha_s value used in ME calculation // double : Maximal mass scale of the problem (e.g. E_CM) // AlphaStrong: Initialised shower alpha_s object for FSR // alpha_s ratio calculation // AlphaStrong: Initialised shower alpha_s object for ISR // alpha_s ratio calculation (can be different from previous) double History::weightTree(PartonLevel* trial, double as0, double aem0, double maxscale, double pdfScale, AlphaStrong * asFSR, AlphaStrong * asISR, AlphaEM * aemFSR, AlphaEM * aemISR, double& asWeight, double& aemWeight, double& pdfWeight) { // Use correct scale double newScale = scale; // For ME state, just multiply by PDF ratios if ( !mother ) { int sideRad = (state[3].pz() > 0) ? 1 :-1; int sideRec = (state[4].pz() > 0) ? 1 :-1; // Calculate PDF first leg if (state[3].colType() != 0) { // Find x value and flavour double x = 2.*state[3].e() / state[0].e(); int flav = state[3].id(); // Find numerator/denominator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; double scaleDen = mergingHooksPtr->muFinME(); // For initial parton, multiply by PDF ratio double ratio = getPDFratio(sideRad, false, false, flav, x, scaleNum, flav, x, scaleDen); pdfWeight *= ratio; } // Calculate PDF ratio for second leg if (state[4].colType() != 0) { // Find x value and flavour double x = 2.*state[4].e() / state[0].e(); int flav = state[4].id(); // Find numerator/denominator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; double scaleDen = mergingHooksPtr->muFinME(); // For initial parton, multiply with PDF ratio double ratio = getPDFratio(sideRec, false, false, flav, x, scaleNum, flav, x, scaleDen); pdfWeight *= ratio; } return 1.0; } // Remember new PDF scale n case true scale should be used for un-ordered // splittings. double newPDFscale = newScale; if (mergingHooksPtr->unorderedPDFscalePrescip() == 1) newPDFscale = clusterIn.pT(); // Recurse double w = mother->weightTree(trial, as0, aem0, newScale, newPDFscale, asFSR, asISR, aemFSR, aemISR, asWeight, aemWeight, pdfWeight); // Do nothing for empty state if (state.size() < 3) return 1.0; // If up to now, trial shower was not successful, return zero if ( w < 1e-12 ) return 0.0; // Do trial shower on current state, return zero if not successful w *= doTrialShower(trial, 1, maxscale); if ( w < 1e-12 ) return 0.0; int emtType = mother->state[clusterIn.emitted].colType(); // Calculate alpha_s ratio for current state. if ( asFSR && asISR && emtType != 0) { double asScale = pow2( newScale ); if (mergingHooksPtr->unorderedASscalePrescip() == 1) asScale = pow2( clusterIn.pT() ); // Add regularisation scale to initial state alpha_s. bool FSR = mother->state[clusterIn.emittor].isFinal(); if (!FSR) asScale += pow2(mergingHooksPtr->pT0ISR()); // Directly get argument of running alpha_s from shower plugin. if (mergingHooksPtr->useShowerPlugin() ) asScale = getShowerPluginScale(mother->state, clusterIn.emittor, clusterIn.emitted, clusterIn.recoiler, "scaleAS", asScale); double alphaSinPS = (FSR) ? (*asFSR).alphaS(asScale) : (*asISR).alphaS(asScale); asWeight *= alphaSinPS / as0; } // Calculate alpha_em ratio for current state. if ( aemFSR && aemISR && emtType == 0 ) { double aemScale = pow2( newScale ); if (mergingHooksPtr->unorderedASscalePrescip() == 1) aemScale = pow2( clusterIn.pT() ); // Add regularisation scale to initial state alpha_s. bool FSR = mother->state[clusterIn.emittor].isFinal(); if (!FSR) aemScale += pow2(mergingHooksPtr->pT0ISR()); // Directly get argument of running alpha_em from shower plugin. if (mergingHooksPtr->useShowerPlugin() ) aemScale = getShowerPluginScale(mother->state, clusterIn.emittor, clusterIn.emitted, clusterIn.recoiler, "scaleEM", aemScale); double alphaEMinPS = (FSR) ? (*aemFSR).alphaEM(aemScale) : (*aemISR).alphaEM(aemScale); aemWeight *= alphaEMinPS / aem0; } // Calculate pdf ratios: Get both sides of event int inP = 3; int inM = 4; int sideP = (mother->state[inP].pz() > 0) ? 1 :-1; int sideM = (mother->state[inM].pz() > 0) ? 1 :-1; if ( mother->state[inP].colType() != 0 ) { // Find x value and flavour double x = getCurrentX(sideP); int flav = getCurrentFlav(sideP); // Find numerator scale double scaleNum = (children.empty()) ? hardFacScale(state) : ( (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? pdfScale : maxscale ); double scaleDen = (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? clusterIn.pT() : newScale; // Multiply PDF ratio double ratio = getPDFratio(sideP, false, false, flav, x, scaleNum, flav, x, scaleDen); pdfWeight *= ratio; } if ( mother->state[inM].colType() != 0 ) { // Find x value and flavour double x = getCurrentX(sideM); int flav = getCurrentFlav(sideM); // Find numerator scale double scaleNum = (children.empty()) ? hardFacScale(state) : ( (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? pdfScale : maxscale ); double scaleDen = (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? clusterIn.pT() : newScale; // Multiply PDF ratio double ratio = getPDFratio(sideM, false, false, flav, x, scaleNum, flav, x, scaleDen); pdfWeight *= ratio; } // Done return w; } //-------------------------------------------------------------------------- // Function to return the \alpha_s-ratio part of the CKKWL weight of a path. double History::weightTreeALPHAS( double as0, AlphaStrong * asFSR, AlphaStrong * asISR, int njetMax ) { // For ME state, do nothing. if ( !mother ) return 1.; // Recurse double w = mother->weightTreeALPHAS( as0, asFSR, asISR, njetMax ); // Do nothing for empty state if (state.size() < 3) return w; // If this node has too many jets, no not calculate no-emission probability. int njetNow = mergingHooksPtr->getNumberOfClusteringSteps( state) ; if (njetNow >= njetMax) return 1.0; // Store variables for easy use. bool FSR = mother->state[clusterIn.emittor].isFinal(); int emtID = mother->state[clusterIn.emitted].id(); // Do not correct alphaS if it is an EW emission. if (abs(emtID) == 22 || abs(emtID) == 23 || abs(emtID) == 24) return w; // Calculate alpha_s ratio for current state if ( asFSR && asISR ) { double asScale = pow2( scale ); if (mergingHooksPtr->unorderedASscalePrescip() == 1) asScale = pow2( clusterIn.pT() ); // Add regularisation scale to initial state alpha_s. if (!FSR) asScale += pow2(mergingHooksPtr->pT0ISR()); // Directly get argument of running alpha_s from shower plugin. if (mergingHooksPtr->useShowerPlugin() ) asScale = getShowerPluginScale(mother->state, clusterIn.emittor, clusterIn.emitted, clusterIn.recoiler, "scaleAS", asScale); double alphaSinPS = (FSR) ? (*asFSR).alphaS(asScale) : (*asISR).alphaS(asScale); w *= alphaSinPS / as0; } // Done return w; } //-------------------------------------------------------------------------- // Function to return the \alpha_em-ratio part of the CKKWL weight of a path. double History::weightTreeALPHAEM( double aem0, AlphaEM * aemFSR, AlphaEM * aemISR, int njetMax ) { // For ME state, do nothing. if ( !mother ) return 1.; // Recurse double w = mother->weightTreeALPHAEM( aem0, aemFSR, aemISR, njetMax ); // Do nothing for empty state if (state.size() < 3) return w; // If this node has too many jets, no not calculate no-emission probability. int njetNow = mergingHooksPtr->getNumberOfClusteringSteps( state) ; if (njetNow >= njetMax) return 1.0; // Store variables for easy use. bool FSR = mother->state[clusterIn.emittor].isFinal(); int emtID = mother->state[clusterIn.emitted].id(); // Do not correct alpha EM if it not an EW emission. if (!(abs(emtID) == 22 || abs(emtID) == 23 || abs(emtID) == 24)) return w; // Calculate alpha_s ratio for current state if ( aemFSR && aemISR ) { double aemScale = pow2( scale ); if (mergingHooksPtr->unorderedASscalePrescip() == 1) aemScale = pow2( clusterIn.pT() ); // Add regularisation scale to initial state alpha_em. if (!FSR) aemScale += pow2(mergingHooksPtr->pT0ISR()); // Directly get argument of running alpha_em from shower plugin. if (mergingHooksPtr->useShowerPlugin() ) aemScale = getShowerPluginScale(mother->state, clusterIn.emittor, clusterIn.emitted, clusterIn.recoiler, "scaleEM", aemScale); double alphaEMinPS = (FSR) ? (*aemFSR).alphaEM(aemScale) : (*aemISR).alphaEM(aemScale); w *= alphaEMinPS / aem0; } // Done return w; } //-------------------------------------------------------------------------- // Function to return the PDF-ratio part of the CKKWL weight of a path. double History::weightTreePDFs( double maxscale, double pdfScale, int njetMax ) { // Use correct scale double newScale = scale; // For ME state, just multiply by PDF ratios if ( !mother ) { // If this node has too many jets, no not calculate PDF ratio. int njet = mergingHooksPtr->getNumberOfClusteringSteps( state); if (njet > njetMax) return 1.0; double wt = 1.; int sideRad = (state[3].pz() > 0) ? 1 :-1; int sideRec = (state[4].pz() > 0) ? 1 :-1; // Calculate PDF first leg if (state[3].colType() != 0) { // Find x value and flavour double x = 2.*state[3].e() / state[0].e(); int flav = state[3].id(); // Find numerator/denominator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; double scaleDen = mergingHooksPtr->muFinME(); // For initial parton, multiply by PDF ratio wt *= getPDFratio(sideRad, false, false, flav, x, scaleNum, flav, x, scaleDen); } // Calculate PDF ratio for second leg if (state[4].colType() != 0) { // Find x value and flavour double x = 2.*state[4].e() / state[0].e(); int flav = state[4].id(); // Find numerator/denominator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; double scaleDen = mergingHooksPtr->muFinME(); // For initial parton, multiply with PDF ratio wt *= getPDFratio(sideRec, false, false, flav, x, scaleNum, flav, x, scaleDen); } return wt; } // Remember new PDF scale n case true scale should be used for un-ordered // splittings. double newPDFscale = newScale; if ( mergingHooksPtr->unorderedPDFscalePrescip() == 1) newPDFscale = clusterIn.pT(); // Recurse double w = mother->weightTreePDFs( newScale, newPDFscale, njetMax ); // Do nothing for empty state if (state.size() < 3) return w; // If this node has too many jets, no not calculate PDF ratio. int njetNow = mergingHooksPtr->getNumberOfClusteringSteps( state) ; // Calculate pdf ratios: Get both sides of event int inP = 3; int inM = 4; int sideP = (mother->state[inP].pz() > 0) ? 1 :-1; int sideM = (mother->state[inM].pz() > 0) ? 1 :-1; if ( mother->state[inP].colType() != 0 ) { // Find x value and flavour double x = getCurrentX(sideP); int flav = getCurrentFlav(sideP); // Find numerator scale double scaleNum = (children.empty()) ? hardFacScale(state) : ( (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? pdfScale : maxscale ); double scaleDen = (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? clusterIn.pT() : newScale; double xDen = (njetNow == njetMax) ? mother->getCurrentX(sideP) : x; int flavDen = (njetNow == njetMax) ? mother->getCurrentFlav(sideP) : flav; double sDen = (njetNow == njetMax) ? mergingHooksPtr->muFinME() : scaleDen; double ratio = getPDFratio(sideP, false, false, flav, x, scaleNum, flavDen, xDen, sDen); w *= ratio; } if ( mother->state[inM].colType() != 0 ) { // Find x value and flavour double x = getCurrentX(sideM); int flav = getCurrentFlav(sideM); // Find numerator scale double scaleNum = (children.empty()) ? hardFacScale(state) : ( (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? pdfScale : maxscale ); double scaleDen = (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? clusterIn.pT() : newScale; double xDen = (njetNow == njetMax) ? mother->getCurrentX(sideM) : x; int flavDen = (njetNow == njetMax) ? mother->getCurrentFlav(sideM) : flav; double sDen = (njetNow == njetMax) ? mergingHooksPtr->muFinME() : scaleDen; double ratio = getPDFratio(sideM, false, false, flav, x, scaleNum, flavDen, xDen, sDen); w *= ratio; } // Done return w; } //-------------------------------------------------------------------------- // Function to return the no-emission probability part of the CKKWL weight. double History::weightTreeEmissions( PartonLevel* trial, int type, int njetMin, int njetMax, double maxscale ) { // Use correct scale double newScale = scale; // For ME state, just multiply by PDF ratios if ( !mother ) return 1.0; // Recurse double w = mother->weightTreeEmissions(trial,type,njetMin,njetMax,newScale); // Do nothing for empty state if (state.size() < 3) return 1.0; // If up to now, trial shower was not successful, return zero if ( w < 1e-12 ) return 0.0; // If this node has too many jets, no not calculate no-emission probability. int njetNow = mergingHooksPtr->getNumberOfClusteringSteps( state) ; if (njetNow >= njetMax) return 1.0; if (njetNow < njetMin ) w *= 1.0; // Do trial shower on current state, return zero if not successful else w *= doTrialShower(trial, type, maxscale); if ( w < 1e-12 ) return 0.0; // Done return w; } //-------------------------------------------------------------------------- // Function to generate the O(\alpha_s)-term of the CKKWL-weight. double History::weightFirst(PartonLevel* trial, double as0, double muR, double maxscale, AlphaStrong * asFSR, AlphaStrong * asISR, Rndm* rndmPtr ) { // Use correct scale double newScale = scale; if ( !mother ) { double weight = 0.; // Calculate PDF first leg if (state[3].colType() != 0) { // Find x value and flavour double x = 2.*state[3].e() / state[0].e(); int flav = state[3].id(); // Find numerator/denominator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; double scaleDen = mergingHooksPtr->muFinME(); // Monte Carlo integrand. double intPDF4 = monteCarloPDFratios(flav, x, scaleNum, scaleDen, mergingHooksPtr->muFinME(), as0, rndmPtr); weight += intPDF4; } // Calculate PDF ratio for second leg if (state[4].colType() != 0) { // Find x value and flavour double x = 2.*state[4].e() / state[0].e(); int flav = state[4].id(); // Find numerator/denominator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; double scaleDen = mergingHooksPtr->muFinME(); // Monte Carlo integrand. double intPDF4 = monteCarloPDFratios(flav, x, scaleNum, scaleDen, mergingHooksPtr->muFinME(), as0, rndmPtr); weight += intPDF4; } return weight; } // Recurse double w = mother->weightFirst(trial, as0, muR, newScale, asFSR, asISR, rndmPtr ); // Do nothing for empty state if (state.size() < 3) return 0.0; // Find right scale double b = 1.; double asScale2 = newScale*newScale; int showerType = (mother->state[clusterIn.emittor].isFinal() ) ? 1 : -1; if (showerType == -1) { asScale2 += pow(mergingHooksPtr->pT0ISR(),2); b = 1.; } // Directly get argument of running alpha_s from shower plugin. if (mergingHooksPtr->useShowerPlugin() ){ asScale2 = getShowerPluginScale(mother->state, clusterIn.emittor, clusterIn.emitted, clusterIn.recoiler, "scaleAS", asScale2); b = 1.; } // Find summand beta_0 / 2 * ln(muR^2/t_i) due to as expansion. double NF = 4.; double BETA0 = 11. - 2./3.* NF; // For fixed \alpha_s in matrix element w += as0 / (2.*M_PI) * 0.5 * BETA0 * log( (muR*muR) / (b*asScale2) ); // Count emissions: New variant // Generate true average, not only one-point. bool fixpdf = true; bool fixas = true; double nWeight1 = 0.; double nWeight2 = 0.; for(int i=0; i < NTRIAL; ++i) { // Get number of emissions vector unresolvedEmissionTerm = countEmissions(trial, maxscale, newScale, 2, as0, asFSR, asISR, 3, fixpdf, fixas); nWeight1 += unresolvedEmissionTerm[1]; } w += nWeight1/double(NTRIAL) + nWeight2/double(NTRIAL); // Calculate pdf ratios: Get both sides of event int inP = 3; int inM = 4; int sideP = (mother->state[inP].pz() > 0) ? 1 :-1; int sideM = (mother->state[inM].pz() > 0) ? 1 :-1; if ( mother->state[inP].colType() != 0 ) { // Find x value and flavour double x = getCurrentX(sideP); int flav = getCurrentFlav(sideP); // Find numerator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; // Monte Carlo integrand. double intPDF4 = monteCarloPDFratios(flav, x, scaleNum, newScale, mergingHooksPtr->muFinME(), as0, rndmPtr); w += intPDF4; } if ( mother->state[inM].colType() != 0 ) { // Find x value and flavour double x = getCurrentX(sideM); int flav = getCurrentFlav(sideM); // Find numerator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; // Monte Carlo integrand. double intPDF4 = monteCarloPDFratios(flav, x, scaleNum, newScale, mergingHooksPtr->muFinME(), as0, rndmPtr); w += intPDF4; } // Done return w; } //-------------------------------------------------------------------------- // Function to generate the O(\alpha_s)-term of the \alpha_s-ratios // appearing in the CKKWL-weight. double History::weightFirstALPHAS( double as0, double muR, AlphaStrong * asFSR, AlphaStrong * asISR ) { // Use correct scale double newScale = scale; // Done if ( !mother ) return 0.; // Recurse double w = mother->weightFirstALPHAS( as0, muR, asFSR, asISR ); // Find right scale int showerType = (mother->state[clusterIn.emittor].isFinal() ) ? 1 : -1; double b = 1.; double asScale = pow2( newScale ); if ( mergingHooksPtr->unorderedASscalePrescip() == 1 ) asScale = pow2( clusterIn.pT() ); if (showerType == -1) { asScale += pow2( mergingHooksPtr->pT0ISR() ); b = 1.; } // Directly get argument of running alpha_s from shower plugin. if (mergingHooksPtr->useShowerPlugin() ) { asScale = getShowerPluginScale(mother->state, clusterIn.emittor, clusterIn.emitted, clusterIn.recoiler, "scaleAS", asScale); b = 1.; } // Find summand beta_0 / 2 * ln(muR^2/t_i) due to as expansion. double NF = 4.; double BETA0 = 11. - 2./3.* NF; // For fixed \alpha_s in matrix element w += as0 / (2.*M_PI) * 0.5 * BETA0 * log( (muR*muR) / (b*asScale) ); // Done return w; } //-------------------------------------------------------------------------- // Function to generate the O(\alpha_s)-term of the PDF-ratios // appearing in the CKKWL-weight. double History::weightFirstPDFs( double as0, double maxscale, double pdfScale, Rndm* rndmPtr ) { // Use correct scale double newScale = scale; if ( !mother ) { double wt = 0.; // Calculate PDF first leg if (state[3].colType() != 0) { // Find x value and flavour double x = 2.*state[3].e() / state[0].e(); int flav = state[3].id(); // Find numerator/denominator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; double scaleDen = mergingHooksPtr->muFinME(); // Monte Carlo integrand. wt += monteCarloPDFratios(flav, x, scaleNum, scaleDen, mergingHooksPtr->muFinME(), as0, rndmPtr); } // Calculate PDF ratio for second leg if (state[4].colType() != 0) { // Find x value and flavour double x = 2.*state[4].e() / state[0].e(); int flav = state[4].id(); // Find numerator/denominator scale double scaleNum = (children.empty()) ? hardFacScale(state) : maxscale; double scaleDen = mergingHooksPtr->muFinME(); // Monte Carlo integrand. wt += monteCarloPDFratios(flav, x, scaleNum, scaleDen, mergingHooksPtr->muFinME(), as0, rndmPtr); } // Done return wt; } // Remember new PDF scale n case true scale should be used for un-ordered // splittings. double newPDFscale = newScale; if (mergingHooksPtr->unorderedPDFscalePrescip() == 1) newPDFscale = clusterIn.pT(); // Recurse double w = mother->weightFirstPDFs( as0, newScale, newPDFscale, rndmPtr); // Calculate pdf ratios: Get both sides of event int inP = 3; int inM = 4; int sideP = (mother->state[inP].pz() > 0) ? 1 :-1; int sideM = (mother->state[inM].pz() > 0) ? 1 :-1; if ( mother->state[inP].colType() != 0 ) { // Find x value and flavour double x = getCurrentX(sideP); int flav = getCurrentFlav(sideP); // Find numerator / denominator scales double scaleNum = (children.empty()) ? hardFacScale(state) : ( (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? pdfScale : maxscale ); double scaleDen = (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? clusterIn.pT() : newScale; // Monte Carlo integrand. w += monteCarloPDFratios(flav, x, scaleNum, scaleDen, mergingHooksPtr->muFinME(), as0, rndmPtr); } if ( mother->state[inM].colType() != 0 ) { // Find x value and flavour double x = getCurrentX(sideM); int flav = getCurrentFlav(sideM); // Find numerator / denominator scales double scaleNum = (children.empty()) ? hardFacScale(state) : ( (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? pdfScale : maxscale ); double scaleDen = (mergingHooksPtr->unorderedPDFscalePrescip() == 1) ? clusterIn.pT() : newScale; // Monte Carlo integrand. w += monteCarloPDFratios(flav, x, scaleNum, scaleDen, mergingHooksPtr->muFinME(), as0, rndmPtr); } // Done return w; } //-------------------------------------------------------------------------- // Function to generate the O(\alpha_s)-term of the no-emission // probabilities appearing in the CKKWL-weight. double History::weightFirstEmissions(PartonLevel* trial, double as0, double maxscale, AlphaStrong * asFSR, AlphaStrong * asISR, bool fixpdf, bool fixas ) { // Use correct scale double newScale = scale; if ( !mother ) return 0.0; // Recurse double w = mother->weightFirstEmissions(trial, as0, newScale, asFSR, asISR, fixpdf, fixas ); // Do nothing for empty state if (state.size() < 3) return 0.0; // Generate true average. double nWeight1 = 0.; double nWeight2 = 0.; for(int i=0; i < NTRIAL; ++i) { // Get number of emissions vector unresolvedEmissionTerm = countEmissions(trial, maxscale, newScale, 2, as0, asFSR, asISR, 3, fixpdf, fixas); nWeight1 += unresolvedEmissionTerm[1]; } w += nWeight1/double(NTRIAL) + nWeight2/double(NTRIAL); // Done return w; } //-------------------------------------------------------------------------- // Function to return the factorisation scale of the hard process in Pythia. double History::hardFacScale(const Event& event) { // Declare output scale. double hardscale = 0.; // If scale should not be reset, done. if ( !mergingHooksPtr->resetHardQFac() ) return mergingHooksPtr->muF(); // For pure QCD dijet events, calculate the hadronic cross section // of the hard process at the pT of the dijet system, rather than at fixed // arbitrary scale. if ( mergingHooksPtr->getProcessString().compare("pp>jj") == 0 || mergingHooksPtr->getProcessString().compare("pp>aj") == 0 || isQCD2to2(event)) { // Find the mT in the hard sub-process. vector mT; for ( int i=0; i < event.size(); ++i) if ( event[i].isFinal() && event[i].colType() != 0 ) mT.push_back( abs(event[i].mT2()) ); if ( int(mT.size()) != 2 ) hardscale = infoPtr->QFac(); else hardscale = sqrt( min( mT[0], mT[1] ) ); } else { hardscale = mergingHooksPtr->muF(); } // Done return hardscale; } //-------------------------------------------------------------------------- // Function to return the factorisation scale of the hard process in Pythia. double History::hardRenScale(const Event& event) { // Declare output scale. double hardscale = 0.; // If scale should not be reset, done. if ( !mergingHooksPtr->resetHardQRen() ) return mergingHooksPtr->muR(); // For pure QCD dijet events, calculate the hadronic cross section // of the hard process at the pT of the dijet system, rather than at fixed // arbitrary scale. if ( mergingHooksPtr->getProcessString().compare("pp>jj") == 0 || mergingHooksPtr->getProcessString().compare("pp>aj") == 0 || isQCD2to2(event)) { // Find the mT in the hard sub-process. vector mT; for ( int i=0; i < event.size(); ++i) if ( event[i].isFinal() && ( event[i].colType() != 0 || event[i].id() == 22 ) ) mT.push_back( abs(event[i].mT()) ); if ( int(mT.size()) != 2 ) hardscale = infoPtr->QRen(); else hardscale = sqrt( mT[0]*mT[1] ); } else { hardscale = mergingHooksPtr->muR(); } // Done return hardscale; } //-------------------------------------------------------------------------- // Perform a trial shower using the \a pythia object between // maxscale down to this scale and return the corresponding Sudakov // form factor. // IN trialShower : Shower object used as trial shower // double : Maximum scale for trial shower branching // OUT 0.0 : trial shower emission outside allowed pT range // 1.0 : trial shower successful (any emission was below // the minimal scale ) double History::doTrialShower( PartonLevel* trial, int type, double maxscaleIn, double minscaleIn ) { // Copy state to local process Event process = state; // Set starting scale. double startingScale = maxscaleIn; // Careful when setting shower starting scale for pure QCD and prompt // photon case. if ( mergingHooksPtr->getNumberOfClusteringSteps(process) == 0 && ( mergingHooksPtr->getProcessString().compare("pp>jj") == 0 || mergingHooksPtr->getProcessString().compare("pp>aj") == 0 || isQCD2to2(state) ) ) startingScale = min( startingScale, hardFacScale(process) ); // Set output. bool doVeto = false; double wt = 1.; bool canEnhanceTrial = (trial->userHooksPtr!=0) && trial->userHooksPtr->canEnhanceTrial(); while ( true ) { // Reset trialShower object trial->resetTrial(); // Construct event to be showered Event event = Event(); event.init("(hard process-modified)", particleDataPtr); event.clear(); // Reset process scale so that shower starting scale is correctly set. process.scale(startingScale); doVeto = false; // Get pT before reclustering double minScale = (minscaleIn > 0.) ? minscaleIn : scale; // If the maximal scale and the minimal scale coincide (as would // be the case for the corrected scales of unordered histories), // do not generate Sudakov if (minScale >= startingScale) break; // Find z and pT values at which the current state was formed, to // ensure that the showers can order the next emission correctly in // rapidity, if required. // NOT CORRECTLY SET FOR HIGHEST MULTIPLICITY STATE! double z = ( mergingHooksPtr->getNumberOfClusteringSteps(state) == 0 || !mother ) ? 0.5 : mother->getCurrentZ(clusterIn.emittor,clusterIn.recoiler, clusterIn.emitted, clusterIn.flavRadBef); // Store z and pT values at which the current state was formed. infoPtr->zNowISR(z); infoPtr->pT2NowISR(pow(startingScale,2)); infoPtr->hasHistory(true); // Setup weak shower settings. if (mergingHooksPtr->doWeakClustering()) setupWeakShower(0); // Perform trial shower emission trial->next(process,event); // Get trial shower pT. double pTtrial = trial->pTLastInShower(); int typeTrial = trial->typeLastInShower(); // Clear parton systems. trial->resetTrial(); // Get enhanced trial emission weight. double pTEnhanced = (canEnhanceTrial) ? trial->userHooksPtr->getEnhancedTrialPT() : 0.; double wtEnhanced = (canEnhanceTrial) ? trial->userHooksPtr->getEnhancedTrialWeight() : 1.; if ( canEnhanceTrial && pTEnhanced > 0.) pTtrial = pTEnhanced; // Get veto (merging) scale value double vetoScale = (mother) ? 0. : mergingHooksPtr->tms(); // Get merging scale in current event double tnow = mergingHooksPtr->tmsNow( event ); // Done if evolution scale has fallen below minimum if ( pTtrial < minScale ) break; // Reset starting scale. startingScale = pTtrial; // Continue if this state is below the veto scale if ( tnow < vetoScale && vetoScale > 0. ) continue; // Retry if the trial emission was not allowed. if ( mergingHooksPtr->canVetoTrialEmission() && mergingHooksPtr->doVetoTrialEmission( process, event) ) continue; int iRecAft = event.size() - 1; int iEmt = event.size() - 2; int iRadAft = event.size() - 3; if ( (event[iRecAft].status() != 52 && event[iRecAft].status() != -53) || event[iEmt].status() != 51 || event[iRadAft].status() != 51) iRecAft = iEmt = iRadAft = -1; for (int i = event.size() - 1; i > 0; i--) { if (iRadAft == -1 && event[i].status() == -41) iRadAft = i; else if (iEmt == -1 && event[i].status() == 43) iEmt = i; else if (iRecAft == -1 && event[i].status() == -42) iRecAft = i; if (iRadAft != -1 && iEmt != -1 && iRecAft != -1) break; } // Only consider allowed emissions for veto: // Only allow MPI for MPI no-emission probability. if ( type == -1 && typeTrial != 1 ) continue; // Only allow ISR or FSR for radiative no-emission probability. if ( type == 1 && !(typeTrial == 2 || typeTrial >= 3) ) continue; // Update enhanced trial shower weight. if (canEnhanceTrial && pTtrial > minScale) wt *= (1. - 1./wtEnhanced); // Done with enhanced trial showers if weight is zero. if ( canEnhanceTrial && wt == 0.) break; // Continue producing trial emissions in case of enhanced showers. if ( canEnhanceTrial && pTtrial > minScale) continue; // Continue if this state is below the veto scale // Veto event if trial pT was above the next nodal scale. if ( pTtrial > minScale ) doVeto = true; //// For last no-emission probability, veto event if the trial state //// is above the merging scale, i.e. in the matrix element region. //if ( !mother && tnow > vetoScale && vetoScale > 0. ) doVeto = true; // For 2 -> 2 pure QCD state, do not allow multiparton interactions // above the kinematical pT of the 2 -> 2 state. if ( type == -1 && typeTrial == 1 && mergingHooksPtr->getNumberOfClusteringSteps(process) == 0 && ( mergingHooksPtr->getProcessString().compare("pp>jj") == 0 || mergingHooksPtr->getProcessString().compare("pp>aj") == 0 || isQCD2to2(state)) && pTtrial > hardFacScale(process) ) return 0.0; // If pT of trial emission was in suitable range (trial shower // successful), return false if ( pTtrial < minScale ) doVeto = false; // Done break; } // Done double res = (canEnhanceTrial) ? wt : ( (doVeto) ? 0. : 1. ); return res; } //-------------------------------------------------------------------------- // Assume we have a vector of i elements containing indices into // another vector with N elements. Update the indices so that all // unique combinations (starting from 0,1,2,3, ...) are // covered. Return false when all combinations have been ehausted. bool History::updateind(vector & ind, int i, int N) { if ( i < 0 ) return false; if ( ++ind[i] < N ) return true; if ( !updateind(ind, i - 1, N - 1) ) return false; ind[i] = ind[i - 1] + 1; return true; } //-------------------------------------------------------------------------- // Return the expansion of the no-emission probability up to the Nth // term. Optionally calculate the the terms using fixed alphaS // and/or PDF ratios. vector History::countEmissions(PartonLevel* trial, double maxscale, double minscale, int showerType, double as0, AlphaStrong * asFSR, AlphaStrong * asISR, int N = 1, bool fixpdf = true, bool fixas = true) { if ( N < 0 ) return vector(); vector result(N+1); result[0] = 1.0; if ( N < 1 ) return result; // Copy state to local process Event process = state; double startingScale = maxscale; // Careful when setting shower starting scale for pure QCD and prompt // photon case. if ( mergingHooksPtr->getNumberOfClusteringSteps(process) == 0 && ( mergingHooksPtr->getProcessString().compare("pp>jj") == 0 || mergingHooksPtr->getProcessString().compare("pp>aj") == 0 || isQCD2to2(state) ) ) startingScale = min( startingScale, hardFacScale(process) ); vector wts; bool canEnhanceTrial = (trial->userHooksPtr!=0) && trial->userHooksPtr->canEnhanceTrial(); while ( true ) { // Reset trialShower object trial->resetTrial(); // Construct event to be showered Event event = Event(); event.init("(hard process-modified)", particleDataPtr); event.clear(); // Reset process scale process.scale(startingScale); // If the maximal scale and the minimal scale coincide (as would // be the case for the corrected scales of unordered histories), // do not generate Sudakov if (minscale >= startingScale) return result; // Find z and pT values at which the current state was formed, to // ensure that the showers can order the next emission correctly in // rapidity, if required if ( mother ) { double z = ( mergingHooksPtr->getNumberOfClusteringSteps(state) == 0) ? 0.5 : mother->getCurrentZ(clusterIn.emittor,clusterIn.recoiler, clusterIn.emitted); // Store z and pT values at which the current state was formed infoPtr->zNowISR(z); infoPtr->pT2NowISR(pow(startingScale,2)); infoPtr->hasHistory(true); } // Setup the weak shower information. if (mergingHooksPtr->doWeakClustering()) setupWeakShower(0); // Perform trial shower emission trial->next(process,event); // Get trial shower pT double pTtrial = trial->pTLastInShower(); int typeTrial = trial->typeLastInShower(); // Clear parton systems. trial->resetTrial(); // Get enhanced trial emission weight. double pTEnhanced = (canEnhanceTrial) ? trial->userHooksPtr->getEnhancedTrialPT() : 0.; double wtEnhanced = (canEnhanceTrial) ? trial->userHooksPtr->getEnhancedTrialWeight() : 1.; if ( canEnhanceTrial && pTEnhanced > 0.) pTtrial = pTEnhanced; // Get veto (merging) scale value double vetoScale = (mother) ? 0. : mergingHooksPtr->tms(); // Get merging scale in current event double tnow = mergingHooksPtr->tmsNow( event ); // Save scale of current state. startingScale = pTtrial; // If the scale of the current state is below the minimal scale, exit. if ( pTtrial < minscale ) break; // If this state is below the merging scale, do not count emission. if ( tnow < vetoScale && vetoScale > 0. ) continue; // Retry if the trial emission was not allowed. if ( mergingHooksPtr->canVetoTrialEmission() && mergingHooksPtr->doVetoTrialEmission( process, event) ) continue; // Set weight of enhanced emission. double enhance = (canEnhanceTrial && pTtrial > minscale) ? wtEnhanced : 1.; // Check if a new emission should be generated, either because // the latest emission was not of the desired kind or if the // emission was above the minimal scale double alphaSinPS = as0; double pdfs = 1.0; double asScale2 = pTtrial*pTtrial; // Directly get argument of running alpha_s from shower plugin. if (mergingHooksPtr->useShowerPlugin() ) asScale2 = getShowerPluginScale(mother->state, clusterIn.emittor, clusterIn.emitted, clusterIn.recoiler, "scaleAS", asScale2); // Initial state splittings. if ( (showerType == -1 || showerType == 2) && typeTrial == 2 ) { // Get weight to translate to alpha_s at fixed renormalisation scale. if ( fixas ) alphaSinPS = (*asISR).alphaS(asScale2); // Get weight to translate to PDFs at fixed factorisation scale. if ( fixpdf ) pdfs = pdfFactor( event, typeTrial, pTtrial, mergingHooksPtr->muFinME() ); // Final state splittings. } else if ( (showerType == 1 || showerType == 2) && typeTrial >= 3 ) { // Get weight to translate to alpha_s at fixed renormalisation scale. if ( fixas ) alphaSinPS = (*asFSR).alphaS(asScale2); // Get weight to translate to PDFs at fixed factorisation scale. Needed // for final state splittings with initial state recoiler. if ( fixpdf ) pdfs = pdfFactor( event, typeTrial, pTtrial, mergingHooksPtr->muFinME() ); } // Save weight correcting to emission generated with fixed scales. if ( typeTrial == 2 || typeTrial >= 3 ) wts.push_back(as0/alphaSinPS * pdfs * 1./enhance); } for ( int n = 1; n <= min(N, int(wts.size())); ++n ) { vector ind(N); for ( int i = 0; i < N; ++i ) ind[i] = i; do { double x = 1.0; for ( int j = 0; j < n; ++j ) x *= wts[ind[j]]; result[n] += x; } while ( updateind(ind, n - 1, wts.size()) ); if ( n%2 ) result[n] *= -1.0; } // Done return result; } //-------------------------------------------------------------------------- // Function to integrate PDF ratios between two scales over x and t, // where the PDFs are always evaluated at the lower t-integration limit double History::monteCarloPDFratios(int flav, double x, double maxScale, double minScale, double pdfScale, double asME, Rndm* rndmPtr) { // Perform numerical integration for PDF ratios // Prefactor is as/2PI double factor = asME / (2.*M_PI); // Scale integration just produces a multiplicative logarithm factor *= log(maxScale/minScale); // For identical scales, done if (factor == 0.) return 0.; // Declare constants double CF = 4./3.; double CA = 3.; double NF = 4.; double TR = 1./2.; double integral = 0.; double RN = rndmPtr->flat(); if (flav == 21) { double zTrial = pow(x,RN); integral = -log(x) * zTrial * integrand(flav, x, pdfScale, zTrial); integral += 1./6.*(11.*CA - 4.*NF*TR) + 2.*CA*log(1.-x); } else { double zTrial = x + RN*(1. - x); integral = (1.-x) * integrand(flav, x, pdfScale, zTrial); integral += 3./2.*CF + 2.*CF*log(1.-x); } // Done return (factor*integral); } /*--------------- METHODS USED FOR CONTRUCTION OF ALL HISTORIES --------- */ // Check if a ordered (and complete) path has been found in the // initial node, in which case we will no longer be interested in // any unordered paths. bool History::onlyOrderedPaths() { if ( !mother || foundOrderedPath ) return foundOrderedPath; return foundOrderedPath = mother->onlyOrderedPaths(); } //-------------------------------------------------------------------------- // Check if a STRONGLY ordered (and complete) path has been found in the // initial node, in which case we will no longer be interested in // any unordered paths. bool History::onlyStronglyOrderedPaths() { if ( !mother || foundStronglyOrderedPath ) return foundStronglyOrderedPath; return foundStronglyOrderedPath = mother->onlyStronglyOrderedPaths(); } //-------------------------------------------------------------------------- // Check if an allowed (according to user-criterion) path has been found in // the initial node, in which case we will no longer be interested in // any forbidden paths. bool History::onlyAllowedPaths() { if ( !mother || foundAllowedPath ) return foundAllowedPath; return foundAllowedPath = mother->onlyAllowedPaths(); } //-------------------------------------------------------------------------- // When a full path has been found, register it with the initial // history node. // IN History : History to be registered as path // bool : Specifying if clusterings so far were ordered // bool : Specifying if path is complete down to 2->2 process // OUT true if History object forms a plausible path (eg prob>0 ...) bool History::registerPath(History & l, bool isOrdered, bool isStronglyOrdered, bool isAllowed, bool isComplete) { // We are not interested in improbable paths. if ( l.prob <= 0.0) return false; // We only register paths in the initial node. if ( mother ) return mother->registerPath(l, isOrdered, isStronglyOrdered, isAllowed, isComplete); // Again, we are not interested in improbable paths. if ( sumpath == sumpath + l.prob ) return false; if ( mergingHooksPtr->canCutOnRecState() && foundAllowedPath && !isAllowed ) return false; if ( mergingHooksPtr->enforceStrongOrdering() && foundStronglyOrderedPath && !isStronglyOrdered ) return false; if ( mergingHooksPtr->orderHistories() && foundOrderedPath && !isOrdered ) { // Prefer complete or allowed paths to ordered paths. if ( (!foundCompletePath && isComplete) || (!foundAllowedPath && isAllowed) ) ; else return false; } if ( foundCompletePath && !isComplete) return false; if ( !mergingHooksPtr->canCutOnRecState() && !mergingHooksPtr->allowCutOnRecState() ) foundAllowedPath = true; if ( mergingHooksPtr->canCutOnRecState() && isAllowed && isComplete) { if ( !foundAllowedPath || !foundCompletePath ) { // If this is the first complete, allowed path, discard the // old, disallowed or incomplete ones. paths.clear(); sumpath = 0.0; } foundAllowedPath = true; } if ( mergingHooksPtr->enforceStrongOrdering() && isStronglyOrdered && isComplete ) { if ( !foundStronglyOrderedPath || !foundCompletePath ) { // If this is the first complete, ordered path, discard the // old, non-ordered or incomplete ones. paths.clear(); sumpath = 0.0; } foundStronglyOrderedPath = true; foundCompletePath = true; } if ( mergingHooksPtr->orderHistories() && isOrdered && isComplete ) { if ( !foundOrderedPath || !foundCompletePath ) { // If this is the first complete, ordered path, discard the // old, non-ordered or incomplete ones. paths.clear(); sumpath = 0.0; } foundOrderedPath = true; foundCompletePath = true; } if ( isComplete ) { if ( !foundCompletePath ) { // If this is the first complete path, discard the old, // incomplete ones. paths.clear(); sumpath = 0.0; } foundCompletePath = true; } // Remember, if this path is ordered, even if no ordering is required if ( isOrdered ) { foundOrderedPath = true; } // Calculate the probability for weak emissions in the path. double weakProb = 1.; if (mergingHooksPtr->doWeakClustering()) weakProb = l.getWeakProb(); // Index path by probability sumpath += l.prob * weakProb; paths[sumpath] = &l; return true; } //-------------------------------------------------------------------------- // For the history-defining state (and if necessary interfering // states), find all possible clusterings. // NO INPUT // OUT vector of all (rad,rec,emt) systems vector History::getAllQCDClusterings() { vector ret; // Initialise vectors to keep track of position of partons in the // history-defining state vector posFinalPartn; vector posInitPartn; vector posFinalGluon; vector posFinalQuark; vector posFinalAntiq; vector posInitGluon; vector posInitQuark; vector posInitAntiq; // Search event record for final state particles and store these in // quark, anti-quark and gluon vectors for ( int i=0; i < state.size(); ++i ) if ( state[i].isFinal() && state[i].colType() !=0 ) { // Store final partons if ( state[i].id() == 21 ) posFinalGluon.push_back(i); else if ( state[i].idAbs() < 10 && state[i].id() > 0) posFinalQuark.push_back(i); else if ( state[i].idAbs() < 10 && state[i].id() < 0) posFinalAntiq.push_back(i); } else if (state[i].status() == -21 && state[i].colType() != 0 ) { // Store initial partons if ( state[i].id() == 21 ) posInitGluon.push_back(i); else if ( state[i].idAbs() < 10 && state[i].id() > 0) posInitQuark.push_back(i); else if ( state[i].idAbs() < 10 && state[i].id() < 0) posInitAntiq.push_back(i); } // Get all clusterings for input state vector systems; systems = getQCDClusterings(state); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); // If valid clusterings were found, return if ( !ret.empty() ) return ret; // If no clusterings have been found until now, try to find // clusterings of diagrams that interfere with the current one // (i.e. change the colours of the current event slightly and run // search again) else if ( ret.empty() && mergingHooksPtr->allowColourShuffling() ) { Event NewState = Event(state); // Start with changing final state quark colour for(int i = 0; i < int(posFinalQuark.size()); ++i) { // Never change the hard process candidates if ( mergingHooksPtr->hardProcess->matchesAnyOutgoing(posFinalQuark[i], NewState) ) continue; int col = NewState[posFinalQuark[i]].col(); for(int j = 0; j < int(posInitAntiq.size()); ++j) { // Now swap colours int acl = NewState[posInitAntiq[j]].acol(); if ( col == acl ) continue; NewState[posFinalQuark[i]].col(acl); NewState[posInitAntiq[j]].acol(col); systems = getQCDClusterings(NewState); if (!systems.empty()) { state = NewState; NewState.clear(); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); return ret; } } } // Now change final state antiquark anticolour for(int i = 0; i < int(posFinalAntiq.size()); ++i) { // Never change the hard process candidates if ( mergingHooksPtr->hardProcess->matchesAnyOutgoing(posFinalAntiq[i], NewState) ) continue; int acl = NewState[posFinalAntiq[i]].acol(); for(int j = 0; j < int(posInitQuark.size()); ++j) { // Now swap colours int col = NewState[posInitQuark[j]].col(); if ( col == acl ) continue; NewState[posFinalAntiq[i]].acol(col); NewState[posInitQuark[j]].col(acl); systems = getQCDClusterings(NewState); if (!systems.empty()) { state = NewState; NewState.clear(); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); return ret; } } } if ( !ret.empty() ) { string message="Warning in History::getAllQCDClusterings: Changed"; message+=" colour structure to allow at least one clustering."; infoPtr->errorMsg(message); } } // Done return ret; } //-------------------------------------------------------------------------- // For one given state, find all possible clusterings. // IN Event : state to be investigated // OUT vector of all (rad,rec,emt) systems in the state vector History::getQCDClusterings( const Event& event) { vector ret; // Initialise vectors to keep track of position of partons in the // input event vector posFinalPartn; vector posInitPartn; vector posFinalGluon; vector posFinalQuark; vector posFinalAntiq; vector posInitGluon; vector posInitQuark; vector posInitAntiq; // Search event record for final state particles and store these in // quark, anti-quark and gluon vectors for (int i=0; i < event.size(); ++i) if ( event[i].isFinal() && event[i].colType() !=0 ) { // Store final partons posFinalPartn.push_back(i); if ( event[i].id() == 21 ) posFinalGluon.push_back(i); else if ( event[i].idAbs() < 10 && event[i].id() > 0) posFinalQuark.push_back(i); else if ( event[i].idAbs() < 10 && event[i].id() < 0) posFinalAntiq.push_back(i); } else if ( event[i].status() == -21 && event[i].colType() != 0 ) { // Store initial partons posInitPartn.push_back(i); if ( event[i].id() == 21 ) posInitGluon.push_back(i); else if ( event[i].idAbs() < 10 && event[i].id() > 0) posInitQuark.push_back(i); else if ( event[i].idAbs() < 10 && event[i].id() < 0) posInitAntiq.push_back(i); } int nFiGluon = int(posFinalGluon.size()); int nFiQuark = int(posFinalQuark.size()); int nFiAntiq = int(posFinalAntiq.size()); int nInGluon = int(posInitGluon.size()); int nInQuark = int(posInitQuark.size()); int nInAntiq = int(posInitAntiq.size()); vector systems; // Find rad + emt + rec systems: // (1) Start from gluon and find all (rad,rec,emt=gluon) triples for (int i = 0; i < nFiGluon; ++i) { int EmtGluon = posFinalGluon[i]; systems = findQCDTriple( EmtGluon, 2, event, posFinalPartn, posInitPartn); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); } // For more than one quark-antiquark pair in final state, check for // g -> qqbar splittings bool check_g2qq = true; if ( ( ( nInQuark + nInAntiq == 0 ) && (nInGluon == 0) && (nFiQuark == 1) && (nFiAntiq == 1) ) || ( ( nFiQuark + nFiAntiq == 0) && (nInQuark == 1) && (nInAntiq == 1) ) ) check_g2qq = false; if ( check_g2qq ) { // (2) Start from quark and find all (rad,rec,emt=quark) triples // ( when g -> q qbar occured ) for( int i=0; i < nFiQuark; ++i) { int EmtQuark = posFinalQuark[i]; systems = findQCDTriple( EmtQuark,1,event, posFinalPartn, posInitPartn); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); } // (3) Start from anti-quark and find all (rad,rec,emt=anti-quark) // triples ( when g -> q qbar occured ) for( int i=0; i < nFiAntiq; ++i) { int EmtAntiq = posFinalAntiq[i]; systems = findQCDTriple( EmtAntiq,1,event, posFinalPartn, posInitPartn); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); } } return ret; } //-------------------------------------------------------------------------- // Function to attach (spin-dependent duplicates of) a clustering. void History::attachClusterings (vector& clus, int iEmt, int iRad, int iRec, int iPartner, double pT, const Event& event) { if ( !mergingHooksPtr->doWeakClustering() ) { clus.push_back( Clustering(iEmt, iRad, iRec, iPartner, pT, 0, 0, 0, 0, 9)); } else { // Check if spins are already assigned. int radSpin = event[iRad].intPol(); int emtSpin = event[iEmt].intPol(); int recSpin = event[iRec].intPol(); bool hasRadSpin = (radSpin != 9); bool hasEmtSpin = (emtSpin != 9); bool hasRecSpin = (recSpin != 9); // Check if any of the partons are quarks. bool radQuark = event[iRad].idAbs() < 10; bool emtQuark = event[iEmt].idAbs() < 10; bool recQuark = event[iRec].idAbs() < 10; // Generate the vector of all spin structures vector < vector > structs; structs.resize(0); for (int i = 0; i < 3; ++i){ int sRad = (i==0) ? -1 : (i==1) ? 1 : 9; for (int j = 0; j < 3; ++j){ int sEmt = (j==0) ? -1 : (j==1) ? 1 : 9; for (int k = 0; k < 3; ++k){ int sRec = (k==0) ? -1 : (k==1) ? 1 : 9; vector s; s.push_back(sRad); s.push_back(sEmt); s.push_back(sRec); structs.push_back(s); } } } vector < vector > allStructs; for (int i = 0; i< int(structs.size()); ++i) { // Only pick allowed combinations if spin is already set. if (hasRadSpin && radQuark && structs[i][0] != radSpin) continue; if (hasEmtSpin && emtQuark && structs[i][1] != emtSpin) continue; if (hasRecSpin && recQuark && structs[i][2] != recSpin) continue; // Set +-1 for previously unset quark spins. if (!hasRadSpin && radQuark && structs[i][0] == 9) continue; if (!hasEmtSpin && emtQuark && structs[i][1] == 9) continue; if (!hasRecSpin && recQuark && structs[i][2] == 9) continue; // Leave spin 9 for gluons. if (!radQuark && structs[i][0] != radSpin) continue; if (!emtQuark && structs[i][1] != emtSpin) continue; if (!recQuark && structs[i][2] != recSpin) continue; // Ensure that the quarks in the g-> q qbar splitting have equal spin. if (radQuark && emtQuark && structs[i][0] != structs[i][1]) continue; // Store spin structure. allStructs.push_back(structs[i]); } // Get flavour of radiator before the splitting. int flavRadBef = getRadBeforeFlav(iRad, iEmt, event); // Push back all spin-dependent clusterings. for (int i = 0; i< int(allStructs.size()); ++i) { // Get spin of radiator before the splitting. int spinRadBef = getRadBeforeSpin(iRad, iEmt, allStructs[i][0], allStructs[i][1], event); clus.push_back( Clustering(iEmt, iRad, iRec, iPartner, pT, flavRadBef, allStructs[i][0], allStructs[i][1], allStructs[i][2], spinRadBef) ); } } // doWeakClustering return; } //-------------------------------------------------------------------------- // Function to construct (rad,rec,emt) triples from the event // IN int : Position of Emitted in event record for which // dipoles should be constructed // int : Colour topogy to be tested // 1= g -> qqbar, causing 2 -> 2 dipole splitting // 2= q(bar) -> q(bar) g && g -> gg, // causing a 2 -> 3 dipole splitting // Event : event record to be checked for ptential partners // OUT vector of all allowed radiator+recoiler+emitted triples vector History::findQCDTriple (int EmtTagIn, int colTopIn, const Event& event, vector posFinalPartn, vector posInitPartn ) { // Copy input parton tag int EmtTag = EmtTagIn; // Copy input colour topology tag // (1: g --> qqbar splitting present, 2:rest) int colTop = colTopIn; // Initialise FinalSize int finalSize = int(posFinalPartn.size()); int initSize = int(posInitPartn.size()); int size = initSize + finalSize; vector clus; // Search final partons to find partons colour-connected to // event[EmtTag], choose radiator, then choose recoiler for ( int a = 0; a < size; ++a ) { int i = (a < finalSize)? a : (a - finalSize) ; int iRad = (a < finalSize)? posFinalPartn[i] : posInitPartn[i]; if ( event[iRad].col() == event[EmtTag].col() && event[iRad].acol() == event[EmtTag].acol() ) continue; if (iRad != EmtTag ) { int pTdef = event[iRad].isFinal() ? 1 : -1; int sign = (a < finalSize)? 1 : -1 ; // First colour topology: g --> qqbar. Here, emt & rad should // have same flavour (causes problems for gamma->qqbar). if (colTop == 1) { if ( event[iRad].id() == -sign*event[EmtTag].id() ) { int col = -1; int acl = -1; if (event[iRad].isFinal() ) { if (event[iRad].id() < 0) { col = event[EmtTag].col(); acl = 0; } else { acl = event[EmtTag].acol(); col = 0; } } else { if (event[iRad].id() < 0) { acl = event[EmtTag].acol(); col = 0; } else { col = event[EmtTag].col(); acl = 0; } } // Recoiler int iRec = 0; // Colour partner int iPartner = 0; if (col > 0) { // Find recoiler by colour iRec = FindCol(col,iRad,EmtTag,event,1,true); // In initial state splitting has final state colour partner, // Save both partner and recoiler if ( (sign < 0) && (event[iRec].isFinal()) ) { // Save colour recoiler iPartner = iRec; // Reset kinematic recoiler to initial state parton for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) iRec = posInitPartn[l]; // For final state splittings, colour partner and recoiler are // identical } else { iPartner = iRec; } if ( iRec != 0 && iPartner != 0 && allowedClustering( iRad, EmtTag, iRec, iPartner, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iPartner, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } // Reset partner iPartner = 0; // Find recoiler by colour iRec = FindCol(col,iRad,EmtTag,event,2,true); // In initial state splitting has final state colour partner, // Save both partner and recoiler if ( (sign < 0) && (event[iRec].isFinal()) ) { // Save colour recoiler iPartner = iRec; // Reset kinematic recoiler to initial state parton for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) iRec = posInitPartn[l]; // For final state splittings, colour partner and recoiler are // identical } else { iPartner = iRec; } if ( iRec != 0 && iPartner != 0 && allowedClustering( iRad, EmtTag, iRec, iPartner, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iPartner, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } } if (acl > 0) { // Reset partner iPartner = 0; // Find recoiler by colour iRec = FindCol(acl,iRad,EmtTag,event,1,true); // In initial state splitting has final state colour partner, // Save both partner and recoiler if ( (sign < 0) && (event[iRec].isFinal()) ) { // Save colour recoiler iPartner = iRec; // Reset kinematic recoiler to initial state parton for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) iRec = posInitPartn[l]; // For final state splittings, colour partner and recoiler are // identical } else { iPartner = iRec; } if ( iRec != 0 && iPartner != 0 && allowedClustering( iRad, EmtTag, iRec, iPartner, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iPartner, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } // Reset partner iPartner = 0; // Find recoiler by colour iRec = FindCol(acl,iRad,EmtTag,event,2,true); // In initial state splitting has final state colour partner, // Save both partner and recoiler if ( (sign < 0) && (event[iRec].isFinal()) ) { // Save colour recoiler iPartner = iRec; // Reset kinematic recoiler to initial state parton for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) iRec = posInitPartn[l]; // For final state splittings, colour partner and recoiler are // identical } else { iPartner = iRec; } if ( iRec != 0 && iPartner != 0 && allowedClustering( iRad, EmtTag, iRec, iPartner, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iPartner, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } } // Initial gluon splitting } else if ( event[iRad].id() == 21 &&( event[iRad].col() == event[EmtTag].col() || event[iRad].acol() == event[EmtTag].acol() )) { // For an initial state radiator, always set recoiler // to the other initial state parton (recoil is taken // by full remaining system, so this is just a // labelling for such a process) int RecInit = 0; for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) RecInit = posInitPartn[l]; // Find the colour connected partner // Find colour index of radiator before splitting int col = getRadBeforeCol(iRad, EmtTag, event); int acl = getRadBeforeAcol(iRad, EmtTag, event); // Find the correct partner: If a colour line has split, // the partner is connected to the radiator before the splitting // by a colour line (same reasoning for anticolour). The colour // that split is the colour appearing twice in the // radiator + emitted pair. // Thus, if we remove a colour index with the clustering, // we should look for a colour partner, else look for // an anticolour partner int colRemove = (event[iRad].col() == event[EmtTag].col()) ? event[iRad].col() : event[iRad].acol(); int iPartner = 0; if (colRemove > 0 && col > 0 && col != colRemove) iPartner = FindCol(col,iRad,EmtTag,event,1,true) + FindCol(col,iRad,EmtTag,event,2,true); else if (colRemove > 0 && acl > 0 && acl != colRemove) iPartner = FindCol(acl,iRad,EmtTag,event,1,true) + FindCol(acl,iRad,EmtTag,event,2,true); if ( allowedClustering( iRad, EmtTag, RecInit, iPartner, event ) ) { attachClusterings (clus, EmtTag, iRad, RecInit, iPartner, pTLund(event, iRad, EmtTag, RecInit, pTdef), event); continue; } } // Second colour topology: Gluon emission } else { if ( (event[iRad].col() == event[EmtTag].acol()) || (event[iRad].acol() == event[EmtTag].col()) || (event[iRad].col() == event[EmtTag].col()) || (event[iRad].acol() == event[EmtTag].acol()) ) { // For the rest, choose recoiler to have a common colour // tag with radiator, while not being the "Emitted" int col = -1; int acl = -1; if (event[iRad].isFinal() ) { if ( event[iRad].id() < 0) { acl = event[EmtTag].acol(); col = event[iRad].col(); } else if ( event[iRad].id() > 0 && event[iRad].id() < 10) { col = event[EmtTag].col(); acl = event[iRad].acol(); } else { col = event[EmtTag].col(); acl = event[EmtTag].acol(); } int iRec = 0; if (col > 0) { iRec = FindCol(col,iRad,EmtTag,event,1,true); if ( (sign < 0) && (event[iRec].isFinal()) ) iRec = 0; if (iRec != 0 && allowedClustering( iRad, EmtTag, iRec, iRec, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iRec, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } iRec = FindCol(col,iRad,EmtTag,event,2,true); if ( (sign < 0) && (event[iRec].isFinal()) ) iRec = 0; if (iRec != 0 && allowedClustering( iRad, EmtTag, iRec, iRec, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iRec, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } } if (acl > 0) { iRec = FindCol(acl,iRad,EmtTag,event,1,true); if ( (sign < 0) && (event[iRec].isFinal()) ) iRec = 0; if (iRec != 0 && allowedClustering( iRad, EmtTag, iRec, iRec, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iRec, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } iRec = FindCol(acl,iRad,EmtTag,event,2,true); if ( (sign < 0) && (event[iRec].isFinal()) ) iRec = 0; if (iRec != 0 && allowedClustering( iRad, EmtTag, iRec, iRec, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iRec, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } } } else { // For an initial state radiator, always set recoiler // to the other initial state parton (recoil is taken // by full remaining system, so this is just a // labelling for such a process) int RecInit = 0; int iPartner = 0; for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) RecInit = posInitPartn[l]; // Find the colour connected partner // Find colour index of radiator before splitting col = getRadBeforeCol(iRad, EmtTag, event); acl = getRadBeforeAcol(iRad, EmtTag, event); // Find the correct partner: If a colour line has split, // the partner is connected to the radiator before the splitting // by a colour line (same reasoning for anticolour). The colour // that split is the colour appearing twice in the // radiator + emitted pair. // Thus, if we remove a colour index with the clustering, // we should look for a colour partner, else look for // an anticolour partner int colRemove = (event[iRad].col() == event[EmtTag].col()) ? event[iRad].col() : 0; iPartner = (colRemove > 0) ? FindCol(col,iRad,EmtTag,event,1,true) + FindCol(col,iRad,EmtTag,event,2,true) : FindCol(acl,iRad,EmtTag,event,1,true) + FindCol(acl,iRad,EmtTag,event,2,true); if ( allowedClustering( iRad, EmtTag, RecInit, iPartner, event)) { attachClusterings (clus, EmtTag, iRad, RecInit, iPartner, pTLund(event, iRad, EmtTag, RecInit, pTdef), event); continue; } } } } } } // Done return clus; } //-------------------------------------------------------------------------- // For the history-defining state (and if necessary interfering // states), find all possible clusterings. // NO INPUT // OUT vector of all (rad,rec,emt) systems vector History::getAllEWClusterings() { vector ret; // Get all clusterings for input state vector systems; systems = getEWClusterings(state); ret.insert(ret.end(), systems.begin(), systems.end()); // Done return ret; } //-------------------------------------------------------------------------- // For one given state, find all possible clusterings. // IN Event : state to be investigated // OUT vector of all (rad,rec,emt) systems in the state vector History::getEWClusterings( const Event& event) { vector ret; // Initialise vectors to keep track of position of partons in the // input event vector posFinalPartn; vector posInitPartn; vector posFinalW; vector posFinalZ; // Search event record for final state particles and store these in // quark, anti-quark and gluon vectors for ( int i=3; i < event.size(); ++i ) if ( event[i].isFinal() ) { // Store final partons posFinalPartn.push_back(i); } else { // Store initial partons posInitPartn.push_back(i); } // Search event record for final W. for ( int i=0; i < event.size(); ++i ) if ( event[i].isFinal() && event[i].idAbs() == 24 ) posFinalW.push_back( i ); // Search event record for final Z. for ( int i=0; i < event.size(); ++i ) if ( event[i].isFinal() && event[i].idAbs() == 23 ) posFinalZ.push_back( i ); vector systems; // Find rad + emt + rec systems: // (1) Start from W boson and find all (rad,rec,emt=W) triples for ( int i = 0; i < int(posFinalW.size()); ++i ) { int emtW = posFinalW[i]; systems = findEWTripleW( emtW, event, posFinalPartn, posInitPartn); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); } // Find rad + emt + rec systems: // (1) Start from Z boson and find all (rad,rec,emt=W) triples for ( int i = 0; i < int(posFinalZ.size()); ++i ) { int emtZ = posFinalZ[i]; systems = findEWTripleZ( emtZ, event, posFinalPartn, posInitPartn); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); } return ret; } //-------------------------------------------------------------------------- // Function to construct (rad,rec,emt) triples from the event // IN int : Position of Emitted in event record for which // dipoles should be constructed // int : Colour topogy to be tested // 1= g -> qqbar, causing 2 -> 2 dipole splitting // 2= q(bar) -> q(bar) g && g -> gg, // causing a 2 -> 3 dipole splitting // Event : event record to be checked for ptential partners // OUT vector of all allowed radiator+recoiler+emitted triples vector History::findEWTripleW ( int emtTagIn, const Event& event, vector posFinalPartn, vector posInitPartn ) { // Copy input parton tag int emtTag = emtTagIn; int flavEmt = event[emtTag].id(); // Copy input colour topology tag // (1: g --> qqbar splitting present, 2:rest) // Initialise FinalSize int finalSize = int(posFinalPartn.size()); int initSize = int(posInitPartn.size()); // Store flavour count to check if the new flavour configuration is valid. vector flavCounts(30,0); for ( int a = 0; a < finalSize; ++a ) { if (event[posFinalPartn[a]].idAbs() < 20) { int sign = 1; if (event[posFinalPartn[a]].id() < 0) sign = -1; flavCounts[event[posFinalPartn[a]].idAbs()] += sign; } if (event[posFinalPartn[a]].idAbs() == 24) flavCounts[24]++; } for ( int a = 0; a < initSize; ++a ) { if (event[posInitPartn[a]].idAbs() < 20) { int sign = 1; if (event[posInitPartn[a]].id() < 0) sign = -1; flavCounts[event[posInitPartn[a]].idAbs()] -= sign; } } vector clus; // Search final partons to find partons colour-connected to // event[EmtTag], choose radiator, then choose recoiler for ( int a = 0; a < finalSize; ++a ) { int iRad = posFinalPartn[a]; if (iRad != emtTag) { // Spin information. int spinRad = event[iRad].intPol(); if (spinRad == -1 || spinRad == 9 || spinRad == 0) { int pTdef = 1; // Find recoiler by flavour. int flavRad = event[iRad].id(); // Only allow quark and leptons to be radiators. if (event[iRad].isQuark() || event[iRad].isLepton()) { // Check if the W+- matches that of the quark/lepton. int flavExp = (flavRad > 0) ? 24 : -24; if (abs(flavRad) % 2 == 0) flavExp = -flavExp; if (flavExp == flavEmt) { // Find possible flavours that the W can change the quark to. vector flavRadBefs = posFlavCKM(flavRad); // Change to anti particles if radiator is an anti particle. if (flavRad < 0) for (int i = 0;i < int(flavRadBefs.size()); ++i) flavRadBefs[i] = - flavRadBefs[i]; // Loop through final partons and try to find matching flavours. for ( int i = 0; i < finalSize; ++i ) { int iRec = posFinalPartn[i]; // Check for particle overlaps. if ( iRec != iRad && iRec != emtTag ) { for (int j = 0;j < int(flavRadBefs.size()); ++j) { // Check new flavour structure. If multiple Ws // are still present, do not check flavour yet. if (flavCounts[24] <= 1 && !checkFlavour(flavCounts, flavRad, flavRadBefs[j], 1)) continue; clus.push_back( Clustering(emtTag, iRad, iRec, iRec, pTLund(event, iRad, emtTag, iRec, pTdef, flavRadBefs[j]), flavRadBefs[j], -1 ) ); } } } } } } } } // Search for initial shower histories. for (int a = 0;a < int(posInitPartn.size()); ++a) { int iRad = posInitPartn[a]; int flavRad = event[iRad].id(); // Only allow quarks and leptons to radiate weak bosons. if (event[iRad].isQuark() || event[iRad].isLepton()) { // Spin information. int spinRad = event[iRad].intPol(); if (spinRad == -1 || spinRad == 9 || spinRad == 0) { // Check if the W+- matches that of the quark/lepton. int flavExp = (flavRad > 0) ? 24 : -24; if (abs(flavRad) % 2 == 1) flavExp = -flavExp; if (flavExp == flavEmt) { // Find possible flavours that the W cam change the quark to. vector flavRadBefs = posFlavCKM(flavRad); // Change to anti particles if radiator is an anti particle. if (flavRad < 0) for (int i = 0;i < int(flavRadBefs.size()); ++i) flavRadBefs[i] = - flavRadBefs[i]; // Loop through final partons and try to find matching flavours. for ( int i = 0; i < int(posInitPartn.size()); ++i ) { int iRec = posInitPartn[i]; // Check for particle overlaps. if ( i != a && iRec != emtTag) { for (int j = 0;j < int(flavRadBefs.size()); ++j) { // Check new flavour structure. // If multiple Ws are still present, do not check flavour yet. if (flavCounts[24] <= 1 && !checkFlavour(flavCounts, flavRad, flavRadBefs[j], -1)) continue; clus.push_back( Clustering(emtTag, iRad, iRec, iRec, pTLund(event, iRad, emtTag, iRec, -1, flavRadBefs[j]), flavRadBefs[j], -1 ) ); } } } } } } } // Done return clus; } //-------------------------------------------------------------------------- // Function to construct (rad,rec,emt) triples from the event // IN int : Position of Emitted in event record for which // dipoles should be constructed // int : Colour topogy to be tested // 1= g -> qqbar, causing 2 -> 2 dipole splitting // 2= q(bar) -> q(bar) g && g -> gg, // causing a 2 -> 3 dipole splitting // Event : event record to be checked for ptential partners // OUT vector of all allowed radiator+recoiler+emitted triples vector History::findEWTripleZ ( int emtTagIn, const Event& event, vector posFinalPartn, vector posInitPartn ) { // Copy input parton tag int emtTag = emtTagIn; // Copy input colour topology tag // (1: g --> qqbar splitting present, 2:rest) // Initialise FinalSize int finalSize = int(posFinalPartn.size()); int initSize = int(posInitPartn.size()); // Store flavour count to check if the new flavour configuration is valid. vector flavCounts(30,0); for ( int a = 0; a < finalSize; ++a ) { if (event[posFinalPartn[a]].idAbs() < 20) { int sign = 1; if (event[posFinalPartn[a]].id() < 0) sign = -1; flavCounts[event[posFinalPartn[a]].idAbs()] += sign; } if (event[posFinalPartn[a]].idAbs() == 24) flavCounts[24]++; } for ( int a = 0; a < initSize; ++a ) { if (event[posInitPartn[a]].idAbs() < 20) { int sign = 1; if (event[posInitPartn[a]].id() < 0) sign = -1; flavCounts[event[posInitPartn[a]].idAbs()] -= sign; } } vector clus; // Add Z reclusterings. // Search final partons to find partons colour-connected to // event[EmtTag], choose radiator, then choose recoiler for ( int a = 0; a < finalSize; ++a ) { int iRad = posFinalPartn[a]; if (iRad != emtTag) { int pTdef = 1; // Find recoiler by flavour. int flavRad = event[iRad].id(); // Only allow quark and leptons to be radiators. if (event[iRad].isQuark() || event[iRad].isLepton()) // Loop through final partons and try to find matching flavours. for ( int i = 0; i < finalSize; ++i ) { int iRec = posFinalPartn[i]; // Check for particle overlaps. if ( iRec != iRad && iRec != emtTag ) { // Check new flavour structure. // If multiple Ws are still present, do not check flavour yet. if (flavCounts[24] <= 1 && !checkFlavour(flavCounts, flavRad, flavRad, 1)) continue; clus.push_back( Clustering(emtTag, iRad, iRec, iRec, pTLund(event, iRad, emtTag, iRec, pTdef, flavRad), flavRad, -1 ) ); } } } } // Search for initial shower histories. for (int a = 0;a < int(posInitPartn.size()); ++a) { int iRad = posInitPartn[a]; int flavRad = event[iRad].id(); // Only allow quarks and leptons to radiate weak bosons. if (event[iRad].isQuark() || event[iRad].isLepton()) { // Loop through initial partons and try to find matching flavours. for ( int i = 0; i < int(posInitPartn.size()); ++i ) { int iRec = posInitPartn[i]; // Check for particle overlaps. if ( i != a && iRec != emtTag) { // Check new flavour structure. // If multiple Ws are still present, do not check flavour yet. if (flavCounts[24] <= 1 && !checkFlavour(flavCounts, flavRad, flavRad, -1)) continue; clus.push_back( Clustering(emtTag, iRad, iRec, iRec, pTLund(event, iRad, emtTag, iRec, -1, flavRad), flavRad, -1 ) ); } } } } // Done return clus; } //-------------------------------------------------------------------------- // For the history-defining state (and if necessary interfering // states), find all possible clusterings. // NO INPUT // OUT vector of all (rad,rec,emt) systems vector History::getAllSQCDClusterings() { vector ret; // Get all clusterings for input state vector systems; systems = getSQCDClusterings(state); ret.insert(ret.end(), systems.begin(), systems.end()); // Done return ret; } //-------------------------------------------------------------------------- // For one given state, find all possible clusterings. // IN Event : state to be investigated // OUT vector of all (rad,rec,emt) systems in the state vector History::getSQCDClusterings( const Event& event) { vector ret; // Initialise vectors to keep track of position of partons in the // input event vector posFinalPartn; vector posInitPartn; vector posFinalGluon; vector posFinalQuark; vector posFinalAntiq; vector posInitGluon; vector posInitQuark; vector posInitAntiq; // Search event record for final state particles and store these in // quark, anti-quark and gluon vectors for (int i=0; i < event.size(); ++i) if ( event[i].isFinal() && event[i].colType() !=0 ) { // Store final partons posFinalPartn.push_back(i); if ( event[i].id() == 21 || event[i].id() == 1000021) posFinalGluon.push_back(i); else if ( (event[i].idAbs() < 10 && event[i].id() > 0) || (event[i].idAbs() < 1000010 && event[i].idAbs() > 1000000 && event[i].id() > 0) || (event[i].idAbs() < 2000010 && event[i].idAbs() > 2000000 && event[i].id() > 0)) posFinalQuark.push_back(i); else if ( (event[i].idAbs() < 10 && event[i].id() < 0) || (event[i].idAbs() < 1000010 && event[i].idAbs() > 1000000 && event[i].id() < 0) || (event[i].idAbs() < 2000010 && event[i].idAbs() > 2000000 && event[i].id() < 0)) posFinalAntiq.push_back(i); } else if ( event[i].status() == -21 && event[i].colType() != 0 ) { // Store initial partons posInitPartn.push_back(i); if ( event[i].id() == 21 || event[i].id() == 1000021) posInitGluon.push_back(i); else if ( (event[i].idAbs() < 10 && event[i].id() > 0) || (event[i].idAbs() < 1000010 && event[i].idAbs() > 1000000 && event[i].id() > 0) || (event[i].idAbs() < 2000010 && event[i].idAbs() > 2000000 && event[i].id() > 0)) posInitQuark.push_back(i); else if ( (event[i].idAbs() < 10 && event[i].id() < 0) || (event[i].idAbs() < 1000010 && event[i].idAbs() > 1000000 && event[i].id() < 0) || (event[i].idAbs() < 2000010 && event[i].idAbs() > 2000000 && event[i].id() < 0)) posInitAntiq.push_back(i); } int nFiGluon = int(posFinalGluon.size()); int nFiQuark = int(posFinalQuark.size()); int nFiAntiq = int(posFinalAntiq.size()); int nInGluon = int(posInitGluon.size()); int nInQuark = int(posInitQuark.size()); int nInAntiq = int(posInitAntiq.size()); vector systems; // Find rad + emt + rec systems: // (1) Start from gluon and find all (rad,rec,emt=gluon) triples for (int i = 0; i < nFiGluon; ++i) { int EmtGluon = posFinalGluon[i]; systems = findSQCDTriple( EmtGluon, 2, event, posFinalPartn, posInitPartn); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); } // For more than one quark-antiquark pair in final state, check for // g -> qqbar splittings bool check_g2qq = true; if ( ( ( nInQuark + nInAntiq == 0 ) && (nInGluon == 0) && (nFiQuark == 1) && (nFiAntiq == 1) ) || ( ( nFiQuark + nFiAntiq == 0) && (nInQuark == 1) && (nInAntiq == 1) ) ) check_g2qq = false; if ( check_g2qq ) { // (2) Start from quark and find all (rad,rec,emt=quark) triples // ( when g -> q qbar occured ) for( int i=0; i < nFiQuark; ++i) { int EmtQuark = posFinalQuark[i]; systems = findSQCDTriple( EmtQuark,1,event, posFinalPartn, posInitPartn); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); } // (3) Start from anti-quark and find all (rad,rec,emt=anti-quark) // triples ( when g -> q qbar occured ) for( int i=0; i < nFiAntiq; ++i) { int EmtAntiq = posFinalAntiq[i]; systems = findSQCDTriple( EmtAntiq,1,event, posFinalPartn, posInitPartn); ret.insert(ret.end(), systems.begin(), systems.end()); systems.resize(0); } } return ret; } //-------------------------------------------------------------------------- // Function to construct (rad,rec,emt) triples from the event // IN int : Position of Emitted in event record for which // dipoles should be constructed // int : Colour topogy to be tested // 1= g -> qqbar, causing 2 -> 2 dipole splitting // 2= q(bar) -> q(bar) g && g -> gg, // causing a 2 -> 3 dipole splitting // Event : event record to be checked for ptential partners // OUT vector of all allowed radiator+recoiler+emitted triples vector History::findSQCDTriple (int EmtTagIn, int colTopIn, const Event& event, vector posFinalPartn, vector posInitPartn ) { // Copy input parton tag int EmtTag = EmtTagIn; // Copy input colour topology tag // (1: g --> qqbar splitting present, 2:rest) int colTop = colTopIn; // PDG numbering offset for squarks int offsetL = 1000000; int offsetR = 2000000; // Initialise FinalSize int finalSize = int(posFinalPartn.size()); int initSize = int(posInitPartn.size()); int size = initSize + finalSize; vector clus; // Search final partons to find partons colour-connected to // event[EmtTag], choose radiator, then choose recoiler for ( int a = 0; a < size; ++a ) { int i = (a < finalSize)? a : (a - finalSize) ; int iRad = (a < finalSize)? posFinalPartn[i] : posInitPartn[i]; if ( event[iRad].col() == event[EmtTag].col() && event[iRad].acol() == event[EmtTag].acol() ) continue; // Save radiator flavour. int radID = event[iRad].id(); // Remember if radiator is BSM. bool isSQCDrad = (abs(radID) > offsetL); // Remember if emitted is BSM. bool isSQCDemt = (event[EmtTag].idAbs() > offsetL ); if (iRad != EmtTag ) { int pTdef = event[iRad].isFinal() ? 1 : -1; int sign = (a < finalSize)? 1 : -1 ; // Disalllow clusterings resulting in an initial state sQCD parton! int radBefID = getRadBeforeFlav(iRad,EmtTag,event); if ( pTdef == -1 && abs(radBefID) > offsetL ) continue; // First colour topology: g --> qqbar. Here, emt & rad should // have same flavour (causes problems for gamma->qqbar). if (colTop == 1) { int radSign = (event[iRad].id() < 0) ? -1 : 1; int emtSign = (event[EmtTag].id() < 0) ? -1 : 1; // Final gluino splitting. bool finalSplitting = false; if ( abs(radID) < 10 && radSign*(abs(radID)+offsetL) == -sign*event[EmtTag].id() ) finalSplitting = true; if ( abs(radID) < 10 && radSign*(abs(radID)+offsetR) == -sign*event[EmtTag].id() ) finalSplitting = true; if ( abs(radID) > offsetL && abs(radID) < offsetL+10 && radID == -sign*emtSign*( event[EmtTag].idAbs() + offsetL) ) finalSplitting = true; if ( abs(radID) > offsetR && abs(radID) < offsetR+10 && radID == -sign*emtSign*( event[EmtTag].idAbs() + offsetR) ) finalSplitting = true; // Initial gluon splitting. bool initialSplitting = false; if ( radID == 21 && ( ( event[EmtTag].idAbs() > offsetL && event[EmtTag].idAbs() < offsetL+10) || ( event[EmtTag].idAbs() > offsetR && event[EmtTag].idAbs() < offsetR+10) ) && ( event[iRad].col() == event[EmtTag].col() || event[iRad].acol() == event[EmtTag].acol() ) ) initialSplitting = true; if ( finalSplitting ) { int col = -1; int acl = -1; if ( radID < 0 && event[iRad].colType() == -1) { acl = event[EmtTag].acol(); col = event[iRad].acol(); } else if ( event[iRad].colType() == 1 ) { col = event[EmtTag].col(); acl = event[iRad].col(); } // Recoiler int iRec = 0; // Colour partner int iPartner = 0; if (col > 0) { // Find recoiler by colour iRec = FindCol(col,iRad,EmtTag,event,1,true); // In initial state splitting has final state colour partner, // Save both partner and recoiler if ( (sign < 0) && (event[iRec].isFinal()) ) { // Save colour recoiler iPartner = iRec; // Reset kinematic recoiler to initial state parton for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) iRec = posInitPartn[l]; // For final state splittings, colour partner and recoiler are // identical } else { iPartner = iRec; } // Not interested in pure QCD triples here. if ( !isSQCDrad && !isSQCDemt && (event[iRec].idAbs() < 10 || event[iRec].id() == 21) ) iRec = 0; if ( iRec != 0 && iPartner != 0 && allowedClustering( iRad, EmtTag, iRec, iPartner, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iPartner, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } // Reset partner iPartner = 0; // Find recoiler by colour iRec = FindCol(col,iRad,EmtTag,event,2,true); // In initial state splitting has final state colour partner, // Save both partner and recoiler if ( (sign < 0) && (event[iRec].isFinal()) ) { // Save colour recoiler iPartner = iRec; // Reset kinematic recoiler to initial state parton for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) iRec = posInitPartn[l]; // For final state splittings, colour partner and recoiler are // identical } else { iPartner = iRec; } // Not interested in pure QCD triples here. if ( !isSQCDrad && !isSQCDemt && (event[iRec].idAbs() < 10 || event[iRec].id() == 21) ) iRec = 0; if ( iRec != 0 && iPartner != 0 && allowedClustering( iRad, EmtTag, iRec, iPartner, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iPartner, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } } if (acl > 0) { // Reset partner iPartner = 0; // Find recoiler by colour iRec = FindCol(acl,iRad,EmtTag,event,1,true); // In initial state splitting has final state colour partner, // Save both partner and recoiler if ( (sign < 0) && (event[iRec].isFinal()) ) { // Save colour recoiler iPartner = iRec; // Reset kinematic recoiler to initial state parton for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) iRec = posInitPartn[l]; // For final state splittings, colour partner and recoiler are // identical } else { iPartner = iRec; } // Not interested in pure QCD triples here. if ( !isSQCDrad && !isSQCDemt && (event[iRec].idAbs() < 10 || event[iRec].id() == 21) ) iRec = 0; if ( iRec != 0 && iPartner != 0 && allowedClustering( iRad, EmtTag, iRec, iPartner, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iPartner, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } // Reset partner iPartner = 0; // Find recoiler by colour iRec = FindCol(acl,iRad,EmtTag,event,2,true); // In initial state splitting has final state colour partner, // Save both partner and recoiler if ( (sign < 0) && (event[iRec].isFinal()) ) { // Save colour recoiler iPartner = iRec; // Reset kinematic recoiler to initial state parton for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) iRec = posInitPartn[l]; // For final state splittings, colour partner and recoiler are // identical } else { iPartner = iRec; } // Not interested in pure QCD triples here. if ( !isSQCDrad && !isSQCDemt && (event[iRec].idAbs() < 10 || event[iRec].id() == 21) ) iRec = 0; if ( iRec != 0 && iPartner != 0 && allowedClustering( iRad, EmtTag, iRec, iPartner, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iPartner, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } } // Initial gluon splitting } else if ( initialSplitting ) { // SM splittings already taken care of in findQCDTriple. if ( !isSQCDrad && !isSQCDemt ) continue; // For an initial state radiator, always set recoiler // to the other initial state parton (recoil is taken // by full remaining system, so this is just a // labelling for such a process) int RecInit = 0; for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) RecInit = posInitPartn[l]; // Find the colour connected partner // Find colour index of radiator before splitting int col = getRadBeforeCol(iRad, EmtTag, event); int acl = getRadBeforeAcol(iRad, EmtTag, event); // Find the correct partner: If a colour line has split, // the partner is connected to the radiator before the splitting // by a colour line (same reasoning for anticolour). The colour // that split is the colour appearing twice in the // radiator + emitted pair. // Thus, if we remove a colour index with the clustering, // we should look for a colour partner, else look for // an anticolour partner int colRemove = (event[iRad].col() == event[EmtTag].col()) ? event[iRad].col() : 0; int iPartner = 0; if (colRemove > 0 && col > 0) iPartner = FindCol(col,iRad,EmtTag,event,1,true) + FindCol(col,iRad,EmtTag,event,2,true); else if (colRemove > 0 && acl > 0) iPartner = FindCol(acl,iRad,EmtTag,event,1,true) + FindCol(acl,iRad,EmtTag,event,2,true); if ( allowedClustering( iRad, EmtTag, RecInit, iPartner, event ) ) { attachClusterings (clus, EmtTag, iRad, RecInit, iPartner, pTLund(event, iRad, EmtTag, RecInit, pTdef), event); continue; } } // Second colour topology: Gluino emission } else { if ( (event[iRad].col() == event[EmtTag].acol()) || (event[iRad].acol() == event[EmtTag].col()) || (event[iRad].col() == event[EmtTag].col()) || (event[iRad].acol() == event[EmtTag].acol()) ) { // For the rest, choose recoiler to have a common colour // tag with radiator, while not being the "Emitted" int col = -1; int acl = -1; if (event[iRad].isFinal() ) { if ( radID < 0 && event[iRad].colType() == -1) { acl = event[EmtTag].acol(); col = event[iRad].col(); } else if ( radID > 0 && event[iRad].colType() == 1 ) { col = event[EmtTag].col(); acl = event[iRad].acol(); } else { col = event[iRad].col(); acl = event[iRad].acol(); } int iRec = 0; if (col > 0) { iRec = FindCol(col,iRad,EmtTag,event,1,true); if ( (sign < 0) && (event[iRec].isFinal()) ) iRec = 0; // Not interested in pure QCD triples here. if ( !isSQCDrad && !isSQCDemt && (event[iRec].idAbs() < 10 || event[iRec].id() == 21) ) iRec = 0; if (iRec != 0 && allowedClustering( iRad, EmtTag, iRec, iRec, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iRec, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } iRec = FindCol(col,iRad,EmtTag,event,2,true); if ( (sign < 0) && (event[iRec].isFinal()) ) iRec = 0; // Not interested in pure QCD triples here. if ( !isSQCDrad && !isSQCDemt && (event[iRec].idAbs() < 10 || event[iRec].id() == 21) ) iRec = 0; if (iRec != 0 && allowedClustering( iRad, EmtTag, iRec, iRec, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iRec, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } } if (acl > 0) { iRec = FindCol(acl,iRad,EmtTag,event,1,true); if ( (sign < 0) && (event[iRec].isFinal()) ) iRec = 0; // Not interested in pure QCD triples here. if ( !isSQCDrad && !isSQCDemt && (event[iRec].idAbs() < 10 || event[iRec].id() == 21) ) iRec = 0; if (iRec != 0 && allowedClustering( iRad, EmtTag, iRec, iRec, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iRec, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } iRec = FindCol(acl,iRad,EmtTag,event,2,true); if ( (sign < 0) && (event[iRec].isFinal()) ) iRec = 0; // Not interested in pure QCD triples here. if ( !isSQCDrad && !isSQCDemt && (event[iRec].idAbs() < 10 || event[iRec].id() == 21) ) iRec = 0; if (iRec != 0 && allowedClustering( iRad, EmtTag, iRec, iRec, event) ) { attachClusterings (clus, EmtTag, iRad, iRec, iRec, pTLund(event, iRad, EmtTag, iRec, pTdef), event); continue; } } } else { // SM splittings already taken care of in findQCDTriple. Since // initial state splittings will not know if the true // colour-connected recoiler is a SM particle, any ISR splitting // of a SM particle will be included by findQCDTriple. To not // include the same splitting twice, continue for SM ISR radiator if ( !isSQCDrad || !isSQCDemt ) continue; // For an initial state radiator, always set recoiler // to the other initial state parton (recoil is taken // by full remaining system, so this is just a // labelling for such a process) int RecInit = 0; int iPartner = 0; for(int l = 0; l < int(posInitPartn.size()); ++l) if (posInitPartn[l] != iRad) RecInit = posInitPartn[l]; // Find the colour connected partner // Find colour index of radiator before splitting col = getRadBeforeCol(iRad, EmtTag, event); acl = getRadBeforeAcol(iRad, EmtTag, event); // Find the correct partner: If a colour line has split, // the partner is connected to the radiator before the splitting // by a colour line (same reasoning for anticolour). The colour // that split is the colour appearing twice in the // radiator + emitted pair. // Thus, if we remove a colour index with the clustering, // we should look for a colour partner, else look for // an anticolour partner int colRemove = (event[iRad].col() == event[EmtTag].col()) ? event[iRad].col() : 0; iPartner = (colRemove > 0) ? FindCol(col,iRad,EmtTag,event,1,true) + FindCol(col,iRad,EmtTag,event,2,true) : FindCol(acl,iRad,EmtTag,event,1,true) + FindCol(acl,iRad,EmtTag,event,2,true); if ( allowedClustering( iRad, EmtTag, RecInit, iPartner, event)) { attachClusterings (clus, EmtTag, iRad, RecInit, iPartner, pTLund(event, iRad, EmtTag, RecInit, pTdef), event); continue; } } } } } } // Done return clus; } //-------------------------------------------------------------------------- // Calculate and return the probability of a clustering. // IN Clustering : rad,rec,emt - System for which the splitting // probability should be calcuated // OUT splitting probability double History::getProb(const Clustering & SystemIn) { // Get local copies of input system int Rad = SystemIn.emittor; int Rec = SystemIn.recoiler; int Emt = SystemIn.emitted; // Initialise shower probability double showerProb = 0.0; // If the splitting resulted in disallowed evolution variable, // disallow the splitting if (SystemIn.pT() <= 0.) return 0.; // Initialise all combinatorical factors double CF = 4./3.; double CA = 3.; // Flavour is known when reclustring, thus n_f=1 double TR = 1./2.; // Split up in FSR and ISR bool isFSR = (state[Rad].isFinal() && state[Rec].isFinal()); bool isFSRinREC = (state[Rad].isFinal() && !state[Rec].isFinal()); bool isISR = !state[Rad].isFinal(); // Check if external splitting probability should be used. if ( mergingHooksPtr->useShowerPlugin() ) { int iPartner = (isISR && SystemIn.partner > 0) ? SystemIn.partner : Rec; double pr = 0.; bool isFSR2 = showers->timesPtr->isTimelike(state, Rad, Emt, iPartner, ""); if (isFSR2) { string name = showers->timesPtr->getSplittingName( state, Rad, Emt, iPartner).front(); pr = showers->timesPtr->getSplittingProb( state, Rad, Emt, iPartner, name); } else { string name = showers->spacePtr->getSplittingName(state, Rad, Emt, iPartner).front(); pr = showers->spacePtr->getSplittingProb(state, Rad, Emt, iPartner, name); } return abs(pr); } // Check if this is the clustering 2->3 to 2->2. // If so, use weight for joined evolution int nFinal = 0; for(int i=0; i < state.size(); ++i) if (state[i].isFinal()) nFinal++; bool isLast = (nFinal == (mergingHooksPtr->hardProcess->nQuarksOut() +mergingHooksPtr->hardProcess->nLeptonOut()+1)); // Do not calculate splitting functions for electroweak emissions bool isElectroWeak = (state[Emt].idAbs() == 23 || state[Emt].idAbs() == 24); if (isISR) { // Find incoming particles int inP = 0; int inM = 0; for(int i=0;i< int(state.size()); ++i) { if (state[i].mother1() == 1) inP = i; if (state[i].mother1() == 2) inM = i; } // Construct dipole mass, eCM and sHat = x1*x2*s Vec4 sum = state[Rad].p() + state[Rec].p() - state[Emt].p(); double m2Dip = sum.m2Calc(); double sHat = (state[inM].p() + state[inP].p()).m2Calc(); // Energy fraction z=E_q1/E_qi in branch q(i)q(2) -> q(1)g(3)q(2) double z1 = m2Dip / sHat; // Virtuality of the splittings Vec4 Q1( state[Rad].p() - state[Emt].p() ); Vec4 Q2( state[Rec].p() - state[Emt].p() ); // Q^2 for emission off radiator line double Q1sq = -Q1.m2Calc(); // pT^2 for emission off radiator line double pT1sq = pow2(pTLund(state, Rad, Emt, Rec, -1, SystemIn.flavRadBef)); // Remember if massive particles involved: Mass corrections for // to g->QQ and Q->Qg splittings bool g2QQmassive = mergingHooksPtr->includeMassive() && state[Rad].id() == 21 && ( (state[Emt].idAbs() >= 4 && state[Emt].idAbs() < 7) || (state[Emt].idAbs() > 1000000 && state[Emt].idAbs() < 1000010 ) || (state[Emt].idAbs() > 2000000 && state[Emt].idAbs() < 2000010 )); bool Q2Qgmassive = mergingHooksPtr->includeMassive() && state[Emt].id() == 21 && ( (state[Rad].idAbs() >= 4 && state[Rad].idAbs() < 7) || (state[Rad].idAbs() > 1000000 && state[Rad].idAbs() < 1000010 ) || (state[Rad].idAbs() > 2000000 && state[Rad].idAbs() < 2000010 )); bool isMassive = mergingHooksPtr->includeMassive() && ( g2QQmassive || Q2Qgmassive || state[Emt].id() == 1000021); double m2Emt0 = state[Emt].p().m2Calc(); double m2Rad0 = pow(particleDataPtr->m0(state[Rad].id()),2); // Correction of virtuality for massive splittings if ( g2QQmassive) Q1sq += m2Emt0; else if (Q2Qgmassive) Q1sq += m2Rad0; // pT0 dependence!!! double pT0sq = pow(mergingHooksPtr->pT0ISR(),2); double Q2sq = -Q2.m2Calc(); // Correction of virtuality of other splitting bool g2QQmassiveRec = mergingHooksPtr->includeMassive() && state[Rec].id() == 21 && ( state[Emt].idAbs() >= 4 && state[Emt].idAbs() < 7); bool Q2QgmassiveRec = mergingHooksPtr->includeMassive() && state[Emt].id() == 21 && ( state[Rec].idAbs() >= 4 && state[Rec].idAbs() < 7); double m2Rec0 = pow(particleDataPtr->m0(state[Rec].id()),2); if ( g2QQmassiveRec) Q2sq += m2Emt0; else if (Q2QgmassiveRec) Q2sq += m2Rec0; bool hasJoinedEvol = (state[Emt].id() == 21 || state[Rad].id() == state[Rec].id()); // Initialise normalization factor multiplying the splitting // function numerator double fac = 1.; if ( mergingHooksPtr->pickByFull() || mergingHooksPtr->pickBySumPT()) { double facJoined = ( Q2sq + pT0sq/(1.-z1) ) * 1./(Q1sq*Q2sq + pT0sq*sHat + pow(pT0sq/(1.-z1),2)); double facSingle = mergingHooksPtr->nonJoinedNorm()*1./( pT1sq + pT0sq); fac = (hasJoinedEvol && isLast) ? facJoined : facSingle; } else if (mergingHooksPtr->pickByPoPT2()) { fac = 1./(pT1sq + pT0sq); } else { string message="Error in History::getProb: Scheme for calculating"; message+=" shower splitting probability is undefined."; infoPtr->errorMsg(message); } // Calculate shower splitting probability: // Splitting functions*normalization*ME reweighting factors if ( isElectroWeak ) { // For electroweak splittings, the probabilities depend on the // full shower history, and can hence not be calculated correctly // here. Thus, use dummy value and "dress" with full probabilities // later, once the path is known. showerProb = 1.; // Calculate branching probability for q -> q g } else if ( state[Emt].id() == 21 && state[Rad].id() != 21) { // Find splitting kernel double num = CF*(1. + pow(z1,2)) / (1.-z1); if (isMassive) num -= CF * z1 * (1.-z1) * (m2Rad0/pT1sq); // Find ME reweighting factor double meReweighting = 1.; // Find the number of final state coloured particles, apart // from those coming from the hard process int nCol = 0; for(int i=0; i < state.size(); ++i) if (state[i].isFinal() && state[i].colType() != 0 && !mergingHooksPtr->hardProcess->matchesAnyOutgoing(i,state) ) nCol++; // For first splitting of single vector boson production, // apply ME corrections if (nCol == 1 && int(mergingHooksPtr->hardProcess->hardIntermediate.size()) == 1) { double sH = m2Dip / z1; double tH = -Q1sq; double uH = Q1sq - m2Dip * (1. - z1) / z1; double misMatch = (uH*tH - (uH + tH)*pT0sq/(1.-z1) + pow(pT0sq/(1.-z1),2) ) / (uH*tH); meReweighting *= (tH*tH + uH*uH + 2. * m2Dip * sH) / (sH*sH + m2Dip*m2Dip); meReweighting *= misMatch; } // Multiply factors showerProb = num*fac*meReweighting; // Calculate branching probability for g -> g g } else if ( state[Emt].id() == 21 && state[Rad].id() == 21) { // Calculate splitting kernel double num = 2.*CA*pow2(1. - z1*(1.-z1)) / (z1*(1.-z1)); // Include ME reweighting for higgs!! // Find ME reweighting factor double meReweighting = 1.; // Find the number of final state coloured particles, apart // from those coming from the hard process int nCol = 0; for(int i=0; i < state.size(); ++i) if (state[i].isFinal() && state[i].colType() != 0 && !mergingHooksPtr->hardProcess->matchesAnyOutgoing(i,state) ) nCol++; // For first splitting of single vector boson production, // apply ME corrections if ( nCol == 1 && mergingHooksPtr->getProcessString().compare("pp>h") == 0 && int(mergingHooksPtr->hardProcess->hardIntermediate.size()) == 1 ) { double sH = m2Dip / z1; double tH = -Q1sq; double uH = Q1sq - m2Dip * (1. - z1) / z1; meReweighting *= 0.5 * (pow4(sH) + pow4(tH) + pow4(uH) + pow4(m2Dip)) / pow2(sH*sH - m2Dip * (sH - m2Dip)); } // Multiply factors showerProb = num*fac*meReweighting; // Calculate branching probability for q -> g q } else if ( state[Emt].id() != 21 && state[Rad].id() != 21 ) { // Calculate splitting kernel double num = CF*(1. + pow2(1.-z1)) / z1; // Include ME reweighting for higgs!! // Find ME reweighting factor double meReweighting = 1.; // Find the number of final state coloured particles, apart // from those coming from the hard process int nCol = 0; for ( int i=0; i < state.size(); ++i ) if ( state[i].isFinal() && state[i].colType() != 0 && !mergingHooksPtr->hardProcess->matchesAnyOutgoing(i,state) ) nCol++; // For first splitting of single vector boson production, // apply ME corrections if (nCol == 1 && mergingHooksPtr->getProcessString().compare("pp>h") == 0 && int(mergingHooksPtr->hardProcess->hardIntermediate.size()) == 1) { double sH = m2Dip / z1; double uH = Q1sq - m2Dip * (1. - z1) / z1; meReweighting *= (sH*sH + uH*uH) / (sH*sH + pow2(sH -m2Dip)); } // Multiply factors showerProb = num*fac*meReweighting; // Calculate branching probability for g -> q qbar } else if ( state[Emt].id() != 21 && state[Rad].id() == 21 ) { // Calculate splitting kernel double num = TR * ( pow(z1,2) + pow(1.-z1,2) ); if (isMassive) num += TR * 2.*z1*(1.-z1)*(m2Emt0/pT1sq); // Calculate ME reweighting factor double meReweighting = 1.; // Find the number of final state coloured particles, apart // from those coming from the hard process int nCol = 0; for ( int i=0; i < state.size(); ++i ) if ( state[i].isFinal() && state[i].colType() != 0 && !mergingHooksPtr->hardProcess->matchesAnyOutgoing(i,state) ) nCol++; // For first splitting of single vector boson production, // apply ME corrections if (nCol == 1 && int(mergingHooksPtr->hardProcess->hardIntermediate.size()) == 1) { double sH = m2Dip / z1; double tH = -Q1sq; double uH = Q1sq - m2Dip * (1. - z1) / z1; swap( tH, uH); double misMatch = ( uH - pT0sq/(1.-z1) ) / uH; double me = (sH*sH + uH*uH + 2. * m2Dip * tH) / (pow2(sH - m2Dip) + m2Dip*m2Dip); // Weight with me/overestimate meReweighting *= me; meReweighting *= misMatch; } // Multiply factors showerProb = num*fac*meReweighting; // Print error if no kernel calculated } else { string message = "Error in History::getProb: Splitting kernel" " undefined in ISR in clustering."; infoPtr->errorMsg(message); } // If corrected pT below zero in ISR, put probability to zero double m2Sister = pow(state[Emt].m(),2); double pT2corr = (Q1sq - z1*(m2Dip + Q1sq)*(Q1sq + m2Sister)/m2Dip); if (pT2corr < 0.) showerProb = 0.0; // If creating heavy quark by Q -> gQ then next need g -> Q + Qbar. // So minimum total mass2 is 4 * m2Sister, but use more to be safe. if ( state[Emt].id() == state[Rad].id() && ( state[Rad].idAbs() == 4 || state[Rad].idAbs() == 5 )) { double m2QQsister = 2.*4.*m2Sister; double pT2QQcorr = Q1sq - z1*(m2Dip + Q1sq)*(Q1sq + m2QQsister) / m2Dip; if (pT2QQcorr < 0.0) showerProb = 0.0; } // Check cuts on momentum fraction. double pT2minNow = mergingHooksPtr->pTcut(); double zMaxAbs = 1. - 0.5 * (pT2minNow / m2Dip) * ( sqrt( 1. + 4. * m2Dip / pT2minNow ) - 1. ); zMaxAbs = min(1.,zMaxAbs); double zMinAbs = max(0.,1. - zMaxAbs); // Massive z limit. int radBefID = getRadBeforeFlav(Rad, Emt, state); if ( abs(radBefID) == 4 || abs(radBefID) == 5 ) { double m2Massive = pow2(particleDataPtr->m0(radBefID)); double mRatio = sqrt( m2Massive / m2Dip ); double zMaxMassive = (1. - mRatio) / ( 1. + mRatio * (1. - mRatio) ); zMaxAbs = min(zMaxAbs, zMaxMassive); } if (z1 < zMinAbs || z1 > zMaxAbs) showerProb = 0.0; if (mergingHooksPtr->includeRedundant()) { // Initialise the spacelike shower alpha_S AlphaStrong* asISR = mergingHooksPtr->AlphaS_ISR(); double as = (*asISR).alphaS(pT1sq + pT0sq) / (2.*M_PI); // Multiply with alpha_S showerProb *= as; } // Done for ISR case, begin FSR case } else if (isFSR || isFSRinREC) { // Construct dipole mass int recSign = (state[Rec].isFinal()) ? 1 : -1; Vec4 sum = state[Rad].p() + recSign*state[Rec].p() + state[Emt].p(); double m2Dip = abs(sum.m2Calc()); // Virtuality of the splittings Vec4 Q1( state[Rad].p() + state[Emt].p() ); Vec4 Q2( state[Rec].p() + state[Emt].p() ); // Get z value. double z1 = getCurrentZ( Rad, Rec, Emt, SystemIn.flavRadBef); // Q^2 for emission off radiator line double Q1sq = Q1.m2Calc(); // pT^2 for emission off radiator line double pT1sq = pow(SystemIn.pT(),2); // Q^2 for emission off recoiler line double Q2sq = Q2.m2Calc(); // Remember if radiator or recoiler is massive. bool isMassiveRad = ( state[Rad].idAbs() >= 4 && state[Rad].id() != 21 ); bool isMassiveRec = ( state[Rec].idAbs() >= 4 && state[Rec].id() != 21 ); // Correction of virtuality for massive splittings. double m2Rad0 = pow(particleDataPtr->m0(state[Rad].id()),2); double m2Rec0 = pow(particleDataPtr->m0(state[Rec].id()),2); if ( mergingHooksPtr->includeMassive() && isMassiveRad ) Q1sq -= m2Rad0; if ( mergingHooksPtr->includeMassive() && isMassiveRec ) Q2sq -= m2Rec0; // Initialise normalization factor multiplying the splitting // function numerator double fac = 1.; if ( mergingHooksPtr->pickByFull() || mergingHooksPtr->pickBySumPT()) { double facJoined = (1.-z1)/Q1sq * m2Dip/( Q1sq + Q2sq ); double facSingle = mergingHooksPtr->fsrInRecNorm() * 1./ pT1sq; fac = (!isFSRinREC && isLast) ? facJoined : facSingle; } else if (mergingHooksPtr->pickByPoPT2()) { fac = 1. / pT1sq; } else { string message="Error in History::getProb: Scheme for calculating"; message+=" shower splitting probability is undefined."; infoPtr->errorMsg(message); } // Calculate shower splitting probability: // Splitting functions*normalization*ME reweighting factors if ( isElectroWeak ) { // For electroweak splittings, the probabilities depend on the // full shower history, and can hence not be calculated correctly // here. Thus, use dummy value and "dress" with full probabilities // later, once the path is known. showerProb = 1.; // Calculate branching probability for g -> g_1 g_2 } else if ( state[Emt].id() == 21 && state[Rad].colType() == 2) { // Calculate splitting kernel double num = 0.5* CA * (1. + pow3(z1)) / (1.-z1); // Multiply factors showerProb = num*fac; // Calculate branching probability for q -> q g with quark recoiler } else if ( state[Emt].id() == 21 && state[Rad].colType() != 2 && state[Rec].colType() != 2) { // For a qqbar dipole in FSR, ME corrections exist and the // splitting function "z-weight" is set to 1.0 (only for 2->2 ??) double num = CF * 2./(1.-z1); // Find the number of final state coloured particles, apart // from those coming from the hard process int nCol = 0; for(int i=0; i < state.size(); ++i) if (state[i].isFinal() && state[i].colType() != 0 && !mergingHooksPtr->hardProcess->matchesAnyOutgoing(i,state) ) nCol++; // Calculate splitting kernel if ( nCol > 3 || int(mergingHooksPtr->hardProcess->hardIntermediate.size()) > 1) num = CF * (1. + pow2(z1)) /(1.-z1); // Calculate ME reweighting factor double meReweighting = 1.; // Correct if this is the process created by the first // FSR splitting of a 2->2 process if ( nCol == 3 && int(mergingHooksPtr->hardProcess->hardIntermediate.size()) == 1 ) { // Construct 2->3 variables for FSR double x1 = 2. * (sum * state[Rad].p()) / m2Dip; double x2 = 2. * (sum * state[Rec].p()) / m2Dip; double prop1 = max(1e-12, 1. - x1); double prop2 = max(1e-12, 1. - x2); double x3 = max(1e-12, 2. - x1 - x2); // Calculate the ME reweighting factor double ShowerRate1 = 2./( x3 * prop2 ); double meDividingFactor1 = prop1 / x3; double me = (pow(x1,2) + pow(x2,2))/(prop1*prop2); meReweighting = meDividingFactor1 * me / ShowerRate1; } // Multiply factors showerProb = num*fac*meReweighting; // Calculate branching probability for q -> q g with gluon recoiler } else if ( state[Emt].id() == 21 && state[Rad].colType() != 2 && state[Rec].colType() == 2 ) { // For qg /qbarg dipoles, the splitting function is // calculated and not weighted by a ME correction factor // Shower splitting function double num = CF * (1. + pow2(z1)) / (1.-z1); showerProb = num*fac; // Calculate branching probability for g -> q qbar } else if ( state[Emt].id() != 21 ) { // Get flavour of quark / antiquark int flavour = state[Emt].id(); // Get correct masses for the quarks // (needed to calculate splitting function?) double mFlavour = particleDataPtr->m0(flavour); // Get mass of quark/antiquark pair double mDipole = m(state[Rad].p(), state[Emt].p()); // Factor determining if gluon decay was allowed double beta = sqrtpos( 1. - 4.*pow2(mFlavour)/pow2(mDipole) ); // Shower splitting function double num = 0.5*TR * ( z1*z1 + (1.-z1)*(1.-z1) ); if (beta <= 0.) beta = 0.; showerProb = num*fac*beta; // Print error if no kernel calculated } else { string message="Error in History::getProb: Splitting kernel undefined"; message+=" in FSR clustering."; infoPtr->errorMsg(message); } if (mergingHooksPtr->includeRedundant()) { // Initialise the spacelike shower alpha_S AlphaStrong* asFSR = mergingHooksPtr->AlphaS_FSR(); double as = (*asFSR).alphaS(pT1sq) / (2.*M_PI); // Multiply with alpha_S showerProb *= as; } double m2DipCorr = pow2(sqrt(m2Dip) - sqrt(m2Rec0)) - m2Rad0; double zMin = 0.5 - sqrtpos( 0.25 - pT1sq / m2DipCorr ); double m2 = m2Rad0 + 2. * state[Rad].p()*state[Emt].p(); bool keepMassive = (z1 > zMin && z1 < 1. - zMin && m2 * m2Dip < z1 * (1. - z1) * pow2(m2Dip + m2 - m2Rec0) ); // No emission probability outside disallowed z range. if (!keepMassive) showerProb *= 0.0; // Done for FSR } else { string message="Error in History::getProb: Radiation could not be"; message+=" interpreted as FSR or ISR."; infoPtr->errorMsg(message); } if (showerProb <= 0.) showerProb = 0.; if (mergingHooksPtr->doWeakClustering()) { // Every time we assign spin, equivalent QCD histories are counted twice, // due to distingushing between quark spins. // To remove this overcounting, multiply by QCD probabilities by 1/2, // for each quark spin that has been assigned. double factor = 1.; if (state[Rad].idAbs() < 10 && state[Rad].intPol() == 9 && SystemIn.spinRad != 9) factor *= 0.5; if (state[Emt].idAbs() < 10 && state[Emt].intPol() == 9 && SystemIn.spinEmt != 9) factor *= 0.5; if (state[Rec].idAbs() < 10 && state[Rec].intPol() == 9 && SystemIn.spinRec != 9) factor *= 0.5; if ( state[Emt].colType() != 0 ) { showerProb *= factor; } // The g -> q qbar splitting leads to two histories, not four. // The previous clustering steps overcounted the number of histories to be // four, leading to a too small probability of a path by 1/2. Correct! if ( state[Emt].idAbs() < 10 && state[Rad].colType() != 0) { showerProb *= 2.0; } } // Done return showerProb; } //-------------------------------------------------------------------------- // Set up the beams (fill the beam particles with the correct // current incoming particles) to allow calculation of splitting // probability. // For interleaved evolution, set assignments dividing PDFs into // sea and valence content. This assignment is, until a history path // is chosen and a first trial shower performed, not fully correct // (since content is chosen form too high x and too low scale). The // assignment used for reweighting will be corrected after trial // showering void History::setupBeams() { // Do nothing for empty event, possible if sequence of // clusterings was ill-advised in that it results in // colour-disconnected states if (state.size() < 4) return; // Do nothing for e+e- beams if ( state[3].colType() == 0 ) return; if ( state[4].colType() == 0 ) return; // Incoming partons to hard process are stored in slots 3 and 4. int inS = 0; int inP = 0; int inM = 0; for(int i=0;i< int(state.size()); ++i) { if (state[i].mother1() == 1) inP = i; if (state[i].mother1() == 2) inM = i; } // Save some info before clearing beams // Mothers of incoming partons companion code int motherPcompRes = -1; int motherMcompRes = -1; bool sameFlavP = false; bool sameFlavM = false; if (mother) { int inMotherP = 0; int inMotherM = 0; for(int i=0;i< int(mother->state.size()); ++i) { if (mother->state[i].mother1() == 1) inMotherP = i; if (mother->state[i].mother1() == 2) inMotherM = i; } sameFlavP = (state[inP].id() == mother->state[inMotherP].id()); sameFlavM = (state[inM].id() == mother->state[inMotherM].id()); motherPcompRes = (sameFlavP) ? beamA[0].companion() : -2; motherMcompRes = (sameFlavM) ? beamB[0].companion() : -2; } // Append the current incoming particles to the beam beamA.clear(); beamB.clear(); // Get energy of incoming particles double Ep = 2. * state[inP].e(); double Em = 2. * state[inM].e(); // If incoming partons are massive then recalculate to put them massless. if (state[inP].m() != 0. || state[inM].m() != 0.) { Ep = state[inP].pPos() + state[inM].pPos(); Em = state[inP].pNeg() + state[inM].pNeg(); } // Add incoming hard-scattering partons to list in beam remnants. double x1 = Ep / state[inS].m(); beamA.append( inP, state[inP].id(), x1); double x2 = Em / state[inS].m(); beamB.append( inM, state[inM].id(), x2); // Scale. For ME multiplicity history, put scale to mu_F // (since sea/valence quark content is chosen from this scale) double scalePDF = (mother) ? scale : infoPtr->QFac(); // Find whether incoming partons are valence or sea. Store. // Can I do better, e.g. by setting the scale to the hard process // scale (= M_W) or by replacing one of the x values by some x/z?? beamA.xfISR( 0, state[inP].id(), x1, scalePDF*scalePDF); if (!mother) { beamA.pickValSeaComp(); } else { beamA[0].companion(motherPcompRes); } beamB.xfISR( 0, state[inM].id(), x2, scalePDF*scalePDF); if (!mother) { beamB.pickValSeaComp(); } else { beamB[0].companion(motherMcompRes); } } //-------------------------------------------------------------------------- // Calculate the PDF ratio used in the argument of the no-emission // probability double History::pdfForSudakov() { // Do nothing for e+e- beams if ( state[3].colType() == 0 ) return 1.0; if ( state[4].colType() == 0 ) return 1.0; // Check if splittings was ISR or FSR bool FSR = ( mother->state[clusterIn.emittor].isFinal() && mother->state[clusterIn.recoiler].isFinal()); bool FSRinRec = ( mother->state[clusterIn.emittor].isFinal() && !mother->state[clusterIn.recoiler].isFinal()); // Done for pure FSR if (FSR) return 1.0; int iInMother = (FSRinRec)? clusterIn.recoiler : clusterIn.emittor; // Find side of event that was reclustered int side = ( mother->state[iInMother].pz() > 0 ) ? 1 : -1; int inP = 0; int inM = 0; for(int i=0;i< int(state.size()); ++i) { if (state[i].mother1() == 1) inP = i; if (state[i].mother1() == 2) inM = i; } // Save mother id int idMother = mother->state[iInMother].id(); // Find daughter position and id int iDau = (side == 1) ? inP : inM; int idDaughter = state[iDau].id(); // Get mother x value double xMother = 2. * mother->state[iInMother].e() / mother->state[0].e(); // Get daughter x value of daughter double xDaughter = 2.*state[iDau].e() / state[0].e(); // x1 before isr // Calculate pdf ratio double ratio = getPDFratio(side, true, false, idMother, xMother, scale, idDaughter, xDaughter, scale); // For FSR with incoming recoiler, maximally return 1.0, as // is done in Pythia::TimeShower. // For ISR, return ratio return ( (FSRinRec)? min(1.,ratio) : ratio); } //-------------------------------------------------------------------------- // Calculate the hard process matrix element to include in the selection // probability. double History::hardProcessME( const Event& event ) { // Calculate prob for Drell-Yan process. if (isEW2to1(event)) { // qqbar -> W. if (event[5].idAbs() == 24) { int idIn1 = event[3].id(); int idIn2 = event[4].id(); double mW = particleDataPtr->m0(24); double gW = particleDataPtr->mWidth(24) / mW; double sH = (event[3].p()+event[4].p()).m2Calc(); double thetaWRat = 1. / (12. * coupSMPtr->sin2thetaW()); double ckmW = coupSMPtr->V2CKMid(abs(idIn1), abs(idIn2)); double bwW = 12. * M_PI / ( pow2(sH - pow2(mW)) + pow2(sH * gW) ); double preFac = thetaWRat * sqrt(sH) * particleDataPtr->mWidth(24); return ckmW * preFac * bwW; } // qqbar -> Z. No interference with gamma included. else if (event[5].idAbs() == 23) { double mZ = particleDataPtr->m0(23); double gZ = particleDataPtr->mWidth(23) / mZ; double sH = (event[3].p()+event[4].p()).m2Calc(); double thetaZRat = (pow2(coupSMPtr->rf( abs(clusterIn.flavRadBef))) + pow2(coupSMPtr->lf( abs(clusterIn.flavRadBef)))) / (24. * coupSMPtr->sin2thetaW() * coupSMPtr->cos2thetaW()); double bwW = 12. * M_PI / ( pow2(sH - pow2(mZ)) + pow2(sH * gZ) ); double preFac = thetaZRat * sqrt(sH) * particleDataPtr->mWidth(23); return preFac * bwW; } else { string message="Warning in History::hardProcessME: Only Z/W are"; message+=" supported as 2->1 processes. Skipping history."; infoPtr->errorMsg(message); return 0; } } // 2 to 2 process, assume QCD. else if (isQCD2to2(event)) { int idIn1 = event[3].id(); int idIn2 = event[4].id(); int idOut1 = event[5].id(); int idOut2 = event[6].id(); double sH = (event[3].p()+event[4].p()).m2Calc(); double tH = (event[3].p()-event[5].p()).m2Calc(); double uH = (event[3].p()-event[6].p()).m2Calc(); // Verify that it is QCD. bool isQCD = true; if (!(abs(idIn1) < 10 || abs(idIn1) == 21) ) isQCD = false; if (!(abs(idIn2) < 10 || abs(idIn2) == 21) ) isQCD = false; if (!(abs(idOut1) < 10 || abs(idOut1) == 21) ) isQCD = false; if (!(abs(idOut2) < 10 || abs(idOut2) == 21) ) isQCD = false; // Overall phase-space constant (dsigma/dcos(theta)). double cor = M_PI / (9. * pow2(sH)); // If it is QCD calculate cross section. if (isQCD) { // Find out which 2->2 process it is. // incoming gluon pair. if (abs(idIn1) == 21 && abs(idIn2) == 21) { if (abs(idOut1) == 21 && abs(idOut2) == 21) return cor * weakShowerMEs.getMEgg2gg(sH, tH, uH); else return cor * weakShowerMEs.getMEgg2qqbar(sH, tH, uH); // Incoming single gluon } else if (abs(idIn1) == 21 || abs(idIn2) == 21) { if (idIn1 != idOut1) swap(uH, tH); return cor * weakShowerMEs.getMEqg2qg(sH, tH, uH); } // Incoming quarks else { if (abs(idOut1) == 21 && abs(idOut2) == 21) return cor * weakShowerMEs.getMEqqbar2gg(sH, tH, uH); if (idIn1 == -idIn2) { if (abs(idIn1) == abs(idOut1)) { if (idIn1 != idOut1) swap(uH, tH); return cor * weakShowerMEs.getMEqqbar2qqbar(sH, tH, uH, true); } else { return cor * weakShowerMEs.getMEqqbar2qqbar(sH, tH, uH, false); } } else if (idIn1 == idIn2) return cor * weakShowerMEs.getMEqq2qq(sH, tH, uH, true); else { if (idIn1 == idOut1) swap(uH,tH); return cor * weakShowerMEs.getMEqq2qq(sH, tH, uH, false); } } } } // Get hard process. string process = mergingHooksPtr->getProcessString(); double result = 1.; if ( process.compare("pp>e+ve") == 0 || process.compare("pp>e-ve~") == 0 || process.compare("pp>LEPTONS,NEUTRINOS") == 0 ) { // Do nothing for incomplete process. int nFinal = 0; for ( int i=0; i < int(event.size()); ++i ) if ( event[i].isFinal() ) nFinal++; if ( nFinal != 2 ) return 1.; // Get W-boson mass and width. double mW = particleDataPtr->m0(24); double gW = particleDataPtr->mWidth(24) / mW; // Get incoming particles. int inP = (event[3].pz() > 0) ? 3 : 4; int inM = (event[3].pz() > 0) ? 4 : 3; // Get outgoing particles. int outP = 0; for ( int i=0; i < int(event.size()); ++i ) { if ( event[i].isFinal() && event[i].px() > 0 ) outP = i; } // Get Mandelstam variables. double sH = (event[inP].p() + event[inM].p()).m2Calc(); double tH = (event[inP].p() - event[outP].p()).m2Calc(); double uH = - sH - tH; // Return kinematic part of matrix element. result = ( 1. + (tH - uH)/sH ) / ( pow2(sH - mW*mW) + pow2(sH*gW) ); } else result = mergingHooksPtr->hardProcessME(event); return result; } //-------------------------------------------------------------------------- // Perform the clustering of the current state and return the // clustered state. // IN Clustering : rad,rec,emt triple to be clustered to two partons // OUT clustered state Event History::cluster( Clustering & inSystem ) { // Initialise tags of particles to be changed int Rad = inSystem.emittor; int Rec = inSystem.recoiler; int Emt = inSystem.emitted; // Initialise eCM,mHat double eCM = state[0].e(); // Flags for type of radiation int radType = state[Rad].isFinal() ? 1 : -1; int recType = state[Rec].isFinal() ? 1 : -1; // Construct the clustered event Event NewEvent = Event(); NewEvent.init("(hard process-modified)", particleDataPtr); NewEvent.clear(); // Check if external clustering should be used. if ( mergingHooksPtr->useShowerPlugin() ) { int iPartner = (radType == -1 && inSystem.partner > 0) ? inSystem.partner : Rec; bool isFSR = showers->timesPtr->isTimelike(state, Rad, Emt, iPartner, ""); if (isFSR) { string name = showers->timesPtr->getSplittingName(state,Rad,Emt, iPartner).front(); return showers->timesPtr->clustered(state,Rad,Emt,iPartner,name); } else { string name = showers->spacePtr->getSplittingName(state,Rad,Emt, iPartner).front(); return showers->spacePtr->clustered(state,Rad,Emt,iPartner,name); } } // Copy all unchanged particles to NewEvent for (int i = 0; i < state.size(); ++i) if ( i != Rad && i != Rec && i != Emt ) NewEvent.append( state[i] ); // Copy all the junctions one by one for (int i = 0; i < state.sizeJunction(); ++i) NewEvent.appendJunction( state.getJunction(i) ); // Find an appropriate scale for the hard process double mu = choseHardScale(state); // Initialise scales for new event NewEvent.saveSize(); NewEvent.saveJunctionSize(); NewEvent.scale(mu); NewEvent.scaleSecond(mu); // Set properties of radiator/recoiler after the clustering // Recoiler properties will be unchanged Particle RecBefore = Particle( state[Rec] ); RecBefore.setEvtPtr(&NewEvent); RecBefore.daughters(0,0); RecBefore.pol(inSystem.spinRec); // Find flavour of radiator before splitting int radID = inSystem.flavRadBef; if (radID == 0) radID = getRadBeforeFlav(Rad, Emt, state); int recID = state[Rec].id(); Particle RadBefore = Particle( state[Rad] ); RadBefore.setEvtPtr(&NewEvent); RadBefore.id(radID); RadBefore.pol(inSystem.spinRadBef); RadBefore.daughters(0,0); // Put dummy values for colours RadBefore.cols(RecBefore.acol(),RecBefore.col()); // Reset status if the reclustered radiator is a resonance. if ( particleDataPtr->isResonance(radID) && radType == 1) RadBefore.status(state[Rad].status()); // Put mass for radiator and recoiler double radMass = particleDataPtr->m0(radID); double recMass = particleDataPtr->m0(recID); if (radType == 1 ) RadBefore.m(radMass); else RadBefore.m(0.0); if (recType == 1 ) RecBefore.m(recMass); else RecBefore.m(0.0); // Construct momenta and colours of clustered particles // ISR/FSR splittings are treated differently if ( radType + recType == 2 && state[Emt].idAbs() != 23 && state[Emt].idAbs() != 24) { // Clustering of final(rad)/final(rec) dipole splitting // Get eCM of (rad,rec,emt) triple Vec4 sum = state[Rad].p() + state[Rec].p() + state[Emt].p(); double eCMME = sum.mCalc(); // Define radiator and recoiler back-to-back in the dipole // rest frame [=(state[rad]+state[emt])+state[rec] rest frame] Vec4 Rad4mom; Vec4 Rec4mom; double mDsq = pow2(eCMME); // If possible, keep the invariant mass of the radiator. double mRsq = (radID == state[Rad].id() ) ? abs(state[Rad].p().m2Calc()) : pow2(particleDataPtr->m0(radID)); double mSsq = abs(state[Rec].p().m2Calc()); double a = 0.5*sqrt(mDsq); double b = 0.25*(mRsq - mSsq) / a; double c = sqrt(pow2(a) + pow2(b) - 2.*a*b - mSsq); Rad4mom.p( 0., 0., c, a+b); Rec4mom.p( 0., 0.,-c, a-b); // Find boost from Rad4mom+Rec4mom rest frame to event cm frame Vec4 old1 = Vec4(state[Rad].p() + state[Emt].p()); Vec4 old2 = Vec4(state[Rec].p()); RotBstMatrix fromCM; fromCM.fromCMframe(old1, old2); // Transform momenta Rad4mom.rotbst(fromCM); Rec4mom.rotbst(fromCM); RadBefore.p(Rad4mom); RecBefore.p(Rec4mom); RadBefore.m(sqrt(mRsq)); RecBefore.m(sqrt(mSsq)); } else if ( radType + recType == 2 && (state[Emt].idAbs() == 23 || state[Emt].idAbs() == 24) ) { // Clustering of final(rad)/final(rec) dipole splitting // Get eCM of (rad,rec,emt) triple Vec4 sum = state[Rad].p() + state[Rec].p() + state[Emt].p(); double eCMME = sum.mCalc(); // Define radiator and recoiler back-to-back in the dipole // rest frame [=(state[rad]+state[emt])+state[rec] rest frame] Vec4 Rad4mom; Vec4 Rec4mom; double mDsq = pow2(eCMME); // If possible, keep the invariant mass of the radiator. double mRsq = (radID == state[Rad].id() ) ? abs(state[Rad].p().m2Calc()) : pow2(particleDataPtr->m0(radID)); double mSsq = abs(state[Rec].p().m2Calc()); double a = 0.5*sqrt(mDsq); double b = 0.25*(mRsq - mSsq) / a; double c = sqrt(pow2(a) + pow2(b) - 2.*a*b - mSsq); Rad4mom.p( 0., 0., c, a+b); Rec4mom.p( 0., 0.,-c, a-b); // Find boost from Rad4mom+Rec4mom rest frame to event cm frame Vec4 old1 = Vec4(state[Rad].p() + state[Emt].p()); Vec4 old2 = Vec4(state[Rec].p()); RotBstMatrix fromCM; fromCM.fromCMframe(old1, old2); // Transform momenta Rad4mom.rotbst(fromCM); Rec4mom.rotbst(fromCM); RadBefore.p(Rad4mom); RecBefore.p(Rec4mom); RadBefore.m(sqrt(mRsq)); RecBefore.m(sqrt(mSsq)); } else if ( radType + recType == 0 ) { // Clustering of final(rad)/initial(rec) dipole splitting // Get eCM of (rad,rec,emt) triple Vec4 sum = state[Rad].p() + state[Rec].p() + state[Emt].p(); double eCMME = sum.mCalc(); // Define radiator and recoiler back-to-back in the dipole // rest frame [=(state[rad]+state[emt])+state[rec] rest frame] Vec4 Rad4mom; Vec4 Rec4mom; double mDsq = pow2(eCMME); // If possible, keep the invariant mass of the radiator. double mRsq = (radID == state[Rad].id() ) ? abs(state[Rad].p().m2Calc()) : pow2(particleDataPtr->m0(radID)); double mSsq = abs(state[Rec].p().m2Calc()); double a = 0.5*sqrt(mDsq); double b = 0.25*(mRsq - mSsq) / a; double c = sqrt(pow2(a) + pow2(b) - 2.*a*b - mSsq); Rad4mom.p( 0., 0., c, a+b); Rec4mom.p( 0., 0.,-c, a-b); // Find boost from Rad4mom+Rec4mom rest frame to event cm frame Vec4 old1 = Vec4(state[Rad].p() + state[Emt].p()); Vec4 old2 = Vec4(state[Rec].p()); RotBstMatrix fromCM; fromCM.fromCMframe(old1, old2); // Transform momenta Rad4mom.rotbst(fromCM); Rec4mom.rotbst(fromCM); // Rescale recoiler momentum Rec4mom = 2.*state[Rec].p() - Rec4mom; // Ensure that recoiler is massless to // very good accuracy. if ( abs(Rec4mom.mCalc()) > 1e-7 ) { double pzSign = (Rec4mom.pz() > 0.) ? 1. : -1.; double eRec = Rec4mom.e(); Rec4mom.p(0., 0., pzSign*eRec, eRec); } RadBefore.p(Rad4mom); RecBefore.p(Rec4mom); RadBefore.m(sqrt(mRsq)); // Set mass of initial recoiler to zero RecBefore.m( 0.0 ); } else { // Clustering of initial(rad)/initial(rec) dipole splitting // We want to cluster: Meaning doing the inverse of a process // ( pDaughter + pRecoiler -> pOut ) // ==> ( pMother + pPartner -> pOut' + pSister ) // produced by an initial state splitting. The matrix element // provides us with pMother, pPartner, pSister and pOut' Vec4 pMother( state[Rad].p() ); Vec4 pSister( state[Emt].p() ); Vec4 pPartner( state[Rec].p() ); Vec4 pDaughterBef( 0.,0.,0.,0. ); Vec4 pRecoilerBef( 0.,0.,0.,0. ); Vec4 pDaughter( 0.,0.,0.,0. ); Vec4 pRecoiler( 0.,0.,0.,0. ); // Find side that radiates event (mother moving in sign * p_z direction). int sign = (state[Rad].pz() > 0.) ? 1 : -1; // Find rotation by phi that would have been done for a // splitting daughter -> mother + sister double phi = pSister.phi(); // Find rotation with -phi RotBstMatrix rot_by_mphi; rot_by_mphi.rot(0.,-phi); // Find rotation with +phi RotBstMatrix rot_by_pphi; rot_by_pphi.rot(0.,phi); // Get mother and partner x values // x1 after isr double x1 = 2. * pMother.e() / eCM; // x2 after isr double x2 = 2. * pPartner.e() / eCM; // Find z of the splitting Vec4 qDip( pMother - pSister); Vec4 qAfter(pMother + pPartner); Vec4 qBefore(qDip + pPartner); double z = qBefore.m2Calc() / qAfter.m2Calc(); // Calculate e_CM^2 before the splitting. double x1New = z*x1; // x1 before isr double x2New = x2; // x2 before isr double sHat = x1New*x2New*eCM*eCM; // Construct daughter and recoiler momenta before the splitting. // (Note: For final result, only needs to be boosted into // frame with unchanged "recoiler" momentum) pDaughterBef.p( 0., 0., sign*0.5*sqrt(sHat), 0.5*sqrt(sHat)); pRecoilerBef.p( 0., 0., -sign*0.5*sqrt(sHat), 0.5*sqrt(sHat)); // Rotate momenta defined in the lab frame by phi pMother.rotbst( rot_by_mphi ); pSister.rotbst( rot_by_mphi ); pPartner.rotbst( rot_by_mphi ); for(int i=3; i< NewEvent.size(); ++i) NewEvent[i].rotbst( rot_by_mphi ); // Find boost from lab frame to rest frame of // off-shell daughter + on-shell recoiler dipole pDaughter.p( pMother - pSister); pRecoiler.p( pPartner ); RotBstMatrix from_CM_to_DRoff; if (sign == 1) from_CM_to_DRoff.toCMframe(pDaughter, pRecoiler); else from_CM_to_DRoff.toCMframe(pRecoiler, pDaughter); // Rotate and boost all momenta to rest frame of off-shell daughter + // on-shell recoiler dipole pMother.rotbst( from_CM_to_DRoff ); pPartner.rotbst( from_CM_to_DRoff ); pSister.rotbst( from_CM_to_DRoff ); for(int i=3; i< NewEvent.size(); ++i) NewEvent[i].rotbst( from_CM_to_DRoff ); // Find longitudinal boost from on-shell daughter + on-shell recoiler // dipole rest frame to the frame in which the recoiler momentum (x-value) // does not change in the splitting process. RotBstMatrix from_DR_to_CM; from_DR_to_CM.bst( 0., 0., sign*( x1New - x2New ) / ( x1New + x2New ) ); // Boost all momenta into the "unchanged recoiler" frame, thereby // correcting for momentum mismatch by transferring the recoil to all // final state particles. pDaughterBef.rotbst( from_DR_to_CM ); pRecoilerBef.rotbst( from_DR_to_CM ); for(int i=3; i< NewEvent.size(); ++i) NewEvent[i].rotbst( from_DR_to_CM ); // Transform outgoing momenta for(int i=3; i< NewEvent.size(); ++i) NewEvent[i].rotbst( rot_by_pphi ); // Ensure that radiator and recoiler are massless to // very good accuracy. if ( abs(pRecoilerBef.mCalc()) > 1e-7 ) { double pzSign = (pRecoilerBef.pz() > 0.) ? 1. : -1.; double eRec = pRecoilerBef.e(); pRecoilerBef.p(0., 0., pzSign*eRec, eRec); } if ( abs(pDaughterBef.mCalc()) > 1e-7 ) { double pzSign = (pDaughterBef.pz() > 0.) ? 1. : -1.; double eDau = pDaughterBef.e(); pDaughterBef.p(0., 0., pzSign*eDau, eDau); } // Transform pMother and outgoing momenta // Set momenta of particles to be attached to new event record RecBefore.p( pRecoilerBef ); RadBefore.p( pDaughterBef ); if (RecBefore.pz() > 0.) RecBefore.mother1(1); else RecBefore.mother1(2); if (RadBefore.pz() > 0.) RadBefore.mother1(1); else RadBefore.mother1(2); } // Put some dummy production scales for RecBefore, RadBefore RecBefore.scale(mu); RadBefore.scale(mu); // Append new recoiler and find new radiator colour NewEvent.append(RecBefore); // Assign the correct colour to re-clustered radiator. // Keep old radiator colours for electroweak emission. int emtID = state[Emt].id(); if ( emtID == 22 || emtID == 23 || abs(emtID) == 24 ) RadBefore.cols( state[Rad].col(), state[Rad].acol() ); // For QCD, carefully construct colour. else if ( !connectRadiator( RadBefore, radType, RecBefore, recType, NewEvent ) ) { // Could happen if previous clustering produced several colour // singlett subsystems in the event NewEvent.reset(); return NewEvent; } // Build the clustered event Event outState = Event(); outState.init("(hard process-modified)", particleDataPtr); outState.clear(); // Copy system and incoming beam particles to outState for (int i = 0; i < 3; ++i) outState.append( NewEvent[i] ); // Copy all the junctions one by one for (int i = 0; i < state.sizeJunction(); ++i) outState.appendJunction( state.getJunction(i) ); // Initialise scales for new event outState.saveSize(); outState.saveJunctionSize(); outState.scale(mu); outState.scaleSecond(mu); bool radAppended = false; bool recAppended = false; int size = int(outState.size()); // Save position of radiator and recoiler in new event record. int radPos = 0, recPos = 0; // Append first incoming particle if ( RecBefore.mother1() == 1) { recPos = outState.append( RecBefore ); recAppended = true; } else if ( RadBefore.mother1() == 1 ) { radPos = outState.append( RadBefore ); radAppended = true; } else { // Find second incoming in input event int in1 = 0; for(int i=0; i < int(state.size()); ++i) if (state[i].mother1() == 1) in1 =i; outState.append( state[in1] ); size++; } // Append second incoming particle if ( RecBefore.mother1() == 2) { recPos = outState.append( RecBefore ); recAppended = true; } else if ( RadBefore.mother1() == 2 ) { radPos = outState.append( RadBefore ); radAppended = true; } else { // Find second incoming in input event int in2 = 0; for(int i=0; i < int(state.size()); ++i) if (state[i].mother1() == 2) in2 =i; outState.append( state[in2] ); size++; } // Append new recoiler if not done already if (!recAppended && !RecBefore.isFinal()) { recAppended = true; recPos = outState.append( RecBefore); } // Append new radiator if not done already if (!radAppended && !RadBefore.isFinal()) { radAppended = true; radPos = outState.append( RadBefore); } // Append intermediate particle // (careful not to append reclustered recoiler) // Append intermediate particle // (careful not to append reclustered recoiler) for (int i = 0; i < int(NewEvent.size()-1); ++i) if (NewEvent[i].status() == -22) outState.append( NewEvent[i] ); // Append final state particles, resonances first for (int i = 0; i < int(NewEvent.size()-1); ++i) if (NewEvent[i].status() == 22) outState.append( NewEvent[i] ); // Then start appending partons if (!radAppended && RadBefore.statusAbs() == 22) radPos = outState.append(RadBefore); if (!recAppended) recPos = outState.append(RecBefore); if (!radAppended && RadBefore.statusAbs() != 22) radPos = outState.append(RadBefore); // Then partons (not reclustered recoiler) for(int i = 0; i < int(NewEvent.size()-1); ++i) if ( NewEvent[i].status() != 22 && NewEvent[i].colType() != 0 && NewEvent[i].isFinal()) outState.append( NewEvent[i] ); // Then the rest for(int i = 0; i < int(NewEvent.size()-1); ++i) if ( NewEvent[i].status() != 22 && NewEvent[i].colType() == 0 && NewEvent[i].isFinal() ) outState.append( NewEvent[i]); // Find intermediate and respective daughters vector posIntermediate; vector posDaughter1; vector posDaughter2; for(int i=0; i < int(outState.size()); ++i) if (outState[i].status() == -22) { posIntermediate.push_back(i); int d1 = outState[i].daughter1(); int d2 = outState[i].daughter2(); // Find daughters in output state int daughter1 = FindParticle( state[d1], outState); int daughter2 = FindParticle( state[d2], outState); // If both daughters found, done // Else put first final particle as first daughter // and last final particle as second daughter if (daughter1 > 0) posDaughter1.push_back( daughter1); else { daughter1 = 0; while(!outState[daughter1].isFinal() ) daughter1++; posDaughter1.push_back( daughter1); } if (daughter2 > 0) posDaughter2.push_back( daughter2); else { daughter2 = outState.size()-1; while(!outState[daughter2].isFinal() ) daughter2--; posDaughter2.push_back( daughter2); } } // Set daughters and mothers for(int i=0; i < int(posIntermediate.size()); ++i) { outState[posIntermediate[i]].daughters(posDaughter1[i],posDaughter2[i]); outState[posDaughter1[i]].mother1(posIntermediate[i]); outState[posDaughter2[i]].mother1(posIntermediate[i]); } // Find range of final state partons int minParFinal = int(outState.size()); int maxParFinal = 0; for(int i=0; i < int(outState.size()); ++i) if (outState[i].mother1() == 3 && outState[i].mother2() == 4) { minParFinal = min(i,minParFinal); maxParFinal = max(i,maxParFinal); } if (minParFinal == maxParFinal) maxParFinal = 0; outState[3].daughters(minParFinal,maxParFinal); outState[4].daughters(minParFinal,maxParFinal); // Update event properties outState.saveSize(); outState.saveJunctionSize(); // Store radiator and recoiler positions. inSystem.recBef = recPos; inSystem.radBef = radPos; // Almost there... // If an intermediate coloured parton exists which was directly // colour connected to the radiator before the splitting, and the // radiator before and after the splitting had only one colour, problems // will arise since the colour of the radiator will be changed, whereas // the intermediate parton still has the old colour. In effect, this // means that when setting up a event for trial showering, one colour will // be free. // Hence, check for an intermediate coloured triplet resonance has been // colour-connected to the "old" radiator. // Find resonance int iColRes = 0; if ( radType == -1 && state[Rad].colType() == 1) { // Find resonance connected to initial colour for(int i=0; i < int(state.size()); ++i) if ( i != Rad && i != Emt && i != Rec && state[i].status() == -22 && state[i].col() == state[Rad].col() ) iColRes = i; } else if ( radType == -1 && state[Rad].colType() == -1) { // Find resonance connected to initial anticolour for(int i=0; i < int(state.size()); ++i) if ( i != Rad && i != Emt && i != Rec && state[i].status() == -22 && state[i].acol() == state[Rad].acol() ) iColRes = i; } else if ( radType == 1 && state[Rad].colType() == 1) { // Find resonance connected to final state colour for(int i=0; i < int(state.size()); ++i) if ( i != Rad && i != Emt && i != Rec && state[i].status() == -22 && state[i].col() == state[Rad].col() ) iColRes = i; } else if ( radType == 1 && state[Rad].colType() == -1) { // Find resonance connected to final state anticolour for(int i=0; i < int(state.size()); ++i) if ( i != Rad && i != Emt && i != Rec && state[i].status() == -22 && state[i].acol() == state[Rad].acol() ) iColRes = i; } if (iColRes > 0) { // Now find this resonance in the reclustered state int iColResNow = FindParticle( state[iColRes], outState); // Find reclustered radiator colours int radCol = outState[radPos].col(); int radAcl = outState[radPos].acol(); // Find resonance radiator colours int resCol = outState[iColResNow].col(); int resAcl = outState[iColResNow].acol(); // Check if any of the reclustered radiators colours match the resonance bool matchesRes = (radCol > 0 && ( radCol == resCol || radCol == resAcl)) || (radAcl > 0 && ( radAcl == resCol || radAcl == resAcl)); // If a resonance has been found, but no colours match, change // the colour of the resonance if (!matchesRes && iColResNow > 0) { if ( radType == -1 && outState[radPos].colType() == 1) outState[iColResNow].col(radCol); else if ( radType ==-1 && outState[radPos].colType() ==-1) outState[iColResNow].acol(radAcl); else if ( radType == 1 && outState[radPos].colType() == 1) outState[iColResNow].col(radCol); else if ( radType == 1 && outState[radPos].colType() ==-1) outState[iColResNow].acol(radAcl); } // If a resonance has been found, but no colours match, and the position // of the resonance in the event record has been changed, update the // radiator mother if (!matchesRes && iColResNow > 0 && iColRes != iColResNow) outState[radPos].mother1(iColResNow); } // If event is not constructed properly, return false if ( !validEvent(outState) ) { // Set momenta of particles to be attached to new event record outState.reset(); return outState; } // Remember position of reclustered radiator in state iReclusteredNew = radPos; // Done return outState; } //-------------------------------------------------------------------------- // Function to get the flavour of the radiator before the splitting // for clustering // IN int : Flavour of the radiator after the splitting // int : Flavour of the emitted after the splitting // OUT int : Flavour of the radiator before the splitting int History::getRadBeforeFlav(const int RadAfter, const int EmtAfter, const Event& event) { int type = event[RadAfter].isFinal() ? 1 :-1; int emtID = event[EmtAfter].id(); int radID = event[RadAfter].id(); int emtCOL = event[EmtAfter].col(); int radCOL = event[RadAfter].col(); int emtACL = event[EmtAfter].acol(); int radACL = event[RadAfter].acol(); bool colConnected = ((type == 1) && ( (emtCOL !=0 && (emtCOL ==radACL)) || (emtACL !=0 && (emtACL ==radCOL)) )) ||((type ==-1) && ( (emtCOL !=0 && (emtCOL ==radCOL)) || (emtACL !=0 && (emtACL ==radACL)) )); // QCD splittings // Gluon radiation if ( emtID == 21 ) return radID; // Final state gluon splitting if ( type == 1 && emtID == -radID && !colConnected ) return 21; // Initial state s-channel gluon splitting if ( type ==-1 && radID == 21 ) return -emtID; // Initial state t-channel gluon splitting if ( type ==-1 && !colConnected && emtID != 21 && radID != 21 && abs(emtID) < 10 && abs(radID) < 10) return 21; // SQCD splittings int radSign = (radID < 0) ? -1 : 1; int offsetL = 1000000; int offsetR = 2000000; // Gluino radiation if ( emtID == 1000021 ) { // Gluino radiation combined with quark yields squark. if (abs(radID) < 10 ) { int offset = offsetL; // Check if righthanded squark present. If so, make the reclustered // squark match. Works for squark pair production + gluino emission. for (int i=0; i < int(event.size()); ++i) if ( event[i].isFinal() && event[i].idAbs() < offsetR+10 && event[i].idAbs() > offsetR) offset = offsetR; return radSign*(abs(radID)+offset); } // Gluino radiation combined with squark yields quark. if (abs(radID) > offsetL && abs(radID) < offsetL+10 ) return radSign*(abs(radID)-offsetL); if (abs(radID) > offsetR && abs(radID) < offsetR+10 ) return radSign*(abs(radID)-offsetR); // Gluino radiation off gluon yields gluino. if (radID == 21 ) return emtID; } int emtSign = (emtID < 0) ? -1 : 1; // Get PDG numbering offsets. int emtOffset = 0; if ( abs(emtID) > offsetL && abs(emtID) < offsetL+10 ) emtOffset = offsetL; if ( abs(emtID) > offsetR && abs(emtID) < offsetR+10 ) emtOffset = offsetR; int radOffset = 0; if ( abs(radID) > offsetL && abs(radID) < offsetL+10 ) radOffset = offsetL; if ( abs(radID) > offsetR && abs(radID) < offsetR+10 ) radOffset = offsetR; // Final state gluino splitting if ( type == 1 && !colConnected ) { // Emitted squark, radiating quark. if ( emtOffset > 0 && radOffset == 0 && emtSign*(abs(emtID) - emtOffset) == -radID ) return 1000021; // Emitted quark, radiating squark. if ( emtOffset == 0 && radOffset > 0 && emtID == -radSign*(abs(radID) - radOffset) ) return 1000021; } // Initial state s-channel gluino splitting if ( type ==-1 && radID == 1000021 ) { // Quark entering underlying hard process. if ( emtOffset > 0 ) return -emtSign*(abs(emtID) - emtOffset); // Squark entering underlying hard process. else return -emtSign*(abs(emtID) + emtOffset); } // Initial state t-channel gluino splitting. if ( type ==-1 && ( (abs(emtID) > offsetL && abs(emtID) < offsetL+10) || (abs(emtID) > offsetR && abs(emtID) < offsetR+10)) && ( (abs(radID) > offsetL && abs(radID) < offsetL+10) || (abs(radID) > offsetR && abs(radID) < offsetR+10)) && emtSign*(abs(emtID)+emtOffset) == radSign*(abs(radID) - radOffset) && !colConnected ) { return 1000021; } // Electroweak splittings splittings // Photon / Z radiation: Calculate invariant mass of system double m2final = (event[RadAfter].p()+ event[EmtAfter].p()).m2Calc(); if ( emtID == 22 || emtID == 23 ) return radID; // Final state Photon splitting if ( type == 1 && emtID == -radID && colConnected && sqrt(m2final) <= 10. ) return 22; // Final state Photon splitting if ( type == 1 && emtID == -radID && colConnected && sqrt(m2final) > 10. ) return 23; // Initial state s-channel photon/ Z splitting if ( type ==-1 && (radID == 22 || radID == 23) ) return -emtID; // Initial state t-channel photon / Z splitting: Always bookkeep as photon if ( type ==-1 && abs(emtID) < 10 && abs(radID) < 10 && colConnected ) return 22; // W+ radiation // Final state W+ splitting if ( emtID == 24 && radID < 0 ) return radID + 1; if ( emtID == 24 && radID > 0 ) return radID + 1; // W- radiation // Final state W- splitting if ( emtID ==-24 && radID < 0 ) return radID - 1; if ( emtID ==-24 && radID > 0 ) return radID - 1; // Done. return 0; } //-------------------------------------------------------------------------- // Function to get the spin of the radiator before the splitting // IN int : Spin of the radiator after the splitting // int : Spin of the emitted after the splitting // OUT int : Spin of the radiator before the splitting int History::getRadBeforeSpin(const int radAfter, const int emtAfter, const int spinRadAfter, const int spinEmtAfter, const Event& event) { // Get flavour before the splitting. int radBeforeFlav = getRadBeforeFlav(radAfter, emtAfter, event); // Gluon in final state g-> q qbar if ( event[radAfter].isFinal() && event[radAfter].id() == -event[emtAfter].id()) return (spinRadAfter == 9) ? spinEmtAfter : spinRadAfter; // Quark in final state q -> q g if ( event[radAfter].isFinal() && abs(radBeforeFlav) < 10 && event[radAfter].idAbs() < 10) // Special oddity: Gluon does not change spin. return spinRadAfter; // Quark in final state q -> g q if ( event[radAfter].isFinal() && abs(radBeforeFlav) < 10 && event[emtAfter].idAbs() < 10) // Special oddity: Gluon does not change spin. return spinEmtAfter; // Gluon in final state g -> g g if ( event[radAfter].isFinal() && radBeforeFlav == 21 && event[radAfter].id() == 21) // Special oddity: Gluon does not change spin. return (spinRadAfter == 9) ? spinEmtAfter : spinRadAfter; // Gluon in initial state g-> q qbar if ( !event[radAfter].isFinal() && radBeforeFlav == -event[emtAfter].id()) return (spinRadAfter == 9) ? spinEmtAfter : spinRadAfter; // Quark in initial state q -> q g if ( !event[radAfter].isFinal() && abs(radBeforeFlav) < 10 && event[radAfter].idAbs() < 10) // Special oddity: Gluon does not change spin. return spinRadAfter; // Gluon in initial state q -> g q if ( !event[radAfter].isFinal() && radBeforeFlav == 21 && event[emtAfter].idAbs() < 10) // Special oddity: Gluon does not change spin. return spinEmtAfter; // Done. Return default value. return 9; } //-------------------------------------------------------------------------- // Function to properly colour-connect the radiator to the rest of // the event, as needed during clustering // IN Particle& : Particle to be connected // Particle : Recoiler forming a dipole with Radiator // Event : event to which Radiator shall be appended // OUT true : Radiator could be connected to the event // false : Radiator could not be connected to the // event or the resulting event was // non-valid bool History::connectRadiator( Particle& Radiator, const int RadType, const Particle& Recoiler, const int RecType, const Event& event ) { // Start filling radiator colour indices with dummy values Radiator.cols( -1, -1 ); // Radiator should always be colour-connected to recoiler. // Three cases (rad = Anti-Quark, Quark, Gluon) to be considered if ( Radiator.colType() == -1 ) { // For final state antiquark radiator, the anticolour is fixed // by the final / initial state recoiler colour / anticolour if ( RadType + RecType == 2 ) Radiator.cols( 0, Recoiler.col()); else if ( RadType + RecType == 0 ) Radiator.cols( 0, Recoiler.acol()); // For initial state antiquark radiator, the anticolour is fixed // by the colour of the emitted gluon (which will be the // leftover anticolour of a final state particle or the leftover // colour of an initial state particle ( = the recoiler)) else { // Set colour of antiquark radiator to zero Radiator.col( 0 ); for (int i = 0; i < event.size(); ++i) { int col = event[i].col(); int acl = event[i].acol(); if ( event[i].isFinal()) { // Search for leftover anticolour in final / initial state if ( acl > 0 && FindCol(acl,i,0,event,1,true) == 0 && FindCol(acl,i,0,event,2,true) == 0 ) Radiator.acol(event[i].acol()); } else { // Search for leftover colour in initial / final state if ( col > 0 && FindCol(col,i,0,event,1,true) == 0 && FindCol(col,i,0,event,2,true) == 0 ) Radiator.acol(event[i].col()); } } // end loop over particles in event record } } else if ( Radiator.colType() == 1 ) { // For final state quark radiator, the colour is fixed // by the final / initial state recoiler anticolour / colour if ( RadType + RecType == 2 ) Radiator.cols( Recoiler.acol(), 0); else if ( RadType + RecType == 0 ) Radiator.cols( Recoiler.col(), 0); // For initial state quark radiator, the colour is fixed // by the anticolour of the emitted gluon (which will be the // leftover colour of a final state particle or the leftover // anticolour of an initial state particle ( = the recoiler)) else { // Set anticolour of quark radiator to zero Radiator.acol( 0 ); for (int i = 0; i < event.size(); ++i) { int col = event[i].col(); int acl = event[i].acol(); if ( event[i].isFinal()) { // Search for leftover colour in final / initial state if ( col > 0 && FindCol(col,i,0,event,1,true) == 0 && FindCol(col,i,0,event,2,true) == 0) Radiator.col(event[i].col()); } else { // Search for leftover anticolour in initial / final state if ( acl > 0 && FindCol(acl,i,0,event,1,true) == 0 && FindCol(acl,i,0,event,2,true) == 0) Radiator.col(event[i].acol()); } } // end loop over particles in event record } // end distinction between fsr / fsr+initial recoiler / isr } else if ( Radiator.colType() == 2 ) { // For a gluon radiator, one (anticolour) colour index is defined // by the recoiler colour (anticolour). // The remaining index is chosen to match the free index in the // event // Search for leftover colour (anticolour) in the final state for (int i = 0; i < event.size(); ++i) { int col = event[i].col(); int acl = event[i].acol(); int iEx = i; if ( event[i].isFinal()) { if ( col > 0 && FindCol(col,iEx,0,event,1,true) == 0 && FindCol(col,iEx,0,event,2,true) == 0) { if (Radiator.status() < 0 ) Radiator.col(event[i].col()); else Radiator.acol(event[i].col()); } if ( acl > 0 && FindCol(acl,iEx,0,event,2,true) == 0 && FindCol(acl,iEx,0,event,1,true) == 0 ) { if (Radiator.status() < 0 ) Radiator.acol(event[i].acol()); else Radiator.col(event[i].acol()); } } else { if ( col > 0 && FindCol(col,iEx,0,event,1,true) == 0 && FindCol(col,iEx,0,event,2,true) == 0) { if (Radiator.status() < 0 ) Radiator.acol(event[i].col()); else Radiator.col(event[i].col()); } if ( acl > 0 && (FindCol(acl,iEx,0,event,2,true) == 0 && FindCol(acl,iEx,0,event,1,true) == 0)) { if (Radiator.status() < 0 ) Radiator.col(event[i].acol()); else Radiator.acol(event[i].acol()); } } } // end loop over particles in event record } // end cases of different radiator colour type // If either colour or anticolour has not been set, return false if (Radiator.col() < 0 || Radiator.acol() < 0) return false; // Done return true; } //-------------------------------------------------------------------------- // Function to find a colour (anticolour) index in the input event // IN int col : Colour tag to be investigated // int iExclude1 : Identifier of first particle to be excluded // from search // int iExclude2 : Identifier of second particle to be excluded // from search // Event event : event to be searched for colour tag // int type : Tag to define if col should be counted as // colour (type = 1) [->find anti-colour index // contracted with col] // anticolour (type = 2) [->find colour index // contracted with col] // OUT int : Position of particle in event record // contraced with col [0 if col is free tag] int History::FindCol(int col, int iExclude1, int iExclude2, const Event& event, int type, bool isHardIn) { bool isHard = isHardIn; int index = 0; if (isHard) { // Search event record for matching colour & anticolour for(int n = 0; n < event.size(); ++n) { if ( n != iExclude1 && n != iExclude2 && event[n].colType() != 0 &&( event[n].status() > 0 // Check outgoing || event[n].status() == -21) ) { // Check incoming if ( event[n].acol() == col ) { index = -n; break; } if ( event[n].col() == col ) { index = n; break; } } } } else { // Search event record for matching colour & anticolour for(int n = 0; n < event.size(); ++n) { if ( n != iExclude1 && n != iExclude2 && event[n].colType() != 0 &&( event[n].status() == 43 // Check outgoing from ISR || event[n].status() == 51 // Check outgoing from FSR || event[n].status() == -41 // first initial || event[n].status() == -42) ) { // second initial if ( event[n].acol() == col ) { index = -n; break; } if ( event[n].col() == col ) { index = n; break; } } } } // if no matching colour / anticolour has been found, return false if ( type == 1 && index < 0) return abs(index); if ( type == 2 && index > 0) return abs(index); return 0; } //-------------------------------------------------------------------------- // Function to in the input event find a particle with quantum // numbers matching those of the input particle // IN Particle : Particle to be searched for // Event : Event to be searched in // OUT int : > 0 : Position of matching particle in event // < 0 : No match in event int History::FindParticle( const Particle& particle, const Event& event, bool checkStatus ) { int index = -1; for ( int i = int(event.size()) - 1; i > 0; --i ) if ( event[i].id() == particle.id() && event[i].colType() == particle.colType() && event[i].chargeType() == particle.chargeType() && event[i].col() == particle.col() && event[i].acol() == particle.acol() && event[i].charge() == particle.charge() ) { index = i; break; } if ( checkStatus && event[index].status() != particle.status() ) index = -1; return index; } //-------------------------------------------------------------------------- // Function to get the colour of the radiator before the splitting // for clustering // IN int : Position of the radiator after the splitting, in the event // int : Position of the emitted after the splitting, in the event // Event : Reference event // OUT int : Colour of the radiator before the splitting int History::getRadBeforeCol(const int rad, const int emt, const Event& event) { // Save type of splitting int type = (event[rad].isFinal()) ? 1 :-1; // Get flavour of radiator after potential clustering int radBeforeFlav = getRadBeforeFlav(rad,emt,event); // Get colours of the radiator before the potential clustering int radBeforeCol = -1; // Get reconstructed gluon colours if (radBeforeFlav == 21) { // Start with quark emissions in FSR if (type == 1 && event[emt].id() != 21) { radBeforeCol = (event[rad].col() > 0) ? event[rad].col() : event[emt].col(); // Quark emissions in ISR } else if (type == -1 && event[emt].id() != 21) { radBeforeCol = (event[rad].col() > 0) ? event[rad].col() : event[emt].acol(); //Gluon emissions in FSR } else if (type == 1 && event[emt].id() == 21) { // If emitted is a gluon, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].col() == event[emt].acol()) ? event[rad].col() : event[rad].acol(); radBeforeCol = (event[rad].col() == colRemove) ? event[emt].col() : event[rad].col(); //Gluon emissions in ISR } else if (type == -1 && event[emt].id() == 21) { // If emitted is a gluon, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].col() == event[emt].col()) ? event[rad].col() : event[rad].acol(); radBeforeCol = (event[rad].col() == colRemove) ? event[emt].acol() : event[rad].col(); } // Get reconstructed quark colours } else if ( radBeforeFlav != 21 && radBeforeFlav > 0) { // Quark emission in FSR if (type == 1 && event[emt].id() != 21) { // If radiating is a quark, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].col() == event[emt].acol()) ? event[rad].acol() : 0; radBeforeCol = (event[rad].col() == colRemove) ? event[emt].col() : event[rad].col(); //Gluon emissions in FSR } else if (type == 1 && event[emt].id() == 21) { // If emitted is a gluon, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].col() == event[emt].acol()) ? event[rad].col() : 0; radBeforeCol = (event[rad].col() == colRemove) ? event[emt].col() : event[rad].col(); //Quark emissions in ISR } else if (type == -1 && event[emt].id() != 21) { // If emitted is a quark, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].col() == event[emt].col()) ? event[rad].col() : 0; radBeforeCol = (event[rad].col() == colRemove) ? event[emt].acol() : event[rad].col(); //Gluon emissions in ISR } else if (type == -1 && event[emt].id() == 21) { // If emitted is a gluon, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].col() == event[emt].col()) ? event[rad].col() : 0; radBeforeCol = (event[rad].col() == colRemove) ? event[emt].acol() : event[rad].col(); } // Other particles are assumed uncoloured } else { radBeforeCol = 0; } return radBeforeCol; } //-------------------------------------------------------------------------- // Function to get the anticolour of the radiator before the splitting // for clustering // IN int : Position of the radiator after the splitting, in the event // int : Position of the emitted after the splitting, in the event // Event : Reference event // OUT int : Anticolour of the radiator before the splitting int History::getRadBeforeAcol(const int rad, const int emt, const Event& event) { // Save type of splitting int type = (event[rad].isFinal()) ? 1 :-1; // Get flavour of radiator after potential clustering int radBeforeFlav = getRadBeforeFlav(rad,emt,event); // Get colours of the radiator before the potential clustering int radBeforeAcl = -1; // Get reconstructed gluon colours if (radBeforeFlav == 21) { // Start with quark emissions in FSR if (type == 1 && event[emt].id() != 21) { radBeforeAcl = (event[rad].acol() > 0) ? event[rad].acol() : event[emt].acol(); // Quark emissions in ISR } else if (type == -1 && event[emt].id() != 21) { radBeforeAcl = (event[rad].acol() > 0) ? event[rad].acol() : event[emt].col(); //Gluon emissions in FSR } else if (type == 1 && event[emt].id() == 21) { // If emitted is a gluon, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].col() == event[emt].acol()) ? event[rad].col() : event[rad].acol(); radBeforeAcl = (event[rad].acol() == colRemove) ? event[emt].acol() : event[rad].acol(); //Gluon emissions in ISR } else if (type == -1 && event[emt].id() == 21) { // If emitted is a gluon, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].col() == event[emt].col()) ? event[rad].col() : event[rad].acol(); radBeforeAcl = (event[rad].acol() == colRemove) ? event[emt].col() : event[rad].acol(); } // Get reconstructed anti-quark colours } else if ( radBeforeFlav != 21 && radBeforeFlav < 0) { // Antiquark emission in FSR if (type == 1 && event[emt].id() != 21) { // If radiating is a antiquark, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].col() == event[emt].acol()) ? event[rad].acol() : 0; radBeforeAcl = (event[rad].acol() == colRemove) ? event[emt].acol() : event[rad].acol(); //Gluon emissions in FSR } else if (type == 1 && event[emt].id() == 21) { // If emitted is a gluon, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].acol() == event[emt].col()) ? event[rad].acol() : 0; radBeforeAcl = (event[rad].acol() == colRemove) ? event[emt].acol() : event[rad].acol(); //Antiquark emissions in ISR } else if (type == -1 && event[emt].id() != 21) { // If emitted is an antiquark, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].acol() == event[emt].acol()) ? event[rad].acol() : 0; radBeforeAcl = (event[rad].acol() == colRemove) ? event[emt].col() : event[rad].acol(); //Gluon emissions in ISR } else if (type == -1 && event[emt].id() == 21) { // If emitted is a gluon, remove the repeated index, and take // the remaining indices as colour and anticolour int colRemove = (event[rad].acol() == event[emt].acol()) ? event[rad].acol() : 0; radBeforeAcl = (event[rad].acol() == colRemove) ? event[emt].col() : event[rad].acol(); } // Other particles are considered uncoloured } else { radBeforeAcl = 0; } return radBeforeAcl; } //-------------------------------------------------------------------------- // Function to get the parton connected to in by a colour line // IN int : Position of parton for which partner should be found // Event : Reference event // OUT int : If a colour line connects the "in" parton with another // parton, return the Position of the partner, else return 0 int History::getColPartner(const int in, const Event& event) { if (event[in].col() == 0) return 0; int partner = 0; // Try to find anticolour index first partner = FindCol(event[in].col(),in,0,event,1,true); // If no anticolour index has been found, try colour if (partner == 0) partner = FindCol(event[in].col(),in,0,event,2,true); return partner; } //-------------------------------------------------------------------------- // Function to get the parton connected to in by an anticolour line // IN int : Position of parton for which partner should be found // Event : Reference event // OUT int : If an anticolour line connects the "in" parton with another // parton, return the Position of the partner, else return 0 int History::getAcolPartner(const int in, const Event& event) { if (event[in].acol() == 0) return 0; int partner = 0; // Try to find colour index first partner = FindCol(event[in].acol(),in,0,event,2,true); // If no colour index has been found, try anticolour if (partner == 0) partner = FindCol(event[in].acol(),in,0,event,1,true); return partner; } //-------------------------------------------------------------------------- // Function to get the list of partons connected to the particle // formed by reclusterinf emt and rad by colour and anticolour lines // IN int : Position of radiator in the clustering // IN int : Position of emitted in the clustering // Event : Reference event // OUT vector : List of positions of all partons that are connected // to the parton that will be formed // by clustering emt and rad. vector History::getReclusteredPartners(const int rad, const int emt, const Event& event) { // Save type int type = event[rad].isFinal() ? 1 : -1; // Get reclustered colours int radBeforeCol = getRadBeforeCol(rad, emt, event); int radBeforeAcl = getRadBeforeAcol(rad, emt, event); // Declare output vector partners; // Start with FSR clusterings if (type == 1) { for(int i=0; i < int(event.size()); ++i) { // Check all initial state partons if ( i != emt && i != rad && event[i].status() == -21 && event[i].col() > 0 && event[i].col() == radBeforeCol) partners.push_back(i); // Check all final state partons if ( i != emt && i != rad && event[i].isFinal() && event[i].acol() > 0 && event[i].acol() == radBeforeCol) partners.push_back(i); // Check all initial state partons if ( i != emt && i != rad && event[i].status() == -21 && event[i].acol() > 0 && event[i].acol() == radBeforeAcl) partners.push_back(i); // Check all final state partons if ( i != emt && i != rad && event[i].isFinal() && event[i].col() > 0 && event[i].col() == radBeforeAcl) partners.push_back(i); } // Start with ISR clusterings } else { for(int i=0; i < int(event.size()); ++i) { // Check all initial state partons if ( i != emt && i != rad && event[i].status() == -21 && event[i].acol() > 0 && event[i].acol() == radBeforeCol) partners.push_back(i); // Check all final state partons if ( i != emt && i != rad && event[i].isFinal() && event[i].col() > 0 && event[i].col() == radBeforeCol) partners.push_back(i); // Check all initial state partons if ( i != emt && i != rad && event[i].status() == -21 && event[i].col() > 0 && event[i].col() == radBeforeAcl) partners.push_back(i); // Check all final state partons if ( i != emt && i != rad && event[i].isFinal() && event[i].acol() > 0 && event[i].acol() == radBeforeAcl) partners.push_back(i); } } // Done return partners; } //-------------------------------------------------------------------------- // Function to extract a chain of colour-connected partons in // the event // IN int : Type of parton from which to start extracting a // parton chain. If the starting point is a quark // i.e. flavType = 1, a chain of partons that are // consecutively connected by colour-lines will be // extracted. If the starting point is an antiquark // i.e. flavType =-1, a chain of partons that are // consecutively connected by anticolour-lines // will be extracted. // IN int : Position of the parton from which a // colour-connected chain should be derived // IN Event : Refernence event // IN/OUT vector : Partons that should be excluded from the search. // OUT vector : Positions of partons along the chain // OUT bool : Found singlet / did not find singlet bool History::getColSinglet( const int flavType, const int iParton, const Event& event, vector& exclude, vector& colSinglet) { // If no possible flavour to start from has been found if (iParton < 0) return false; // If no further partner has been found in a previous iteration, // and the whole final state has been excluded, we're done if (iParton == 0) { // Count number of final state partons int nFinal = 0; for(int i=0; i < int(event.size()); ++i) if ( event[i].isFinal() && event[i].colType() != 0) nFinal++; // Get number of initial state partons in the list of // excluded partons int nExclude = int(exclude.size()); int nInitExclude = 0; if (!event[exclude[2]].isFinal()) nInitExclude++; if (!event[exclude[3]].isFinal()) nInitExclude++; // If the whole final state has been considered, return if (nFinal == nExclude - nInitExclude) return true; else return false; } // Declare colour partner int colP = 0; // Save the colour partner colSinglet.push_back(iParton); // Remove the partner from the list exclude.push_back(iParton); // When starting out from a quark line, follow the colour lines if (flavType == 1) colP = getColPartner(iParton,event); // When starting out from an antiquark line, follow the anticolour lines else colP = getAcolPartner(iParton,event); // Do not count excluded partons twice for(int i = 0; i < int(exclude.size()); ++i) if (colP == exclude[i]) return true; // Recurse return getColSinglet(flavType,colP,event,exclude,colSinglet); } //-------------------------------------------------------------------------- // Function to check that a set of partons forms a colour singlet // IN Event : Reference event // IN vector : Positions of the partons in the set // OUT bool : Is a colour singlet / is not bool History::isColSinglet( const Event& event, vector system ) { // Check if system forms a colour singlet for(int i=0; i < int(system.size()); ++i ) { // Match quark and gluon colours if ( system[i] > 0 && (event[system[i]].colType() == 1 || event[system[i]].colType() == 2) ) { for(int j=0; j < int(system.size()); ++j) // If flavour matches, remove both partons and continue if ( system[i] > 0 && system[j] > 0 && event[system[i]].col() == event[system[j]].acol()) { // Remove index and break system[i] = 0; system[j] = 0; break; } } // Match antiquark and gluon anticolours if ( system[i] > 0 && (event[system[i]].colType() == -1 || event[system[i]].colType() == 2) ) { for(int j=0; j < int(system.size()); ++j) // If flavour matches, remove both partons and continue if ( system[i] > 0 && system[j] > 0 && event[system[i]].acol() == event[system[j]].col()) { // Remove index and break system[i] = 0; system[j] = 0; break; } } } // The system is a colour singlet if for all colours, // an anticolour was found bool isColSing = true; for(int i=0; i < int(system.size()); ++i) if ( system[i] != 0 ) isColSing = false; // Return return isColSing; } //-------------------------------------------------------------------------- // Function to check that a set of partons forms a flavour singlet // IN Event : Reference event // IN vector : Positions of the partons in the set // IN int : Flavour of all the quarks in the set, if // all quarks in a set should have a fixed flavour // OUT bool : Is a flavour singlet / is not bool History::isFlavSinglet( const Event& event, vector system, int flav) { // If a decoupled colour singlet has been found, check if this is also // a flavour singlet // Check that each quark matches an antiquark for(int i=0; i < int(system.size()); ++i) if ( system[i] > 0 ) { for(int j=0; j < int(system.size()); ++j) { // If flavour of outgoing partons matches, // remove both partons and continue. // Skip all bosons if ( event[i].idAbs() != 21 && event[i].idAbs() != 22 && event[i].idAbs() != 23 && event[i].idAbs() != 24 && system[i] > 0 && system[j] > 0 && event[system[i]].isFinal() && event[system[j]].isFinal() && event[system[i]].id() == -1*event[system[j]].id()) { // If we want to check if only one flavour of quarks // exists if (abs(flav) > 0 && event[system[i]].idAbs() != flav) return false; // Remove index and break system[i] = 0; system[j] = 0; break; } // If flavour of outgoing and incoming partons match, // remove both partons and continue. // Skip all bosons if ( event[i].idAbs() != 21 && event[i].idAbs() != 22 && event[i].idAbs() != 23 && event[i].idAbs() != 24 && system[i] > 0 && system[j] > 0 && ( ( !event[system[i]].isFinal() && event[system[j]].isFinal()) ||( !event[system[j]].isFinal() && event[system[i]].isFinal()) ) && event[system[i]].id() == event[system[j]].id()) { // If we want to check if only one flavour of quarks // exists if (abs(flav) > 0 && event[system[i]].idAbs() != flav) return false; // Remove index and break system[i] = 0; system[j] = 0; break; } } } // The colour singlet is a flavour singlet if for all quarks, // an antiquark was found bool isFlavSing = true; for(int i=0; i < int(system.size()); ++i) if ( system[i] != 0 ) isFlavSing = false; // Return return isFlavSing; } //-------------------------------------------------------------------------- // Function to check if rad,emt,rec triple is allowed for clustering // IN int rad,emt,rec : Positions (in event record) of the three // particles considered for clustering // Event event : Reference event bool History::allowedClustering( int rad, int emt, int rec, int partner, const Event& event ) { // Declare output bool allowed = true; // CONSTRUCT SOME PROPERTIES FOR LATER INVESTIGATION // Check if the triple forms a colour singlett bool isSing = isSinglett(rad,emt,partner,event); int type = (event[rad].isFinal()) ? 1 :-1; // Get flavour of radiator after potential clustering int radBeforeFlav = getRadBeforeFlav(rad,emt,event); // Get colours of the radiator before the potential clustering int radBeforeCol = getRadBeforeCol(rad,emt,event); int radBeforeAcl = getRadBeforeAcol(rad,emt,event); // Get colour partner of reclustered parton vector radBeforeColP = getReclusteredPartners(rad, emt, event); // Count coloured partons in hard process int nPartonInHard = 0; for(int i=0; i < int(event.size()); ++i) // Check all final state partons if ( event[i].isFinal() && event[i].colType() != 0 && mergingHooksPtr->hardProcess->matchesAnyOutgoing(i, event) ) nPartonInHard++; // Count coloured final state partons in event, excluding // rad, rec, emt and hard process int nPartons = 0; for(int i=0; i < int(event.size()); ++i) // Check all final state partons if ( i!=emt && i!=rad && i!=rec && event[i].isFinal() && event[i].colType() != 0 && !mergingHooksPtr->hardProcess->matchesAnyOutgoing(i, event) ) nPartons++; // Count number of initial state partons int nInitialPartons = 0; for(int i=0; i < int(event.size()); ++i) if ( event[i].status() == -21 && event[i].colType() != 0 ) nInitialPartons++; // Get number of non-charged final state particles int nFinalEW = 0; for(int i=0; i < int(event.size()); ++i) if ( event[i].isFinal() &&( event[i].id() == 22 || event[i].id() == 23 || event[i].id() == 24 ||(event[i].idAbs() > 10 && event[i].idAbs() < 20) ||(event[i].idAbs() > 10 && event[i].idAbs() < 20) ||(event[i].idAbs() > 1000010 && event[i].idAbs() < 1000020) ||(event[i].idAbs() > 2000010 && event[i].idAbs() < 2000020) )) nFinalEW++; // Check if event after potential clustering contains an even // number of quarks and/or antiquarks // (otherwise no electroweak vertex could be formed!) // Get number of final quarks int nFinalQuark = 0; // Get number of excluded final state quarks as well int nFinalQuarkExc = 0; for(int i=0; i < int(event.size()); ++i) { if (i !=rad && i != emt && i != rec) { if (event[i].isFinal() && abs(event[i].colType()) == 1 ) { if ( !mergingHooksPtr->hardProcess->matchesAnyOutgoing(i,event) ) nFinalQuark++; else nFinalQuarkExc++; } } } // Add recoiler to number of final quarks if (event[rec].isFinal() && event[rec].isQuark()) nFinalQuark++; // Add radiator after clustering to number of final quarks if ( event[rad].isFinal() && abs(particleDataPtr->colType(radBeforeFlav)) == 1) nFinalQuark++; // Get number of initial quarks int nInitialQuark = 0; if (type == 1) { if (event[rec].isFinal()) { if (event[3].isQuark()) nInitialQuark++; if (event[4].isQuark()) nInitialQuark++; } else { int iOtherIn = (rec == 3) ? 4 : 3; if (event[rec].isQuark()) nInitialQuark++; if (event[iOtherIn].isQuark()) nInitialQuark++; } } else { // Add recoiler to number of initial quarks if (event[rec].isQuark()) nInitialQuark++; // Add radiator after clustering to number of initial quarks if (abs(radBeforeFlav) < 10) nInitialQuark++; } // BEGIN CHECKING THE CLUSTERING // Do not allow clusterings that lead to a disallowed proton content. int proton[] = {1,2,3,4,5,21,22,23,24}; bool isInProton = false; for(int i=0; i < 9; ++i) if (abs(radBeforeFlav) == proton[i]) isInProton = true; if (type == -1 && !isInProton) return false; // Check if colour is conserved vector unmatchedCol; vector unmatchedAcl; // Check all unmatched colours for ( int i = 0; i < event.size(); ++i) if ( i != emt && i != rad && (event[i].isFinal() || event[i].status() == -21) && event[i].colType() != 0 ) { int colP = getColPartner(i,event); int aclP = getAcolPartner(i,event); if (event[i].col() > 0 && (colP == emt || colP == rad || colP == 0) ) unmatchedCol.push_back(i); if (event[i].acol() > 0 && (aclP == emt || aclP == rad || aclP == 0) ) unmatchedAcl.push_back(i); } // If more than one colour or more than one anticolour are unmatched, // there is no way to make this clustering work if (int(unmatchedCol.size()) + int(unmatchedAcl.size()) > 2) return false; // If triple forms colour singlett, check that resulting state // matches hard core process if (isSing) allowed = false; if ( isSing && event[rec].isQuark() && abs(particleDataPtr->colType(radBeforeFlav)) == 1) allowed = true; // Never recluster any outgoing partons of the core V -> qqbar' splitting! if ( mergingHooksPtr->hardProcess->matchesAnyOutgoing(emt,event) ) { // Check if any other particle could replace "emt" as part of the candidate // core process. If so, replace emt with the new candidate and allow the // clustering. bool canReplace = mergingHooksPtr->hardProcess->findOtherCandidates(emt, event, true); if (canReplace) allowed = true; else allowed = false; } // Never allow clustering of any outgoing partons of the hard process // which would change the flavour of one of the hard process partons! if ( mergingHooksPtr->hardProcess->matchesAnyOutgoing(rad,event) && event[rad].id() != radBeforeFlav ) allowed = false; // If only gluons in initial state and no quarks in final state, // reject (no electroweak vertex can be formed) if (nFinalEW != 0 && nInitialQuark == 0 && nFinalQuark == 0 && nFinalQuarkExc == 0) allowed = false; if ( (nInitialQuark + nFinalQuark + nFinalQuarkExc)%2 != 0 ) allowed = false; // Disallow final state splittings that lead to a purely gluonic final // state, while having a completely colour-connected initial state. // This means that the clustering is discarded if it does not lead to the // t-channel gluon needed to connect the final state to a qq~ initial state. // Here, partons excluded from clustering are not counted as possible // partners to form a t-channel gluon vector in; for(int i=0; i < int(event.size()); ++i) if ( i!=emt && i!=rad && i!=rec && (event[i].mother1() == 1 || event[i].mother1() == 2)) in.push_back(event[i].id()); if (!event[rad].isFinal()) in.push_back(radBeforeFlav); if (!event[rec].isFinal()) in.push_back(event[rec].id()); vector out; for(int i=0; i < int(event.size()); ++i) if ( i!=emt && i!=rad && i!=rec && event[i].isFinal()) out.push_back(event[i].id()); if (event[rad].isFinal()) out.push_back(radBeforeFlav); if (event[rec].isFinal()) out.push_back(event[rec].id()); if (event[3].col() == event[4].acol() && event[3].acol() == event[4].col() && !mergingHooksPtr->allowEffectiveVertex( in, out) && nFinalQuark == 0){ // Careful if rad and rec are the only quarks in the final state, but // were both excluded from the list of final state quarks. int nTripletts = abs(event[rec].colType()) + abs(particleDataPtr->colType(radBeforeFlav)); if (event[3].isGluon()) allowed = false; else if (nTripletts != 2 && nFinalQuarkExc%2 == 0) allowed = false; } // Minimal phase space checks. if ( abs((event[rad].p()+type*event[emt].p()+event[rec].p()).pz()) > (event[rad].p()+type*event[emt].p()+event[rec].p()).e() || (type == -1 && (event[rad].p()-event[emt].p()+event[rec].p()).m2Calc() < 0.) ){ return false; } // No problems with gluon radiation if (event[emt].id() == 21) return allowed; // No problems with gluino radiation if (event[emt].id() == 1000021) return allowed; // Save all hard process candidates vector outgoingParticles; int nOut1 = int(mergingHooksPtr->hardProcess->PosOutgoing1.size()); for ( int i=0; i < nOut1; ++i ) { int iPos = mergingHooksPtr->hardProcess->PosOutgoing1[i]; outgoingParticles.push_back( mergingHooksPtr->hardProcess->state[iPos].id() ); } int nOut2 = int(mergingHooksPtr->hardProcess->PosOutgoing2.size()); for ( int i=0; i < nOut2; ++i ) { int iPos = mergingHooksPtr->hardProcess->PosOutgoing2[i]; outgoingParticles.push_back( mergingHooksPtr->hardProcess->state[iPos].id() ); } // Start more involved checks. g -> q_1 qbar_1 splittings are // particularly problematic if more than one quark of the emitted // flavour is present. // Count number of initial quarks of radiator or emitted flavour vector iInQuarkFlav; for(int i=0; i < int(event.size()); ++i) // Check all initial state partons if ( i != emt && i != rad && event[i].status() == -21 && event[i].idAbs() == event[emt].idAbs() ) iInQuarkFlav.push_back(i); // Count number of final quarks of radiator or emitted flavour vector iOutQuarkFlav; for(int i=0; i < int(event.size()); ++i) // Check all final state partons if ( i != emt && i != rad && event[i].isFinal() && event[i].idAbs() == event[emt].idAbs() ) { // Loop through final state hard particles. If one matches, remove the // matching one, and do not count. bool matchOut = false; for (int j = 0; j < int(outgoingParticles.size()); ++j) if ( event[i].idAbs() == abs(outgoingParticles[j])) { matchOut = true; outgoingParticles[j] = 99; } if (!matchOut) iOutQuarkFlav.push_back(i); } // Save number of potentially dangerous quarks int nInQuarkFlav = int(iInQuarkFlav.size()); int nOutQuarkFlav = int(iOutQuarkFlav.size()); // Easiest problem 0: // Radiator before splitting exactly matches the partner // after the splitting if ( event[partner].isFinal() && event[partner].id() == 21 && radBeforeFlav == 21 && event[partner].col() == radBeforeCol && event[partner].acol() == radBeforeAcl) return false; // If there are no ambiguities in qqbar pairs, return if (nInQuarkFlav + nOutQuarkFlav == 0) return allowed; // Save all quarks and gluons that will not change colour vector gluon; vector quark; vector antiq; vector partons; for(int i=0; i < int(event.size()); ++i) // Check initial and final state partons if ( i!=emt && i!=rad && event[i].colType() != 0 && (event[i].isFinal() || event[i].status() == -21) ) { // Save index partons.push_back(i); // Split into components if (event[i].colType() == 2) gluon.push_back(i); else if (event[i].colType() == 1) quark.push_back(i); else if (event[i].colType() == -1) antiq.push_back(i); } // We split up the test of the g->qq splitting into final state // and initial state problems bool isFSRg2qq = ((type == 1) && (event[rad].id() == -1*event[emt].id()) ); bool isISRg2qq = ((type ==-1) && (event[rad].id() == event[emt].id()) ); // First check general things about colour connections // Check that clustering does not produce a gluon that is exactly // matched in the final state, or does not have any colour connections if ( (isFSRg2qq || isISRg2qq) && int(quark.size()) + int(antiq.size()) + int(gluon.size()) > nPartonInHard ) { vector colours; vector anticolours; // Add the colour and anticolour of the gluon before the emission // to the list, bookkeep initial colour as final anticolour, and // initial anticolour as final colour if (type == 1) { colours.push_back(radBeforeCol); anticolours.push_back(radBeforeAcl); } else { colours.push_back(radBeforeAcl); anticolours.push_back(radBeforeCol); } // Now store gluon colours and anticolours. for(int i=0; i < int(gluon.size()); ++i) if (event[gluon[i]].isFinal()) { colours.push_back(event[gluon[i]].col()); anticolours.push_back(event[gluon[i]].acol()); } else { colours.push_back(event[gluon[i]].acol()); anticolours.push_back(event[gluon[i]].col()); } // Loop through colours and check if any match with // anticolours. If colour matches, remove from list for(int i=0; i < int(colours.size()); ++i) for(int j=0; j < int(anticolours.size()); ++j) if (colours[i] > 0 && anticolours[j] > 0 && colours[i] == anticolours[j]) { colours[i] = 0; anticolours[j] = 0; } // If all gluon anticolours and all colours matched, disallow // the clustering bool allMatched = true; for(int i=0; i < int(colours.size()); ++i) if (colours[i] != 0) allMatched = false; for(int i=0; i < int(anticolours.size()); ++i) if (anticolours[i] != 0) allMatched = false; if (allMatched) return false; // Now add the colours of the hard process, and check if all // colours match. for(int i=0; i < int(quark.size()); ++i) if ( event[quark[i]].isFinal() && mergingHooksPtr->hardProcess->matchesAnyOutgoing(quark[i], event) ) colours.push_back(event[quark[i]].col()); for(int i=0; i < int(antiq.size()); ++i) if ( event[antiq[i]].isFinal() && mergingHooksPtr->hardProcess->matchesAnyOutgoing(antiq[i], event) ) anticolours.push_back(event[antiq[i]].acol()); // Loop through colours again and check if any match with // anticolours. If colour matches, remove from list for(int i=0; i < int(colours.size()); ++i) for(int j=0; j < int(anticolours.size()); ++j) if (colours[i] > 0 && anticolours[j] > 0 && colours[i] == anticolours[j]) { colours[i] = 0; anticolours[j] = 0; } // Check if clustering would produce the hard process int nNotInHard = 0; for ( int i=0; i < int(quark.size()); ++i ) if ( !mergingHooksPtr->hardProcess->matchesAnyOutgoing( quark[i], event) ) nNotInHard++; for ( int i=0; i < int(antiq.size()); ++i ) if ( !mergingHooksPtr->hardProcess->matchesAnyOutgoing( antiq[i], event) ) nNotInHard++; for(int i=0; i < int(gluon.size()); ++i) if ( event[gluon[i]].isFinal() ) nNotInHard++; if ( type == 1 ) nNotInHard++; // If all colours are matched now, and since we have more quarks than // present in the hard process, disallow the clustering allMatched = true; for(int i=0; i < int(colours.size()); ++i) if (colours[i] != 0) allMatched = false; for(int i=0; i < int(anticolours.size()); ++i) if (anticolours[i] != 0) allMatched = false; if (allMatched && nNotInHard > 0) return false; } // FSR PROBLEMS if (isFSRg2qq && nInQuarkFlav + nOutQuarkFlav > 0) { // Easiest problem 1: // RECLUSTERED FINAL STATE GLUON MATCHES INITIAL STATE GLUON for(int i=0; i < int(gluon.size()); ++i) { if (!event[gluon[i]].isFinal() && event[gluon[i]].col() == radBeforeCol && event[gluon[i]].acol() == radBeforeAcl) return false; } // Easiest problem 2: // RECLUSTERED FINAL STATE GLUON MATCHES FINAL STATE GLUON for(int i=0; i < int(gluon.size()); ++i) { if (event[gluon[i]].isFinal() && event[gluon[i]].col() == radBeforeAcl && event[gluon[i]].acol() == radBeforeCol) return false; } // Easiest problem 3: // RECLUSTERED FINAL STATE GLUON MATCHES FINAL STATE Q-QBAR PAIR if ( int(radBeforeColP.size()) == 2 && event[radBeforeColP[0]].isFinal() && event[radBeforeColP[1]].isFinal() && event[radBeforeColP[0]].id() == -1*event[radBeforeColP[1]].id() ) { // This clustering is allowed if there is no colour in the // initial state if (nInitialPartons > 0) return false; } // Next-to-easiest problem 1: // RECLUSTERED FINAL STATE GLUON MATCHES ONE FINAL STARE Q_1 // AND ONE INITIAL STATE Q_1 if ( int(radBeforeColP.size()) == 2 && (( event[radBeforeColP[0]].status() == -21 && event[radBeforeColP[1]].isFinal()) ||( event[radBeforeColP[0]].isFinal() && event[radBeforeColP[1]].status() == -21)) && event[radBeforeColP[0]].id() == event[radBeforeColP[1]].id() ) { // In principle, clustering this splitting can disconnect // the colour lines of a graph. However, the colours can be connected // again if a final or initial partons of the correct flavour exists. // Check which of the partners are final / initial int incoming = (event[radBeforeColP[0]].isFinal()) ? radBeforeColP[1] : radBeforeColP[0]; int outgoing = (event[radBeforeColP[0]].isFinal()) ? radBeforeColP[0] : radBeforeColP[1]; // Loop through event to find "recovery partons" bool clusPossible = false; for(int i=0; i < int(event.size()); ++i) if ( i != emt && i != rad && i != incoming && i != outgoing && !mergingHooksPtr->hardProcess->matchesAnyOutgoing(i,event) ) { // Check if an incoming parton matches if ( event[i].status() == -21 && (event[i].id() == event[outgoing].id() ||event[i].id() == -1*event[incoming].id()) ) clusPossible = true; // Check if a final parton matches if ( event[i].isFinal() && (event[i].id() == -1*event[outgoing].id() ||event[i].id() == event[incoming].id()) ) clusPossible = true; } // There can be a further complication: If e.g. in // t-channel photon exchange topologies, both incoming // partons are quarks, and form colour singlets with any // number of final state partons, at least try to // recluster as much as possible. // For this, check if the incoming parton // connected to the radiator is connected to a // colour and flavour singlet vector excludeIn1; for(int i=0; i < 4; ++i) excludeIn1.push_back(0); vector colSingletIn1; int flavIn1Type = (event[incoming].id() > 0) ? 1 : -1; // Try finding colour singlets bool isColSingIn1 = getColSinglet(flavIn1Type,incoming,event, excludeIn1,colSingletIn1); // Check if colour singlet also is a flavour singlet bool isFlavSingIn1 = isFlavSinglet(event,colSingletIn1); // Check if the incoming parton not // connected to the radiator is connected to a // colour and flavour singlet int incoming2 = (incoming == 3) ? 4 : 3; vector excludeIn2; for(int i=0; i < 4; ++i) excludeIn2.push_back(0); vector colSingletIn2; int flavIn2Type = (event[incoming2].id() > 0) ? 1 : -1; // Try finding colour singlets bool isColSingIn2 = getColSinglet(flavIn2Type,incoming2,event, excludeIn2,colSingletIn2); // Check if colour singlet also is a flavour singlet bool isFlavSingIn2 = isFlavSinglet(event,colSingletIn2); // If no "recovery clustering" is possible, reject clustering if (!clusPossible && (!isColSingIn1 || !isFlavSingIn1 || !isColSingIn2 || !isFlavSingIn2)) return false; } // Next-to-easiest problem 2: // FINAL STATE Q-QBAR CLUSTERING DISCONNECTS SINGLETT SUBSYSTEM WITH // FINAL STATE Q-QBAR PAIR FROM GRAPH // Prepare to check for colour singlet combinations of final state quarks // Start by building a list of partons to exclude when checking for // colour singlet combinations int flav = event[emt].id(); vector exclude; exclude.push_back(emt); exclude.push_back(rad); exclude.push_back(radBeforeColP[0]); exclude.push_back(radBeforeColP[1]); vector colSinglet; // Now find parton from which to start checking colour singlets int iOther = -1; // Loop through event to find a parton of correct flavour for(int i=0; i < int(event.size()); ++i) // Check final state for parton equalling emitted flavour. // Exclude the colour system coupled to the clustering if ( i != emt && i != rad && i != radBeforeColP[0] && i != radBeforeColP[1] && event[i].isFinal() ) { // Stop if one parton of the correct flavour is found if (event[i].id() == flav) { iOther = i; break; } } // Save the type of flavour int flavType = (iOther > 0 && event[iOther].id() > 0) ? 1 : (iOther > 0) ? -1 : 0; // Try finding colour singlets bool isColSing = getColSinglet(flavType,iOther,event,exclude,colSinglet); // Check if colour singlet also is a flavour singlet bool isFlavSing = isFlavSinglet(event,colSinglet); // Check if the colour singlet is precisely contained in the hard process. // If so, then we're safe to recluster. bool isHardSys = true; for(int i=0; i < int(colSinglet.size()); ++i) isHardSys = mergingHooksPtr->hardProcess->matchesAnyOutgoing( colSinglet[i], event); // Nearly there... // If the decoupled colour singlet system is NOT contained in the hard // process, we need to check the whole final state. if (isColSing && isFlavSing && !isHardSys) { // In a final check, ensure that the final state does not only // consist of colour singlets that are also flavour singlets // of the identical (!) flavours // Loop through event and save all final state partons vector allFinal; for(int i=0; i < int(event.size()); ++i) if ( event[i].isFinal() ) allFinal.push_back(i); // Check if all final partons form a colour singlet bool isFullColSing = isColSinglet(event,allFinal); // Check if all final partons form a flavour singlet bool isFullFlavSing = isFlavSinglet(event,allFinal,flav); // If all final quarks are of identical flavour, // no possible clustering should be discriminated. // Otherwise, disallow if (!isFullColSing || !isFullFlavSing) return false; } } // ISR PROBLEMS if (isISRg2qq && nInQuarkFlav + nOutQuarkFlav > 0) { // Easiest problem 1: // RECLUSTERED INITIAL STATE GLUON MATCHES FINAL STATE GLUON for(int i=0; i < int(gluon.size()); ++i) { if (event[gluon[i]].isFinal() && event[gluon[i]].col() == radBeforeCol && event[gluon[i]].acol() == radBeforeAcl) return false; } // Easiest problem 2: // RECLUSTERED INITIAL STATE GLUON MATCHES INITIAL STATE GLUON for(int i=0; i < int(gluon.size()); ++i) { if (event[gluon[i]].status() == -21 && event[gluon[i]].acol() == radBeforeCol && event[gluon[i]].col() == radBeforeAcl) return false; } // Next-to-easiest problem 1: // RECLUSTERED INITIAL STATE GLUON MATCHES FINAL STATE Q-QBAR PAIR if ( int(radBeforeColP.size()) == 2 && event[radBeforeColP[0]].isFinal() && event[radBeforeColP[1]].isFinal() && event[radBeforeColP[0]].id() == -1*event[radBeforeColP[1]].id() ) { // In principle, clustering this splitting can disconnect // the colour lines of a graph. However, the colours can be connected // again if final state partons of the correct (anti)flavour, or // initial state partons of the correct flavour exist // Loop through event to check bool clusPossible = false; for(int i=0; i < int(event.size()); ++i) if ( i != emt && i != rad && i != radBeforeColP[0] && i != radBeforeColP[1] && !mergingHooksPtr->hardProcess->matchesAnyOutgoing(i,event) ) { if (event[i].status() == -21 && ( event[radBeforeColP[0]].id() == event[i].id() || event[radBeforeColP[1]].id() == event[i].id() )) clusPossible = true; if (event[i].isFinal() && ( event[radBeforeColP[0]].id() == -1*event[i].id() || event[radBeforeColP[1]].id() == -1*event[i].id() )) clusPossible = true; } // There can be a further complication: If e.g. in // t-channel photon exchange topologies, both incoming // partons are quarks, and form colour singlets with any // number of final state partons, at least try to // recluster as much as possible. // For this, check if the incoming parton // connected to the radiator is connected to a // colour and flavour singlet int incoming1 = 3; vector excludeIn1; for(int i=0; i < 4; ++i) excludeIn1.push_back(0); vector colSingletIn1; int flavIn1Type = (event[incoming1].id() > 0) ? 1 : -1; // Try finding colour singlets bool isColSingIn1 = getColSinglet(flavIn1Type,incoming1,event, excludeIn1,colSingletIn1); // Check if colour singlet also is a flavour singlet bool isFlavSingIn1 = isFlavSinglet(event,colSingletIn1); // Check if the incoming parton not // connected to the radiator is connected to a // colour and flavour singlet int incoming2 = 4; vector excludeIn2; for(int i=0; i < 4; ++i) excludeIn2.push_back(0); vector colSingletIn2; int flavIn2Type = (event[incoming2].id() > 0) ? 1 : -1; // Try finding colour singlets bool isColSingIn2 = getColSinglet(flavIn2Type,incoming2,event, excludeIn2,colSingletIn2); // Check if colour singlet also is a flavour singlet bool isFlavSingIn2 = isFlavSinglet(event,colSingletIn2); // If no "recovery clustering" is possible, reject clustering if (!clusPossible && (!isColSingIn1 || !isFlavSingIn1 || !isColSingIn2 || !isFlavSingIn2)) return false; } } // Done return allowed; } //-------------------------------------------------------------------------- // Function to check if rad,emt,rec triple is results in // colour singlet radBefore+recBefore // IN int rad,emt,rec : Positions (in event record) of the three // particles considered for clustering // Event event : Reference event bool History::isSinglett( int rad, int emt, int rec, const Event& event ) { int radCol = event[rad].col(); int emtCol = event[emt].col(); int recCol = event[rec].col(); int radAcl = event[rad].acol(); int emtAcl = event[emt].acol(); int recAcl = event[rec].acol(); int recType = event[rec].isFinal() ? 1 : -1; bool isSing = false; if ( ( recType == -1 && radCol + emtCol == recCol && radAcl + emtAcl == recAcl) ||( recType == 1 && radCol + emtCol == recAcl && radAcl + emtAcl == recCol) ) isSing = true; return isSing; } //-------------------------------------------------------------------------- // Function to check if event is sensibly constructed: Meaning // that all colour indices are contracted and that the charge in // initial and final states matches // IN event : event to be checked // OUT TRUE : event is properly construced // FALSE : event not valid bool History::validEvent( const Event& event ) { // Check if event is coloured bool validColour = true; for ( int i = 0; i < event.size(); ++i) // Check colour of quarks if ( event[i].isFinal() && event[i].colType() == 1 // No corresponding anticolour in final state && ( FindCol(event[i].col(),i,0,event,1,true) == 0 // No corresponding colour in initial state && FindCol(event[i].col(),i,0,event,2,true) == 0 )) { validColour = false; break; // Check anticolour of antiquarks } else if ( event[i].isFinal() && event[i].colType() == -1 // No corresponding colour in final state && ( FindCol(event[i].acol(),i,0,event,2,true) == 0 // No corresponding anticolour in initial state && FindCol(event[i].acol(),i,0,event,1,true) == 0 )) { validColour = false; break; // No uncontracted colour (anticolour) charge of gluons } else if ( event[i].isFinal() && event[i].colType() == 2 // No corresponding anticolour in final state && ( FindCol(event[i].col(),i,0,event,1,true) == 0 // No corresponding colour in initial state && FindCol(event[i].col(),i,0,event,2,true) == 0 ) // No corresponding colour in final state && ( FindCol(event[i].acol(),i,0,event,2,true) == 0 // No corresponding anticolour in initial state && FindCol(event[i].acol(),i,0,event,1,true) == 0 )) { validColour = false; break; } // Check charge sum in initial and final state bool validCharge = true; double initCharge = event[3].charge() + event[4].charge(); double finalCharge = 0.0; for(int i = 0; i < event.size(); ++i) if (event[i].isFinal()) finalCharge += event[i].charge(); if (abs(initCharge-finalCharge) > 1e-12) validCharge = false; return (validColour && validCharge); } //-------------------------------------------------------------------------- // Function to check whether two clusterings are identical, used // for finding the history path in the mother -> children direction bool History::equalClustering( Clustering clus1 , Clustering clus2 ) { return ( (clus1.emittor == clus2.emittor) && (clus1.emitted == clus2.emitted) && (clus1.recoiler == clus2.recoiler) && (clus1.partner == clus2.partner) && (clus1.pT() == clus2.pT()) && (clus1.spinRadBef == clus2.spinRadBef) && (clus1.spinRad == clus2.spinRad) && (clus1.spinEmt == clus2.spinEmt) && (clus1.spinRec == clus2.spinRec) && (clus1.flavRadBef == clus2.flavRadBef)); } //-------------------------------------------------------------------------- // Chose dummy scale for event construction. By default, choose // sHat for 2->Boson(->2)+ n partons processes and // M_Boson for 2->Boson(->) processes double History::choseHardScale( const Event& event ) const { // Get sHat double mHat = (event[3].p() + event[4].p()).mCalc(); // Find number of final state particles and bosons int nFinal = 0; int nFinBos= 0; int nBosons= 0; double mBos = 0.0; for(int i = 0; i < event.size(); ++i) if ( event[i].isFinal() ) { nFinal++; // Remember final state unstable bosons if ( event[i].idAbs() == 23 || event[i].idAbs() == 24 ) { nFinBos++; nBosons++; mBos += event[i].m(); } } else if ( abs(event[i].status()) == 22 && ( event[i].idAbs() == 23 || event[i].idAbs() == 24 )) { nBosons++; mBos += event[i].m(); // Real mass } // Return averaged boson masses if ( nBosons > 0 && (nFinal + nFinBos*2) <= 3) return (mBos / double(nBosons)); else return mHat; } //-------------------------------------------------------------------------- // If the state has an incoming hadron return the flavour of the // parton entering the hard interaction. Otherwise return 0 int History::getCurrentFlav(const int side) const { int in = (side == 1) ? 3 : 4; return state[in].id(); } //-------------------------------------------------------------------------- double History::getCurrentX(const int side) const { int in = (side == 1) ? 3 : 4; return ( 2.*state[in].e()/state[0].e() ); } //-------------------------------------------------------------------------- double History::getCurrentZ(const int rad, const int rec, const int emt, int idRadBef) const { int type = state[rad].isFinal() ? 1 : -1; double z = 0.; if (type == 1) { Vec4 radAfterBranch(state[rad].p()); Vec4 recAfterBranch(state[rec].p()); Vec4 emtAfterBranch(state[emt].p()); // Store masses both after and prior to emission. double m2RadAft = radAfterBranch.m2Calc(); double m2EmtAft = emtAfterBranch.m2Calc(); double m2RadBef = 0.; if ( state[rad].idAbs() != 21 && state[rad].idAbs() != 22 && state[emt].idAbs() != 24 && state[rad].idAbs() != state[emt].idAbs()) m2RadBef = m2RadAft; else if ( state[emt].idAbs() == 24) { if (idRadBef != 0) m2RadBef = pow2(particleDataPtr->m0(abs(idRadBef))); } double Qsq = (radAfterBranch + emtAfterBranch).m2Calc(); // Calculate dipole invariant mass. double m2final = (radAfterBranch + recAfterBranch + emtAfterBranch).m2Calc(); // More complicated for initial state recoiler. if ( !state[rec].isFinal() ){ double mar2 = m2final - 2. * Qsq + 2. * m2RadBef; recAfterBranch *= (1. - (Qsq - m2RadBef)/(mar2 - m2RadBef)) /(1. + (Qsq - m2RadBef)/(mar2 - m2RadBef)); // If Qsq is larger than mar2 the event is not kinematically possible. // Just return random z, since clustering will be discarded. if (Qsq > mar2) return 0.5; } Vec4 sum = radAfterBranch + recAfterBranch + emtAfterBranch; double m2Dip = sum.m2Calc(); // Construct 2->3 variables for FSR double x1 = 2. * (sum * radAfterBranch) / m2Dip; double x2 = 2. * (sum * recAfterBranch) / m2Dip; // Prepare for more complicated z definition for massive splittings. double lambda13 = sqrt( pow2(Qsq - m2RadAft - m2EmtAft ) - 4.*m2RadAft*m2EmtAft); double k1 = ( Qsq - lambda13 + (m2EmtAft - m2RadAft ) ) / ( 2. * Qsq ); double k3 = ( Qsq - lambda13 - (m2EmtAft - m2RadAft ) ) / ( 2. * Qsq ); // Calculate z of splitting, different for FSR z = 1./ ( 1- k1 -k3) * ( x1 / (2.-x2) - k3); } else { // Construct momenta of dipole before/after splitting for ISR Vec4 qBR(state[rad].p() - state[emt].p() + state[rec].p()); Vec4 qAR(state[rad].p() + state[rec].p()); // Calculate z of splitting, different for ISR z = (qBR.m2Calc())/( qAR.m2Calc()); } return z; } //-------------------------------------------------------------------------- // Function to compute "pythia pT separation" from Particle input double History::pTLund(const Event& event, int rad, int emt, int rec, int ShowerType, int idRadBef) { Particle RadAfterBranch = event[rad]; Particle EmtAfterBranch = event[emt]; Particle RecAfterBranch = event[rec]; // Use external shower for merging. if ( mergingHooksPtr->useShowerPlugin() ) { map stateVars; bool isFSR = showers->timesPtr->isTimelike(event, rad, emt, rec, ""); if (isFSR) { string name = showers->timesPtr->getSplittingName(event, rad, emt, rec).front(); stateVars = showers->timesPtr->getStateVariables(event, rad, emt, rec, name); } else { string name = showers->spacePtr->getSplittingName(event, rad, emt, rec).front(); stateVars = showers->spacePtr->getStateVariables(event, rad, emt, rec, name); } return ( (stateVars.size() > 0 && stateVars.find("t") != stateVars.end()) ? sqrt(stateVars["t"]) : -1.0 ); } // Save type: 1 = FSR pT definition, else ISR definition int Type = ShowerType; // Calculate virtuality of splitting int sign = (Type==1) ? 1 : -1; Vec4 Q(RadAfterBranch.p() + sign*EmtAfterBranch.p()); double Qsq = sign * Q.m2Calc(); // Construct 2->3 variables for FSR Vec4 radAft(RadAfterBranch.p()); Vec4 recAft(RecAfterBranch.p()); Vec4 emtAft(EmtAfterBranch.p()); // Store masses both after and prior to emission. double m2RadAft = radAft.m2Calc(); double m2EmtAft = emtAft.m2Calc(); double m2RadBef = 0.; if ( RadAfterBranch.idAbs() != 21 && RadAfterBranch.idAbs() != 22 && EmtAfterBranch.idAbs() != 24 && RadAfterBranch.idAbs() != EmtAfterBranch.idAbs() ) m2RadBef = m2RadAft; else if (EmtAfterBranch.idAbs() == 24) { if (idRadBef != 0) m2RadBef = pow2(particleDataPtr->m0(abs(idRadBef))); } else if (!RadAfterBranch.isFinal()) { if (RadAfterBranch.idAbs() == 21 && EmtAfterBranch.idAbs() != 21) m2RadBef = m2EmtAft; } // Calculate dipole invariant mass. double m2final = (radAft + recAft + emtAft).m2Calc(); // More complicated for initial state recoiler. if ( !RecAfterBranch.isFinal() && RadAfterBranch.isFinal() ){ double mar2 = m2final - 2. * Qsq + 2. * m2RadBef; recAft *= (1. - (Qsq - m2RadBef)/(mar2 - m2RadBef)) /(1. + (Qsq - m2RadBef)/(mar2 - m2RadBef)); // Reclustering not kinematically possible if Qsq is larger than mar2. if (Qsq > mar2) return 0.; } Vec4 sum = radAft + recAft + emtAft; double m2Dip = sum.m2Calc(); double x1 = 2. * (sum * radAft) / m2Dip; double x2 = 2. * (sum * recAft) / m2Dip; // Construct momenta of dipole before/after splitting for ISR double q2BR = (RadAfterBranch.p() - EmtAfterBranch.p() + RecAfterBranch.p()).m2Calc(); double q2AR = (RadAfterBranch.p() + RecAfterBranch.p()).m2Calc(); // Prepare for more complicated z definition for massive splittings. double lambda13 = sqrt( pow2(Qsq - m2RadAft - m2EmtAft ) - 4. * m2RadAft*m2EmtAft ); double k1 = ( Qsq - lambda13 + (m2EmtAft - m2RadAft ) ) / ( 2. * Qsq ); double k3 = ( Qsq - lambda13 - (m2EmtAft - m2RadAft ) ) / ( 2. * Qsq ); // Calculate z of splitting, different for FSR and ISR double z = (Type==1) ? 1./ ( 1- k1 -k3) * ( x1 / (2.-x2) - k3) : q2BR / q2AR; // Separation of splitting, different for FSR and ISR double pTpyth = (Type==1) ? z*(1.-z) : (1.-z); // pT^2 = separation*virtuality if (Type == 1) pTpyth *= (Qsq - m2RadBef); else pTpyth *= Qsq; // Check for threshold in ISR, only relevant for c and b. // Use pT2 = (1 - z) * (Qsq + m^2). if ( Type != 1) { if ( (RadAfterBranch.idAbs() == 4 || EmtAfterBranch.idAbs() == 4) && RadAfterBranch.idAbs() != EmtAfterBranch.idAbs()) { if (pTpyth < 2 * pow2(particleDataPtr->m0(4))) pTpyth = (Qsq + pow2(particleDataPtr->m0(4)) ) * (1. - q2BR/q2AR); } else if ( (RadAfterBranch.idAbs() == 5 || EmtAfterBranch.idAbs() == 5) && RadAfterBranch.idAbs() != EmtAfterBranch.idAbs()) { if (pTpyth < 2 * pow2(particleDataPtr->m0(5))) pTpyth = (Qsq + pow2(particleDataPtr->m0(5)) ) * (1. - q2BR/q2AR); } } if ( pTpyth < 0. ) pTpyth = 0.; // Return pT return sqrt(pTpyth); } //-------------------------------------------------------------------------- // Function to return the position of the initial line before (or after) // a single (!) splitting. int History::posChangedIncoming(const Event& event, bool before) { // Check for initial state splittings. // Consider a splitting to exist if both mother and sister were found. // Find sister int iSister = 0; for (int i =0; i < event.size(); ++i) if (event[i].status() == 43) { iSister = i; break; } // Find mother int iMother = 0; if (iSister > 0) iMother = event[iSister].mother1(); // Initial state splitting has been found. if (iSister > 0 && iMother > 0) { // Find flavour, mother flavour int flavSister = event[iSister].id(); int flavMother = event[iMother].id(); // Find splitting flavour int flavDaughter = 0; if ( abs(flavMother) < 21 && flavSister == 21) flavDaughter = flavMother; else if ( flavMother == 21 && flavSister == 21) flavDaughter = flavMother; else if ( flavMother == 21 && abs(flavSister) < 21) flavDaughter = -1*flavSister; else if ( abs(flavMother) < 21 && abs(flavSister) < 21) flavDaughter = 21; // Find initial state (!) daughter int iDaughter = 0; for (int i =0; i < event.size(); ++i) if ( !event[i].isFinal() && event[i].mother1() == iMother && event[i].id() == flavDaughter ) iDaughter = i; // Done for initial state splitting. if ( !before ) return iMother; else return iDaughter; } // Check for final state splittings with initial state recoiler. // Consider a splitting to exist if both mother and daughter were found. // Find new mother iMother = 0; for (int i =0; i < event.size(); ++i) if ( abs(event[i].status()) == 53 || abs(event[i].status()) == 54) { iMother = i; break; } // Find daughter int iDaughter = 0; if (iMother > 0) iDaughter = event[iMother].daughter1(); // Done if final state splitting has been found. if (iDaughter > 0 && iMother > 0) { // Done for final state splitting. if ( !before ) return iMother; else return iDaughter; } // If no splitting has been found, return zero. return 0; } //-------------------------------------------------------------------------- // Function to give back the ratio of PDFs and PDF * splitting kernels needed // to convert a splitting at scale pdfScale, chosen with running PDFs, to a // splitting chosen with PDFs at a fixed scale mu. As needed to properly count // emissions. double History::pdfFactor( const Event& event, const int type, double pdfScale, double mu ) { double weight = 1.; // Final state splittings if (type >= 3) { // Find new mother int iMother = 0; for (int i =0; i < event.size(); ++i) if ( abs(event[i].status()) == 53 || abs(event[i].status()) == 54) { iMother = i; break; } int flavMother = event[iMother].id(); // Done if no initial state recoiler was found if ( iMother == 0 ) return 1.; // Find daughter int iDaughter = event[iMother].daughter1(); int flavDaughter = event[iDaughter].id(); // Find x values double xMother = 2.*event[iMother].e() / event[0].e(); double xDaughter = 2.*event[iDaughter].e() / event[0].e(); // Calculate PDF ratios int sideSplit = ( event[iMother].pz() > 0.) ? 1 : -1; double pdfDen1, pdfDen2, pdfNum1, pdfNum2; pdfDen1 = pdfDen2 = pdfNum1 = pdfNum2 = 1.; if ( sideSplit == 1 ) { // Find PDFs pdfDen1 = max(1e-15,beamA.xfISR(0, flavDaughter, xDaughter, pow2(mu)) ); pdfNum1 = beamA.xfISR(0, flavDaughter, xDaughter, pow2(pdfScale) ); pdfNum2 = beamA.xfISR(0, flavMother, xMother, pow2(mu) ); pdfDen2 = max(1e-15,beamA.xfISR(0,flavMother, xMother, pow2(pdfScale)) ); } else { // Find PDFs pdfDen1 = max(1e-15,beamB.xfISR(0, flavDaughter, xDaughter, pow2(mu)) ); pdfNum1 = beamB.xfISR(0, flavDaughter, xDaughter, pow2(pdfScale) ); pdfNum2 = beamB.xfISR(0, flavMother, xMother, pow2(mu) ); pdfDen2 = max(1e-15,beamB.xfISR(0,flavMother, xMother, pow2(pdfScale)) ); } // The magnitude of the PDF ratio in FSR is limited to one. If that was // the case, return one. if ( pdfDen2/pdfNum1 > 1. ) return 1.; // Calculate PDF weight to reweight emission to emission evaluated at // constant factorisation scale. No need to include the splitting kernel in // the weight, since it will drop out anyway. weight = (pdfNum1/pdfDen1) * (pdfNum2)/(pdfDen2); // Initial state splittings } else if (type == 2) { // Find sister int iSister = 0; for (int i =0; i < event.size(); ++i) if (event[i].status() == 43) { iSister = i; break; } int flavSister = event[iSister].id(); // Find mother int iMother = event[iSister].mother1(); int flavMother = event[iMother].id(); // Find splitting flavour int flavDaughter = 0; if ( abs(flavMother) < 21 && flavSister == 21) flavDaughter = flavMother; else if ( flavMother == 21 && flavSister == 21) flavDaughter = flavMother; else if ( flavMother == 21 && abs(flavSister) < 21) flavDaughter = -1*flavSister; else if ( abs(flavMother) < 21 && abs(flavSister) < 21) flavDaughter = 21; // Find x values double xMother = 2.*event[iMother].e() / event[0].e(); // Find initial state (!) daughter int iDaughter = 0; for (int i =0; i < event.size(); ++i) if ( !event[i].isFinal() && event[i].mother1() == iMother && event[i].id() == flavDaughter ) iDaughter = i; double xDaughter = 2.*event[iDaughter].e() / event[0].e(); // Calculate PDF weight to reweight emission to emission evaluated at // constant factorisation scale. No need to include the splitting kernel // in the weight, since it will drop out anyway. int sideSplit = ( event[iMother].pz() > 0.) ? 1 : -1; double ratio1 = getPDFratio( sideSplit, false, false, flavDaughter, xDaughter, pdfScale, flavDaughter, xDaughter, mu ); double ratio2 = getPDFratio( sideSplit, false, false, flavMother, xMother, mu, flavMother, xMother, pdfScale ); weight = ratio1*ratio2; // Do nothing for MPI } else { weight = 1.; } // Done return weight; } //-------------------------------------------------------------------------- // Function giving the product of splitting kernels and PDFs so that the // resulting flavour is given by flav. This is used as a helper routine // to dgauss double History::integrand(int flav, double x, double scaleInt, double z) { // Declare constants double CF = 4./3.; double TR = 1./2.; double CA = 3.; double result = 0.; // Integrate NLL sudakov remainder if (flav==0) { AlphaStrong* as = mergingHooksPtr->AlphaS_ISR(); double asNow = (*as).alphaS(z); result = 1./z *asNow*asNow* ( log(scaleInt/z) -3./2. ); // Integrand for PDF ratios. Careful about factors if 1/z, since formulae // are expressed in terms if f(x,mu), while Pythia uses x*f(x,mu)! } else if (flav==21) { double measure1 = 1./(1. - z); double measure2 = 1.; double integrand1 = 2.*CA * z * beamB.xf( 21,x/z,pow(scaleInt,2)) / beamB.xf( 21,x, pow(scaleInt,2)) - 2.*CA; double integrand2 = // G -> G terms 2.*CA *((1. -z)/z + z*(1.-z)) * beamB.xf( 21,x/z,pow(scaleInt,2)) / beamB.xf( 21,x, pow(scaleInt,2)) // G -> Q terms + CF * ((1+pow(1-z,2))/z) *( beamB.xf( 1, x/z,pow(scaleInt,2)) / beamB.xf( 21, x, pow(scaleInt,2)) + beamB.xf( -1, x/z,pow(scaleInt,2)) / beamB.xf( 21, x, pow(scaleInt,2)) + beamB.xf( 2, x/z,pow(scaleInt,2)) / beamB.xf( 21, x, pow(scaleInt,2)) + beamB.xf( -2, x/z,pow(scaleInt,2)) / beamB.xf( 21, x, pow(scaleInt,2)) + beamB.xf( 3, x/z,pow(scaleInt,2)) / beamB.xf( 21, x, pow(scaleInt,2)) + beamB.xf( -3, x/z,pow(scaleInt,2)) / beamB.xf( 21, x, pow(scaleInt,2)) + beamB.xf( 4, x/z,pow(scaleInt,2)) / beamB.xf( 21, x, pow(scaleInt,2)) + beamB.xf( -4, x/z,pow(scaleInt,2)) / beamB.xf( 21, x, pow(scaleInt,2)) ); // Done result = integrand1*measure1 + integrand2*measure2; } else { double measure1 = 1./(1. -z); double measure2 = 1.; // Q -> Q terms double integrand1 = CF * (1+pow(z,2)) * beamB.xf( flav, x/z, pow(scaleInt,2)) / beamB.xf( flav, x, pow(scaleInt,2)) - 2.*CF; // Q -> G terms double integrand2 = + TR * (pow(z,2) + pow(1-z,2)) * beamB.xf( 21, x/z, pow(scaleInt,2)) / beamB.xf( flav, x, pow(scaleInt,2)); // Done result = measure1*integrand1 + measure2*integrand2; } return result; } //-------------------------------------------------------------------------- // Function providing a list of possible new flavours after a w emssion // from the input flavour. vector History::posFlavCKM(int flav) { // absolute values! int flavAbs = abs(flav); vector flavRadBefs; // (e,mu,tau) if (flavAbs > 10 && flavAbs % 2 == 1) flavRadBefs.push_back(flavAbs + 1); // (neutrinoes) else if (flavAbs > 10 && flavAbs % 2 == 0) flavRadBefs.push_back(flavAbs - 1); // Full CKM for quarks. else if (flavAbs < 10 && flavAbs % 2 == 1) { flavRadBefs.push_back(2); flavRadBefs.push_back(4); flavRadBefs.push_back(6); } else if (flavAbs < 10 && flavAbs % 2 == 0) { flavRadBefs.push_back(1); flavRadBefs.push_back(3); flavRadBefs.push_back(5); } // Return the possible flavours. return flavRadBefs; } //-------------------------------------------------------------------------- // Check if the new flavour structure is possible. // If clusType is 1 final clustering is assumed, otherwise initial clustering // is assumed. bool History::checkFlavour(vector& flavCounts, int flavRad, int flavRadBef, int clusType) { // Loop over event. for(int k = 0; k < 20; ++k) { // Find changes from this W emission. int cor = 0; if (abs(flavRad) == k) { cor = -1; if (flavRad < 0) cor = 1; } if (abs(flavRadBef) == k) { cor = 1; if (flavRadBef < 0) cor = -1; } // if flavour and flavRadBef is the same, no correction. if (flavRadBef == flavRad) cor = 0; // Check if flavour is consistent. if (clusType == 1) { if (flavCounts[k] + cor != 0) return false; } else if (flavCounts[k] - cor != 0) return false; } // No more checks. return true; } //-------------------------------------------------------------------------- // Reverse the boost carried out by the ISR. // The three first momenta are given by the ME, // the last two are filled in by this function. void History::reverseBoostISR(Vec4& pMother, Vec4& pSister, Vec4& pPartner, Vec4& pDaughter, Vec4& pRecoiler, int sign, double eCM, double& phi ) { // Find rotation by phi that would have been done for a // splitting daughter -> mother + sister phi = pSister.phi(); // Find rotation with -phi RotBstMatrix rot_by_mphi; rot_by_mphi.rot(0.,-phi); // Find rotation with +phi RotBstMatrix rot_by_pphi; rot_by_pphi.rot(0.,phi); // Get mother and partner x values // x1 after isr double x1 = 2. * pMother.e() / eCM; // x2 after isr double x2 = 2. * pPartner.e() / eCM; // Find z of the splitting Vec4 qDip( pMother - pSister); Vec4 qAfter(pMother + pPartner); Vec4 qBefore(qDip + pPartner); double z = qBefore.m2Calc() / qAfter.m2Calc(); // Calculate e_CM^2 before the splitting. double x1New = z*x1; // x1 before isr double x2New = x2; // x2 before isr double sHat = x1New*x2New*eCM*eCM; // Construct daughter and recoiler momenta before the splitting. // (Note: For final result, only needs to be boosted into // frame with unchanged "recoiler" momentum) Vec4 pDaughterBef( 0., 0., sign*0.5*sqrt(sHat), 0.5*sqrt(sHat)); Vec4 pRecoilerBef( 0., 0., -sign*0.5*sqrt(sHat), 0.5*sqrt(sHat)); // Rotate momenta defined in the lab frame by phi pMother.rotbst( rot_by_mphi ); pSister.rotbst( rot_by_mphi ); pPartner.rotbst( rot_by_mphi ); // Find boost from lab frame to rest frame of // off-shell daughter + on-shell recoiler dipole pDaughter.p( pMother - pSister); pRecoiler.p( pPartner ); RotBstMatrix from_CM_to_DRoff; if (sign == 1) from_CM_to_DRoff.toCMframe(pDaughter, pRecoiler); else from_CM_to_DRoff.toCMframe(pRecoiler, pDaughter); // Rotate and boost all momenta to rest frame of off-shell daughter + // on-shell recoiler dipole pMother.rotbst( from_CM_to_DRoff ); pPartner.rotbst( from_CM_to_DRoff ); pSister.rotbst( from_CM_to_DRoff ); // Find longitudinal boost from on-shell daughter + on-shell recoiler // dipole rest frame to the frame in which the recoiler momentum (x-value) // does not change in the splitting process. RotBstMatrix from_DR_to_CM; from_DR_to_CM.bst( 0., 0., sign*( x1New - x2New ) / ( x1New + x2New ) ); // Boost all momenta into the "unchanged recoiler" frame, thereby // correcting for momentum mismatch by transferring the recoil to all // final state particles. pDaughterBef.rotbst( from_DR_to_CM ); pRecoilerBef.rotbst( from_DR_to_CM ); // Ensure that radiator and recoiler are massless to // very good accuracy. if ( abs(pRecoilerBef.mCalc()) > 1e-7 ) { double pzSign = (pRecoilerBef.pz() > 0.) ? 1. : -1.; double eRec = pRecoilerBef.e(); pRecoilerBef.p(0., 0., pzSign*eRec, eRec); } if ( abs(pDaughterBef.mCalc()) > 1e-7 ) { double pzSign = (pDaughterBef.pz() > 0.) ? 1. : -1.; double eDau = pDaughterBef.e(); pDaughterBef.p(0., 0., pzSign*eDau, eDau); } // Done. return; } //-------------------------------------------------------------------------- // Check if an event reclustered into a 2 -> 2 dijet. // (Only enabled if W reclustering is used). bool History::isQCD2to2(const Event& event) { if (!mergingHooksPtr->doWeakClustering()) return false; //if (event.size() == 7) return true; //else return false; int nFinalPartons = 0, nFinal = 0;; for (int i = 0;i < event.size();++i) if (event[i].isFinal()) { nFinal++; if ( event[i].idAbs() < 10 || event[i].idAbs() == 21) nFinalPartons++; } if (nFinalPartons == 2 && nFinal == 2) return true; else return false; } //-------------------------------------------------------------------------- // Check if an event reclustered into a 2 -> 1 Drell-Yan. // (Only enabled if W reclustering is used). bool History::isEW2to1(const Event& event) { if (!mergingHooksPtr->doWeakClustering()) return false; int nVector = 0; for (int i = 0;i < event.size();++i) { if (event[i].isFinal()) { if (event[i].idAbs() == 23 || event[i].idAbs() == 24 || event[i].idAbs() == 22) nVector++; else return false; } } // Only true if a single vector boson as outgoing process. if (nVector == 1) return true; // Done return false; } //-------------------------------------------------------------------------- // Set selected child indices. void History::setSelectedChild() { if (mother == 0) return; for (int i = 0;i < int(mother->children.size());++i) if (mother->children[i] == this) mother->selectedChild = i; mother->setSelectedChild(); } //-------------------------------------------------------------------------- void History::setupWeakShower(int nSteps) { // Go back to original 2 to 2 process. if (selectedChild != -1) { children[selectedChild]->setupWeakShower(nSteps+1); return; } // Defining needed containers. vector mode, fermionLines; vector mom; vector > dipoles; // Setup hard process. setupWeakHard(mode,fermionLines, mom); // Setup dipoles if (isQCD2to2(state)) { // Add dipoles. if (state[3].idAbs() < 10) dipoles.push_back(make_pair(3,4)); if (state[4].idAbs() < 10) dipoles.push_back(make_pair(4,3)); if (state[5].idAbs() < 10) dipoles.push_back(make_pair(5,6)); if (state[6].idAbs() < 10) dipoles.push_back(make_pair(6,5)); } else if (isEW2to1(state)) { if (state[3].idAbs() < 10) dipoles.push_back(make_pair(3,4)); if (state[4].idAbs() < 10) dipoles.push_back(make_pair(4,3)); } // Update the dipoles untill the desired number of emissions is reached. transferWeakShower(mode, mom, fermionLines, dipoles, nSteps); } //-------------------------------------------------------------------------- // Update weak dipoles after an emission. void History::transferWeakShower(vector &mode, vector &mom, vector fermionLines, vector > &dipoles, int nSteps) { // store information in info pointer when reached last step. if (nSteps == 0) { infoPtr->setWeakModes(mode); infoPtr->setWeakDipoles(dipoles); infoPtr->setWeakMomenta(mom); infoPtr->setWeak2to2lines(fermionLines); return; } // Find the transfer map. map stateTransfer; findStateTransfer(stateTransfer); // Update modes, fermion lines and dipoles. vector modeNew = updateWeakModes(mode, stateTransfer); vector fermionLinesNew = updateWeakFermionLines(fermionLines, stateTransfer); vector > dipolesNew = updateWeakDipoles(dipoles, stateTransfer); // Recursive call to transfer to desired final step. mother->transferWeakShower(modeNew, mom, fermionLinesNew, dipolesNew, nSteps - 1); } //-------------------------------------------------------------------------- // Update the weak modes after an emission. vector History::updateWeakModes(vector& mode, map& stateTransfer) { vector modeNew(mode.size() + 1,0); // Update all modes not involved in emission. for (map::iterator it = stateTransfer.begin(); it != stateTransfer.end(); ++it) modeNew[it->second] = mode[it->first]; modeNew[clusterIn.emitted] = mode[clusterIn.radBef]; // Update splittings. // g -> q Q mark as s-channel. if (state[clusterIn.radBef].idAbs() == 21 && mother->state[clusterIn.emittor].idAbs() != 21) { // Set FSR dipole to S-channel. if (state[clusterIn.radBef].status() > 0) modeNew[clusterIn.emittor] = 1; // Set ISR dipole depending on recoiler. else { if (modeNew[clusterIn.emittor] == 1); else if ( mother->state[clusterIn.recoiler].id() == 21) modeNew[clusterIn.emittor] = 2; else if ( mother->state[clusterIn.recoiler].id() == mother->state[clusterIn.emittor].id() ) modeNew[clusterIn.emittor] = 4; else modeNew[clusterIn.emittor] = 3; } // Emitted is always FSR. modeNew[clusterIn.emitted] = 1; } // ISR q -> q g if (state[clusterIn.radBef].idAbs() < 10 && mother->state[clusterIn.emittor].idAbs() == 21) { if (state[clusterIn.radBef].status() < 0) { modeNew[clusterIn.emitted] = 1; } } // gamma -> q Q mark as s-channel if (state[clusterIn.radBef].idAbs() == 22) { // Only FSR particles change to S-channel. if (state[clusterIn.radBef].status() > 0) modeNew[clusterIn.emittor] = 1; // Set ISR dipole depending on recoiler. else { if (modeNew[clusterIn.emittor] == 1); else if ( mother->state[clusterIn.recoiler].id() == 21) modeNew[clusterIn.emittor] = 2; else if ( mother->state[clusterIn.recoiler].id() == mother->state[clusterIn.emittor].id() ) modeNew[clusterIn.emittor] = 4; else modeNew[clusterIn.emittor] = 3; } // Emitted is always FSR. modeNew[clusterIn.emitted] = 1; } return modeNew; } //-------------------------------------------------------------------------- // Update the fermion lines for the 2 -> 2 process. This is needed for // the weak probabilities. vector History::updateWeakFermionLines(vector fermionLines, map& stateTransfer) { // Update fermion lines to 2-to-2 process. if (!fermionLines.empty()) { // Initial state lines always goes back to radiator. fermionLines[0] = stateTransfer[fermionLines[0]]; fermionLines[1] = stateTransfer[fermionLines[1]]; // If not involved in splitting just update index. bool lines[2] = {false,false}; if (fermionLines[2] != clusterIn.radBef) fermionLines[2] = stateTransfer[fermionLines[2]]; else lines[0] = true; if (fermionLines[3] != clusterIn.radBef) fermionLines[3] = stateTransfer[fermionLines[3]]; else lines[1] = true; // If involved in splitting follow the fermion line. for (int i = 0;i < 2; ++i) { if (lines[i]) { if (state[fermionLines[2 + i]].isQuark() || state[fermionLines[2 + i]].isLepton()) { if (mother->state[clusterIn.emittor].isQuark() || mother->state[clusterIn.emittor].isLepton()) fermionLines[2 + i] = clusterIn.emittor; else fermionLines[2 + i] = clusterIn.emitted; } // Stop tracing if gluon splitting. else fermionLines[2 + i] = 0; } } } return fermionLines; } //-------------------------------------------------------------------------- // Update the list of weak dipoles. This is needed to setup the PS correctly. vector > History::updateWeakDipoles( vector > &dipoles, map& stateTransfer) { vector > dipolesNew; for (int i = 0;i < int(dipoles.size());++i) { int iRecNew = -1, iRadNew = -1; // Find new radiator. if (dipoles[i].first != clusterIn.radBef) iRadNew = stateTransfer[dipoles[i].first]; // FSR emission follow the quark line. else if (state[clusterIn.radBef].status() > 0) { if (mother->state[clusterIn.emitted].id() == state[clusterIn.radBef].id()) iRadNew = clusterIn.emitted; else iRadNew = clusterIn.emittor; // OSR emission always choose the emittor. } else if (mother->state[clusterIn.emittor].idAbs() < 10) iRadNew = clusterIn.emittor; // If no radiator is found skip the dipole. if (iRadNew == -1) continue; // Find new recoiler if (dipoles[i].second != clusterIn.radBef) iRecNew = stateTransfer[dipoles[i].second]; // FSR emission follow the quark line. else if (state[clusterIn.radBef].status() > 0) { // If g -> g g, choose the one with the highest invariant mass. if (mother->state[clusterIn.emitted].id() == 21 && mother->state[clusterIn.emittor].id() == 21) { double m1 = (mother->state[clusterIn.emitted].p() + mother->state[iRadNew].p()).m2Calc(); double m2 = (mother->state[clusterIn.emittor].p() + mother->state[iRadNew].p()).m2Calc(); iRecNew = (m1 > m2) ? clusterIn.emitted : clusterIn.emittor; } // Otherwise choose matching flavour. else if (mother->state[clusterIn.emitted].id() == state[clusterIn.radBef].id()) iRecNew = clusterIn.emitted; else iRecNew = clusterIn.emittor; // ISR emission always choose the emittor. } else iRecNew = clusterIn.emittor; dipolesNew.push_back(make_pair(iRadNew,iRecNew)); } // If g -> q qbar add new dipoles. if (state[clusterIn.radBef].idAbs() == 21 && mother->state[clusterIn.emittor].idAbs() != 21) { // FSR. if (state[clusterIn.radBef].status() > 0) { dipolesNew.push_back(make_pair(clusterIn.emittor,clusterIn.emitted)); dipolesNew.push_back(make_pair(clusterIn.emitted,clusterIn.emittor)); // ISR. } else { int iRad = clusterIn.emittor; int iRec = (iRad == 3) ? 4 : 3; dipolesNew.push_back(make_pair(iRad,iRec)); dipolesNew.push_back(make_pair(clusterIn.emitted,findISRRecoiler())); } } // if an ISR quark goes into a gluon. if (state[clusterIn.radBef].idAbs() < 10 && mother->state[clusterIn.emittor].idAbs() == 21 && state[clusterIn.radBef].status() < 0) dipolesNew.push_back(make_pair(clusterIn.emitted,findISRRecoiler())); return dipolesNew; } //-------------------------------------------------------------------------- // Setup the hard process information needed for calculating weak probabilities // and setting up the shower. void History::setupWeakHard(vector& mode, vector& fermionLines, vector& mom) { if (!isQCD2to2(state)) { // Not a 2 -> 2 process, mark everything as s-channel. mode.resize(state.size(), 1); } else { // Store momenta. for (int i = 0;i < 4; ++i) { mom.push_back(state[3 + i].p()); fermionLines.push_back(3 + i); } // All gluon case, everything is s-channel. if ( state[3].idAbs() == 21 && state[4].idAbs() == 21 && state[5].idAbs() == 21 && state[6].idAbs() == 21) mode.resize(state.size(), 1); // s-channel if quark-anti quark final state or gluon final state. else if (state[5].id() == -state[6].id() || (state[5].idAbs() == 21 && state[6].idAbs() == 21)) mode.resize(state.size(), 1); // t-channel gluon. else if (state[5].idAbs() == 21 || state[6].idAbs() == 21) { mode.resize(state.size(), 2); if (state[3].id() != state[5].id()) { swap(mom[0], mom[1]); swap(mom[2], mom[3]); } } // Double (different) quark t-channel. else if (state[5].id() != state[6].id()) { mode.resize(state.size(), 3); if (state[3].id() != state[5].id()) { swap(mom[0], mom[1]); swap(mom[2], mom[3]); } } // 4 quarks of the same type. // (might need to try both combinations). else if (state[5].id() == state[6].id()) { mode.resize(state.size(), 4); } } } //-------------------------------------------------------------------------- // Function to retrieve scale information from external showers. double History::getShowerPluginScale(const Event& event, int rad, int emt, int rec, string key, double scalePythia) { // Done if no shower plugin is used. if ( !mergingHooksPtr->useShowerPlugin() ) return scalePythia; // Retrieve state variables. map stateVars; bool isFSR = showers->timesPtr->isTimelike(event, rad, emt, rec, ""); if (isFSR) { string name = showers->timesPtr->getSplittingName(event, rad, emt, rec).front(); stateVars = showers->timesPtr->getStateVariables(event, rad, emt, rec, name); } else { string name = showers->spacePtr->getSplittingName(event, rad, emt, rec).front(); stateVars = showers->spacePtr->getStateVariables(event, rad, emt, rec, name); } return ( (stateVars.size() > 0 && stateVars.find(key) != stateVars.end()) ? stateVars[key] : -1.0 ); } //========================================================================== } // end namespace Pythia8