diff --git a/Hadronization/ClusterFinder.cc b/Hadronization/ClusterFinder.cc --- a/Hadronization/ClusterFinder.cc +++ b/Hadronization/ClusterFinder.cc @@ -1,710 +1,723 @@ // -*- C++ -*- // // ClusterFinder.cc is a part of Herwig - A multi-purpose Monte Carlo event generator // Copyright (C) 2002-2019 The Herwig Collaboration // // Herwig is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // // // This is the implementation of the non-inlined, non-templated member // functions of the ClusterFinder class. // #include "ClusterFinder.h" #include <ThePEG/Interface/ClassDocumentation.h> #include <ThePEG/Interface/Switch.h> #include <ThePEG/Persistency/PersistentOStream.h> #include <ThePEG/Persistency/PersistentIStream.h> #include <ThePEG/PDT/StandardMatchers.h> #include <ThePEG/PDT/EnumParticles.h> #include <ThePEG/Repository/EventGenerator.h> #include <ThePEG/EventRecord/Collision.h> #include "Herwig/Utilities/EnumParticles.h" #include "Herwig/Utilities/Kinematics.h" #include "Cluster.h" #include <ThePEG/Utilities/DescribeClass.h> #include "ThePEG/Interface/Reference.h" using namespace Herwig; DescribeClass<ClusterFinder,Interfaced> describeClusterFinder("Herwig::ClusterFinder","Herwig.so"); IBPtr ClusterFinder::clone() const { return new_ptr(*this); } IBPtr ClusterFinder::fullclone() const { return new_ptr(*this); } void ClusterFinder::persistentOutput(PersistentOStream & os) const { os << heavyDiquarks_ << diQuarkSelection_ << diQuarkOnShell_ << _hadronSpectrum; } void ClusterFinder::persistentInput(PersistentIStream & is, int) { is >> heavyDiquarks_ >> diQuarkSelection_ >> diQuarkOnShell_ >> _hadronSpectrum; } void ClusterFinder::Init() { static ClassDocumentation<ClusterFinder> documentation ("This class is responsible of finding clusters."); static Reference<ClusterFinder,HadronSpectrum> interfaceHadronSpectrum ("HadronSpectrum", "Set the hadron spectrum for this parton splitter.", &ClusterFinder::_hadronSpectrum, false, false, false, false, false); static Switch<ClusterFinder,unsigned int> interfaceHeavyDiquarks ("HeavyDiquarks", "How to treat heavy quarks in baryon number violating clusters", &ClusterFinder::heavyDiquarks_, 2, false, false); static SwitchOption interfaceHeavyDiquarksDefault (interfaceHeavyDiquarks, "Allow", "No special treatment, allow both heavy and doubly heavy diquarks", 0); static SwitchOption interfaceHeavyDiquarksNoDoublyHeavy (interfaceHeavyDiquarks, "NoDoublyHeavy", "Avoid having diquarks with two heavy quarks", 1); static SwitchOption interfaceHeavyDiquarksNoHeavy (interfaceHeavyDiquarks, "NoHeavy", "Try and avoid diquarks contain c and b altogether", 2); static Switch<ClusterFinder,unsigned int> interfaceDiQuarkSelection ("DiQuarkSelection", "Option controlling the selection of quarks to merge into a diquark in baryon-number violating clusters", &ClusterFinder::diQuarkSelection_, 1, false, false); static SwitchOption interfaceDiQuarkSelectionRandom (interfaceDiQuarkSelection, "Random", "Randomly pick a pair to combine", 0); static SwitchOption interfaceDiQuarkSelectionLowestMass (interfaceDiQuarkSelection, "LowestMass", "Combine the lowest mass pair", 1); static SwitchOption interfaceDiQuarkSelectionLowestMassRescaled (interfaceDiQuarkSelection, "LowestMassRescaled", "Combine the quark pair with the lowest value of (p1+p2)^2/(m1+m2)^2", 2); static Switch<ClusterFinder,int> interfaceDiQuarkOnShell ("DiQuarkOnShell", "Force the diquark produced in baryon-number violating clusters to be on-shell", &ClusterFinder::diQuarkOnShell_, -1, false, false); static SwitchOption interfaceDiQuarkOnShellYes (interfaceDiQuarkOnShell, "Yes", "Force to be on-shell", 1); static SwitchOption interfaceDiQuarkOnShellMixed (interfaceDiQuarkOnShell, "Mixed", "Set Mass to constituent mass, but leave momentum as is.", -1); static SwitchOption interfaceDiQuarkOnShellNo (interfaceDiQuarkOnShell, "No", "Leave off-shell", 0); } ClusterVector ClusterFinder::formClusters(const PVector & partons) { set<tPPtr> examinedSet; // colour particles already included in a cluster map<tColinePtr, pair<tPPtr,tPPtr> > quarkQuark; // quark quark map<tColinePtr, pair<tPPtr,tPPtr> > aQuarkQuark; // anti quark anti quark ParticleSet inputParticles(partons.begin(),partons.end()); ClusterVector clusters; // Loop over all current particles. for(PVector::const_iterator pit=partons.begin();pit!=partons.end();++pit){ // Skip to the next particle if it is not coloured or already examined. assert(*pit); assert((*pit)->dataPtr()); if(!(**pit).data().coloured() || examinedSet.find(*pit) != examinedSet.end()) { continue; } // We assume that a cluster is made of, at most, 3 constituents although // in most cases the number will be 2; however, for baryon violating decays // (for example in Susy model without R parity conservation) we can have 3 // constituents. In the latter case, a quark (antiquark) do not have an // anticolour (colour) partner as usual, but its colour line either stems // from a colour source, or ends in a colour sink. In the case of double // baryon violating decays, but with overall baryon conservation // ( for instance: // tilde_u_R -> dbar_1 + dbar_2 // tilde_u_R_star -> d1 + d2 // where tilde_u_R and tilde_u_R_star are colour connected ) // a special treatment is needed, because first we have to process all // partons in the current step, and then for each left pair of quarks which // stem from a colour source we have to find the corresponding pair of // anti-quarks which ends in a colour sink and is connected with the // above colour source. These special pairs are kept into the maps: // spec/CluHadConfig.hialQuarkQuarkMap and specialAntiQuarkAntiQuarkMap. tParticleVector connected(3); int iElement = 0; connected[iElement++] = *pit; bool specialCase = false; if((*pit)->hasColour()) { tPPtr partner = (*pit)->colourLine()->getColouredParticle(partons.begin(), partons.end(), true); if(partner) { connected[iElement++]= partner; } // colour source : baryon-violating process else { if((*pit)->colourLine()->sourceNeighbours() != tColinePair()) { tColinePair sourcePair = (*pit)->colourLine()->sourceNeighbours(); tColinePtr intCL = tColinePtr(); for(int i = 0; i < 2; ++i) { tColinePtr pLine = i==0 ? sourcePair.first : sourcePair.second; int saveNumElements = iElement; for(tPVector::const_iterator cit = pLine->coloured().begin(); cit != pLine->coloured().end(); ++cit ) { ParticleSet::const_iterator cjt = inputParticles.find(*cit); if(cjt!=inputParticles.end()) connected[iElement++]= (*cit); } if(iElement == saveNumElements) intCL = pLine; } if(intCL && iElement == 2) { specialCase = true; pair<tPPtr,tPPtr> qp=pair<tPPtr,tPPtr>(connected[0],connected[1]); quarkQuark.insert(pair<tColinePtr,pair<tPPtr,tPPtr> >(intCL,qp)); } else if(iElement != 3) { throw Exception() << "Colour connections fail in the hadronization for " << **pit << "in ClusterFinder::formClusters" << " for a coloured particle." << " Failed to find particles from a source" << Exception::runerror; } } else { throw Exception() << "Colour connections fail in the hadronization for " << **pit << "in ClusterFinder::formClusters for" << " a coloured particle" << Exception::runerror; } } } if((*pit)->hasAntiColour()) { tPPtr partner = (*pit)->antiColourLine()->getColouredParticle(partons.begin(), partons.end(), false); if(partner) { connected[iElement++]=partner; } // colour sink : baryon-violating process else { if((*pit)->antiColourLine()->sinkNeighbours() != tColinePair()) { tColinePair sinkPair = (*pit)->antiColourLine()->sinkNeighbours(); tColinePtr intCL = tColinePtr(); for(int i = 0; i < 2; ++i) { tColinePtr pLine = i==0 ? sinkPair.first : sinkPair.second; int saveNumElements = iElement; for(tPVector::const_iterator cit = pLine->antiColoured().begin(); cit != pLine->antiColoured().end(); ++cit ) { ParticleSet::const_iterator cjt = inputParticles.find(*cit); if(cjt!=inputParticles.end()) connected[iElement++]= (*cit); } if(iElement == saveNumElements) intCL = pLine; } if(intCL && iElement == 2) { specialCase = true; pair<tPPtr,tPPtr> aqp=pair<tPPtr,tPPtr>(connected[0],connected[1]); aQuarkQuark.insert(pair<tColinePtr,pair<tPPtr,tPPtr> >(intCL,aqp)); } else if( iElement !=3) { throw Exception() << "Colour connections fail in the hadronization for " << **pit << "in ClusterFinder::formClusters for" << " an anti-coloured particle." << " Failed to find particles from a sink" << Exception::runerror; } } else { throw Exception() << "Colour connections fail in the hadronization for " << **pit << "in ClusterFinder::formClusters for" << " an anti-coloured particle" << Exception::runerror; } } } if(!specialCase) { // Tag the components of the found cluster as already examined. for (int i=0; i<iElement; ++i) examinedSet.insert(connected[i]); // Create the cluster object with the colour connected particles ClusterPtr cluPtr = new_ptr(Cluster(connected[0],connected[1], connected[2])); // add to the step connected[0]->addChild(cluPtr); connected[1]->addChild(cluPtr); if(connected[2]) connected[2]->addChild(cluPtr); clusters.push_back(cluPtr); // Check if any of the components is a beam remnant, and if this // is the case then inform the cluster. // this will only work for baryon collisions for (int i=0; i<iElement; ++i) { bool fromRemnant = false; tPPtr parent=connected[i]; while(parent) { if(parent->id()==ParticleID::Remnant) { fromRemnant = true; break; } parent = parent->parents().empty() ? tPPtr() : parent->parents()[0]; } if(fromRemnant&&DiquarkMatcher::Check(connected[i]->id())) cluPtr->isBeamCluster(connected[i]); } } } // Treat now the special cases, if any. The idea is to find for each pair // of quarks coming from a common colour source the corresponding pair of // antiquarks coming from a common colour sink, connected to the above // colour source via the same colour line. Then, randomly couple one of // the two quarks with one of the two antiquarks, and do the same with the // quark and antiquark left. for(map<tColinePtr, pair<tPPtr,tPPtr> >::const_iterator cit = quarkQuark.begin(); cit != quarkQuark.end(); ++cit ) { tColinePtr coline = cit->first; pair<tPPtr,tPPtr> quarkPair = cit->second; if(aQuarkQuark.find( coline ) != aQuarkQuark.end()) { pair<tPPtr,tPPtr> antiQuarkPair = aQuarkQuark.find(coline)->second; ClusterPtr cluPtr1, cluPtr2; if ( UseRandom::rndbool() ) { cluPtr1 = new_ptr(Cluster(quarkPair.first , antiQuarkPair.first)); cluPtr2 = new_ptr(Cluster(quarkPair.second , antiQuarkPair.second)); quarkPair.first->addChild(cluPtr1); antiQuarkPair.first->addChild(cluPtr1); quarkPair.second->addChild(cluPtr2); antiQuarkPair.second->addChild(cluPtr2); } else { cluPtr1 = new_ptr(Cluster(quarkPair.first , antiQuarkPair.second)); cluPtr2 = new_ptr(Cluster(quarkPair.second , antiQuarkPair.first)); quarkPair.second->addChild(cluPtr2); antiQuarkPair.first->addChild(cluPtr2); quarkPair.first->addChild(cluPtr1); antiQuarkPair.second->addChild(cluPtr1); } clusters.push_back(cluPtr1); clusters.push_back(cluPtr2); } else { throw Exception() << "ClusterFinder::formClusters : " << "***Skip event: unable to match pairs in " << "Baryon-violating processes***" << Exception::eventerror; } } return clusters; } namespace { bool PartOrdering(tPPtr p1,tPPtr p2) { return abs(p1->id())<abs(p2->id()); } } +ClusterPtr ClusterFinder::handleBaryonicCluster(ClusterPtr clu) const { + // A modification of reduceToTwoComponents that is dedicated + // to make a quark-diquark or antiquark-antidiquark out of a + // baryonic or antibaryonic cluster + tParticleVector vec; + tPPtr other; + for(unsigned int i = 0; i<clu->numComponents(); i++) { + tPPtr part = clu->particle(i); + if(!DiquarkMatcher::Check(*(part->dataPtr()))) + vec.push_back(part); + else + other = part; + } + + if(vec.size()<2) { + throw Exception() << "Could not make a diquark for a baryonic cluster decay from " + << clu->particle(0)->PDGName() << " " + << clu->particle(1)->PDGName() << " " + << clu->particle(2)->PDGName() << " " + << " in ClusterFinder::reduceToTwoComponents()." + << Exception::eventerror; + } + + // order the vector so heaviest at the end + std::sort(vec.begin(),vec.end(),PartOrdering); + + // Special treatment of heavy quarks + // avoid doubly heavy diquarks + if(heavyDiquarks_>=1 && vec.size()>2 && + abs(vec[1]->id())>3 && abs(vec[0]->id())<=3) { + // TODO: Note this change is in principle not identical to + // Default, but IMO is reasonable + // if(UseRandom::rndbool()) swap(vec[1],vec[2]); + other = vec[2]; + vec.pop_back(); + } + // avoid singly heavy diquarks + if(heavyDiquarks_==2 && vec.size()>2 && + abs(vec[2]->id())>3 && abs(vec[1]->id())<=3) { + other = vec[2]; + vec.pop_back(); + } + + // if there's a choice pick the pair to make a diquark from + if(vec.size()>2) { + // TODO refactor this + unsigned int ichoice(0); + // random choice + if(diQuarkSelection_==0) { + ichoice = UseRandom::rnd3(1.0, 1.0, 1.0); + } + // pick the lightest quark pair + else if(diQuarkSelection_==1) { + Energy m12 = (vec[0]->momentum()+vec[1]->momentum()).m(); + Energy m13 = (vec[0]->momentum()+vec[2]->momentum()).m(); + Energy m23 = (vec[1]->momentum()+vec[2]->momentum()).m(); + if (m13<=m12&&m13<=m23) ichoice = 2; + else if(m23<=m12&&m23<=m13) ichoice = 1; + } + // pick the lightest quark pair rescaled to the mass + else if(diQuarkSelection_==2) { + double m12 = (vec[0]->momentum()+vec[1]->momentum()).m()/(vec[0]->momentum().m()+vec[1]->momentum().m()); + double m13 = (vec[0]->momentum()+vec[2]->momentum()).m()/(vec[0]->momentum().m()+vec[2]->momentum().m()); + double m23 = (vec[1]->momentum()+vec[2]->momentum()).m()/(vec[1]->momentum().m()+vec[2]->momentum().m()); + if (m13<=m12&&m13<=m23) ichoice = 2; + else if(m23<=m12&&m23<=m13) ichoice = 1; + } + else + assert(false); + // make the swaps so select pair first + switch (ichoice) { + case 0: + break; + case 1: + swap(vec[2],vec[0]); + break; + case 2: + swap(vec[2],vec[1]); + break; + } + } + // set up + tcPDPtr temp1 = vec[0]->dataPtr(); + tcPDPtr temp2 = vec[1]->dataPtr(); + if(!other) other = vec[2]; + + tcPDPtr dataDiquark = _hadronSpectrum->makeDiquark(temp1,temp2); + + if(!dataDiquark) + throw Exception() << "Could not make a diquark from " + << temp1->PDGName() << " and " + << temp2->PDGName() + << " in ClusterFinder::reduceToTwoComponents()" + << Exception::eventerror; + + + // Create the new cluster (with two components) and assign to it the same + // momentum and position of the original (with three components) one. + // Furthermore, assign to the diquark component a momentum given by the + // sum of the two original components from which has been formed; for the + // position, we are assuming, very simply, that the diquark position is + // the average positions of the two original components. + // Notice that the mass (5-th component of the 5-momentum) of the diquark + // is set by hand to the constituent mass of the diquark (which is equal + // to the sum of the constituent masses of the two quarks which form the + // diquark) because the sum of 5-component vectors do add only the "normal" + // 4-components, not the 5-th one. After that, the 5-momentum of the diquark + // is in an inconsistent state, because the mass (5-th component) is not + // equal to the invariant mass obtained from the 4-momemtum. This is not + // unique to this kind of component (all perturbative components are in + // a similar situation), but it is not harmful. + + // construct the diquark + PPtr diquark = dataDiquark->produceParticle(); + vec[0]->addChild(diquark); + vec[1]->addChild(diquark); + // use the same method as for cluster to determine the diquark position + diquark->setVertex(Cluster::calculateX(vec[0],vec[1])); + // Set momenta of diquarks + Lorentz5Momentum pDiq = vec[0]->momentum() + vec[1]->momentum(); + pDiq.setMass(dataDiquark->constituentMass()); + diquark->set5Momentum(Lorentz5Momentum(pDiq, dataDiquark->constituentMass())); + switch (diQuarkOnShell_) + { + case 0: + { + // No: Set the momentum of the diquarks to the original and mass to the Off-shell mass + diquark->set5Momentum(Lorentz5Momentum(pDiq, pDiq.m())); + break; + } + case -1: + { + // Mixed (default): Set the momentum of the diquarks to the original, but mass to constituentMass + // as above + break; + } + case 1: + { + // Yes: Rescale the momentum and mass of the diquarks to their diquark constituentMass + Lorentz5Momentum psum = pDiq+other->momentum(); + psum.rescaleMass(); + Boost boost = psum.boostVector(); + Lorentz5Momentum pother = other->momentum(); + Lorentz5Momentum pdiquark = pDiq; + pother.boost(-boost); + pdiquark.boost(-boost); + Energy pcm = Kinematics::pstarTwoBodyDecay(psum.mass(), + other->dataPtr()->constituentMass(), + diquark->dataPtr()->constituentMass()); + if(pcm>ZERO) { + double fact = pcm/pother.vect().mag(); + pother *= fact; + pdiquark *= fact; + pother .setMass(other->dataPtr()->constituentMass()); + pdiquark.setMass(dataDiquark ->constituentMass()); + pother .rescaleEnergy(); + pdiquark.rescaleEnergy(); + pother .boost(boost); + pdiquark.boost(boost); + other->set5Momentum(pother); + diquark->set5Momentum(pdiquark); + } else { + throw Exception() + << "Not enough mass for baryonic cluster M = " << psum.mass()/GeV + << " m1 = " << other->dataPtr()->constituentMass()/GeV + << " m2 = " << dataDiquark ->constituentMass()/GeV + << " to be on shell! Pcom = " << pcm/GeV + << Exception::runerror; + } + break; + } + default: + assert(false); + } + + // make the new cluster + ClusterPtr nclus = new_ptr(Cluster(other,diquark)); + //vec[0]->addChild(nclus); + //diquark->addChild(nclus); + + // Set the parent/children relationship between the original cluster + // (the one with three components) with the new one (the one with two components) + // and add the latter to the vector of new redefined clusters. + clu->addChild(nclus); + return nclus; +} ClusterPtr ClusterFinder::handleDiquarkCluster(ClusterPtr clu) const { // A modification of reduceToTwoComponents that is dedicated // to make two diquark pairs out of the diquark cluster tParticleVector vec; tPPtr other; for(unsigned int i = 0; i<clu->numComponents(); i++) { tPPtr part = clu->particle(i); // Check if the parton is already a diquark // if it isn't, add it to the 'to-be-diquarked' list if(!DiquarkMatcher::Check(*(part->dataPtr()))) vec.push_back(part); else other = part; } assert(clu->particle(0)->id()*clu->particle(1)->id()>0); assert(clu->particle(2)->id()*clu->particle(3)->id()>0); if(vec.size()<4) { throw Exception() << "Could not make diquark pairs for a diquark cluster decay from " << clu->particle(0)->PDGName() << " " << clu->particle(1)->PDGName() << " " << clu->particle(2)->PDGName() << " " << clu->particle(3)->PDGName() << " " << " in ClusterFinder::handleDiquarkCluster()." << Exception::eventerror; } // order the vector so heaviest at the end //std::sort(vec.begin(),vec.end(),PartOrdering); // TODO: Figure out heavy quark stuff for tetraquark situation // Special treatment of heavy quarks // avoid doubly heavy diquarks // if(heavyDiquarks_>=1 && abs(vec[1]->id())>3 && abs(vec[0]->id())<=3) { // if(UseRandom::rndbool()) swap(vec[1],vec[2]); // other = vec[2]; // vec.pop_back(); // } // avoid singly heavy diquarks // if(heavyDiquarks_==2 && abs(vec[2]->id())>3 && abs(vec[1]->id())<=3) { // other = vec[2]; // vec.pop_back(); // } // For tetraquarks there is no choice available, there are 2 quarks, // and 2 antiquarks, and they have to be paired up together. tcPDPtr tempQ1 = vec[0]->dataPtr(); tcPDPtr tempQ2 = vec[1]->dataPtr(); tcPDPtr tempQb1 = vec[2]->dataPtr(); tcPDPtr tempQb2 = vec[3]->dataPtr(); tcPDPtr dataDiquark = _hadronSpectrum->makeDiquark(tempQ1, tempQ2); tcPDPtr dataDiquarkBar = _hadronSpectrum->makeDiquark(tempQb1, tempQb2); if (!dataDiquark){ throw Exception() << "Could not make a diquark from" << tempQ1->PDGName() << " and " << tempQ2->PDGName() << " in ClusterFinder::handleDiquarkCluster()" << Exception::eventerror; } if (!dataDiquarkBar){ throw Exception() << "Could not make an anti-diquark from" << tempQb1->PDGName() << " and " << tempQb2->PDGName() << " in ClusterFinder::handleDiquarkCluster()" << Exception::eventerror; } // Create the new cluster (with two components) and assign it the same // momentum and position of the original (with 4 components). // Construct the diquarks PPtr diquark = dataDiquark->produceParticle(); PPtr diquarkBar = dataDiquarkBar->produceParticle(); // update the new child cluster for the partons vec[0]->addChild(diquark); vec[1]->addChild(diquark); vec[2]->addChild(diquarkBar); vec[3]->addChild(diquarkBar); // use the same method as for cluster to determine the diquark position diquark->setVertex(Cluster::calculateX(vec[0],vec[1])); diquarkBar->setVertex(Cluster::calculateX(vec[2],vec[3])); // Set momenta of diquarks Lorentz5Momentum pDiq = vec[0]->momentum() + vec[1]->momentum(); Lorentz5Momentum pAntiDiq = vec[2]->momentum() + vec[3]->momentum(); pDiq.setMass(dataDiquark->constituentMass()); pAntiDiq.setMass(dataDiquarkBar->constituentMass()); // Set the momentum of the diquarks diquark->set5Momentum(Lorentz5Momentum(pDiq, dataDiquark->constituentMass())); diquarkBar->set5Momentum(Lorentz5Momentum(pAntiDiq, dataDiquarkBar->constituentMass())); switch (diQuarkOnShell_) { case 0: { // No: Set the momentum of the diquarks to the original and mass to the Off-shell mass diquark->set5Momentum(Lorentz5Momentum(pDiq, pDiq.m())); diquarkBar->set5Momentum(Lorentz5Momentum(pAntiDiq, pAntiDiq.m())); break; } case -1: { // Mixed (default): Set the momentum of the diquarks to the original, but mass to constituentMass // as above break; } case 1: { // Yes: Rescale the momentum and mass of the diquarks to their diquark constituentMass Lorentz5Momentum psum = pDiq + pAntiDiq; psum.rescaleMass(); Boost boost = psum.boostVector(); Lorentz5Momentum pdiquarkBar = pAntiDiq; Lorentz5Momentum pdiquark = pDiq; pdiquarkBar.boost(-boost); pdiquark.boost(-boost); Energy pcm = Kinematics::pstarTwoBodyDecay(psum.mass(), diquarkBar->dataPtr()->constituentMass(), diquark->dataPtr()->constituentMass()); if(pcm>ZERO) { double fact = pcm/pdiquarkBar.vect().mag(); pdiquarkBar *= fact; pdiquark *= fact; pdiquarkBar.setMass(dataDiquarkBar->constituentMass()); pdiquark .setMass(dataDiquark ->constituentMass()); pdiquarkBar.rescaleEnergy(); pdiquark .rescaleEnergy(); pdiquarkBar.boost(boost); pdiquark .boost(boost); diquarkBar->set5Momentum(pdiquarkBar); diquark ->set5Momentum(pdiquark); } else { throw Exception() << "Not enough mass for cluster Diquark cluster M = " << psum.mass()/GeV << " m1 = " << diquarkBar->dataPtr()->constituentMass()/GeV << " m2 = " << diquark->dataPtr()->constituentMass()/GeV << " to be on shell! Pcom = " << pcm/GeV << Exception::runerror; } break; } default: assert(false); } // make the new cluster ClusterPtr nclus = new_ptr(Cluster(diquark, diquarkBar)); // Set the parent/child relationship clu->addChild(nclus); return nclus; } void ClusterFinder::reduceToTwoComponents(ClusterVector & clusters) { // In order to preserve all of the information, we do not modify the // directly the 3-component clusters, but instead we define new clusters, // which are related to the original ones by a child-parent relationship, // by considering two randomly chosen components as a diquark (or anti-diquark). // These new clusters are first added to the vector vecNewRedefinedCluPtr, // and at the end, when all input clusters have been examined, the elements of // this vector will be copied in collecCluPtr (the reason is that it is not // allowed to modify a STL container while iterating over it). vector<tClusterPtr> redefinedClusters; for(ClusterVector::iterator cluIter = clusters.begin() ; cluIter != clusters.end() ; ++cluIter) { tParticleVector vec; if ( (*cluIter)->numComponents() == 2 && ( DiquarkMatcher::Check(*((*cluIter)->particle(0)->dataPtr())) || DiquarkMatcher::Check(*((*cluIter)->particle(1)->dataPtr())))){ if (fabs((*cluIter)->momentum().m()/GeV-((*cluIter)->particle(0)->momentum()+(*cluIter)->particle(1)->momentum()).m()/GeV)>1e-5){ std::cout << "momentum().m() "<< std::setprecision(18) << (*cluIter)->momentum().m()/GeV << std::endl; std::cout << "(p1+p2).m() "<< std::setprecision(18) << ((*cluIter)->particle(0)->momentum()+(*cluIter)->particle(1)->momentum()).m()/GeV << std::endl; } } if ( (*cluIter)->numComponents() == 4 && (*cluIter)->isAvailable() ){ - ClusterPtr nclus = handleDiquarkCluster(*cluIter); - assert(nclus->numComponents()==2); - redefinedClusters.push_back(nclus); + // Handle diquark clusters + ClusterPtr DiquarkClusterReduced = handleDiquarkCluster(*cluIter); + assert(DiquarkClusterReduced->numComponents()==2); + redefinedClusters.push_back(DiquarkClusterReduced); continue; } if ( (*cluIter)->numComponents() != 3 || ! (*cluIter)->isAvailable() ) continue; - tPPtr other; - for(unsigned int i = 0; i<(*cluIter)->numComponents(); i++) { - tPPtr part = (*cluIter)->particle(i); - if(!DiquarkMatcher::Check(*(part->dataPtr()))) - vec.push_back(part); - else - other = part; - } - - if(vec.size()<2) { - throw Exception() << "Could not make a diquark for a baryonic cluster decay from " - << (*cluIter)->particle(0)->PDGName() << " " - << (*cluIter)->particle(1)->PDGName() << " " - << (*cluIter)->particle(2)->PDGName() << " " - << " in ClusterFinder::reduceToTwoComponents()." - << Exception::eventerror; - } - - // order the vector so heaviest at the end - std::sort(vec.begin(),vec.end(),PartOrdering); - - // Special treatment of heavy quarks - // avoid doubly heavy diquarks - if(heavyDiquarks_>=1 && vec.size()>2 && - abs(vec[1]->id())>3 && abs(vec[0]->id())<=3) { - if(UseRandom::rndbool()) swap(vec[1],vec[2]); - other = vec[2]; - vec.pop_back(); - } - // avoid singly heavy diquarks - if(heavyDiquarks_==2 && vec.size()>2 && - abs(vec[2]->id())>3 && abs(vec[1]->id())<=3) { - other = vec[2]; - vec.pop_back(); - } - - // if there's a choice pick the pair to make a diquark from - if(vec.size()>2) { - // TODO refactor this - unsigned int ichoice(0); - // random choice - if(diQuarkSelection_==0) { - ichoice = UseRandom::rnd3(1.0, 1.0, 1.0); - } - // pick the lightest quark pair - else if(diQuarkSelection_==1) { - Energy m12 = (vec[0]->momentum()+vec[1]->momentum()).m(); - Energy m13 = (vec[0]->momentum()+vec[2]->momentum()).m(); - Energy m23 = (vec[1]->momentum()+vec[2]->momentum()).m(); - if (m13<=m12&&m13<=m23) ichoice = 2; - else if(m23<=m12&&m23<=m13) ichoice = 1; - } - // pick the lightest quark pair rescaled to the mass - else if(diQuarkSelection_==2) { - double m12 = (vec[0]->momentum()+vec[1]->momentum()).m()/(vec[0]->momentum().m()+vec[1]->momentum().m()); - double m13 = (vec[0]->momentum()+vec[2]->momentum()).m()/(vec[0]->momentum().m()+vec[2]->momentum().m()); - double m23 = (vec[1]->momentum()+vec[2]->momentum()).m()/(vec[1]->momentum().m()+vec[2]->momentum().m()); - if (m13<=m12&&m13<=m23) ichoice = 2; - else if(m23<=m12&&m23<=m13) ichoice = 1; - } - else - assert(false); - // make the swaps so select pair first - switch (ichoice) { - case 0: - break; - case 1: - swap(vec[2],vec[0]); - break; - case 2: - swap(vec[2],vec[1]); - break; - } - } - // set up - tcPDPtr temp1 = vec[0]->dataPtr(); - tcPDPtr temp2 = vec[1]->dataPtr(); - if(!other) other = vec[2]; - - tcPDPtr dataDiquark = _hadronSpectrum->makeDiquark(temp1,temp2); - - if(!dataDiquark) - throw Exception() << "Could not make a diquark from " - << temp1->PDGName() << " and " - << temp2->PDGName() - << " in ClusterFinder::reduceToTwoComponents()" - << Exception::eventerror; - - - // Create the new cluster (with two components) and assign to it the same - // momentum and position of the original (with three components) one. - // Furthermore, assign to the diquark component a momentum given by the - // sum of the two original components from which has been formed; for the - // position, we are assuming, very simply, that the diquark position is - // the average positions of the two original components. - // Notice that the mass (5-th component of the 5-momentum) of the diquark - // is set by hand to the constituent mass of the diquark (which is equal - // to the sum of the constituent masses of the two quarks which form the - // diquark) because the sum of 5-component vectors do add only the "normal" - // 4-components, not the 5-th one. After that, the 5-momentum of the diquark - // is in an inconsistent state, because the mass (5-th component) is not - // equal to the invariant mass obtained from the 4-momemtum. This is not - // unique to this kind of component (all perturbative components are in - // a similar situation), but it is not harmful. - - // construct the diquark - PPtr diquark = dataDiquark->produceParticle(); - vec[0]->addChild(diquark); - vec[1]->addChild(diquark); - // use the same method as for cluster to determine the diquark position - diquark->setVertex(Cluster::calculateX(vec[0],vec[1])); - // Set momenta of diquarks - Lorentz5Momentum pDiq = vec[0]->momentum() + vec[1]->momentum(); - pDiq.setMass(dataDiquark->constituentMass()); - diquark->set5Momentum(Lorentz5Momentum(pDiq, dataDiquark->constituentMass())); - switch (diQuarkOnShell_) - { - case 0: - { - // No: Set the momentum of the diquarks to the original and mass to the Off-shell mass - diquark->set5Momentum(Lorentz5Momentum(pDiq, pDiq.m())); - break; - } - case -1: - { - // Mixed (default): Set the momentum of the diquarks to the original, but mass to constituentMass - // as above - break; - } - case 1: - { - // Yes: Rescale the momentum and mass of the diquarks to their diquark constituentMass - Lorentz5Momentum psum = pDiq+other->momentum(); - psum.rescaleMass(); - Boost boost = psum.boostVector(); - Lorentz5Momentum pother = other->momentum(); - Lorentz5Momentum pdiquark = pDiq; - pother.boost(-boost); - pdiquark.boost(-boost); - Energy pcm = Kinematics::pstarTwoBodyDecay(psum.mass(), - other->dataPtr()->constituentMass(), - diquark->dataPtr()->constituentMass()); - if(pcm>ZERO) { - double fact = pcm/pother.vect().mag(); - pother *= fact; - pdiquark *= fact; - pother .setMass(other->dataPtr()->constituentMass()); - pdiquark.setMass(dataDiquark ->constituentMass()); - pother .rescaleEnergy(); - pdiquark.rescaleEnergy(); - pother .boost(boost); - pdiquark.boost(boost); - other->set5Momentum(pother); - diquark->set5Momentum(pdiquark); - } else { - throw Exception() - << "Not enough mass for baryonic cluster M = " << psum.mass()/GeV - << " m1 = " << other->dataPtr()->constituentMass()/GeV - << " m2 = " << dataDiquark ->constituentMass()/GeV - << " to be on shell! Pcom = " << pcm/GeV - << Exception::runerror; - } - break; - } - default: - assert(false); - } - - // make the new cluster - ClusterPtr nclus = new_ptr(Cluster(other,diquark)); - //vec[0]->addChild(nclus); - //diquark->addChild(nclus); - - // Set the parent/children relationship between the original cluster - // (the one with three components) with the new one (the one with two components) - // and add the latter to the vector of new redefined clusters. - (*cluIter)->addChild(nclus); - - redefinedClusters.push_back(nclus); + // Should now only have baryonic clusters + // Handle baryonic clusters + ClusterPtr BaryonicClusterReduced = handleBaryonicCluster(*cluIter); + assert(BaryonicClusterReduced->numComponents()==2); + redefinedClusters.push_back(BaryonicClusterReduced); } // Add to collecCluPtr all of the redefined new clusters (indeed the // pointers to them are added) contained in vecNewRedefinedCluPtr. /// \todo why do we keep the original of the redefined clusters? for (tClusterVector::const_iterator it = redefinedClusters.begin(); it != redefinedClusters.end(); ++it) { clusters.push_back(*it); } } diff --git a/Hadronization/ClusterFinder.h b/Hadronization/ClusterFinder.h --- a/Hadronization/ClusterFinder.h +++ b/Hadronization/ClusterFinder.h @@ -1,176 +1,186 @@ // -*- C++ -*- // // ClusterFinder.h is a part of Herwig - A multi-purpose Monte Carlo event generator // Copyright (C) 2002-2019 The Herwig Collaboration // // Herwig is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // #ifndef HERWIG_ClusterFinder_H #define HERWIG_ClusterFinder_H #include <ThePEG/Interface/Interfaced.h> #include "CluHadConfig.h" #include "ClusterFinder.fh" #include "HadronSpectrum.h" namespace Herwig { using namespace ThePEG; /*! \ingroup Hadronization * \class ClusterFinder * \brief This class forms clusters from the partons produced in the Shower. * \author Philip Stephens * \author Alberto Ribon * * This class scans through the particles in the event and produces a * collection of clusters, defined as a colour-singlet combinations of * colour-connected particles. There are no assumptions about the type * (i.e. quark or diquark) or number of the component particles of the * cluster; however, most of the time clusters are formed by quark-antiquark * pairs. In special situations, such as baryon-violating processes in * R-nonconserved Susy, three quarks (or three antiquarks) could form a * cluster. Because at the moment we don't know how to handle 3-component * clusters (i.e. how to fission heavy ones, or how to decay clusters), we * provide also a separate method, reduceToTwoComponents, which * does the job of redefining these 3-component clusters as "normal" * 2-component ones, simply by randomly considering two (anti-) quarks as a * (anti-) diquark. Notice that if in the future the method * reduceToTwoComponents is modified or even eliminated, the * main method for finding clusters, formClusters, will not need * any change. * * @see \ref ClusterFinderInterfaces "The interfaces" * defined for ClusterFinder. */ class ClusterFinder: public Interfaced { public : /** @name Standard constructors and destructors. */ //@{ /** * Default constructor. */ ClusterFinder() : heavyDiquarks_(2), diQuarkSelection_(1), diQuarkOnShell_(-1) {} //@} public: /** * This routine forms the clusters of the event. * * Form clusters starting from the list of partons given. * It also checks if the cluster is a beam cluster, that is if * at least one of its components is a beam remnant. */ ClusterVector formClusters(const PVector & partons); /** * Reduces three component clusters into two components. * * For the eventual clusters that have three components * (quark, quark, quark) or (antiquark, antiquark, antiquark), * it redefines them as "normal" clusters with two components: * (quark,diquark) or (antiquark,antidiquark), by a random drawing. * This could be eliminated or changed in the future. */ void reduceToTwoComponents(ClusterVector&); /** * handling Diquark clusters: * For the eventual clusters that have four components * (quark, quark, antiquark, antiquark) * it redefines them as "normal" clusters with two components: * (antidiquark,diquark) by a random drawing. */ ClusterPtr handleDiquarkCluster(ClusterPtr clu) const; + + /** + * handling Baryonic clusters: + * For the eventual clusters that have three components + * (quark, quark, quark) or (antiquark, antiquark, antiquark) + * it redefines them as "normal" clusters with two components: + * (quark, diquark) or (antiquark, antidiquark) by a prescription. + */ + ClusterPtr handleBaryonicCluster(ClusterPtr clu) const; + /** * Return the hadron spectrum */ Ptr<HadronSpectrum>::tptr spectrum() const { return _hadronSpectrum; } /** * access for diquark treatment */ int diquarkOnShell() { return diQuarkOnShell_; } public: /** * Standard Init function used to initialize the interfaces. */ static void Init(); /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * Private and non-existent assignment operator. */ ClusterFinder & operator=(const ClusterFinder &) = delete; private: /** * Treatment of diquarks contain heavy quarks in baryon-number violating clusters */ unsigned int heavyDiquarks_; /** * Option for the selection of which quarks to make into a diquark */ unsigned int diQuarkSelection_; /** * Force diquarks to be on-shell */ int diQuarkOnShell_; /** * The hadron spectrum to consider */ Ptr<HadronSpectrum>::ptr _hadronSpectrum; }; } #endif /* HERWIG_ClusterFinder_H */