diff --git a/Hadronization/ClusterFissioner.cc b/Hadronization/ClusterFissioner.cc --- a/Hadronization/ClusterFissioner.cc +++ b/Hadronization/ClusterFissioner.cc @@ -1,2135 +1,2139 @@ // -*- C++ -*- // // ClusterFissioner.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. // // // Thisk is the implementation of the non-inlined, non-templated member // functions of the ClusterFissioner class. // #include "ClusterFissioner.h" #include <ThePEG/Interface/ClassDocumentation.h> #include <ThePEG/Interface/Reference.h> #include <ThePEG/Interface/Parameter.h> #include <ThePEG/Interface/Switch.h> #include <ThePEG/Persistency/PersistentOStream.h> #include <ThePEG/Persistency/PersistentIStream.h> #include <ThePEG/PDT/EnumParticles.h> #include "Herwig/Utilities/Kinematics.h" #include "Cluster.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include <ThePEG/Utilities/DescribeClass.h> #include "ThePEG/Interface/ParMap.h" #include <boost/numeric/ublas/matrix.hpp> #include <boost/numeric/ublas/io.hpp> #include <boost/numeric/ublas/lu.hpp> using namespace Herwig; DescribeClass<ClusterFissioner,Interfaced> describeClusterFissioner("Herwig::ClusterFissioner","Herwig.so"); ClusterFissioner::ClusterFissioner() : _clMaxLight(3.35*GeV), _clMaxExotic(3.35*GeV), _clPowLight(2.0), _clPowExotic(2.0), _pSplitLight(1.0), _pSplitExotic(1.0), _phaseSpaceWeights(false), _dim(4), _fissionCluster(0), _kinematicThresholdChoice(0), _pwtDIquark(0.0), _diquarkClusterFission(0), _btClM(1.0*GeV), _iopRem(1), _kappa(1.0e15*GeV/meter), _kinematics(0), _fluxTubeWidth(1.0*GeV), _enhanceSProb(0), _m0Fission(2.*GeV), _massMeasure(0), _probPowFactor(4.0), _probShift(0.0), _kinThresholdShift(1.0*sqr(GeV)), _strictDiquarkKinematics(0), _covariantBoost(false) {} IBPtr ClusterFissioner::clone() const { return new_ptr(*this); } IBPtr ClusterFissioner::fullclone() const { return new_ptr(*this); } void ClusterFissioner::persistentOutput(PersistentOStream & os) const { os << ounit(_clMaxLight,GeV) << ounit(_clMaxHeavy,GeV) << ounit(_clMaxExotic,GeV) << _clPowLight << _clPowHeavy << _clPowExotic << _pSplitLight << _pSplitHeavy << _pSplitExotic << _fissionCluster << _fissionPwt << _pwtDIquark << _diquarkClusterFission << _kinematics << ounit(_fluxTubeWidth,GeV) << ounit(_btClM,GeV) << _iopRem << ounit(_kappa, GeV/meter) << _enhanceSProb << ounit(_m0Fission,GeV) << _massMeasure << _dim << _phaseSpaceWeights << _hadronSpectrum << _probPowFactor << _probShift << ounit(_kinThresholdShift,sqr(GeV)) << _strictDiquarkKinematics << _covariantBoost; } void ClusterFissioner::persistentInput(PersistentIStream & is, int) { is >> iunit(_clMaxLight,GeV) >> iunit(_clMaxHeavy,GeV) >> iunit(_clMaxExotic,GeV) >> _clPowLight >> _clPowHeavy >> _clPowExotic >> _pSplitLight >> _pSplitHeavy >> _pSplitExotic >> _fissionCluster >> _fissionPwt >> _pwtDIquark >> _diquarkClusterFission >> _kinematics >> iunit(_fluxTubeWidth,GeV) >> iunit(_btClM,GeV) >> _iopRem >> iunit(_kappa, GeV/meter) >> _enhanceSProb >> iunit(_m0Fission,GeV) >> _massMeasure >> _dim >> _phaseSpaceWeights >> _hadronSpectrum >> _probPowFactor >> _probShift >> iunit(_kinThresholdShift,sqr(GeV)) >> _strictDiquarkKinematics >> _covariantBoost; } void ClusterFissioner::doinit() { Interfaced::doinit(); for ( const long& id : spectrum()->heavyHadronizingQuarks() ) { if ( _pSplitHeavy.find(id) == _pSplitHeavy.end() || _clPowHeavy.find(id) == _clPowHeavy.end() || _clMaxHeavy.find(id) == _clMaxHeavy.end() ) throw InitException() << "not all parameters have been set for heavy quark cluster fission"; } // for default Pwts not needed to initialize if (_fissionCluster==0) return; for ( const long& id : spectrum()->lightHadronizingQuarks() ) { if ( _fissionPwt.find(id) == _fissionPwt.end() ) // check that all relevant weights are set throw InitException() << "fission weights for light quarks have not been set"; } /* // Not needed since we set Diquark weights from quark weights for ( const long& id : spectrum()->lightHadronizingDiquarks() ) { if ( _fissionPwt.find(id) == _fissionPwt.end() ) throw InitException() << "fission weights for light diquarks have not been set"; }*/ double pwtDquark=_fissionPwt.find(ParticleID::d)->second; double pwtUquark=_fissionPwt.find(ParticleID::u)->second; double pwtSquark=_fissionPwt.find(ParticleID::s)->second; // ERROR: TODO makeDiquarkID is protected function ? // _fissionPwt[spectrum()->makeDiquarkID(ParticleID::d,ParticleID::d,3)] = _pwtDIquark * pwtDquark * pwtDquark; // _fissionPwt[spectrum()->makeDiquarkID(ParticleID::u,ParticleID::d,1)] = 0.5 * _pwtDIquark * pwtUquark * pwtDquark; // _fissionPwt[spectrum()->makeDiquarkID(ParticleID::u,ParticleID::u,3)] = _pwtDIquark * pwtUquark * pwtUquark; // _fissionPwt[spectrum()->makeDiquarkID(ParticleID::s,ParticleID::d,1)] = 0.5 * _pwtDIquark * pwtSquark * pwtDquark; // _fissionPwt[spectrum()->makeDiquarkID(ParticleID::s,ParticleID::u,1)] = 0.5 * _pwtDIquark * pwtSquark * pwtUquark; // _fissionPwt[spectrum()->makeDiquarkID(ParticleID::s,ParticleID::s,3)] = _pwtDIquark * pwtSquark * pwtSquark; // TODO magic number alternative _fissionPwt[1103] = _pwtDIquark * pwtDquark * pwtDquark; _fissionPwt[2101] = 0.5 * _pwtDIquark * pwtUquark * pwtDquark; _fissionPwt[2203] = _pwtDIquark * pwtUquark * pwtUquark; _fissionPwt[3101] = 0.5 * _pwtDIquark * pwtSquark * pwtDquark; _fissionPwt[3201] = 0.5 * _pwtDIquark * pwtSquark * pwtUquark; _fissionPwt[3303] = _pwtDIquark * pwtSquark * pwtSquark; } void ClusterFissioner::Init() { static ClassDocumentation<ClusterFissioner> documentation ("Class responsibles for chopping up the clusters"); static Reference<ClusterFissioner,HadronSpectrum> interfaceHadronSpectrum ("HadronSpectrum", "Set the Hadron spectrum for this cluster fissioner.", &ClusterFissioner::_hadronSpectrum, false, false, true, false); // ClMax for light, Bottom, Charm and exotic (e.g. Susy) quarks static Parameter<ClusterFissioner,Energy> interfaceClMaxLight ("ClMaxLight","cluster max mass for light quarks (unit [GeV])", &ClusterFissioner::_clMaxLight, GeV, 3.35*GeV, ZERO, 10.0*GeV, false,false,false); static ParMap<ClusterFissioner,Energy> interfaceClMaxHeavy ("ClMaxHeavy", "ClMax for heavy quarkls", &ClusterFissioner::_clMaxHeavy, GeV, -1, 3.35*GeV, ZERO, 10.0*GeV, false, false, Interface::upperlim); static Parameter<ClusterFissioner,Energy> interfaceClMaxExotic ("ClMaxExotic","cluster max mass for exotic quarks (unit [GeV])", &ClusterFissioner::_clMaxExotic, GeV, 3.35*GeV, ZERO, 10.0*GeV, false,false,false); // ClPow for light, Bottom, Charm and exotic (e.g. Susy) quarks static Parameter<ClusterFissioner,double> interfaceClPowLight ("ClPowLight","cluster mass exponent for light quarks", &ClusterFissioner::_clPowLight, 0, 2.0, 0.0, 10.0,false,false,false); static ParMap<ClusterFissioner,double> interfaceClPowHeavy ("ClPowHeavy", "ClPow for heavy quarks", &ClusterFissioner::_clPowHeavy, -1, 1.0, 0.0, 10.0, false, false, Interface::upperlim); static Parameter<ClusterFissioner,double> interfaceClPowExotic ("ClPowExotic","cluster mass exponent for exotic quarks", &ClusterFissioner::_clPowExotic, 0, 2.0, 0.0, 10.0,false,false,false); // PSplit for light, Bottom, Charm and exotic (e.g. Susy) quarks static Parameter<ClusterFissioner,double> interfacePSplitLight ("PSplitLight","cluster mass splitting param for light quarks", &ClusterFissioner::_pSplitLight, 0, 1.0, 0.0, 10.0,false,false,false); static ParMap<ClusterFissioner,double> interfacePSplitHeavy ("PSplitHeavy", "PSplit for heavy quarks", &ClusterFissioner::_pSplitHeavy, -1, 1.0, 0.0, 10.0, false, false, Interface::upperlim); static Parameter<ClusterFissioner,double> interfacePSplitExotic ("PSplitExotic","cluster mass splitting param for exotic quarks", &ClusterFissioner::_pSplitExotic, 0, 1.0, 0.0, 10.0,false,false,false); static Switch<ClusterFissioner,int> interfaceFission ("Fission", "Option for different Fission options", &ClusterFissioner::_fissionCluster, 1, false, false); static SwitchOption interfaceFissionDefault (interfaceFission, "default", "Normal cluster fission which depends on the hadron selector class.", 0); static SwitchOption interfaceFissionNew (interfaceFission, "new", "Alternative cluster fission which does not depend on the hadron selector class", 1); static Switch<ClusterFissioner,int> interfaceDiquarkClusterFission ("DiquarkClusterFission", "Allow clusters to fission to 1 or 2 diquark Clusters", &ClusterFissioner::_diquarkClusterFission, 0, false, false); static SwitchOption interfaceDiquarkClusterFissionAll (interfaceDiquarkClusterFission, "All", "Allow diquark clusters and baryon clusters to fission to new diquark Clusters", 2); static SwitchOption interfaceDiquarkClusterFissionOnlyBaryonClusters (interfaceDiquarkClusterFission, "OnlyBaryonClusters", "Allow only baryon clusters to fission to new diquark Clusters", 1); static SwitchOption interfaceDiquarkClusterFissionNo (interfaceDiquarkClusterFission, "No", "Don't allow clusters to fission to new diquark Clusters", 0); static ParMap<ClusterFissioner,double> interfaceFissionPwt ("FissionPwt", "The weights for quarks in the fission process.", &ClusterFissioner::_fissionPwt, -1, 1.0, 0.0, 10.0, false, false, Interface::upperlim); static Switch<ClusterFissioner,int> interfaceRemnantOption ("RemnantOption", "Option for the treatment of remnant clusters", &ClusterFissioner::_iopRem, 1, false, false); static SwitchOption interfaceRemnantOptionSoft (interfaceRemnantOption, "Soft", "Both clusters produced in the fission of the beam cluster" " are treated as soft clusters.", 0); static SwitchOption interfaceRemnantOptionHard (interfaceRemnantOption, "Hard", "Only the cluster containing the remnant is treated as a soft cluster.", 1); static SwitchOption interfaceRemnantOptionVeryHard (interfaceRemnantOption, "VeryHard", "Even remnant clusters are treated as hard, i.e. all clusters the same", 2); static Parameter<ClusterFissioner,Energy> interfaceBTCLM ("SoftClusterFactor", "Parameter for the mass spectrum of remnant clusters", &ClusterFissioner::_btClM, GeV, 1.*GeV, 0.1*GeV, 10.0*GeV, false, false, Interface::limited); static Parameter<ClusterFissioner,Tension> interfaceStringTension ("StringTension", "String tension used in vertex displacement calculation", &ClusterFissioner::_kappa, GeV/meter, 1.0e15*GeV/meter, ZERO, ZERO, false, false, Interface::lowerlim); static Switch<ClusterFissioner,int> interfaceEnhanceSProb ("EnhanceSProb", "Option for enhancing strangeness", &ClusterFissioner::_enhanceSProb, 0, false, false); static SwitchOption interfaceEnhanceSProbNo (interfaceEnhanceSProb, "No", "No strangeness enhancement.", 0); static SwitchOption interfaceEnhanceSProbScaled (interfaceEnhanceSProb, "Scaled", "Scaled strangeness enhancement", 1); static SwitchOption interfaceEnhanceSProbExponential (interfaceEnhanceSProb, "Exponential", "Exponential strangeness enhancement", 2); static Switch<ClusterFissioner,int> interfaceKinematics ("Kinematics", "Option for selecting different Kinematics for ClusterFission", &ClusterFissioner::_kinematics, 0, false, false); static SwitchOption interfaceKinematicsDefault (interfaceKinematics, "Default", "Fully aligned Cluster Fission along the Original cluster direction", 0); static SwitchOption interfaceKinematicsIsotropic (interfaceKinematics, "Isotropic", "Fully isotropic two body decay. Not recommended!", 1); static SwitchOption interfaceKinematicsFluxTube (interfaceKinematics, "FluxTube", "Aligned decay with gaussian pT kick with sigma=ClusterFissioner::FluxTube", 2); static Switch<ClusterFissioner,int> interfaceMassMeasure ("MassMeasure", "Option to use different mass measures", &ClusterFissioner::_massMeasure,0,false,false); static SwitchOption interfaceMassMeasureMass (interfaceMassMeasure, "Mass", "Mass Measure", 0); static SwitchOption interfaceMassMeasureLambda (interfaceMassMeasure, "Lambda", "Lambda Measure", 1); static Parameter<ClusterFissioner,Energy> interfaceFissionMassScale ("FissionMassScale", "Cluster fission mass scale", &ClusterFissioner::_m0Fission, GeV, 2.0*GeV, 0.1*GeV, 50.*GeV, false, false, Interface::limited); static Parameter<ClusterFissioner,double> interfaceProbPowFactor ("ProbablityPowerFactor", "Power factor in ClausterFissioner bell probablity function", &ClusterFissioner::_probPowFactor, 2.0, 1.0, 20.0, false, false, Interface::limited); static Parameter<ClusterFissioner,Energy> interfaceFluxTubeWidth ("FluxTubeWidth", "sigma of gaussian sampling of pT for FluxTube kinematics", &ClusterFissioner::_fluxTubeWidth, GeV, 2.0*GeV, 0.0*GeV, 5.0*GeV, false, false, Interface::limited); static Parameter<ClusterFissioner,double> interfaceProbShift ("ProbablityShift", "Shifts from the center in ClausterFissioner bell probablity function", &ClusterFissioner::_probShift, 0.0, -10.0, 10.0, false, false, Interface::limited); static Parameter<ClusterFissioner,Energy2> interfaceKineticThresholdShift ("KineticThresholdShift", "Shifts from the kinetic threshold in ClausterFissioner", &ClusterFissioner::_kinThresholdShift, sqr(GeV), 0.*sqr(GeV), -10.0*sqr(GeV), 10.0*sqr(GeV), false, false, Interface::limited); static Switch<ClusterFissioner,int> interfaceKinematicThreshold ("KinematicThreshold", "Option for using static or dynamic kinematic thresholds in cluster splittings", &ClusterFissioner::_kinematicThresholdChoice, 0, false, false); static SwitchOption interfaceKinematicThresholdStatic (interfaceKinematicThreshold, "Static", "Set static kinematic thresholds for cluster splittings.", 0); static SwitchOption interfaceKinematicThresholdDynamic (interfaceKinematicThreshold, "Dynamic", "Set dynamic kinematic thresholds for cluster splittings.", 1); static Switch<ClusterFissioner,bool> interfacePhaseSpaceWeights ("PhaseSpaceWeights", "Include phase space weights.", &ClusterFissioner::_phaseSpaceWeights, false, false, false); static SwitchOption interfacePhaseSpaceWeightsYes (interfacePhaseSpaceWeights, "Yes", "Do include the effect of cluster fission phase space", true); static SwitchOption interfacePhaseSpaceWeightsNo (interfacePhaseSpaceWeights, "No", "Do not include the effect of cluster phase space", false); static Switch<ClusterFissioner,bool> interfaceCovariantBoost ("CovariantBoost", "Use single Covariant Boost for Cluster Fission", &ClusterFissioner::_covariantBoost, false, false, false); static SwitchOption interfaceCovariantBoostYes (interfaceCovariantBoost, "Yes", "Use Covariant boost", true); static SwitchOption interfaceCovariantBoostNo (interfaceCovariantBoost, "No", "Do NOT use Covariant boost", false); static Parameter<ClusterFissioner,double> interfaceDim ("Dimension","Dimension in which phase space weights are calculated", &ClusterFissioner::_dim, 0, 4.0, 3.0, 10.0,false,false, Interface::limited); static Switch<ClusterFissioner,int> interfaceStrictDiquarkKinematics ("StrictDiquarkKinematics", "Option for selecting different selection criterions of diquarks for ClusterFission", &ClusterFissioner::_strictDiquarkKinematics, 0, false, false); static SwitchOption interfaceStrictDiquarkKinematicsLoose (interfaceStrictDiquarkKinematics, "Loose", "No kinematic threshold for diquark selection except for Mass bigger than 2 baryons", 0); static SwitchOption interfaceStrictDiquarkKinematicsStrict (interfaceStrictDiquarkKinematics, "Strict", "Resulting clusters are at least as heavy as 2 lightest baryons", 1); static Parameter<ClusterFissioner,double> interfacePwtDIquark ("PwtDIquark", "specific probability for choosing a d diquark", &ClusterFissioner::_pwtDIquark, 0.0, 0.0, 10.0, false, false, Interface::limited); } tPVector ClusterFissioner::fission(ClusterVector & clusters, bool softUEisOn) { // return if no clusters if (clusters.empty()) return tPVector(); /***************** * Loop over the (input) collection of cluster pointers, and store in * the vector splitClusters all the clusters that need to be split * (these are beam clusters, if soft underlying event is off, and * heavy non-beam clusters). ********************/ stack<ClusterPtr> splitClusters; for(ClusterVector::iterator it = clusters.begin() ; it != clusters.end() ; ++it) { /************** * Skip 3-component clusters that have been redefined (as 2-component * clusters) or not available clusters. The latter check is indeed * redundant now, but it is used for possible future extensions in which, * for some reasons, some of the clusters found by ClusterFinder are tagged * straight away as not available. **************/ if((*it)->isRedefined() || !(*it)->isAvailable()) continue; // if the cluster is a beam cluster add it to the vector of clusters // to be split or if it is heavy if((*it)->isBeamCluster() || isHeavy(*it)) splitClusters.push(*it); } tPVector finalhadrons; cut(splitClusters, clusters, finalhadrons, softUEisOn); return finalhadrons; } void ClusterFissioner::cut(stack<ClusterPtr> & clusterStack, ClusterVector &clusters, tPVector & finalhadrons, bool softUEisOn) { /************************************************** * This method does the splitting of the cluster pointed by cluPtr * and "recursively" by all of its cluster children, if heavy. All of these * new children clusters are added (indeed the pointers to them) to the * collection of cluster pointers collecCluPtr. The method works as follows. * Initially the vector vecCluPtr contains just the input pointer to the * cluster to be split. Then it will be filled "recursively" by all * of the cluster's children that are heavy enough to require, in their turn, * to be split. In each loop, the last element of the vector vecCluPtr is * considered (only once because it is then removed from the vector). * This approach is conceptually recursive, but avoid the overhead of * a concrete recursive function. Furthermore it requires minimal changes * in the case that the fission of an heavy cluster could produce more * than two cluster children as assumed now. * * Draw the masses: for normal, non-beam clusters a power-like mass dist * is used, whereas for beam clusters a fast-decreasing exponential mass * dist is used instead (to avoid many iterative splitting which could * produce an unphysical large transverse energy from a supposed soft beam * remnant process). ****************************************/ // Here we recursively loop over clusters in the stack and cut them while (!clusterStack.empty()) { // take the last element of the vector ClusterPtr iCluster = clusterStack.top(); clusterStack.pop(); // split it cutType ct = iCluster->numComponents() == 2 ? cutTwo(iCluster, finalhadrons, softUEisOn) : cutThree(iCluster, finalhadrons, softUEisOn); // There are cases when we don't want to split, even if it fails mass test if(!ct.first.first || !ct.second.first) { // if an unsplit beam cluster leave if for the underlying event if(iCluster->isBeamCluster() && softUEisOn) iCluster->isAvailable(false); continue; } // check if clusters ClusterPtr one = dynamic_ptr_cast<ClusterPtr>(ct.first.first); ClusterPtr two = dynamic_ptr_cast<ClusterPtr>(ct.second.first); // is a beam cluster must be split into two clusters if(iCluster->isBeamCluster() && (!one||!two) && softUEisOn) { iCluster->isAvailable(false); continue; } // There should always be a intermediate quark(s) from the splitting assert(ct.first.second && ct.second.second); /// \todo sort out motherless quark pairs here. Watch out for 'quark in final state' errors iCluster->addChild(ct.first.first); // iCluster->addChild(ct.first.second); // ct.first.second->addChild(ct.first.first); iCluster->addChild(ct.second.first); // iCluster->addChild(ct.second.second); // ct.second.second->addChild(ct.second.first); // Sometimes the clusters decay C -> H + C' or C -> H + H' rather then C -> C' + C'' if(one) { clusters.push_back(one); if(one->isBeamCluster() && softUEisOn) one->isAvailable(false); if(isHeavy(one) && one->isAvailable()) clusterStack.push(one); } if(two) { clusters.push_back(two); if(two->isBeamCluster() && softUEisOn) two->isAvailable(false); if(isHeavy(two) && two->isAvailable()) clusterStack.push(two); } } } ClusterFissioner::cutType ClusterFissioner::cutTwo(ClusterPtr & cluster, tPVector & finalhadrons, bool softUEisOn) { // need to make sure only 2-cpt clusters get here assert(cluster->numComponents() == 2); tPPtr ptrQ1 = cluster->particle(0); tPPtr ptrQ2 = cluster->particle(1); // Energy Mc = cluster->mass(); Energy Mc = cluster->momentum().m(); assert(ptrQ1); assert(ptrQ2); // And check if those particles are from a beam remnant bool rem1 = cluster->isBeamRemnant(0); bool rem2 = cluster->isBeamRemnant(1); // workout which distribution to use bool soft1(false),soft2(false); switch (_iopRem) { case 0: soft1 = rem1 || rem2; soft2 = rem2 || rem1; break; case 1: soft1 = rem1; soft2 = rem2; break; } // Initialization for the exponential ("soft") mass distribution. static const int max_loop = 1000; int counter = 0; Energy Mc1 = ZERO, Mc2 = ZERO,m1=ZERO,m2=ZERO,m=ZERO; tcPDPtr toHadron1, toHadron2; PPtr newPtr1 = PPtr (); PPtr newPtr2 = PPtr (); bool succeeded = false; Lorentz5Momentum pClu1, pClu2, pQ1, pQone, pQtwo, pQ2; do { succeeded = false; ++counter; // get a flavour for the qqbar pair drawNewFlavour(newPtr1,newPtr2,cluster); // check for right ordering assert (ptrQ2); assert (newPtr2); assert (ptrQ2->dataPtr()); assert (newPtr2->dataPtr()); // TODO careful if DiquarkClusters can exist bool Q1diq = DiquarkMatcher::Check(ptrQ1->id()); bool Q2diq = DiquarkMatcher::Check(ptrQ2->id()); bool newQ1diq = DiquarkMatcher::Check(newPtr1->id()); bool newQ2diq = DiquarkMatcher::Check(newPtr2->id()); bool diqClu1 = Q1diq && newQ1diq; bool diqClu2 = Q2diq && newQ2diq; // std::cout << "Make Clusters: ( " << ptrQ1->PDGName() << " " << newPtr1->PDGName() << " ), ( " // << ptrQ2->PDGName() << " " << newPtr2->PDGName() << " )\n"; if (!( diqClu1 || diqClu2 ) && (cantMakeHadron(ptrQ1, newPtr1) || cantMakeHadron(ptrQ2, newPtr2))) { swap(newPtr1, newPtr2); // check again if(cantMakeHadron(ptrQ1, newPtr1) || cantMakeHadron(ptrQ2, newPtr2)) { throw Exception() << "ClusterFissioner cannot split the cluster (" << ptrQ1->PDGName() << ' ' << ptrQ2->PDGName() << ") into hadrons.\n" << "drawn Flavour: "<< newPtr1->PDGName()<<"\n"<< Exception::runerror; } } else if ( diqClu1 || diqClu2 ){ bool swapped=false; if ( !diqClu1 && cantMakeHadron(ptrQ1,newPtr1) ) { swap(newPtr1, newPtr2); swapped=true; } if ( !diqClu2 && cantMakeHadron(ptrQ2,newPtr2) ) { assert(!swapped); swap(newPtr1, newPtr2); } if ( diqClu2 && diqClu1 && ptrQ1->id()*newPtr1->id()>0 ) { assert(!swapped); swap(newPtr1, newPtr2); } // std::cout << "Make Clusters: ( " << ptrQ1->PDGName() << " " << newPtr1->PDGName() << " ), ( " // << ptrQ2->PDGName() << " " << newPtr2->PDGName() << " )\n"; if (!diqClu1) assert(!cantMakeHadron(ptrQ1,newPtr1)); if (!diqClu2) assert(!cantMakeHadron(ptrQ2,newPtr2)); } // Check that new clusters can produce particles and there is enough // phase space to choose the drawn flavour m1 = ptrQ1->data().constituentMass(); m2 = ptrQ2->data().constituentMass(); m = newPtr1->data().constituentMass(); // Do not split in the case there is no phase space available if(Mc < m1+m + m2+m) continue; pQ1.setMass(m1); pQone.setMass(m); pQtwo.setMass(m); pQ2.setMass(m2); pair<Energy,Energy> res = drawNewMasses(Mc, soft1, soft2, pClu1, pClu2, ptrQ1, pQ1, newPtr1, pQone, newPtr2, pQtwo, ptrQ2, pQ2); // derive the masses of the children Mc1 = res.first; Mc2 = res.second; // static kinematic threshold if(_kinematicThresholdChoice == 0) { if(Mc1 < m1+m || Mc2 < m+m2 || Mc1+Mc2 > Mc) continue; // dynamic kinematic threshold } else if(_kinematicThresholdChoice == 1) { bool C1 = ( sqr(Mc1) )/( sqr(m1) + sqr(m) + _kinThresholdShift ) < 1.0 ? true : false; bool C2 = ( sqr(Mc2) )/( sqr(m2) + sqr(m) + _kinThresholdShift ) < 1.0 ? true : false; bool C3 = ( sqr(Mc1) + sqr(Mc2) )/( sqr(Mc) ) > 1.0 ? true : false; if( C1 || C2 || C3 ) continue; } if ( _phaseSpaceWeights ) { if ( phaseSpaceVeto(Mc,Mc1,Mc2,m,m1,m2) ) continue; } if ( Mc1 < spectrum()->massLightestHadronPair(ptrQ1->dataPtr(),newPtr1->dataPtr())) { // std::cout << "Cluster decays to hadron MC"<< Mc1/GeV << "\t MLHP "<< spectrum()->massLightestHadronPair(ptrQ1->dataPtr(),newPtr1->dataPtr())/GeV<<"\n"; // std::cout << "Flavour " << ptrQ1->PDGName() <<", " << newPtr1->PDGName()<< "\n"; continue; } if ( Mc2 < spectrum()->massLightestHadronPair(ptrQ2->dataPtr(),newPtr2->dataPtr())) { // std::cout << "Cluster decays to hadron MC"<< Mc2/GeV << "\t MLHP "<< spectrum()->massLightestHadronPair(ptrQ2->dataPtr(),newPtr2->dataPtr())/GeV<<"\n"; // std::cout << "Flavour " << ptrQ2->PDGName() <<", " << newPtr2->PDGName()<< "\n"; continue; } // if (diqClu1) { // if (Mc1 < spectrum()->massLightestBaryonPair(ptrQ1->dataPtr(),newPtr1->dataPtr())) { // continue; // } // } // if (diqClu2) { // if (Mc2 < spectrum()->massLightestBaryonPair(ptrQ2->dataPtr(),newPtr2->dataPtr())) { // continue; // } // } /************************** * New (not present in Fortran Herwig): * check whether the fragment masses Mc1 and Mc2 are above the * threshold for the production of the lightest pair of hadrons with the * right flavours. If not, then set by hand the mass to the lightest * single hadron with the right flavours, in order to solve correctly * the kinematics, and (later in this method) create directly such hadron * and add it to the children hadrons of the cluster that undergoes the * fission (i.e. the one pointed by iCluPtr). Notice that in this special * case, the heavy cluster that undergoes the fission has one single * cluster child and one single hadron child. We prefer this approach, * rather than to create a light cluster, with the mass set equal to * the lightest hadron, and let then the class LightClusterDecayer to do * the job to decay it to that single hadron, for two reasons: * First, because the sum of the masses of the two constituents can be, * in this case, greater than the mass of that hadron, hence it would * be impossible to solve the kinematics for such two components, and * therefore we would have a cluster whose components are undefined. * Second, the algorithm is faster, because it avoids the reshuffling * procedure that would be necessary if we used LightClusterDecayer * to decay the light cluster to the lightest hadron. ****************************/ // override chosen masses if needed toHadron1 = _hadronSpectrum->chooseSingleHadron(ptrQ1->dataPtr(), newPtr1->dataPtr(),Mc1); if(toHadron1) {std::cout << "\nHadron1\n" << std::endl; Mc1 = toHadron1->mass(); pClu1.setMass(Mc1); } toHadron2 = _hadronSpectrum->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc2); if(toHadron2) {std::cout << "\nHadron2\n" << std::endl; Mc2 = toHadron2->mass(); pClu2.setMass(Mc2); } if (toHadron1 || toHadron2) throw Exception() << "Cluster wants to go to H,C or H,H'" << Exception::runerror; // if a beam cluster not allowed to decay to hadrons if(cluster->isBeamCluster() && (toHadron1||toHadron2) && softUEisOn) continue; // Check if the decay kinematics is still possible: if not then // force the one-hadron decay for the other cluster as well. if(Mc1 + Mc2 > Mc) { if(!toHadron1) { toHadron1 = _hadronSpectrum->chooseSingleHadron(ptrQ1->dataPtr(), newPtr1->dataPtr(),Mc-Mc2); if(toHadron1) {std::cout << "\nHadron1\n" << std::endl; Mc1 = toHadron1->mass(); pClu1.setMass(Mc1); } } else if(!toHadron2) { toHadron2 = _hadronSpectrum->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc-Mc1); if(toHadron2) {std::cout << "\nHadron2\n" << std::endl; Mc2 = toHadron2->mass(); pClu2.setMass(Mc2); } } } succeeded = (Mc >= Mc1+Mc2); } while (!succeeded && counter < max_loop); if(counter >= max_loop) { static const PPtr null = PPtr(); return cutType(PPair(null,null),PPair(null,null)); } // Determined the (5-components) momenta (all in the LAB frame) Lorentz5Momentum pClu = cluster->momentum(); // known Lorentz5Momentum p0Q1 = ptrQ1->momentum(); // known (mom Q1 before fission) p0Q1.setMass(ptrQ1->mass()); // known (mom Q1 before fission) p0Q1.rescaleEnergy(); pClu.rescaleMass(); calculateKinematics(pClu,p0Q1,toHadron1,toHadron2, pClu1,pClu2,pQ1,pQone,pQtwo,pQ2); /****************** * The previous methods have determined the kinematics and positions * of C -> C1 + C2. * In the case that one of the two product is light, that means either * decayOneHadronClu1 or decayOneHadronClu2 is true, then the momenta * of the components of that light product have not been determined, * and a (light) cluster will not be created: the heavy father cluster * decays, in this case, into a single (not-light) cluster and a * single hadron. In the other, "normal", cases the father cluster * decays into two clusters, each of which has well defined components. * Notice that, in the case of components which point to particles, the * momenta of the components is properly set to the new values, whereas * we do not change the momenta of the pointed particles, because we * want to keep all of the information (that is the new momentum of a * component after the splitting, which is contained in the _momentum * member of the Component class, and the (old) momentum of that component * before the splitting, which is contained in the momentum of the * pointed particle). Please not make confusion of this only apparent * inconsistency! ********************/ LorentzPoint posC,pos1,pos2; posC = cluster->vertex(); calculatePositions(pClu, posC, pClu1, pClu2, pos1, pos2); cutType rval; if(toHadron1) { rval.first = produceHadron(toHadron1, newPtr1, pClu1, pos1); finalhadrons.push_back(rval.first.first); } else { rval.first = produceCluster(ptrQ1, newPtr1, pClu1, pos1, pQ1, pQone, rem1); } if(toHadron2) { rval.second = produceHadron(toHadron2, newPtr2, pClu2, pos2); finalhadrons.push_back(rval.second.first); } else { rval.second = produceCluster(ptrQ2, newPtr2, pClu2, pos2, pQ2, pQtwo, rem2); } return rval; } ClusterFissioner::cutType ClusterFissioner::cutThree(ClusterPtr & cluster, tPVector & finalhadrons, bool softUEisOn) { // need to make sure only 3-cpt clusters get here assert(cluster->numComponents() == 3); // extract quarks tPPtr ptrQ[3] = {cluster->particle(0),cluster->particle(1),cluster->particle(2)}; assert( ptrQ[0] && ptrQ[1] && ptrQ[2] ); // find maximum mass pair Energy mmax(ZERO); Lorentz5Momentum pDiQuark; int iq1(-1),iq2(-1); Lorentz5Momentum psum; for(int q1=0;q1<3;++q1) { psum+= ptrQ[q1]->momentum(); for(int q2=q1+1;q2<3;++q2) { Lorentz5Momentum ptest = ptrQ[q1]->momentum()+ptrQ[q2]->momentum(); ptest.rescaleMass(); Energy mass = ptest.m(); if(mass>mmax) { mmax = mass; pDiQuark = ptest; iq1 = q1; iq2 = q2; } } } // and the spectators int iother(-1); for(int ix=0;ix<3;++ix) if(ix!=iq1&&ix!=iq2) iother=ix; assert(iq1>=0&&iq2>=0&&iother>=0); // And check if those particles are from a beam remnant bool rem1 = cluster->isBeamRemnant(iq1); bool rem2 = cluster->isBeamRemnant(iq2); // workout which distribution to use bool soft1(false),soft2(false); switch (_iopRem) { case 0: soft1 = rem1 || rem2; soft2 = rem2 || rem1; break; case 1: soft1 = rem1; soft2 = rem2; break; } // Initialization for the exponential ("soft") mass distribution. static const int max_loop = 1000; int counter = 0; Energy Mc1 = ZERO, Mc2 = ZERO, m1=ZERO, m2=ZERO, m=ZERO; tcPDPtr toHadron; bool toDiQuark(false); PPtr newPtr1 = PPtr(),newPtr2 = PPtr(); PDPtr diquark; bool succeeded = false; Lorentz5Momentum pClu1, pClu2, pQ1, pQone, pQtwo, pQ2; do { succeeded = false; ++counter; // get a flavour for the qqbar pair drawNewFlavour(newPtr1,newPtr2,cluster); // randomly pick which will be (anti)diquark and which a mesonic cluster if(UseRandom::rndbool()) { swap(iq1,iq2); swap(rem1,rem2); } // check first order if(cantMakeHadron(ptrQ[iq1], newPtr1) || !spectrum()->canMakeDiQuark(ptrQ[iq2], newPtr2)) { swap(newPtr1,newPtr2); } // check again if(cantMakeHadron(ptrQ[iq1], newPtr1) || !spectrum()->canMakeDiQuark(ptrQ[iq2], newPtr2)) { throw Exception() << "ClusterFissioner cannot split the cluster (" << ptrQ[iq1]->PDGName() << ' ' << ptrQ[iq2]->PDGName() << ") into a hadron and diquark.\n" << Exception::runerror; } // Check that new clusters can produce particles and there is enough // phase space to choose the drawn flavour m1 = ptrQ[iq1]->data().constituentMass(); m2 = ptrQ[iq2]->data().constituentMass(); m = newPtr1->data().constituentMass(); // Do not split in the case there is no phase space available if(mmax < m1+m + m2+m) continue; pQ1.setMass(m1); pQone.setMass(m); pQtwo.setMass(m); pQ2.setMass(m2); pair<Energy,Energy> res = drawNewMasses(mmax, soft1, soft2, pClu1, pClu2, ptrQ[iq1], pQ1, newPtr1, pQone, newPtr2, pQtwo, ptrQ[iq1], pQ2); Mc1 = res.first; Mc2 = res.second; if(Mc1 < m1+m || Mc2 < m+m2 || Mc1+Mc2 > mmax) continue; if ( _phaseSpaceWeights ) { if ( phaseSpaceVeto(mmax,Mc1,Mc2,m,m1,m2) ) continue; } // check if need to force meson clster to hadron toHadron = _hadronSpectrum->chooseSingleHadron(ptrQ[iq1]->dataPtr(), newPtr1->dataPtr(),Mc1); if(toHadron) { Mc1 = toHadron->mass(); pClu1.setMass(Mc1); } // check if need to force diquark cluster to be on-shell toDiQuark = false; diquark = spectrum()->makeDiquark(ptrQ[iq2]->dataPtr(), newPtr2->dataPtr()); if(Mc2 < diquark->constituentMass()) { Mc2 = diquark->constituentMass(); pClu2.setMass(Mc2); toDiQuark = true; } // if a beam cluster not allowed to decay to hadrons if(cluster->isBeamCluster() && toHadron && softUEisOn) continue; // Check if the decay kinematics is still possible: if not then // force the one-hadron decay for the other cluster as well. if(Mc1 + Mc2 > mmax) { if(!toHadron) { toHadron = _hadronSpectrum->chooseSingleHadron(ptrQ[iq1]->dataPtr(), newPtr1->dataPtr(),mmax-Mc2); if(toHadron) { Mc1 = toHadron->mass(); pClu1.setMass(Mc1); } } else if(!toDiQuark) { Mc2 = _hadronSpectrum->massLightestHadron(ptrQ[iq2]->dataPtr(), newPtr2->dataPtr()); pClu2.setMass(Mc2); toDiQuark = true; } } succeeded = (mmax >= Mc1+Mc2); } while (!succeeded && counter < max_loop); // check no of tries if(counter >= max_loop) return cutType(); // Determine the (5-components) momenta (all in the LAB frame) Lorentz5Momentum p0Q1 = ptrQ[iq1]->momentum(); calculateKinematics(pDiQuark,p0Q1,toHadron,toDiQuark, pClu1,pClu2,pQ1,pQone,pQtwo,pQ2); // positions of the new clusters LorentzPoint pos1,pos2; Lorentz5Momentum pBaryon = pClu2+ptrQ[iother]->momentum(); calculatePositions(cluster->momentum(), cluster->vertex(), pClu1, pBaryon, pos1, pos2); // first the mesonic cluster/meson cutType rval; if(toHadron) { rval.first = produceHadron(toHadron, newPtr1, pClu1, pos1); finalhadrons.push_back(rval.first.first); } else { rval.first = produceCluster(ptrQ[iq1], newPtr1, pClu1, pos1, pQ1, pQone, rem1); } if(toDiQuark) { rem2 |= cluster->isBeamRemnant(iother); PPtr newDiQuark = diquark->produceParticle(pClu2); rval.second = produceCluster(newDiQuark, ptrQ[iother], pBaryon, pos2, pClu2, ptrQ[iother]->momentum(), rem2); } else { rval.second = produceCluster(ptrQ[iq2], newPtr2, pBaryon, pos2, pQ2, pQtwo, rem2, ptrQ[iother],cluster->isBeamRemnant(iother)); } cluster->isAvailable(false); return rval; } ClusterFissioner::PPair ClusterFissioner::produceHadron(tcPDPtr hadron, tPPtr newPtr, const Lorentz5Momentum &a, const LorentzPoint &b) const { PPair rval; if(hadron->coloured()) { rval.first = (_hadronSpectrum->lightestHadron(hadron,newPtr->dataPtr()))->produceParticle(); } else rval.first = hadron->produceParticle(); rval.second = newPtr; rval.first->set5Momentum(a); rval.first->setVertex(b); return rval; } ClusterFissioner::PPair ClusterFissioner::produceCluster(tPPtr ptrQ, tPPtr newPtr, const Lorentz5Momentum & a, const LorentzPoint & b, const Lorentz5Momentum & c, const Lorentz5Momentum & d, bool isRem, tPPtr spect, bool remSpect) const { PPair rval; rval.second = newPtr; ClusterPtr cluster = !spect ? new_ptr(Cluster(ptrQ,rval.second)) : new_ptr(Cluster(ptrQ,rval.second,spect)); rval.first = cluster; cluster->set5Momentum(a); cluster->setVertex(b); assert(cluster->particle(0)->id() == ptrQ->id()); cluster->particle(0)->set5Momentum(c); cluster->particle(1)->set5Momentum(d); cluster->setBeamRemnant(0,isRem); if(remSpect) cluster->setBeamRemnant(2,remSpect); return rval; } void ClusterFissioner::drawNewFlavourDiquarks(PPtr& newPtrPos,PPtr& newPtrNeg, const ClusterPtr & clu) const { // Flavour is assumed to be only u, d, s, with weights // (which are not normalized probabilities) given // by the same weights as used in HadronsSelector for // the decay of clusters into two hadrons. unsigned hasDiquarks=0; assert(clu->numComponents()==2); tcPDPtr pD1=clu->particle(0)->dataPtr(); tcPDPtr pD2=clu->particle(1)->dataPtr(); bool isDiq1=DiquarkMatcher::Check(pD1->id()); if (isDiq1) hasDiquarks++; bool isDiq2=DiquarkMatcher::Check(pD2->id()); if (isDiq2) hasDiquarks++; assert(hasDiquarks<=2); Energy Mc=(clu->momentum().mass()); // if (fabs(clu->momentum().massError() )>1e-14) std::cout << "Mass inconsistency CF : " << std::scientific << clu->momentum().massError() <<"\n"; // Not allow yet Diquark Clusters // if ( hasDiquarks>=1 || Mc < spectrum()->massLightestBaryonPair(pD1,pD2) ) // return drawNewFlavour(newPtrPos,newPtrNeg); Energy minMass; Selector<long> choice; // int countQ=0; // int countDiQ=0; // adding quark-antiquark pairs to the selection list for ( const long& id : spectrum()->lightHadronizingQuarks() ) { // TODO uncommenting below gives sometimes 0 selection possibility, // maybe need to be checked in the LightClusterDecayer and ColourReconnector // if (Mc < spectrum()->massLightestHadronPair(pD1,pD2)) continue; // countQ++; if (_fissionCluster==0) choice.insert(_hadronSpectrum->pwtQuark(id),id); else if (_fissionCluster==1) choice.insert(_fissionPwt.find(id)->second,id); else assert(false); } // adding diquark-antidiquark pairs to the selection list switch (hasDiquarks) { case 0: for ( const long& id : spectrum()->lightHadronizingDiquarks() ) { if (_strictDiquarkKinematics) { tPDPtr cand = getParticleData(id); minMass = spectrum()->massLightestHadron(pD2,cand) + spectrum()->massLightestHadron(cand,pD1); } else minMass = spectrum()->massLightestBaryonPair(pD1,pD2); if (Mc < minMass) continue; // countDiQ++; if (_fissionCluster==0) choice.insert(_hadronSpectrum->pwtQuark(id),id); else if (_fissionCluster==1) choice.insert(_fissionPwt.find(id)->second,id); else assert(false); } break; case 1: if (_diquarkClusterFission<1) break; for ( const long& id : spectrum()->lightHadronizingDiquarks() ) { tPDPtr diq = getParticleData(id); if (isDiq1) minMass = spectrum()->massLightestHadron(pD2,diq) + spectrum()->massLightestBaryonPair(diq,pD1); else minMass = spectrum()->massLightestHadron(pD1,diq) + spectrum()->massLightestBaryonPair(diq,pD2); if (Mc < minMass) continue; // countDiQ++; if (_fissionCluster==0) choice.insert(_hadronSpectrum->pwtQuark(id),id); else if (_fissionCluster==1) choice.insert(_fissionPwt.find(id)->second,id); else assert(false); } break; case 2: if (_diquarkClusterFission<2) break; for ( const long& id : spectrum()->lightHadronizingDiquarks() ) { tPDPtr diq = getParticleData(id); if (Mc < spectrum()->massLightestBaryonPair(pD1,pD2)) { throw Exception() << "Found Diquark Cluster:\n" << *clu << "\nwith MassCluster = " << ounit(Mc,GeV) <<" GeV MassLightestBaryonPair = " << ounit(spectrum()->massLightestBaryonPair(pD1,pD2) ,GeV) << " GeV cannot decay" << Exception::eventerror; } minMass = spectrum()->massLightestBaryonPair(pD1,diq) + spectrum()->massLightestBaryonPair(diq,pD2); if (Mc < minMass) continue; // countDiQ++; if (_fissionCluster==0) choice.insert(_hadronSpectrum->pwtQuark(id),id); else if (_fissionCluster==1) choice.insert(_fissionPwt.find(id)->second,id); else assert(false); } break; default: assert(false); } assert(choice.size()>0); long idNew = choice.select(UseRandom::rnd()); newPtrPos = getParticle(idNew); newPtrNeg = getParticle(-idNew); assert (newPtrPos); assert(newPtrNeg); assert (newPtrPos->dataPtr()); assert(newPtrNeg->dataPtr()); } void ClusterFissioner::drawNewFlavour(PPtr& newPtrPos,PPtr& newPtrNeg) const { // Flavour is assumed to be only u, d, s, with weights // (which are not normalized probabilities) given // by the same weights as used in HadronsSelector for // the decay of clusters into two hadrons. Selector<long> choice; switch(_fissionCluster){ case 0: for ( const long& id : spectrum()->lightHadronizingQuarks() ) choice.insert(_hadronSpectrum->pwtQuark(id),id); break; case 1: for ( const long& id : spectrum()->lightHadronizingQuarks() ) choice.insert(_fissionPwt.find(id)->second,id); break; default : assert(false); } long idNew = choice.select(UseRandom::rnd()); newPtrPos = getParticle(idNew); newPtrNeg = getParticle(-idNew); assert (newPtrPos); assert(newPtrNeg); assert (newPtrPos->dataPtr()); assert(newPtrNeg->dataPtr()); } void ClusterFissioner::drawNewFlavourEnhanced(PPtr& newPtrPos,PPtr& newPtrNeg, Energy2 mass2) const { if ( spectrum()->gluonId() != ParticleID::g ) throw Exception() << "strange enhancement only working with Standard Model hadronization" << Exception::runerror; // Flavour is assumed to be only u, d, s, with weights // (which are not normalized probabilities) given // by the same weights as used in HadronsSelector for // the decay of clusters into two hadrons. double prob_d = 0.; double prob_u = 0.; double prob_s = 0.; double scale = abs(double(sqr(_m0Fission)/mass2)); // Choose which splitting weights you wish to use switch(_fissionCluster){ // 0: ClusterFissioner and ClusterDecayer use the same weights case 0: prob_d = _hadronSpectrum->pwtQuark(ParticleID::d); prob_u = _hadronSpectrum->pwtQuark(ParticleID::u); /* Strangeness enhancement: Case 1: probability scaling Case 2: Exponential scaling */ if (_enhanceSProb == 1) prob_s = (_maxScale < scale) ? 0. : pow(_hadronSpectrum->pwtQuark(ParticleID::s),scale); else if (_enhanceSProb == 2) prob_s = (_maxScale < scale) ? 0. : exp(-scale); break; /* 1: ClusterFissioner uses its own unique set of weights, i.e. decoupled from ClusterDecayer */ case 1: prob_d = _fissionPwt.find(ParticleID::d)->second; prob_u = _fissionPwt.find(ParticleID::u)->second; if (_enhanceSProb == 1) prob_s = (_maxScale < scale) ? 0. : pow(_fissionPwt.find(ParticleID::s)->second,scale); else if (_enhanceSProb == 2) prob_s = (_maxScale < scale) ? 0. : exp(-scale); break; default: assert(false); } int choice = UseRandom::rnd3(prob_u, prob_d, prob_s); long idNew = 0; switch (choice) { case 0: idNew = ThePEG::ParticleID::u; break; case 1: idNew = ThePEG::ParticleID::d; break; case 2: idNew = ThePEG::ParticleID::s; break; } newPtrPos = getParticle(idNew); newPtrNeg = getParticle(-idNew); assert (newPtrPos); assert(newPtrNeg); assert (newPtrPos->dataPtr()); assert(newPtrNeg->dataPtr()); } Energy2 ClusterFissioner::clustermass(const ClusterPtr & cluster) const { Lorentz5Momentum pIn = cluster->momentum(); Energy2 endpointmass2 = sqr(cluster->particle(0)->mass() + cluster->particle(1)->mass()); Energy2 singletm2 = pIn.m2(); // Return either the cluster mass, or the lambda measure return (_massMeasure == 0) ? singletm2 : singletm2 - endpointmass2; } Energy ClusterFissioner::drawChildMass(const Energy M, const Energy m1, const Energy m2, const Energy m, const double expt, const bool soft) const { /*************************** * This method, given in input the cluster mass Mclu of an heavy cluster C, * made of consituents of masses m1 and m2, draws the masses Mclu1 and Mclu2 * of, respectively, the children cluster C1, made of constituent masses m1 * and m, and cluster C2, of mass Mclu2 and made of constituent masses m2 * and m. The mass is extracted from one of the two following mass * distributions: * --- power-like ("normal" distribution) * d(Prob) / d(M^exponent) = const * where the exponent can be different from the two children C1 (exp1) * and C2 (exponent2). * --- exponential ("soft" distribution) * d(Prob) / d(M^2) = exp(-b*M) * where b = 2.0 / average. * Such distributions are limited below by the masses of * the constituents quarks, and above from the mass of decaying cluster C. * The choice of which of the two mass distributions to use for each of the * two cluster children is dictated by iRemnant (see below). * If the number of attempts to extract a pair of mass values that are * kinematically acceptable is above some fixed number (max_loop, see below) * the method gives up and returns false; otherwise, when it succeeds, it * returns true. * * These distributions have been modified from HERWIG: * Before these were: * Mclu1 = m1 + (Mclu - m1 - m2)*pow( rnd(), 1.0/exponent1 ); * The new one coded here is a more efficient version, same density * but taking into account 'in phase space from' beforehand ***************************/ // hard cluster if(!soft) { return pow(UseRandom::rnd(pow((M-m1-m2-m)*UnitRemoval::InvE, expt), pow(m*UnitRemoval::InvE, expt)), 1./expt )*UnitRemoval::E + m1; } // Otherwise it uses a soft mass distribution else { static const InvEnergy b = 2.0 / _btClM; Energy max = M-m1-m2-2.0*m; double rmin = b*max; rmin = ( rmin < 50 ) ? exp(-rmin) : 0.; double r1; do { r1 = UseRandom::rnd(rmin, 1.0) * UseRandom::rnd(rmin, 1.0); } while (r1 < rmin); return m1 + m - log(r1)/b; } } Axis ClusterFissioner::sampleDirectionAligned(const Lorentz5Momentum & pRelCOM) const { return pRelCOM.vect().unit(); } Axis ClusterFissioner::sampleDirectionUniform() const { double cosTheta = -1 + 2.0 * UseRandom::rnd(); double sinTheta = sqrt(1.0-cosTheta*cosTheta); double Phi = 2.0 * Constants::pi * UseRandom::rnd(); Axis uClusterUniform(cos(Phi)*cosTheta, sin(Phi)*cosTheta, sinTheta); return uClusterUniform.unit(); } Axis ClusterFissioner::sampleDirectionSemiUniform(const Lorentz5Momentum & pRelCOM) const { Axis dir=pRelCOM.vect().unit(); Axis res; do { res=sampleDirectionUniform(); } while (dir*res<0); return res; } Energy ClusterFissioner::sample2DGaussianPT(const Energy & Pcom) const { Energy sigmaPT=_fluxTubeWidth*Pcom/GeV; if (_fluxTubeWidth==ZERO) return ZERO; Energy magnitude; Energy pTx,pTy; // Energy2 pT2; const int maxcount=100; int count=0; do { if (count>=maxcount) { if (Pcom>0.5*sigmaPT) throw Exception() << "Could not sample direction in ClusterFissioner::sampleDirectionGaussianPT() " << Exception::eventerror; // Fallback uniform sampling // double phi=UseRandom::rnd()*2.0*Constants::pi; magnitude=UseRandom::rnd()*Pcom; // pTx = magnitude*cos(phi); // pTy = magnitude*sin(phi); break; } pTx = UseRandom::rndGauss(sigmaPT); pTy = UseRandom::rndGauss(sigmaPT); // pT2 = sqr(pTx)+sqr(pTy); magnitude=sqrt(sqr(pTx)+sqr(pTy)); count++; } while (magnitude>Pcom); return magnitude; } Axis ClusterFissioner::sampleDirectionGaussianPT(const Lorentz5Momentum & pRelCOM, const Energy Mass, const Energy mass1, const Energy mass2) const { Energy pstarCOM = Kinematics::pstarTwoBodyDecay(Mass,mass1,mass2); Energy pT=sample2DGaussianPT(pstarCOM); Energy2 pT2=pT*pT; double phi=UseRandom::rnd()*2.0*Constants::pi; Energy pTx=pT*cos(phi); Energy pTy=pT*sin(phi); // const int maxcount=100; // int count=0; // Energy sigmaPT=_fluxTubeWidth; // do { // if (count>=maxcount) { // if (pstarCOM>0.5*sigmaPT) throw Exception() << "Could not sample direction in ClusterFissioner::sampleDirectionGaussianPT() " // << Exception::eventerror; // //Fallback uniform sampling // double phi=UseRandom::rnd()*2.0*Constants::pi; // Energy magnitude=UseRandom::rnd()*pstarCOM; // pTx = magnitude*cos(phi); // pTy = magnitude*sin(phi); // pT2 = sqr(pTx)+sqr(pTy); // break; // } // pTx = UseRandom::rndGauss(sigmaPT); // pTy = UseRandom::rndGauss(sigmaPT); // pT2 = sqr(pTx)+sqr(pTy); // count++; // } // while (pT2>sqr(pstarCOM)); Axis pRelativeDir=pRelCOM.vect().unit(); Axis TrvX = pRelativeDir.orthogonal().unit(); Axis TrvY = TrvX.cross(pRelativeDir).unit(); Axis pTkick = pTx/pstarCOM*TrvX + pTy/pstarCOM*TrvY; Axis uClusterFluxTube = (pRelativeDir*sqrt(1.0-pT2/sqr(pstarCOM)) + pTkick); return uClusterFluxTube.unit(); } static void printVec(const Lorentz5Momentum & p); static void printVec(const Lorentz5Momentum & p){ std::cout << "( " << std::setprecision(8) << p.t()/GeV << ",\t" << p.vect().x()/GeV << ",\t" << p.vect().y()/GeV << ",\t" << p.vect().z()/GeV << " )\t|p| = " << sqrt(sqr(p.vect())/GeV2) <<"\tm = " << p.m()/GeV <<"\tmass = " << p.mass()/GeV << std::setprecision(3) <<"\n"; } // static void printVecBoost(const boost::numeric::ublas::vector<Energy> & p); // static void printVecBoost(const boost::numeric::ublas::vector<Energy> & p){ // std::cout << "( " // << p[0]/GeV << ",\t" // << p[1]/GeV << ",\t" // << p[2]/GeV << ",\t" // << p[3]/GeV // << " )\t|p| = " // << sqrt(sqr(p[1]*p[1]+p[2]*p[2]+p[3]*p[3])/GeV2) <<"\tmass = " // << sqrt(sqr(p[0]*p[0]-(p[1]*p[1]+p[2]*p[2]+p[3]*p[3]))/GeV2) <<"\n"; // } static void printVecBoostDBL(const boost::numeric::ublas::vector<double> & p); static void printVecBoostDBL(const boost::numeric::ublas::vector<double> & p){ std::cout << "( " << std::setprecision(8) << p[0] << ",\t" << p[1] << ",\t" << p[2] << ",\t" << p[3] << " )\t|p| = " << sqrt(p[1]*p[1]+p[2]*p[2]+p[3]*p[3]) <<"\tmass = " << sqrt(p[0]*p[0]-(p[1]*p[1]+p[2]*p[2]+p[3]*p[3])) << std::setprecision(3) <<"\n"; } static int printIfNotNullBOOST(const boost::numeric::ublas::vector<double> & p1,const boost::numeric::ublas::vector<double> & p2); static int printIfNotNullBOOST(const boost::numeric::ublas::vector<double> & p1,const boost::numeric::ublas::vector<double> & p2) { double eps=1e-5; double Eavg=(p1(0)+p2(0))/2.0; if (fabs(p1(0)-p2(0))>eps*Eavg || std::isnan(p1(0)-p2(0))) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaE not 0" << std::endl; printVecBoostDBL(p1); printVecBoostDBL(p2); std::cout << std::setprecision(3); return 1; } double P1=sqrt(pow(p1(1),2)+pow(p1(2),2)+pow(p1(3),2)); double P2=sqrt(pow(p2(1),2)+pow(p2(2),2)+pow(p2(3),2)); double Pavg=(P1+P2)/2.0; if (fabs(p1(1)-p2(1))>eps*Pavg || std::isnan(p1(1)-p2(1))) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaPx not 0" << std::endl; printVecBoostDBL(p1); printVecBoostDBL(p2); std::cout << std::setprecision(3); return 1; } if (fabs(p1(2)-p2(2))>eps*Pavg|| std::isnan(p1(2)-p2(2)) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaPy not 0" << std::endl; printVecBoostDBL(p1); printVecBoostDBL(p2); std::cout << std::setprecision(3); return 1; } if (fabs(p1(3)-p2(3))>eps*Pavg|| std::isnan(p1(3)-p2(3)) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaPz not 0" << std::endl; printVecBoostDBL(p1); printVecBoostDBL(p2); std::cout << std::setprecision(3); return 1; } return 0; } static int printIfNotNull(const Lorentz5Momentum & p); static int printIfNotNull(const Lorentz5Momentum & p) { double eps=1e-5; if (abs(p.t()/GeV)>eps || std::isnan(p.t()/GeV) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaE not 0" << std::endl; printVec(p); std::cout << std::setprecision(3); return 1; } if (abs(p.x()/GeV)>eps|| std::isnan(p.x()/GeV) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaPx not 0" << std::endl; printVec(p); std::cout << std::setprecision(3); return 1; } if (abs(p.y()/GeV)>eps|| std::isnan(p.y()/GeV) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaPy not 0" << std::endl; printVec(p); std::cout << std::setprecision(3); return 1; } if (abs(p.z()/GeV)>eps|| std::isnan(p.z()/GeV) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaPz not 0" << std::endl; printVec(p); std::cout << std::setprecision(3); return 1; } return 0; } static int printIfNotNullComp(const Lorentz5Momentum & p1,const boost::numeric::ublas::vector<double> & p2); static int printIfNotNullComp(const Lorentz5Momentum & p1,const boost::numeric::ublas::vector<double> & p2) { double eps=1e-5; double Eavg=(p1.t()/GeV+p2(0))/2.0; if (fabs(p1.t()/GeV-p2(0))>eps*Eavg|| std::isnan(p1.t()/GeV-p2(0)) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaE not 0" << std::endl; printVec(p1); printVecBoostDBL(p2); std::cout << std::setprecision(3); return 1; } double P1=p1.vect().mag()/GeV; double P2=sqrt(pow(p2(1),2)+pow(p2(2),2)+pow(p2(3),2)); double Pavg=(P1+P2)/2.0; if (abs(p1.x()/GeV-p2(1))>eps*Pavg|| std::isnan(p1.x()/GeV-p2(1)) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaPx not 0" << std::endl; printVec(p1); printVecBoostDBL(p2); std::cout << std::setprecision(3); return 1; } if (abs(p1.y()/GeV-p2(2))>eps*Pavg|| std::isnan(p1.y()/GeV-p2(2)) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaPy not 0" << std::endl; printVec(p1); printVecBoostDBL(p2); std::cout << std::setprecision(3); return 1; } if (abs(p1.z()/GeV-p2(3))>eps*Pavg|| std::isnan(p1.z()/GeV-p2(3)) ) { std::cout << std::setprecision(int(-log10(eps))) << "DeltaPz not 0" << std::endl; printVec(p1); printVecBoostDBL(p2); std::cout << std::setprecision(3); return 1; } return 0; } void ClusterFissioner::calculateKinematicsClusterCOM(const Energy M, const Lorentz5Momentum & piLab, const Lorentz5Momentum & pjLab, Lorentz5Momentum & pClu1, Lorentz5Momentum & pClu2) const { // std::cout << std::setprecision(18)<< "######### Info #########" << std::endl; // std::cout << std::setprecision(18)<< "M = "<< M/GeV << std::endl; // std::cout << std::setprecision(18)<< "pi+pj mass = "<< (piLab+pjLab).m()/GeV << std::endl; // std::cout << std::setprecision(18)<< "piLab.mass = "<< piLab.mass()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pjLab.mass = "<< pjLab.mass()/GeV << std::endl; // std::cout << std::setprecision(18)<< "piLab.m = "<< piLab.m()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pjLab.m = "<< pjLab.m()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pClu1.mass = "<< pClu1.mass()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pClu2.mass = "<< pClu2.mass()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pClu1.m = "<< pClu1.m()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pClu2.m = "<< pClu2.m()/GeV << std::endl; // printIfNotNull(pClu1+pClu2); // std::cout << "######### End #########" << std::endl; // const Lorentz5Momentum piLab(p0Q1); // const Lorentz5Momentum pjLab(p0Q2); // piLab.setMass(p0Q1.mass()); // pjLab.setMass(p0Q2.mass()); // Energy Ei=sqrt(piLab.mass()*piLab.mass()+sqr(piLab.vect())); // Energy Ej=sqrt(pjLab.mass()*pjLab.mass()+sqr(pjLab.vect())); Energy Ei=piLab.e(); Energy Ej=pjLab.e(); // Final angle choice : double cosPhi=piLab.vect().cosTheta(pjLab.vect()); double Phi=acos(cosPhi); double sinPhi=sin(Phi); if (std::isnan(Phi) || std::isinf(Phi)) throw Exception() << "NAN in Phi\n" << Exception::runerror; Energy2 Ei2=Ei*Ei; Energy2 Ej2=Ej*Ej; Energy mi=piLab.mass(); Energy mj=pjLab.mass(); Energy2 mi2=mi*mi; Energy2 mj2=mj*mj; Energy Pi=piLab.vect().mag(); Energy Pj=pjLab.vect().mag(); - if (fabs((Ei-Pi)/GeV)<1e-5) std::cout<< std::setprecision(18) << "WARNING: Ei-Pi " << (Ei-Pi)/GeV<< std::endl; - if (fabs((Ej-Pj)/GeV)<1e-5) std::cout<< std::setprecision(18) << "WARNING: Ej-Pj " << (Ej-Pj)/GeV<< std::endl; + // if (fabs((Ei-Pi)/GeV)<1e-5) std::cout<< std::setprecision(18) << "WARNING: Ei-Pi " << (Ei-Pi)/GeV<< std::endl; + // if (fabs((Ej-Pj)/GeV)<1e-5) std::cout<< std::setprecision(18) << "WARNING: Ej-Pj " << (Ej-Pj)/GeV<< std::endl; boost::numeric::ublas::vector<double> Pclu1Hat(4); boost::numeric::ublas::vector<double> Pclu2Hat(4); // const Energy M = (piLab+pjLab).m(); const Energy M1 = pClu1.mass(); const Energy M2 = pClu2.mass(); // const Energy PcomClu=Kinematics::pstarTwoBodyDecay(M,M1,M2); // Energy pT = sample2DGaussianPT(PcomClu); // Energy pZ = sqrt(PcomClu*PcomClu - pT*pT); // Pclu1Hat(0)=sqrt(M1*M1+PcomClu*PcomClu)/GeV; // Pclu1Hat(1)=pT/GeV*cos(phi); // Pclu1Hat(2)=pT/GeV*sin(phi); // Pclu1Hat(3)=pZ/GeV; // Pclu2Hat(0)=sqrt(M2*M2+PcomClu*PcomClu)/GeV; // Pclu2Hat(1)=-pT/GeV*cos(phi); // Pclu2Hat(2)=-pT/GeV*sin(phi); // Pclu2Hat(3)=-pZ/GeV; Pclu1Hat(0)=pClu1.e()/GeV; Pclu1Hat(1)=pClu1.x()/GeV; Pclu1Hat(2)=pClu1.y()/GeV; Pclu1Hat(3)=pClu1.z()/GeV; Pclu2Hat(0)=pClu2.e()/GeV; Pclu2Hat(1)=pClu2.x()/GeV; Pclu2Hat(2)=pClu2.y()/GeV; Pclu2Hat(3)=pClu2.z()/GeV; // const Energy2 pipj=piLab*pjLab; // std::cout << "pipjDiff = " << sqrt(fabs((mi2+mj2+2*pipj-(M1*M1+M2*M2+2*pClu1*pClu2))/GeV2))<< std::endl; // std::cout << "pipjDiff23 = " << sqrt(fabs((mi2+mj2+2*pipj-((piLab+pjLab).m2()))/GeV2))<< std::endl; // std::cout << "pipjDiff3 = " << sqrt(fabs(((M1*M1+M2*M2+2*pClu1*pClu2)-((piLab+pjLab).m2()))/GeV2))<< std::endl; // if (std::isnan(pipj/GeV2) || std::isinf(pipj/GeV2)) throw Exception() << "NAN in pipj/GeV2\n" // << Exception::runerror; boost::numeric::ublas::matrix<double> Lambda(4,4); // Energy sqrtS=sqrt(mi2+mj2+2*pipj); // const Energy sqrtS=(pClu1+pClu2).m(); // const Energy sqrtS=(piLab+pjLab).m(); const Energy sqrtS=M; // TODO better numerics for pipj: const Energy2 pipj=(sqrtS*sqrtS-mi2-mj2)/2.0; // lambda(sqrtS,mi,mj) // Energy4 A4=(pipj*pipj-mi2*mj2); Energy pstar=Kinematics::pstarTwoBodyDecay(sqrtS,mi,mj); Energy2 A2=pstar*sqrtS; Energy2 B2=Pi*Pj*sinPhi; Energy B2divPi=Pj*sinPhi; + double sinSqrPhidiv2=pow(sin(Phi/2.0),2); if (std::isnan(A2/GeV2) || std::isinf(A2/GeV2)) throw Exception() << "NAN in A2/GeV2\n" << Exception::runerror; Lambda(0,0) = (Ei+Ej)/sqrtS; Lambda(0,1) = B2/A2; Lambda(0,2) = 0; Lambda(0,3) = (Ei-Ej)/(2.0*pstar)-((mi2-mj2)*(Ei+Ej))/(2.0*sqrtS*A2); Lambda(1,0) = B2divPi/sqrtS; - Lambda(1,1) = (Pi*Ej-Ei*Pj*cosPhi)/A2; + // Lambda(1,1) = (Pi*Ej-Ei*Pj*cosPhi)/A2; + // // TODO + Lambda(1,1) = Phi<1e-10? 1:(Pi*Ej-Ei*Pj*cosPhi)/A2; Lambda(1,2) = 0; Lambda(1,3) = -(sqrtS*sqrtS-(mj2-mi2))*B2divPi/(2.0*sqrtS*A2); Lambda(2,0) = 0; Lambda(2,1) = 0; Lambda(2,2) = 1; Lambda(2,3) = 0; Lambda(3,0) = (Pi+Pj*cosPhi)/sqrtS; Lambda(3,1) = Ei*B2divPi/A2; Lambda(3,2) = 0; // Lambda(3,3) = (mj2*Pi+Pj*cosPhi*(Pi*Pj*cosPhi-Ei2-Ei*Ej)+Pi*Ei*Ej)/(sqrtS*A2); assert(Pi>ZERO); // Lambda(3,3) = (A2*A2+0.5*Ei*(sqrtS*sqrtS*(Ei-Ej)-(mi2-mj2)*(Ei+Ej)))/(Pi*sqrtS*A2); + // // TODO Lambda(3,3) = (A2*A2-0.5*Ei*(Ej*(sqrtS*sqrtS-(mj2-mi2))-Ei*(sqrtS*sqrtS-(mi2-mj2))))/(Pi*sqrtS*A2); double det=0; det += Lambda(0,0)*Lambda(1,1)*Lambda(3,3); det += Lambda(1,0)*Lambda(3,1)*Lambda(0,3); det += Lambda(3,0)*Lambda(0,1)*Lambda(1,3); det -= Lambda(0,3)*Lambda(1,1)*Lambda(3,0); det -= Lambda(1,0)*Lambda(0,1)*Lambda(3,3); det -= Lambda(1,3)*Lambda(3,1)*Lambda(0,0); - if (fabs(det-1.0)>1e-3 || std::isnan(det)) { + if (fabs(det-1.0)>1e-1 || std::isnan(det)) { std::cout << "A2 = "<<std::setprecision(18)<< A2/(GeV2) << std::endl; std::cout << "B2 = "<< B2/(GeV2)<< std::endl; std::cout << "DET-1 = "<< det-1.0 << std::setprecision(3)<< std::endl; std::cout << "Lambda:" << std::endl; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { std::cout<< std::setprecision(18) << Lambda(i,j)<< std::setprecision(3) << "\t"; } std::cout << "\n"; } std::cout << "pi,pj in Lab" << std::endl; printVec(piLab); printVec(pjLab); std::cout << "Phi = " << std::setprecision(18) << Phi << std::endl; std::cout << "Phi - pi = " << std::setprecision(18) << Phi - M_PI << std::endl; } boost::numeric::ublas::vector<double> Pclu1=boost::numeric::ublas::prod(Lambda,Pclu1Hat); boost::numeric::ublas::vector<double> Pclu2=boost::numeric::ublas::prod(Lambda,Pclu2Hat); Axis zAxis(0,0,1); Axis xAxis(1,0,0); Lorentz5Momentum piRes(mi,Pi*zAxis); Lorentz5Momentum pjRes(mj,Pj*(xAxis*sinPhi+zAxis*cosPhi)); Lorentz5Momentum pClu1Res(M1,GeV*Axis(Pclu1[1],Pclu1[2],Pclu1[3])); Lorentz5Momentum pClu2Res(M2,GeV*Axis(Pclu2[1],Pclu2[2],Pclu2[3])); Axis omega1=piRes.vect().unit().cross(piLab.vect().unit()); double cosAngle1=piRes.vect().unit()*piLab.vect().unit(); double angle1=acos(cosAngle1); piRes.rotate(angle1, omega1); pjRes.rotate(angle1, omega1); pClu1Res.rotate(angle1, omega1); pClu2Res.rotate(angle1, omega1); Axis omega2=piRes.vect().unit(); // if (fabs(sinPhi)<1e-13) // { // std::cout << "sinPhi "<<std::setprecision(18)<< sinPhi<<std::setprecision(3)<<"\n"; // std::cout << "piLab\n"; // printVec(piLab); // std::cout << "pjLab\n"; // printVec(pjLab); // std::cout << "piRes\n"; // printVec(piRes); // std::cout << "pjRes\n"; // printVec(pjRes); // if (fabs(sinPhi)<1e-14) { // std::cout << "r1 = "<<std::setprecision(18)<< (pjLab.vect()-piRes.vect().unit()*(pjLab.vect()*piRes.vect().unit())).mag()/GeV << "\n"; // std::cout << "r2 = "<< (pjRes.vect()-piRes.vect().unit()*(pjRes.vect()*piRes.vect().unit())).mag()/GeV << std::setprecision(3)<<std::endl; // } // } Momentum3 r1dim=(pjLab.vect()-piRes.vect().unit()*(pjLab.vect()*piRes.vect().unit())); Momentum3 r2dim=(pjRes.vect()-piRes.vect().unit()*(pjRes.vect()*piRes.vect().unit())); if (r1dim.mag()==ZERO || r2dim.mag()==ZERO || fabs(sinPhi)<1e-14) { // std::cout << "!!!!!!!!!!!RETURNN " << std::endl; pClu1=pClu1Res; // pClu1.setMass(M1); // pClu1.rescaleMass(); // pClu1.rescaleEnergy(); // if (fabs((pClu1.mass()-pClu1.m())/GeV)>1e-6*pClu1.mass()/GeV) { // std::cout << "M1.mass == " <<std::setprecision(18)<< pClu1.mass()/GeV << std::endl; // std::cout << "M1.m == " <<std::setprecision(18)<< pClu1.m()/GeV << std::endl; // std::cout << "DeltaM1 == " <<std::setprecision(18)<< pClu1.mass()/GeV - pClu1.m()/GeV << std::endl; // std::cout << "Phi = " << std::setprecision(18) << Phi << std::endl; // std::cout << "Phi - pi = " << std::setprecision(18) << Phi - M_PI << std::endl; // } pClu2=pClu2Res; // pClu2.setMass(M2); // pClu2.rescaleMass(); // pClu2.rescaleEnergy(); // // if (fabs((pClu2.mass()-pClu2.m())/GeV)>1e-10*pClu2.mass()/GeV) { // std::cout << "M2.mass == " <<std::setprecision(18)<< pClu2.mass()/GeV << std::endl; // std::cout << "M2.m == " <<std::setprecision(18)<< pClu2.m()/GeV << std::endl; // std::cout << "DeltaM2 == " <<std::setprecision(18)<< pClu2.mass()/GeV - pClu2.m()/GeV << std::endl; // std::cout << "Phi = " << std::setprecision(18) << Phi << std::endl; // std::cout << "Phi - pi = " << std::setprecision(18) << Phi - M_PI << std::endl; // } return; } Axis r1=r1dim.unit(); Axis r2=r2dim.unit(); int signToPi = (piRes.vect()*pjLab.vect())/GeV2 > 0 ? 1:-1; int signToR1R2 = signToPi*(r2.unit().cross(r1.unit())*piRes.vect())/GeV> 0 ? 1:-1; double angle2=acos(r1.unit()*r2.unit()); if (signToR1R2<0) angle2=-angle2; // std::cout << "angle2 = " << angle2 << "\t cosAngle2 = "<< cos(angle2) << std::endl; // piRes.rotate(-angle2, omega2.unit()); // pjRes.rotate(-angle2, signToPi*omega2.unit()); pjRes.rotate(angle2, signToPi*omega2); pClu1Res.rotate(angle2, signToPi*omega2); pClu2Res.rotate(angle2, signToPi*omega2); if (printIfNotNull(piRes-piLab) ){std::cout << "^- piRes after rot\n"; std::cout << "omega2 = (" << omega2.unit().x() << "\t" << omega2.unit().y() << "\t" << omega2.unit().z() << ")\n"; std::cout << "Phi = "<<std::setprecision(18)<< Phi<<std::setprecision(3)<< std::endl; std::cout << "Phi -pi = "<<std::setprecision(18)<< Phi-M_PI<<std::setprecision(3)<<std::endl; printVec(piRes); printVec(piLab); } if (printIfNotNull(pjRes-pjLab) ){std::cout << "^- pjRes after rot\n"; std::cout << "omega2 = (" << omega2.unit().x() << "\t" << omega2.unit().y() << "\t" << omega2.unit().z() << ")\n"; std::cout << "Phi = "<<std::setprecision(18)<< Phi<<std::setprecision(3)<< std::endl; std::cout << "Phi -pi = "<<std::setprecision(18)<< Phi-M_PI<<std::setprecision(3)<<std::endl; printVec(pjRes); printVec(pjLab); } if (printIfNotNull(piRes+pjRes-pClu1Res-pClu2Res) ){std::cout << "^- sanity total mom conserv\n"; std::cout << "Phi = "<<std::setprecision(18)<< Phi<<std::setprecision(3)<<std::endl; std::cout << "Phi - pi= "<<std::setprecision(18)<< Phi-M_PI<<std::setprecision(3)<<std::endl; printVec(piRes+pjRes); printVec(pClu1Res+pClu2Res); std::cout << "M = " <<std::setprecision(18)<< M/GeV << std::endl; std::cout << "M1 = "<<std::setprecision(18) << M1/GeV << std::endl; std::cout << "M2 = " <<std::setprecision(18)<< M2/GeV << std::endl; } pClu1=pClu1Res; // pClu1.setMass(M1); // pClu1.rescaleMass(); // pClu1.rescaleEnergy(); - if (fabs((pClu1.mass()-pClu1.m())/GeV)>1e-10*pClu1.mass()/GeV) { - std::cout << "M1.mass == " <<std::setprecision(18)<< pClu1.mass()/GeV << std::endl; - std::cout << "M1.m == " <<std::setprecision(18)<< pClu1.m()/GeV << std::endl; - std::cout << "DeltaM1 == " <<std::setprecision(18)<< pClu1.mass()/GeV - pClu1.m()/GeV << std::endl; - } + // if (fabs((pClu1.mass()-pClu1.m())/GeV)>1e-10*pClu1.mass()/GeV) { + // std::cout << "M1.mass == " <<std::setprecision(18)<< pClu1.mass()/GeV << std::endl; + // std::cout << "M1.m == " <<std::setprecision(18)<< pClu1.m()/GeV << std::endl; + // std::cout << "DeltaM1 == " <<std::setprecision(18)<< pClu1.mass()/GeV - pClu1.m()/GeV << std::endl; + // } pClu2=pClu2Res; // pClu2.setMass(M2); // pClu2.rescaleMass(); // pClu2.rescaleEnergy(); - if (fabs((pClu2.mass()-pClu2.m())/GeV)>1e-10*pClu2.mass()/GeV) { - std::cout << "M2.mass == " <<std::setprecision(18)<< pClu2.mass()/GeV << std::endl; - std::cout << "M2.m == " <<std::setprecision(18)<< pClu2.m()/GeV << std::endl; - std::cout << "DeltaM2 == " <<std::setprecision(18)<< pClu2.mass()/GeV - pClu2.m()/GeV << std::endl; - } + // if (fabs((pClu2.mass()-pClu2.m())/GeV)>1e-10*pClu2.mass()/GeV) { + // std::cout << "M2.mass == " <<std::setprecision(18)<< pClu2.mass()/GeV << std::endl; + // std::cout << "M2.m == " <<std::setprecision(18)<< pClu2.m()/GeV << std::endl; + // std::cout << "DeltaM2 == " <<std::setprecision(18)<< pClu2.mass()/GeV - pClu2.m()/GeV << std::endl; + // } // std::cout << "############################\n"; } void ClusterFissioner::calculateKinematics(const Lorentz5Momentum & pClu, const Lorentz5Momentum & p0Q1, const bool toHadron1, const bool toHadron2, Lorentz5Momentum & pClu1, Lorentz5Momentum & pClu2, Lorentz5Momentum & pQ1, Lorentz5Momentum & pQbar, Lorentz5Momentum & pQ, Lorentz5Momentum & pQ2bar) const { /****************** * This method solves the kinematics of the two body cluster decay: * C (Q1 Q2bar) ---> C1 (Q1 Qbar) + C2 (Q Q2bar) * In input we receive the momentum of C, pClu, and the momentum * of the quark Q1 (constituent of C), p0Q1, both in the LAB frame. * Furthermore, two boolean variables inform whether the two fission * products (C1, C2) decay immediately into a single hadron (in which * case the cluster itself is identify with that hadron) and we do * not have to solve the kinematics of the components (Q1,Qbar) for * C1 and (Q,Q2bar) for C2. * The output is given by the following momenta (all 5-components, * and all in the LAB frame): * pClu1 , pClu2 respectively of C1 , C2 * pQ1 , pQbar respectively of Q1 , Qbar in C1 * pQ , pQ2bar respectively of Q , Q2 in C2 * The assumption, suggested from the string model, is that, in C frame, * C1 and its constituents Q1 and Qbar are collinear, and collinear to * the direction of Q1 in C (that is before cluster decay); similarly, * (always in the C frame) C2 and its constituents Q and Q2bar are * collinear (and therefore anti-collinear with C1,Q1,Qbar). * The solution is then obtained by using Lorentz boosts, as follows. * The kinematics of C1 and C2 is solved in their parent C frame, * and then boosted back in the LAB. The kinematics of Q1 and Qbar * is solved in their parent C1 frame and then boosted back in the LAB; * similarly, the kinematics of Q and Q2bar is solved in their parent * C2 frame and then boosted back in the LAB. In each of the three * "two-body decay"-like cases, we use the fact that the direction * of the motion of the decay products is known in the rest frame of * their parent. This is obvious for the first case in which the * parent rest frame is C; but it is also true in the other two cases * where the rest frames are C1 and C2. This is because C1 and C2 * are boosted w.r.t. C in the same direction where their components, * respectively (Q1,Qbar) and (Q,Q2bar) move in C1 and C2 rest frame * respectively. * Of course, although the notation used assumed that C = (Q1 Q2bar) * where Q1 is a quark and Q2bar an antiquark, indeed everything remain * unchanged also in all following cases: * Q1 quark, Q2bar antiquark; --> Q quark; * Q1 antiquark , Q2bar quark; --> Q antiquark; * Q1 quark, Q2bar diquark; --> Q quark * Q1 antiquark, Q2bar anti-diquark; --> Q antiquark * Q1 diquark, Q2bar quark --> Q antiquark * Q1 anti-diquark, Q2bar antiquark; --> Q quark **************************/ if (pClu.mass() < pClu1.mass() + pClu2.mass() || pClu1.mass()<ZERO || pClu2.mass()<ZERO ) { throw Exception() << "Impossible Kinematics in ClusterFissioner::calculateKinematics[FluxTube]() (A)" << Exception::eventerror; } // Calculate the unit three-vector, in the Cluster frame, // using the sampling funtion sampleDirection in the respective // clusters rest frame // Calculate the momenta of C1 and C2 in the (parent) C frame first, // where the direction of C1 is samples according to sampleDirection, // and then boost back in the LAB frame. // // relative momentum in centre of mass of cluster // OLD Lorentz5Momentum uRelCluster(p0Q1); uRelCluster.boost( -pClu.boostVector() ); // boost from LAB to C // NEW ? : // Lorentz5Momentum uRelCluster(p0Q1); // Lorentz5Momentum pCluTmp(pClu); // uRelCluster.boost(-p0Q1.boostVector()); // pCluTmp.boost(-p0Q1.boostVector()); // uRelCluster.boost(-pCluTmp.boostVector()); // sample direction (options = Default(aligned), Isotropic // or FluxTube(gaussian pT kick)) Axis DirClu = sampleDirection(uRelCluster, pClu.mass(), pClu1.mass(), pClu2.mass()); Axis DirClu1; Axis DirClu2; // int _covariantBoost=1; if (_covariantBoost) { Lorentz5Momentum p0Q2(pQ2bar.mass(),(pClu-p0Q1).vect()); if (fabs((p0Q1+p0Q2).m()/GeV- pClu.mass()/GeV)>1e-5*pClu.mass()/GeV ){ // printVec(p0Q1); // printVec(p0Q2); // printVec(p0Q1+p0Q2); // printVec(pClu); - Lorentz5Momentum pTottmp(p0Q1+p0Q2,pClu.mass()); - std::cout << "M tot = "<<std::setprecision(18)<< (pTottmp).massError() << std::endl; - std::cout << "M.m clu = "<<std::setprecision(18)<< pClu.m()/GeV << std::endl; - std::cout << "M.mass clu = "<< pClu.mass()/GeV << std::endl; - std::cout << "MassError pTottmp = "<<std::setprecision(18)<< (pTottmp).massError() << std::endl; - std::cout << "MassError pClu = "<<std::setprecision(18)<< pClu.massError() << std::endl; - std::cout << "Mclu1 = "<<std::setprecision(18)<< pClu1.mass()/GeV << std::endl; - std::cout << "Mclu2 = "<<std::setprecision(18)<< pClu2.mass()/GeV << std::endl; - std::cout << "MassError pClu1 = "<<std::setprecision(18)<< pClu1.massError() << std::endl; - std::cout << "MassError pClu2 = "<<std::setprecision(18)<< pClu2.massError() << std::endl; + // Lorentz5Momentum pTottmp(p0Q1+p0Q2,pClu.mass()); + // std::cout << "M tot = "<<std::setprecision(18)<< (pTottmp).massError() << std::endl; + // std::cout << "M.m clu = "<<std::setprecision(18)<< pClu.m()/GeV << std::endl; + // std::cout << "M.mass clu = "<< pClu.mass()/GeV << std::endl; + // std::cout << "MassError pTottmp = "<<std::setprecision(18)<< (pTottmp).massError() << std::endl; + // std::cout << "MassError pClu = "<<std::setprecision(18)<< pClu.massError() << std::endl; + // std::cout << "Mclu1 = "<<std::setprecision(18)<< pClu1.mass()/GeV << std::endl; + // std::cout << "Mclu2 = "<<std::setprecision(18)<< pClu2.mass()/GeV << std::endl; + // std::cout << "MassError pClu1 = "<<std::setprecision(18)<< pClu1.massError() << std::endl; + // std::cout << "MassError pClu2 = "<<std::setprecision(18)<< pClu2.massError() << std::endl; } const Energy M = pClu.mass(); const Energy M1 = pClu1.mass(); const Energy M2 = pClu2.mass(); const Energy PcomClu=Kinematics::pstarTwoBodyDecay(M,M1,M2); Energy pT = sample2DGaussianPT(PcomClu); Energy pZ = sqrt(PcomClu*PcomClu - pT*pT); double phi = 2*Constants::pi*UseRandom::rnd(); Momentum3 pClu1sampVect( pT*cos(phi), pT*sin(phi), pZ); Momentum3 pClu2sampVect(-pT*cos(phi),-pT*sin(phi),-pZ); // Lorentz5Momentum pClu1Samp(M1,pClu1sampVect); // Lorentz5Momentum pClu2Samp(M2,pClu2sampVect); // pClu1=pClu1Samp; pClu1.setMass(M1); pClu1.setVect(pClu1sampVect); pClu1.rescaleEnergy(); // pClu2=pClu2Samp; pClu2.setMass(M2); pClu2.setVect(pClu2sampVect); pClu2.rescaleEnergy(); // std::cout << std::setprecision(18)<< "######### Info2 #########" << std::endl; // std::cout << std::setprecision(18)<< "M = "<< pClu.mass()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pi+pj mass = "<< (p0Q1+p0Q2).m()/GeV << std::endl; // std::cout << std::setprecision(18)<< "p0Q1.mass = "<< p0Q1.mass()/GeV << std::endl; // std::cout << std::setprecision(18)<< "p0Q2.mass = "<< p0Q2.mass()/GeV << std::endl; // std::cout << std::setprecision(18)<< "p0Q1.m = "<< p0Q1.m()/GeV << std::endl; // std::cout << std::setprecision(18)<< "p0Q2.m = "<< p0Q2.m()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pClu1.mass = "<< pClu1.mass()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pClu2.mass = "<< pClu2.mass()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pClu1.m = "<< pClu1.m()/GeV << std::endl; // std::cout << std::setprecision(18)<< "pClu2.m = "<< pClu2.m()/GeV << std::endl; // printIfNotNull(pClu1+pClu2); // std::cout << "######### End #########" << std::endl; calculateKinematicsClusterCOM(pClu.mass(),p0Q1, p0Q2, pClu1, pClu2); // Kinematics::BoostIntoTwoParticleFrame(p0Q1, p0Q2, pClu1, pClu2); // Lorentz5Momentum pClu1_tmp(pClu1); // pClu1_tmp.rescaleMass(); // pClu1_tmp.boost( -pClu.boostVector() ); // if ((pClu1_tmp.vect().mag2())>ZERO){ // } // else { // std::cout << "Danger:!!!!" << std::endl; // std::cout << "pClu1_tmp.vect().mag2() == " << pClu1_tmp.vect().mag2()/GeV2<< std::endl; // std::cout << "M.mass clu = "<< pClu.mass()/GeV << std::endl; // std::cout << "Mclu1 = "<<std::setprecision(18)<< pClu1.mass()/GeV << std::endl; // std::cout << "Mclu2 = "<<std::setprecision(18)<< pClu2.mass()/GeV << std::endl; // std::cout << "pClu.m = "<<pClu.m()/GeV << std::endl; // std::cout << "pClu.mass = "<<pClu.mass()/GeV << std::endl; // std::cout << "pClu.t = "<<pClu.t()/GeV << std::endl; // std::cout << "pClu.x = "<<pClu.vect().x()/GeV << std::endl; // std::cout << "pClu.y = "<<pClu.vect().y()/GeV << std::endl; // std::cout << "pClu.z = "<<pClu.vect().z()/GeV << std::endl; // std::cout << "pClu2.m = "<<pClu2.m()/GeV << std::endl; // std::cout << "pClu2.mass = "<<pClu2.mass()/GeV << std::endl; // std::cout << "pClu2.t = "<<pClu2.t()/GeV << std::endl; // std::cout << "pClu2.x = "<<pClu2.vect().x()/GeV << std::endl; // std::cout << "pClu2.y = "<<pClu2.vect().y()/GeV << std::endl; // std::cout << "pClu2.z = "<<pClu2.vect().z()/GeV << std::endl; // std::cout << "pClu1.m = "<<pClu1.m()/GeV << std::endl; // std::cout << "pClu1.mass = "<<pClu1.mass()/GeV << std::endl; // std::cout << "pClu1.t = "<<pClu1.t()/GeV << std::endl; // std::cout << "pClu1.x = "<<pClu1.vect().x()/GeV << std::endl; // std::cout << "pClu1.y = "<<pClu1.vect().y()/GeV << std::endl; // std::cout << "pClu1.z = "<<pClu1.vect().z()/GeV << std::endl; // std::cout << "pClu1_tmp.m = "<<pClu1_tmp.m()/GeV << std::endl; // std::cout << "pClu1_tmp.mass = "<<pClu1_tmp.mass()/GeV << std::endl; // std::cout << "pClu1_tmp.t = "<<pClu1_tmp.t()/GeV << std::endl; // std::cout << "pClu1_tmp.x = "<<pClu1_tmp.vect().x()/GeV << std::endl; // std::cout << "pClu1_tmp.y = "<<pClu1_tmp.vect().y()/GeV << std::endl; // std::cout << "pClu1_tmp.z = "<<pClu1_tmp.vect().z()/GeV << std::endl; // } // DirClu=pClu1_tmp.vect().mag2()>ZERO ? pClu1_tmp.vect().unit():sampleDirectionUniform(); DirClu1=sampleDirectionUniform(); DirClu2=sampleDirectionUniform(); } else { Kinematics::twoBodyDecay(pClu, pClu1.mass(), pClu2.mass(),DirClu, pClu1, pClu2); DirClu1=sampleDirectionUniform(); DirClu2=sampleDirectionUniform(); } // In the case that cluster1 does not decay immediately into a single hadron, // calculate the momenta of Q1 (as constituent of C1) and Qbar in the // (parent) C1 frame first, where the direction of Q1 is u.vect().unit(), // and then boost back in the LAB frame. if(!toHadron1) { if (pClu1.m() < pQ1.mass() + pQbar.mass() ) { throw Exception() << "Impossible Kinematics in ClusterFissioner::calculateKinematics[FluxTube]() (B)" << Exception::eventerror; } // sample direction (options = Default(aligned), Isotropic // or FluxTube(gaussian pT kick)) // Axis DirClu1 = sampleDirection(uRelCluster, // pClu1.mass(), pQ1.mass(), pQbar.mass()); Kinematics::twoBodyDecay(pClu1, pQ1.mass(), pQbar.mass(), DirClu1, pQ1, pQbar); } // In the case that cluster2 does not decay immediately into a single hadron, // Calculate the momenta of Q and Q2bar (as constituent of C2) in the // (parent) C2 frame first, where the direction of Q is u.vect().unit(), // and then boost back in the LAB frame. if(!toHadron2) { if (pClu2.m() < pQ.mass() + pQ2bar.mass() ) { throw Exception() << "Impossible Kinematics in ClusterFissioner::calculateKinematics[FluxTube]() (C)" << Exception::eventerror; } // sample direction (options = Default(aligned), Isotropic // or FluxTube(gaussian pT kick)) // Axis DirClu2 = sampleDirection(uRelCluster, // pClu2.mass(), pQ2bar.mass(), pQ.mass()); Kinematics::twoBodyDecay(pClu2, pQ.mass(), pQ2bar.mass(), DirClu2, pQ, pQ2bar); } } void ClusterFissioner::calculatePositions(const Lorentz5Momentum & pClu, const LorentzPoint & positionClu, const Lorentz5Momentum & pClu1, const Lorentz5Momentum & pClu2, LorentzPoint & positionClu1, LorentzPoint & positionClu2) const { // Determine positions of cluster children. // See Marc Smith's thesis, page 127, formulas (4.122) and (4.123). Energy Mclu = pClu.m(); Energy Mclu1 = pClu1.m(); Energy Mclu2 = pClu2.m(); // Calculate the unit three-vector, in the C frame, along which // children clusters move. Lorentz5Momentum u(pClu1); u.boost( -pClu.boostVector() ); // boost from LAB to C frame // the unit three-vector is then u.vect().unit() Energy pstarChild = Kinematics::pstarTwoBodyDecay(Mclu,Mclu1,Mclu2); // First, determine the relative positions of the children clusters // in the parent cluster reference frame. Energy2 mag2 = u.vect().mag2(); InvEnergy fact = mag2>ZERO ? 1./sqrt(mag2) : 1./GeV; Length x1 = ( 0.25*Mclu + 0.5*( pstarChild + (sqr(Mclu2) - sqr(Mclu1))/(2.0*Mclu)))/_kappa; Length t1 = Mclu/_kappa - x1; LorentzDistance distanceClu1( x1 * fact * u.vect(), t1 ); Length x2 = (-0.25*Mclu + 0.5*(-pstarChild + (sqr(Mclu2) - sqr(Mclu1))/(2.0*Mclu)))/_kappa; Length t2 = Mclu/_kappa + x2; LorentzDistance distanceClu2( x2 * fact * u.vect(), t2 ); // Then, transform such relative positions from the parent cluster // reference frame to the Lab frame. distanceClu1.boost( pClu.boostVector() ); distanceClu2.boost( pClu.boostVector() ); // Finally, determine the absolute positions in the Lab frame. positionClu1 = positionClu + distanceClu1; positionClu2 = positionClu + distanceClu2; } bool ClusterFissioner::ProbablityFunction(double scale, double threshold) { double cut = UseRandom::rnd(0.0,1.0); return 1./(1.+pow(abs((threshold-_probShift)/scale),_probPowFactor)) > cut ? true : false; } bool ClusterFissioner::isHeavy(tcClusterPtr clu) { // particle data for constituents tcPDPtr cptr[3]={tcPDPtr(),tcPDPtr(),tcPDPtr()}; for(size_t ix=0;ix<min(clu->numComponents(),3);++ix) { cptr[ix]=clu->particle(ix)->dataPtr(); } // different parameters for exotic, bottom and charm clusters double clpow = !spectrum()->isExotic(cptr[0],cptr[1],cptr[1]) ? _clPowLight : _clPowExotic; Energy clmax = !spectrum()->isExotic(cptr[0],cptr[1],cptr[1]) ? _clMaxLight : _clMaxExotic; for ( const long& id : spectrum()->heavyHadronizingQuarks() ) { if ( spectrum()->hasHeavy(id,cptr[0],cptr[1],cptr[1]) ) { clpow = _clPowHeavy[id]; clmax = _clMaxHeavy[id]; } } // required test for SUSY clusters, since aboveCutoff alone // cannot guarantee (Mc > m1 + m2 + 2*m) in cut() static const Energy minmass = getParticleData(ParticleID::d)->constituentMass(); bool aboveCutoff = false, canSplitMinimally = false; // static kinematic threshold if(_kinematicThresholdChoice == 0) { aboveCutoff = ( pow(clu->mass()*UnitRemoval::InvE , clpow) > pow(clmax*UnitRemoval::InvE, clpow) + pow(clu->sumConstituentMasses()*UnitRemoval::InvE, clpow) ); canSplitMinimally = clu->mass() > clu->sumConstituentMasses() + 2.0 * minmass; } // dynamic kinematic threshold else if(_kinematicThresholdChoice == 1) { //some smooth probablity function to create a dynamic thershold double scale = pow(clu->mass()/GeV , clpow); double threshold = pow(clmax/GeV, clpow) + pow(clu->sumConstituentMasses()/GeV, clpow); aboveCutoff = ProbablityFunction(scale,threshold); scale = clu->mass()/GeV; threshold = clu->sumConstituentMasses()/GeV + 2.0 * minmass/GeV; canSplitMinimally = ProbablityFunction(scale,threshold); } return aboveCutoff && canSplitMinimally; } diff --git a/Hadronization/ClusterHadronizationHandler.cc b/Hadronization/ClusterHadronizationHandler.cc --- a/Hadronization/ClusterHadronizationHandler.cc +++ b/Hadronization/ClusterHadronizationHandler.cc @@ -1,594 +1,592 @@ // -*- C++ -*- // // ClusterHadronizationHandler.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 ClusterHadronizationHandler class. // #include "ClusterHadronizationHandler.h" #include <ThePEG/Interface/ClassDocumentation.h> #include <ThePEG/Persistency/PersistentOStream.h> #include <ThePEG/Persistency/PersistentIStream.h> #include <ThePEG/Interface/Switch.h> #include <ThePEG/Interface/Parameter.h> #include <ThePEG/Interface/Reference.h> #include <ThePEG/Handlers/EventHandler.h> #include <ThePEG/Handlers/Hint.h> #include <ThePEG/PDT/ParticleData.h> #include <ThePEG/EventRecord/Particle.h> #include <ThePEG/EventRecord/Step.h> #include <ThePEG/PDT/PDT.h> #include <ThePEG/PDT/EnumParticles.h> #include <ThePEG/Utilities/Throw.h> #include "Herwig/Utilities/EnumParticles.h" #include "CluHadConfig.h" #include "Cluster.h" #include <ThePEG/Utilities/DescribeClass.h> #include <ThePEG/Interface/Command.h> using namespace Herwig; ClusterHadronizationHandler * ClusterHadronizationHandler::currentHandler_ = 0; DescribeClass<ClusterHadronizationHandler,HadronizationHandler> describeClusterHadronizationHandler("Herwig::ClusterHadronizationHandler","Herwig.so"); IBPtr ClusterHadronizationHandler::clone() const { return new_ptr(*this); } IBPtr ClusterHadronizationHandler::fullclone() const { return new_ptr(*this); } void ClusterHadronizationHandler::persistentOutput(PersistentOStream & os) const { os << _partonSplitters << _clusterFinders << _colourReconnectors << _clusterFissioners << _lightClusterDecayers << _clusterDecayers << _gluonMassGenerators << _partonSplitter << _clusterFinder << _colourReconnector << _clusterFissioner << _lightClusterDecayer << _clusterDecayer << reshuffle_ << _gluonMassGenerator << ounit(_minVirtuality2,GeV2) << ounit(_maxDisplacement,mm) << _underlyingEventHandler << _reduceToTwoComponents; } void ClusterHadronizationHandler::persistentInput(PersistentIStream & is, int) { is >> _partonSplitters >> _clusterFinders >> _colourReconnectors >> _clusterFissioners >> _lightClusterDecayers >> _clusterDecayers >> _gluonMassGenerators >> _partonSplitter >> _clusterFinder >> _colourReconnector >> _clusterFissioner >> _lightClusterDecayer >> _clusterDecayer >> reshuffle_ >> _gluonMassGenerator >> iunit(_minVirtuality2,GeV2) >> iunit(_maxDisplacement,mm) >> _underlyingEventHandler >> _reduceToTwoComponents; } void ClusterHadronizationHandler::Init() { static ClassDocumentation<ClusterHadronizationHandler> documentation ("This is the main handler class for the Cluster Hadronization", "The hadronization was performed using the cluster model of \\cite{Webber:1983if}.", "%\\cite{Webber:1983if}\n" "\\bibitem{Webber:1983if}\n" " B.~R.~Webber,\n" " ``A QCD Model For Jet Fragmentation Including Soft Gluon Interference,''\n" " Nucl.\\ Phys.\\ B {\\bf 238}, 492 (1984).\n" " %%CITATION = NUPHA,B238,492;%%\n" // main manual ); static Reference<ClusterHadronizationHandler,PartonSplitter> interfacePartonSplitter("PartonSplitter", "A reference to the PartonSplitter object", &Herwig::ClusterHadronizationHandler::_partonSplitter, false, false, true, false); static Reference<ClusterHadronizationHandler,GluonMassGenerator> interfaceGluonMassGenerator ("GluonMassGenerator", "Set a reference to a gluon mass generator.", &ClusterHadronizationHandler::_gluonMassGenerator, false, false, true, true, false); static Switch<ClusterHadronizationHandler,int> interfaceReshuffle ("Reshuffle", "Perform reshuffling if constituent masses have not yet been included by the shower", &ClusterHadronizationHandler::reshuffle_, 0, false, false); static SwitchOption interfaceReshuffleColourConnected (interfaceReshuffle, "ColourConnected", "Separate reshuffling for colour connected partons.", 2); static SwitchOption interfaceReshuffleGlobal (interfaceReshuffle, "Global", "Reshuffle globally.", 1); static SwitchOption interfaceReshuffleNo (interfaceReshuffle, "No", "Do not reshuffle.", 0); static Reference<ClusterHadronizationHandler,ClusterFinder> interfaceClusterFinder("ClusterFinder", "A reference to the ClusterFinder object", &Herwig::ClusterHadronizationHandler::_clusterFinder, false, false, true, false); static Reference<ClusterHadronizationHandler,ColourReconnector> interfaceColourReconnector("ColourReconnector", "A reference to the ColourReconnector object", &Herwig::ClusterHadronizationHandler::_colourReconnector, false, false, true, false); static Reference<ClusterHadronizationHandler,ClusterFissioner> interfaceClusterFissioner("ClusterFissioner", "A reference to the ClusterFissioner object", &Herwig::ClusterHadronizationHandler::_clusterFissioner, false, false, true, false); static Reference<ClusterHadronizationHandler,LightClusterDecayer> interfaceLightClusterDecayer("LightClusterDecayer", "A reference to the LightClusterDecayer object", &Herwig::ClusterHadronizationHandler::_lightClusterDecayer, false, false, true, false); static Reference<ClusterHadronizationHandler,ClusterDecayer> interfaceClusterDecayer("ClusterDecayer", "A reference to the ClusterDecayer object", &Herwig::ClusterHadronizationHandler::_clusterDecayer, false, false, true, false); // functions to move the handlers so they are set static Parameter<ClusterHadronizationHandler,Energy2> interfaceMinVirtuality2 ("MinVirtuality2", "Minimum virtuality^2 of partons to use in calculating distances (unit [GeV2]).", &ClusterHadronizationHandler::_minVirtuality2, GeV2, 0.1*GeV2, ZERO, 10.0*GeV2,false,false,false); static Parameter<ClusterHadronizationHandler,Length> interfaceMaxDisplacement ("MaxDisplacement", "Maximum displacement that is allowed for a particle (unit [millimeter]).", &ClusterHadronizationHandler::_maxDisplacement, mm, 1.0e-10*mm, 0.0*mm, 1.0e-9*mm,false,false,false); static Reference<ClusterHadronizationHandler,StepHandler> interfaceUnderlyingEventHandler ("UnderlyingEventHandler", "Pointer to the handler for the Underlying Event. " "Set to NULL to disable.", &ClusterHadronizationHandler::_underlyingEventHandler, false, false, true, true, false); static Switch<ClusterHadronizationHandler,bool> interfaceReduceToTwoComponents ("ReduceToTwoComponents", "Whether or not to reduce three component baryon-number violating clusters to two components before cluster splitting or leave" " this till after the cluster splitting", &ClusterHadronizationHandler::_reduceToTwoComponents, true, false, false); static SwitchOption interfaceReduceToTwoComponentsYes (interfaceReduceToTwoComponents, "BeforeSplitting", "Reduce to two components", true); static SwitchOption interfaceReduceToTwoComponentsNo (interfaceReduceToTwoComponents, "AfterSplitting", "Treat as three components", false); static Command<ClusterHadronizationHandler> interfaceUseHandlersForInteraction ("UseHandlersForInteraction", "Use the current set of handlers for the given interaction.", &ClusterHadronizationHandler::_setHandlersForInteraction, false); } void ClusterHadronizationHandler::rebind(const TranslationMap & trans) { for (auto iter : _partonSplitters) { _partonSplitters[iter.first] = trans.translate(iter.second); } for (auto iter : _clusterFinders) { _clusterFinders[iter.first] = trans.translate(iter.second); } for (auto iter : _colourReconnectors) { _colourReconnectors[iter.first] = trans.translate(iter.second); } for (auto iter : _clusterFissioners) { _clusterFissioners[iter.first] = trans.translate(iter.second); } for (auto iter : _lightClusterDecayers) { _lightClusterDecayers[iter.first] = trans.translate(iter.second); } for (auto iter : _clusterDecayers) { _clusterDecayers[iter.first] = trans.translate(iter.second); } for (auto iter : _gluonMassGenerators) { _gluonMassGenerators[iter.first] = trans.translate(iter.second); } HandlerBase::rebind(trans); } IVector ClusterHadronizationHandler::getReferences() { IVector ret = HandlerBase::getReferences(); for (auto iter : _partonSplitters) { ret.push_back(iter.second); } for (auto iter : _clusterFinders) { ret.push_back(iter.second); } for (auto iter : _colourReconnectors) { ret.push_back(iter.second); } for (auto iter : _clusterFissioners) { ret.push_back(iter.second); } for (auto iter : _lightClusterDecayers) { ret.push_back(iter.second); } for (auto iter : _clusterDecayers) { ret.push_back(iter.second); } for (auto iter : _gluonMassGenerators) { ret.push_back(iter.second); } return ret; } void ClusterHadronizationHandler::doinit() { HadronizationHandler::doinit(); // put current handlers into action for QCD to guarantee default behaviour if ( _partonSplitters.empty() ) { _partonSplitters[PDT::ColouredQCD] = _partonSplitter; _clusterFinders[PDT::ColouredQCD] = _clusterFinder; _colourReconnectors[PDT::ColouredQCD] = _colourReconnector; _clusterFissioners[PDT::ColouredQCD] = _clusterFissioner; _lightClusterDecayers[PDT::ColouredQCD] = _lightClusterDecayer; _clusterDecayers[PDT::ColouredQCD] = _clusterDecayer; _gluonMassGenerators[PDT::ColouredQCD] = _gluonMassGenerator; } } string ClusterHadronizationHandler::_setHandlersForInteraction(string interaction) { int interactionId = -1; if ( interaction == " QCD" ) { interactionId = PDT::ColouredQCD; } else { return "unknown interaction " + interaction; } _partonSplitters[interactionId] = _partonSplitter; _clusterFinders[interactionId] = _clusterFinder; _colourReconnectors[interactionId] = _colourReconnector; _clusterFissioners[interactionId] = _clusterFissioner; _lightClusterDecayers[interactionId] = _lightClusterDecayer; _clusterDecayers[interactionId] = _clusterDecayer; _gluonMassGenerators[interactionId] = _gluonMassGenerator; return ""; } namespace { void extractChildren(tPPtr p, set<PPtr> & all) { if (p->children().empty()) return; for (PVector::const_iterator child = p->children().begin(); child != p->children().end(); ++child) { all.insert(*child); extractChildren(*child, all); } } } void ClusterHadronizationHandler:: handle(EventHandler & ch, const tPVector & tagged, const Hint &) { useMe(); currentHandler_ = this; for (auto iter : _clusterFinders) { iter.second->spectrum()->setGenerator(currentHandler_->generator()); } PVector theList(tagged.begin(),tagged.end()); // set the scale for coloured particles to just above the gluon mass squared // if less than this so they are classed as perturbative // TODO: Should this be hard-coded here, or defined in inputs? map<int,int> interactions = {{PDT::ColouredQCD, ParticleID::g}}; // PVector currentlist(tagged.begin(),tagged.end()); map<int,PVector> currentlists; for ( const auto & p : theList ) { for ( auto i : interactions ) { if ( p->data().colouredInteraction() == i.first ) { currentlists[i.first].push_back(p); Energy2 Q02 = 1.01*sqr(getParticleData(i.second)->constituentMass()); if(currentlists[i.first].back()->scale()<Q02) currentlists[i.first].back()->scale(Q02); } } } map<int,ClusterVector> clusters; // ATTENTION this needs to have changes to include the new hadron spectrum functionality (gluon mass generator) for ( auto i : interactions ) { // reshuffle to the constituents if ( reshuffle_ ) { vector<PVector> reshufflelists; if ( reshuffle_ == 1 ) { // global reshuffling reshufflelists.push_back(currentlists[i.first]); } else if ( reshuffle_ == 2 ) {// colour connected reshuffling splitIntoColourSinglets(currentlists[i.first], reshufflelists, i.first); } Ptr<GluonMassGenerator>::ptr gMassGenerator; auto git = _gluonMassGenerators.find(i.first); if ( git != _gluonMassGenerators.end() ) gMassGenerator = git->second; for (auto currentlist : reshufflelists){ // get available energy and energy needed for constituent mass shells LorentzMomentum totalQ; Energy needQ = ZERO; size_t nGluons = 0; // number of gluons for which a mass need be generated for ( auto p : currentlist ) { totalQ += p->momentum(); if ( p->id() == i.second && gMassGenerator ) { ++nGluons; needQ += gMassGenerator->minGluonMass(); continue; } needQ += p->dataPtr()->constituentMass(); } Energy Q = totalQ.m(); if ( needQ > Q ) throw Exception() << "cannot reshuffle to constituent mass shells" << Exception::eventerror; // generate gluon masses if needed list<Energy> gluonMasses; if ( nGluons && gMassGenerator ) gluonMasses = gMassGenerator->generateMany(nGluons,Q-needQ); // set masses for inidividual particles vector<Energy> masses; for ( auto p : currentlist ) { if ( p->id() == i.second && gMassGenerator ) { list<Energy>::const_iterator it = gluonMasses.begin(); advance(it,UseRandom::irnd(gluonMasses.size())); masses.push_back(*it); gluonMasses.erase(it); } else { masses.push_back(p->dataPtr()->constituentMass()); } } // reshuffle to new masses reshuffle(currentlist,masses); } } // split the gluons if ( !currentlists[i.first].empty() ) { assert(_partonSplitters.find(i.first) != _partonSplitters.end()); _partonSplitters[i.first]->split(currentlists[i.first]); } // form the clusters if ( !currentlists[i.first].empty() ) { assert(_clusterFinders.find(i.first) != _clusterFinders.end()); clusters[i.first] = _clusterFinders[i.first]->formClusters(currentlists[i.first]); // reduce BV clusters to two components now if needed if(_reduceToTwoComponents) _clusterFinders[i.first]->reduceToTwoComponents(clusters[i.first]); } } // perform colour reconnection if needed and then // decay the clusters into one hadron for ( auto i : interactions ) { if ( clusters[i.first].empty() ) continue; bool lightOK = false; short tried = 0; const ClusterVector savedclusters = clusters[i.first]; tPVector finalHadrons; // only needed for partonic decayer while (!lightOK && tried++ < 10) { // no colour reconnection with baryon-number-violating (BV) clusters ClusterVector CRclusters, BVclusters; CRclusters.reserve( clusters[i.first].size() ); BVclusters.reserve( clusters[i.first].size() ); for (size_t ic = 0; ic < clusters[i.first].size(); ++ic) { ClusterPtr cl = clusters[i.first].at(ic); bool hasClusterParent = false; for (unsigned int ix=0; ix < cl->parents().size(); ++ix) { if (cl->parents()[ix]->id() == ParticleID::Cluster) { hasClusterParent = true; break; } } if (cl->numComponents() > 2 || hasClusterParent) BVclusters.push_back(cl); else CRclusters.push_back(cl); } // colour reconnection assert(_colourReconnectors.find(i.first) != _colourReconnectors.end()); _colourReconnectors[i.first]->rearrange(CRclusters); // tag new clusters as children of the partons to hadronize _setChildren(CRclusters); // forms diquarks // we should already have used _clusterFinder[i.first] _clusterFinders[i.first]->reduceToTwoComponents(CRclusters); // recombine vectors of (possibly) reconnected and BV clusters clusters[i.first].clear(); clusters[i.first].insert( clusters[i.first].end(), CRclusters.begin(), CRclusters.end() ); clusters[i.first].insert( clusters[i.first].end(), BVclusters.begin(), BVclusters.end() ); // fission of heavy clusters // NB: during cluster fission, light hadrons might be produced straight away assert(_clusterFissioners.find(i.first) != _clusterFissioners.end()); - if (clusters[i.first].size()<=2) std::cout << "Diffractive event maybe" << std::endl; finalHadrons = _clusterFissioners[i.first]->fission(clusters[i.first],i.first == PDT::ColouredQCD ? isSoftUnderlyingEventON() : false); - if (clusters[i.first].size()<=2) std::cout << "Diffractive event finished" << std::endl; // if clusters not previously reduced to two components do it now if(!_reduceToTwoComponents) _clusterFinders[i.first]->reduceToTwoComponents(clusters[i.first]); assert(_lightClusterDecayers.find(i.first) != _lightClusterDecayers.end()); lightOK = _lightClusterDecayers[i.first]->decay(clusters[i.first],finalHadrons); // if the decay of the light clusters was not successful, undo the cluster // fission and decay steps and revert to the original state of the event // record if (!lightOK) { clusters[i.first] = savedclusters; for_each(clusters[i.first].begin(), clusters[i.first].end(), std::mem_fn(&Particle::undecay)); } } if (!lightOK) { throw Exception( "CluHad::handle(): tried LightClusterDecayer 10 times!", Exception::eventerror); } // decay the remaining clusters assert(_clusterDecayers[i.first]); _clusterDecayers[i.first]->decay(clusters[i.first],finalHadrons); } // ***************************************** // ***************************************** // ***************************************** bool finalStateCluster=false; StepPtr pstep = newStep(); set<PPtr> allDecendants; for (tPVector::const_iterator it = tagged.begin(); it != tagged.end(); ++it) { extractChildren(*it, allDecendants); } for(set<PPtr>::const_iterator it = allDecendants.begin(); it != allDecendants.end(); ++it) { // this is a workaround because the set sometimes // re-orders parents after their children if ((*it)->children().empty()){ // If there is a cluster in the final state throw an event error if((*it)->id()==81) { finalStateCluster=true; } pstep->addDecayProduct(*it); } else { pstep->addDecayProduct(*it); pstep->addIntermediate(*it); } } // For very small center of mass energies it might happen that baryonic clusters cannot decay into hadrons if (finalStateCluster){ throw Exception( "CluHad::Handle(): Cluster in the final state", Exception::eventerror); } // ***************************************** // ***************************************** // ***************************************** // soft underlying event if needed if (isSoftUnderlyingEventON()) { assert(_underlyingEventHandler); ch.performStep(_underlyingEventHandler,Hint::Default()); } } // Sets parent child relationship of all clusters with two components // Relationships for clusters with more than two components are set elsewhere in the Colour Reconnector void ClusterHadronizationHandler::_setChildren(const ClusterVector & clusters) const { // erase existing information about the partons' children tPVector partons; for ( const auto & cl : clusters ) { if ( cl->numComponents() > 2 ) continue; partons.push_back( cl->colParticle() ); partons.push_back( cl->antiColParticle() ); } // erase all previous information about parent child relationship for_each(partons.begin(), partons.end(), std::mem_fn(&Particle::undecay)); // give new parents to the clusters: their constituents for ( const auto & cl : clusters ) { if ( cl->numComponents() > 2 ) continue; cl->colParticle()->addChild(cl); cl->antiColParticle()->addChild(cl); } } void ClusterHadronizationHandler::splitIntoColourSinglets(PVector copylist, vector<PVector>& reshufflelists, int){ PVector currentlist; bool gluonloop; PPtr firstparticle, temp; reshufflelists.clear(); while (copylist.size()>0){ gluonloop=false; currentlist.clear(); firstparticle=copylist.back(); copylist.pop_back(); if (!firstparticle->coloured()){ continue; //non-coloured particles are not included } currentlist.push_back(firstparticle); //go up the anitColourLine and check if we are in a gluon loop temp=firstparticle; while( temp->hasAntiColour()){ temp = temp->antiColourLine()->endParticle(); if(temp==firstparticle){ gluonloop=true; break; } else{ currentlist.push_back(temp); copylist.erase(remove(copylist.begin(),copylist.end(), temp), copylist.end()); } } //if not a gluon loop, go up the ColourLine if(!gluonloop){ temp=firstparticle; while( temp->hasColour()){ temp=temp->colourLine()->startParticle(); currentlist.push_back(temp); copylist.erase(remove(copylist.begin(),copylist.end(), temp), copylist.end()); } } reshufflelists.push_back(currentlist); } }