diff --git a/Hadronization/ClusterFissioner.cc b/Hadronization/ClusterFissioner.cc --- a/Hadronization/ClusterFissioner.cc +++ b/Hadronization/ClusterFissioner.cc @@ -1,1841 +1,1768 @@ // -*- 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 "Herwig/Utilities/AlphaS.h" #include <boost/numeric/ublas/matrix.hpp> #include <boost/numeric/ublas/io.hpp> #include <boost/numeric/ublas/lu.hpp> #include <cassert> #include <vector> using namespace Herwig; DescribeClass<ClusterFissioner,Interfaced> describeClusterFissioner("Herwig::ClusterFissioner","Herwig.so"); ClusterFissioner::ClusterFissioner() : _clMaxLight(3.35*GeV), _clMaxDiquark(3.35*GeV), _clMaxExotic(3.35*GeV), _clPowLight(2.0), _clPowDiquark(2.0), _clPowExotic(2.0), _pSplitLight(1.0), _pSplitExotic(1.0), _phaseSpaceWeights(0), _dim(4), _fissionCluster(0), _kinematicThresholdChoice(0), _pwtDIquark(0.0), _diquarkClusterFission(0), _btClM(1.0*GeV), _iopRem(1), _kappa(1.0e15*GeV/meter), _enhanceSProb(0), _m0Fission(2.*GeV), _massMeasure(0), _probPowFactor(4.0), _probShift(0.0), _kinThresholdShift(1.0*sqr(GeV)), _strictDiquarkKinematics(0), _covariantBoost(false), _hadronizingStrangeDiquarks(2) { } ClusterFissioner::~ClusterFissioner(){ } 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(_clMaxDiquark,GeV) << ounit(_clMaxExotic,GeV) << _clPowLight << _clPowHeavy << _clPowDiquark << _clPowExotic << _pSplitLight << _pSplitHeavy << _pSplitExotic << _fissionCluster << _fissionPwt << _pwtDIquark << _diquarkClusterFission << ounit(_btClM,GeV) << _iopRem << ounit(_kappa, GeV/meter) << _enhanceSProb << ounit(_m0Fission,GeV) << _massMeasure << _dim << _phaseSpaceWeights << _hadronSpectrum << _kinematicThresholdChoice << _probPowFactor << _probShift << ounit(_kinThresholdShift,sqr(GeV)) << _strictDiquarkKinematics << _covariantBoost << _hadronizingStrangeDiquarks ; } void ClusterFissioner::persistentInput(PersistentIStream & is, int) { is >> iunit(_clMaxLight,GeV) >> iunit(_clMaxHeavy,GeV) >> iunit(_clMaxDiquark,GeV) >> iunit(_clMaxExotic,GeV) >> _clPowLight >> _clPowHeavy >> _clPowDiquark >> _clPowExotic >> _pSplitLight >> _pSplitHeavy >> _pSplitExotic >> _fissionCluster >> _fissionPwt >> _pwtDIquark >> _diquarkClusterFission >> iunit(_btClM,GeV) >> _iopRem >> iunit(_kappa, GeV/meter) >> _enhanceSProb >> iunit(_m0Fission,GeV) >> _massMeasure >> _dim >> _phaseSpaceWeights >> _hadronSpectrum >> _kinematicThresholdChoice >> _probPowFactor >> _probShift >> iunit(_kinThresholdShift,sqr(GeV)) >> _strictDiquarkKinematics >> _covariantBoost >> _hadronizingStrangeDiquarks ; } /* namespace{ void printV(Lorentz5Momentum p) { std::cout << "("<<p.e()/GeV<<"|"<<p.vect().x()/GeV<<","<<p.vect().y()/GeV<<","<<p.vect().z()/GeV<<") Mass = "<<p.mass()/GeV<<" m = "<<p.m()/GeV<<"\n"; } } */ void ClusterFissioner::doinit() { Interfaced::doinit(); if (_writeOut){ std::ofstream out("data_CluFis.dat", std::ios::out); out.close(); } - // Energy m1=1.2*GeV; - // Energy m2=2.3*GeV; - // std::cout <<std::setprecision(18)<< sqr(m1+m2)/GeV2 << std::endl; - // std::cout <<std::setprecision(18)<< ((m1+m2)*(m1+m2))/GeV2 << std::endl; - // std::cout <<std::setprecision(18)<< ((sqr(m1+m2))-((m1+m2)*(m1+m2)))/GeV2 << std::endl; - /* TESTs for Two particle boost - * TODO - * */ - /* - Energy M,M1,M2; - Energy m,m1,m2; - - M=20.1*GeV; - M1=10.1*GeV; - M2=5.1*GeV; - m2=0.325*GeV; - m1=0.625*GeV; - m=1.1*GeV; - - // double cosTheta1=0.3; - // double phi1=0.7*3.14; - // double cosTheta2=-0.4; - // double phi2=-0.2*3.14; - - // Axis LABz(0,0,1); - // Energy Plab=80.1*GeV; - - // Lorentz5Momentum P(M,Plab*LABz); - // Energy PCOM=P.boost(-P.boostVector()).vect().mag(); - - - double cosTheta=0.0; - double phi=0.7*3.14; - - Energy P1=1.2*GeV; - Energy P2=10.2*GeV; - Energy Q=1.2*GeV; - Lorentz5Momentum p1(m1,P1*Axis(cosTheta*cos(phi),cosTheta*sin(phi),sin(acos(cosTheta)))); - Lorentz5Momentum p2(m1*2,Momentum3(-p1.vect().x(),-p1.vect().y(),P2)); - Lorentz5Momentum qOld(m,Q*sampleDirectionIsotropic()); - Lorentz5Momentum qNew=qOld; - Lorentz5Momentum pC=p1+p2; - printV(p1); - printV(p2); - printV(p1+p2); - printV(pC); - printV(qOld); - printV(qNew); - // p1.boost(-pC.boostVector()); - // p2.boost(-pC.boostVector()); - // q.boost(-pC.boostVector()); - // printV(q); - std::vector<Lorentz5Momentum *> v; - v.push_back(&qNew); - Kinematics::BoostIntoTwoParticleFrame(pC.m(),p1,p2,v); - // Kinematics::twoBodDecay(pC,); - printV(qOld); - printV(qNew); - // std::cout << p1 << std::endl; - // std::cout << p2 << std::endl; - // std::cout << p1+p2 << std::endl; - // std::cout << Q << std::endl; - - - - -*/ - - - - - - - - for ( const long& id : spectrum()->heavyHadronizingQuarks() ) { if ( _pSplitHeavy.find(id) == _pSplitHeavy.end() || _clPowHeavy.find(id) == _clPowHeavy.end() || - _clMaxHeavy.find(id) == _clMaxHeavy.end() ) + _clMaxHeavy.find(id) == _clMaxHeavy.end() ){ + std::cout << "id = "<<id << std::endl; throw InitException() << "not all parameters have been set for heavy quark cluster fission"; + } } // for ( const long& id : spectrum()->lightHadronizingDiquarks() ) { // if ( // _clPowDiquark.find(id) == _clPowDiquark.end() || // _clMaxDiquark.find(id) == _clMaxDiquark.end() ) // throw InitException() << "not all parameters have been set for diquarks 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 better solution for this magic number alternative _fissionPwt[1103] = _pwtDIquark * pwtDquark * pwtDquark; _fissionPwt[2101] = 0.5 * _pwtDIquark * pwtUquark * pwtDquark; _fissionPwt[2203] = _pwtDIquark * pwtUquark * pwtUquark; if (_hadronizingStrangeDiquarks>0) { _fissionPwt[3101] = 0.5 * _pwtDIquark * pwtSquark * pwtDquark; _fissionPwt[3201] = 0.5 * _pwtDIquark * pwtSquark * pwtUquark; if (_hadronizingStrangeDiquarks==2) { _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, 100.0*GeV, false,false,false); static Parameter<ClusterFissioner,Energy> interfaceClMaxDiquark ("ClMaxDiquark","cluster max mass for light hadronizing diquarks (unit [GeV])", &ClusterFissioner::_clMaxDiquark, GeV, 3.35*GeV, ZERO, 100.0*GeV, false,false,false); static ParMap<ClusterFissioner,Energy> interfaceClMaxHeavy ("ClMaxHeavy", "ClMax for heavy quarks", &ClusterFissioner::_clMaxHeavy, GeV, -1, 3.35*GeV, ZERO, 100.0*GeV, false, false, Interface::upperlim); // static ParMap<ClusterFissioner,Energy> interfaceClMaxDiquark // ("ClMaxDiquark", // "ClMax for light hadronizing diquarks", // &ClusterFissioner::_clMaxDiquark, GeV, -1, 3.35*GeV, ZERO, 100.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, 100.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 ParMap<ClusterFissioner,double> interfaceClPowDiquark // ("ClPowDiquark", // "ClPow for light hadronizing diquarks", // &ClusterFissioner::_clPowDiquark, -1, 1.0, 0.0, 10.0, // false, false, Interface::upperlim); static Parameter<ClusterFissioner,double> interfaceClPowDiquark ("ClPowDiquark","cluster mass exponent for light hadronizing diquarks", &ClusterFissioner::_clPowDiquark, 0, 2.0, 0.0, 10.0,false,false,false); 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 spectrum class.", 0); static SwitchOption interfaceFissionNew (interfaceFission, "New", "Alternative cluster fission which does not depend on the hadron spectrum class", 1); static SwitchOption interfaceFissionNewDiquarkSuppression (interfaceFission, "NewDiquarkSuppression", "Alternative cluster fission which does not depend on the hadron spectrum class" " and includes a suppression of AlphaS^2(Mc) for Diquark Production during " "Cluster Fission", -1); static Switch<ClusterFissioner,int> interfaceDiquarkClusterFission ("DiquarkClusterFission", "Allow clusters to fission to 1 or 2 diquark Clusters or Turn off diquark fission completely", &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 SwitchOption interfaceDiquarkClusterFissionOff (interfaceDiquarkClusterFission, "Off", "Don't allow clusters fission to draw diquarks ", -1); 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> 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 ClusterFissioner bell probablity function", &ClusterFissioner::_probPowFactor, 2.0, 0.001, 20.0, 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 ClusterFissioner", &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> 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 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); static Switch<ClusterFissioner,int> interfacePhaseSpaceWeights ("PhaseSpaceWeights", "Include phase space weights.", &ClusterFissioner::_phaseSpaceWeights, 0, false, false); static SwitchOption interfacePhaseSpaceWeightsNo (interfacePhaseSpaceWeights, "No", "Do not include the effect of cluster phase space", 0); static SwitchOption interfacePhaseSpaceWeightsYes (interfacePhaseSpaceWeights, "Yes", "Do include the effect of cluster fission phase space " "related to constituent masses." "Note: Need static Threshold choice", 1); static SwitchOption interfacePhaseSpaceWeightsUseHadronMasses (interfacePhaseSpaceWeights, "UseHadronMasses", "Do include the effect of cluster fission phase space " "related to hadron masses." "Note: Need static Threshold choice", 2); static SwitchOption interfacePhaseSpaceWeightsNoConstituentMasses (interfacePhaseSpaceWeights, "NoConstituentMasses", "Do not include the effect of cluster fission phase space " "related to constituent masses." "Note: Need static Threshold choice", 3); static Parameter<ClusterFissioner,double> interfaceDim ("Dimension","Dimension in which phase space weights are calculated", &ClusterFissioner::_dim, 0, 4.0, 0.0, 10.0,false,false,false); // Allowing for strange diquarks in the ClusterFission static Switch<ClusterFissioner,unsigned int> interfaceHadronizingStrangeDiquarks ("HadronizingStrangeDiquarks", "Option for adding strange diquarks to Cluster Fission (if Fission = New or Hybrid is enabled)", &ClusterFissioner::_hadronizingStrangeDiquarks, 0, false, false); static SwitchOption interfaceHadronizingStrangeDiquarksNo (interfaceHadronizingStrangeDiquarks, "No", "No strangeness containing diquarks during Cluster Fission", 0); static SwitchOption interfaceHadronizingStrangeDiquarksOnlySingleStrange (interfaceHadronizingStrangeDiquarks, "OnlySingleStrange", "Only one strangeness containing diquarks during Cluster Fission i.e. su,sd", 1); static SwitchOption interfaceHadronizingStrangeDiquarksAll (interfaceHadronizingStrangeDiquarks, "All", "All strangeness containing diquarks during Cluster Fission i.e. su,sd,ss", 2); } 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(); 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()); if(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" << Exception::runerror; } } // 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); double weightMasses = drawNewMasses(Mc, soft1, soft2, pClu1, pClu2, ptrQ1, pQ1, newPtr1, pQone, newPtr2, pQtwo, ptrQ2, pQ2); if (weightMasses==0.0) continue; // derive the masses of the children Mc1 = pClu1.mass(); Mc2 = pClu2.mass(); // static kinematic threshold if(_kinematicThresholdChoice == 0) { if (Mc1 < m1+m || Mc2 < m+m2 || Mc1+Mc2 > Mc) continue; if (_phaseSpaceWeights==2 && ( Mc1 < spectrum()->massLightestHadronPair(ptrQ1->dataPtr(),newPtr1->dataPtr()) || Mc2 < spectrum()->massLightestHadronPair(ptrQ2->dataPtr(),newPtr2->dataPtr()) )) 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 && phaseSpaceVeto(Mc,Mc1,Mc2,m,m1,m2, ptrQ1, ptrQ2, newPtr1, _powerLawPower) ) { // reduce counter as it regards only the mass sampling counter--; 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) { Mc1 = toHadron1->mass(); pClu1.setMass(Mc1); } toHadron2 = _hadronSpectrum->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc2); if(toHadron2) { Mc2 = toHadron2->mass(); pClu2.setMass(Mc2); } // 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) { Mc1 = toHadron1->mass(); pClu1.setMass(Mc1); } } else if(!toHadron2) { toHadron2 = _hadronSpectrum->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc-Mc1); if(toHadron2) { 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) 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); double weightMasses = drawNewMasses(mmax, soft1, soft2, pClu1, pClu2, ptrQ[iq1], pQ1, newPtr1, pQone, newPtr2, pQtwo, ptrQ[iq1], pQ2); if (weightMasses == 0.0) continue; Mc1 = pClu1.mass(); Mc2 = pClu2.mass(); if(Mc1 < m1+m || Mc2 < m+m2 || Mc1+Mc2 > mmax) continue; if ( _phaseSpaceWeights && phaseSpaceVeto(mmax,Mc1,Mc2,m,m1,m2) ) { // reduce counter as it regards only the mass sampling counter--; 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; } /** * Calculate the phase space weight for M1*M2*(2 body PhaseSpace) ignore constituent masses */ double ClusterFissioner::weightFlatPhaseSpaceNoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2) const { double M_temp = Mc/GeV; double M1_temp = Mc1/GeV; double M2_temp = Mc2/GeV; if (sqr(M_temp)<sqr(M1_temp+M2_temp)) { // This should be checked before throw Exception() << "ClusterFissioner has not checked Masses properly\n" << "Mc = " << M_temp << "\n" << "Mc1 = " << M1_temp << "\n" << "Mc2 = " << M2_temp << "\n" << Exception::warning; return 0.0; } double lam = Kinematics::kaellen(M_temp, M1_temp, M2_temp); double ratio; // old weight of Jan without the Jacobi factor M1*M2 of the Mass integration // double PSweight = pow(lam,_dim-3.)*pow(M1_temp*M2_temp,2.-_dim); // new weight with the Jacobi factor M1*M2 of the Mass integration double PSweight = M1_temp*M2_temp*pow(sqrt(lam),_dim-3.); // overestimate only possible for dim>=3.0 assert(_dim>=3.0); // old overestimate of Jan without the Jacobi factor M1*M2 of the Mass integration // double overEstimate = _dim>=4.0 ? pow(M_temp,4.*_dim-14.):pow(M_temp,2*(_dim-3.0))/pow((m1_temp+m_temp)*(m2_temp+m_temp),4.0-_dim); // new improved overestimate with the Jacobi factor M1*M2 of the Mass integration double overEstimate = pow(6.0*sqrt(3.0), 3.0 - _dim)*pow(M_temp, 2.*(_dim-2.)); ratio = PSweight/overEstimate; if (!(ratio>=0)) std::cout << "ratio = " <<ratio<<" M "<<M_temp<<" M1 "<<M1_temp<<" M2 "<<M2_temp<<"\t"<<_dim<<"\t" << lam <<"\t"<< overEstimate<<"\n\n"; if (!(ratio<=1)) std::cout << "ratio = " <<ratio<<" M "<<M_temp<<" M1 "<<M1_temp<<" M2 "<<M2_temp<<"\t"<<_dim<<"\t" << lam <<"\t"<< overEstimate<<"\n\n"; // if (ratio > 0.9) std::cout << "ratio = " << ratio <<"\n"; assert (ratio >= 0); assert (ratio <= 1); return ratio; } /** * Calculate the phase space weight for M1*M2*(2 body PhaseSpace)^3 */ double ClusterFissioner::weightPhaseSpaceConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, const double power) const { double M_temp = Mc/GeV; double M1_temp = Mc1/GeV; double M2_temp = Mc2/GeV; double m_temp = m/GeV; double m1_temp = m1/GeV; double m2_temp = m2/GeV; if (sqr(M_temp)<sqr(M1_temp+M2_temp) || sqr(M1_temp)<sqr(m1_temp+m_temp) || sqr(M2_temp)<sqr(m2_temp+m_temp) ) { // This should be checked before throw Exception() << "ClusterFissioner has not checked Masses properly\n" << "Mc = " << M_temp << "\n" << "Mc1 = " << M1_temp << "\n" << "Mc2 = " << M2_temp << "\n" << "m1 = " << m1_temp << "\n" << "m2 = " << m2_temp << "\n" << "m = " << m_temp << "\n" << Exception::warning; return 0.0; } double lam1 = Kinematics::kaellen(M1_temp, m1_temp, m_temp); double lam2 = Kinematics::kaellen(M2_temp, m2_temp, m_temp); double lam3 = Kinematics::kaellen(M_temp, M1_temp, M2_temp); double ratio; // old weight of Jan without the Jacobi factor M1*M2 of the Mass integration // double PSweight = pow(lam1*lam2*lam3,_dim-3.)*pow(M1_temp*M2_temp,2.-_dim); // new weight with the Jacobi factor M1*M2 of the Mass integration double PSweight = pow(lam1*lam2*lam3,(_dim-3.)/2.0)*pow(M1_temp*M2_temp,3.-_dim); // overestimate only possible for dim>=3.0 assert(_dim>=3.0); // old overestimate of Jan without the Jacobi factor M1*M2 of the Mass integration // double overEstimate = _dim>=4.0 ? pow(M_temp,4.*_dim-14.):pow(M_temp,2*(_dim-3.0))/pow((m1_temp+m_temp)*(m2_temp+m_temp),4.0-_dim); // new improved overestimate with the Jacobi factor M1*M2 of the Mass integration double overEstimate = pow(6.0*sqrt(3.0), 3.0 - _dim)*pow(M_temp, 4.*_dim-12.); ratio = PSweight/overEstimate; if (!(ratio>=0)) std::cout << "ratio = " <<ratio<<" M "<<M_temp<<" M1 "<<M1_temp<<" M2 "<<M2_temp<<" m1 "<<m1_temp<<" m2 "<<m2_temp<<" m "<<m_temp<<"\t"<<_dim<<"\t" << lam1<<"\t"<< lam2<<"\t" << lam3 <<"\t"<< overEstimate<<"\n\n"; if (power) { double powerLawOver = power<0 ? pow(Mc1*Mc2/((m1+m)*(m2+m)),power):pow(Mc1*Mc2/((Mc-(m1+m))*(Mc-(m2+m))),power); ratio*=powerLawOver; } // if (ratio > 0.9) std::cout << "ratio = " << ratio <<"\n"; assert (ratio >= 0); assert (ratio <= 1); return ratio; } /** * Calculate the phase space weight for M1*M2*(2 body PhaseSpace)^3 * using Hadron Masses */ double ClusterFissioner::weightFlatPhaseSpaceHadronMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, tcPPtr pQ, tcPPtr pQ1, tcPPtr pQ2) const { auto LHP1 = spectrum()->lightestHadronPair(pQ1->dataPtr(),pQ->dataPtr()); auto LHP2 = spectrum()->lightestHadronPair(pQ2->dataPtr(),pQ->dataPtr()); if (sqr(Mc1)<sqr(LHP1.first->mass()+LHP1.second->mass())) return true; if (sqr(Mc2)<sqr(LHP2.first->mass()+LHP2.second->mass())) return true; // double weigthHadrons = Kinematics::pstarTwoBodyDecay(Mc1,m1,m)*Kinematics::pstarTwoBodyDecay(Mc2,m2,m)/(Kinematics::pstarTwoBodyDecay(Mc1,LHP1.first->mass(),LHP1.second->mass())*Kinematics::pstarTwoBodyDecay(Mc2,LHP2.first->mass(),LHP2.second->mass())); // double tot= weightFlatPhaseSpace(Mc, Mc1, Mc2, m, m1, m2)/weigthHadrons; // if (std::isinf(tot) || std::isnan(tot) || tot<0.0 || tot>1.0) // std::cout << "tot = " << double lam1 = sqrt(Kinematics::kaellen(Mc1/GeV, LHP1.first->mass()/GeV, LHP1.second->mass()/GeV)); double lam2 = sqrt(Kinematics::kaellen(Mc2/GeV, LHP2.first->mass()/GeV, LHP2.second->mass()/GeV)); double lam3 = sqrt(Kinematics::kaellen(Mc/GeV, Mc1/GeV, Mc2/GeV)); double ratio; // old weight of Jan without the Jacobi factor M1*M2 of the Mass integration // double PSweight = pow(lam1*lam2*lam3,_dim-3.)*pow(M1_temp*M2_temp,2.-_dim); // new weight with the Jacobi factor M1*M2 of the Mass integration double PSweight = pow(lam1*lam2*lam3,_dim-3.)*pow(Mc1*Mc2/GeV2,3.-_dim); // overestimate only possible for dim>=3.0 assert(_dim>=3.0); // old overestimate of Jan without the Jacobi factor M1*M2 of the Mass integration // double overEstimate = _dim>=4.0 ? pow(M_temp,4.*_dim-14.):pow(M_temp,2*(_dim-3.0))/pow((m1_temp+m_temp)*(m2_temp+m_temp),4.0-_dim); // new improved overestimate with the Jacobi factor M1*M2 of the Mass integration double overEstimate = pow(6.0*sqrt(3.0), 3.0 - _dim)*pow(Mc/GeV, 4.*_dim-12.); ratio = PSweight/overEstimate; // if (!(ratio>=0)) std::cout << "M "<<Mc/GeV<<" M1 "<<Mc1/GeV<<" M2 "<<Mc2/GeV<<" m1 "<<m1_temp<<" m2 "<<m2_temp<<" m "<<m_temp<<"\n\n"; // if (ratio > 0.9) std::cout << "ratio = " << ratio <<"\n"; if (std::isinf(ratio) || std::isnan(ratio) || ratio<0.0 || ratio>1.0) std::cout << "ratio = " << ratio<<std::endl; assert (ratio >= 0); assert (ratio <= 1); return ratio; } /** * Veto for the phase space weight * returns true if proposed Masses are rejected * else returns false */ bool ClusterFissioner::phaseSpaceVeto(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, tcPPtr pQ1, tcPPtr pQ2, tcPPtr pQ, const double power) const { switch (_phaseSpaceWeights) { case 1: return phaseSpaceVetoConstituentMasses(Mc, Mc1, Mc2, m, m1, m2, power); case 2: return phaseSpaceVetoHadronPairs(Mc, Mc1, Mc2, pQ, pQ1, pQ2); case 3: return phaseSpaceVetoNoConstituentMasses(Mc, Mc1, Mc2); default: assert(false); } } /** * Veto for the phase space weight * returns true if proposed Masses are rejected * else returns false */ bool ClusterFissioner::phaseSpaceVetoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, const double power) const { return (UseRandom::rnd()>weightPhaseSpaceConstituentMasses(Mc, Mc1, Mc2, m, m1, m2, power)); } bool ClusterFissioner::phaseSpaceVetoNoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2) const { return (UseRandom::rnd()>weightFlatPhaseSpaceNoConstituentMasses(Mc, Mc1, Mc2)); } bool ClusterFissioner::phaseSpaceVetoHadronPairs(const Energy Mc, const Energy Mc1, const Energy Mc2, tcPPtr pQ, tcPPtr pQ1, tcPPtr pQ2) const { return (UseRandom::rnd()>weightFlatPhaseSpaceHadronMasses(Mc, Mc1, Mc2, pQ, pQ1, pQ2)); } /** * Calculate the masses and possibly kinematics of the cluster * fission at hand; if claculateKineamtics is perfomring non-trivial * steps kinematics calulcated here will be overriden. Currently resorts to the default * @return the potentially non-trivial distribution weight=f(M1,M2) * On Failure we return 0 */ // double ClusterFissioner::drawNewMasses(const Energy Mc, const bool soft1, const bool soft2, // Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, // tcPPtr ptrQ1, const Lorentz5Momentum& pQ1, // tcPPtr newPtr1, const Lorentz5Momentum& pQone, // tcPPtr, const Lorentz5Momentum& pQtwo, // tcPPtr ptrQ2, const Lorentz5Momentum& pQ2) const ; // { // // TODO add precise weightMS that could be used used for improving the rejection Sampling // return drawNewMassesDefault(Mc, soft1, soft2, pClu1, pClu2, ptrQ1, pQ1, tcPPtr(), pQone, tcPPtr(),pQtwo, ptrQ2, pQ2); // break; // case 1: // return drawNewMassesUniform(Mc, pClu1, pClu2, pQ1, pQone, pQ2); // break; // case 2: // return drawNewMassesPhaseSpace(Mc, pClu1, pClu2, pQ1, pQone, pQ2, ptrQ1, newPtr1, ptrQ2); // break; // case 3: // return drawNewMassesPhaseSpacePowerLaw(Mc, pClu1, pClu2, pQ1, pQone, pQ2, ptrQ1, newPtr1, ptrQ2); // break; // default: // assert(false); // } // return 0;// failure // } /** * Calculate the masses and possibly kinematics of the cluster * fission at hand; if calculateKineamtics is perfomring non-trivial * steps kinematics claulcated here will be overriden. Currentl;y resorts to the default */ double ClusterFissioner::drawNewMasses(const Energy Mc, const bool soft1, const bool soft2, Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, tcPPtr ptrQ1, const Lorentz5Momentum& pQ1, tcPPtr, const Lorentz5Momentum& pQone, tcPPtr, const Lorentz5Momentum& pQtwo, tcPPtr ptrQ2, const Lorentz5Momentum& pQ2) const { // power for splitting double exp1 = !spectrum()->isExotic(ptrQ1->dataPtr()) ? _pSplitLight : _pSplitExotic; double exp2 = !spectrum()->isExotic(ptrQ2->dataPtr()) ? _pSplitLight : _pSplitExotic; for ( const long& id : spectrum()->heavyHadronizingQuarks() ) { assert(_pSplitHeavy.find(id) != _pSplitHeavy.end()); if ( spectrum()->hasHeavy(id,ptrQ1->dataPtr()) ) exp1 = _pSplitHeavy.find(id)->second; if ( spectrum()->hasHeavy(id,ptrQ2->dataPtr()) ) exp2 = _pSplitHeavy.find(id)->second; } Energy M1 = drawChildMass(Mc,pQ1.mass(),pQ2.mass(),pQone.mass(),exp1,soft1); Energy M2 = drawChildMass(Mc,pQ2.mass(),pQ1.mass(),pQtwo.mass(),exp2,soft2); pClu1.setMass(M1); pClu2.setMass(M2); return 1.0; // succeeds } 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; double weight; // double factorPS; 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++; minMass=spectrum()->massLightestHadronPair(pD1,pD2); if (_fissionCluster==0) choice.insert(_hadronSpectrum->pwtQuark(id),id); else if (abs(_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); Energy mH1=spectrum()->massLightestHadron(pD2,cand); Energy mH2=spectrum()->massLightestHadron(cand,pD1); // factorPS = Kinematics::pstarTwoBodyDecay(Mc,mH1,mH2)/(Mc/2.0); // factorPS = 1.0; minMass = mH1 + mH2; } else { minMass = spectrum()->massLightestBaryonPair(pD1,pD2); // factorPS = 1.0; } if (Mc < minMass) continue; // countDiQ++; if (_fissionCluster==0) weight = _hadronSpectrum->pwtQuark(id); else if (abs(_fissionCluster)==1) weight = _fissionPwt.find(id)->second; else assert(false); if (_fissionCluster==-1) weight*=sqr(Herwig::Math::alphaS(Mc, 0.25*GeV,3, 2)); // weight/=factorPS; choice.insert(weight,id); } 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) weight = _hadronSpectrum->pwtQuark(id); else if (abs(_fissionCluster)==1) weight = _fissionPwt.find(id)->second; else assert(false); if (_fissionCluster==-1) weight*=sqr(Herwig::Math::alphaS(Mc, 0.25*GeV,3, 2)); choice.insert(weight,id); } 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) weight = _hadronSpectrum->pwtQuark(id); else if (abs(_fissionCluster)==1) weight = _fissionPwt.find(id)->second; else assert(false); if (_fissionCluster==-1) weight*=sqr(Herwig::Math::alphaS(Mc, 0.25*GeV,3, 2)); choice.insert(weight,id); } 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::drawNewFlavourQuarks(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(abs(_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(abs(_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; } } 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 **************************/ // Calculate the unit three-vector, in the C frame, along which // all of the constituents and children clusters move. Lorentz5Momentum u(p0Q1); u.boost( -pClu.boostVector() ); // boost from LAB to C // the unit three-vector is then u.vect().unit() // Calculate the momenta of C1 and C2 in the (parent) C frame first, // where the direction of C1 is u.vect().unit(), and then boost back in the // LAB frame. if (pClu.m() < pClu1.mass() + pClu2.mass() ) { throw Exception() << "Impossible Kinematics in ClusterFissioner::calculateKinematics() (A)" << Exception::eventerror; } Kinematics::twoBodyDecay(pClu, pClu1.mass(), pClu2.mass(), u.vect().unit(), pClu1, pClu2); // 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() (B)" << Exception::eventerror; } Kinematics::twoBodyDecay(pClu1, pQ1.mass(), pQbar.mass(), u.vect().unit(), 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() (C)" << Exception::eventerror; } Kinematics::twoBodyDecay(pClu2, pQ.mass(), pQ2bar.mass(), u.vect().unit(), 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::ProbablityFunctionTest(double Mass, double threshold) { double cut = UseRandom::rnd(0.0,1.0); // double arg = scale/threshold; // double epsilon = 1e-5; // if (arg<(1.0+epsilon) ){ // // std::cout << "arg = "<<arg << std::endl; // arg=1.0+epsilon; // } // return 1./(1.+_probPowFactor*pow(log(arg),-2.0)) > cut ? true : false; if ((Mass-threshold)<=0) return false; return 1.0/(1.0 + _probPowFactor*pow(1.0/(Mass-threshold),_clPowLight)) > cut ? true : false; } bool ClusterFissioner::isHeavy(tcClusterPtr clu) { // particle data for constituents tcPDPtr cptr[3]={tcPDPtr(),tcPDPtr(),tcPDPtr()}; bool hasDiquark=0; for(size_t ix=0;ix<min(clu->numComponents(),3);++ix) { cptr[ix]=clu->particle(ix)->dataPtr(); // Assuming diquark masses are ordered with larger id corresponding to larger masses if (DiquarkMatcher::Check(*(cptr[ix]))) { hasDiquark=true; break; } } // 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; // if no heavy quark is found in the cluster, but diquarks are present use // different ClMax and ClPow if ( hasDiquark) { clpow = _clPowDiquark; clmax = _clMaxDiquark; } 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); } // probablistic kinematic threshold else if(_kinematicThresholdChoice == 2) { //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); double Mass = clu->mass()/GeV; double threshold = clu->sumConstituentMasses()/GeV + 2.0 * minmass/GeV; aboveCutoff = ProbablityFunctionTest(Mass,threshold + clmax/GeV); canSplitMinimally = Mass - threshold>ZERO; } return aboveCutoff && canSplitMinimally; } diff --git a/Hadronization/ClusterFissioner.h b/Hadronization/ClusterFissioner.h --- a/Hadronization/ClusterFissioner.h +++ b/Hadronization/ClusterFissioner.h @@ -1,641 +1,646 @@ // -*- C++ -*- // // ClusterFissioner.h is a part of Herwig - A multi-purpose Monte Carlo event generator // Copyright (C) 2002-2019 The Herwig Collaboration // // Herwig is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // #ifndef HERWIG_ClusterFissioner_H #define HERWIG_ClusterFissioner_H #include <ThePEG/Interface/Interfaced.h> #include "CluHadConfig.h" #include "ClusterFissioner.fh" #include "HadronSpectrum.h" namespace Herwig { using namespace ThePEG; //class Cluster; // forward declaration /** \ingroup Hadronization * \class ClusterFissioner * \brief This class handles clusters which are too heavy. * \author Philip Stephens * \author Alberto Ribon * \author Stefan Gieseke * * This class does the job of chopping up either heavy clusters or beam * clusters in two lighter ones. The procedure is repeated recursively until * all of the cluster children have masses below some threshold values. * * For the beam remnant clusters, at the moment what is done is the following. * In the case that the soft underlying event is switched on, the * beam remnant clusters are tagged as not available, * therefore they will not be treated at all during the hadronization. * In the case instead that the soft underlying event is switched off, * then the beam remnant clusters are treated exactly as "normal" clusters, * with the only exception of the mass spectrum used to generate the * cluster children masses. For non-beam clusters, the masses of the cluster * children are draw from a power-like mass distribution; for beam clusters, * according to the value of the flag _IOpRem, either both * children masses are draw from a fast-decreasing exponential mass * distribution (case _IOpRem == 0, or, indendently by * _IOpRem, in the special case that the beam cluster contains two * beam remnants), or one mass from the exponential distribution (corresponding * of the cluster child with the beam remnant) and the other with the usual * power-like distribution (case _IOpRem == 1, which is the * default one, as in Herwig 6.3). * * The reason behind the use of a fast-decreasing exponential distribution * is that to avoid a large transverse energy from the many sequential * fissions that would otherwise occur due to the typical large cluster * mass of beam clusters. Using instead an exponential distribution * the masses of the two cluster children will be very small (order of * GeV). * * The rationale behind the implementation of the splitting of clusters * has been to preserve *all* of the information about such splitting * process. More explicitly a ThePEG::Step class is passed in and the * new clusters are added to the step as the decay products of the * heavy cluster. This approach has the twofold * advantage to provide all of the information that could be needed * (expecially in future developments), without any information loss, * and furthermore it allows a better debugging. * * @see \ref ClusterFissionerInterfaces "The interfaces" * defined for ClusterFissioner. */ class ClusterFissioner: public Interfaced { public: /** @name Standard constructors and destructors. */ //@{ /** * Default constructor. */ ClusterFissioner(); /** * Default destructor. */ virtual ~ClusterFissioner(); //@} /** Splits the clusters which are too heavy. * * Split either heavy clusters or beam clusters recursively until all * children have mass below some threshold. Heavy clusters are those that * satisfy the condition * \f[ M^P > C^P + S^P \f] * where \f$ M \f$ is the clusters mass, \f$ P \f$ is the parameter * ClPow, \f$ C \f$ is the parameter ClMax and \f$ S \f$ is the * sum of the clusters constituent partons. * For beam clusters, they are split only if the soft underlying event * is switched off, otherwise these clusters will be tagged as unavailable * and they will not be treated by the hadronization altogether. * In the case beam clusters will be split, the procedure is exactly * the same as for normal non-beam clusters, with the only exception * of the mass spectrum from which to draw the masses of the two * cluster children (see method drawChildrenMasses for details). */ virtual tPVector fission(ClusterVector & clusters, bool softUEisOn); /** * Return the hadron spectrum */ virtual Ptr<HadronSpectrum>::tptr spectrum() const { return _hadronSpectrum; } public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * Standard Init function used to initialize the interfaces. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * Private and non-existent assignment operator. */ ClusterFissioner & operator=(const ClusterFissioner &) = delete; /** * This method directs the splitting of the heavy clusters * * This method does the splitting of the clusters and all of its cluster * children, if heavy. All of these new children clusters are added to the * collection of clusters. The method works as follows. * Initially the vector contains just the stack of input pointers to the * clusters to be split. Then it will be filled recursively by all * of the cluster's children that are heavy enough to require * to be split. In each loop, the last element of the vector is * considered (only once because it is then removed from the vector). * * \todo is the following still true? * For normal, non-beam clusters, a power-like mass distribution * is used, whereas for beam clusters a fast-decreasing exponential mass * distribution is used instead. This avoids many iterative splitting which * could produce an unphysical large transverse energy from a supposed * soft beam remnant process. */ void cut(stack<ClusterPtr> &, ClusterVector&, tPVector & finalhadrons, bool softUEisOn); public: /** * Definition for easy passing of two particles. */ typedef pair<PPtr,PPtr> PPair; /** * Definition for use in the cut function. */ typedef pair<PPair,PPair> cutType; /** * Splits the input cluster. * * Split the input cluster (which can be either an heavy non-beam * cluster or a beam cluster). The result is two pairs of particles. The * first element of each pair is new cluster/hadron, while the second * element of each pair is the particle drawn from the vacuum to create * the new cluster/hadron. * Notice that this method treats also beam clusters by using a different * mass spectrum used to generate the cluster child masses (see method * drawChildMass). */ //@{ /** * Split two-component cluster */ virtual cutType cutTwo(ClusterPtr &, tPVector & finalhadrons, bool softUEisOn); /** * Split three-component cluster */ virtual cutType cutThree(ClusterPtr &, tPVector & finalhadrons, bool softUEisOn); //@} public: /** * Produces a hadron and returns the flavour drawn from the vacuum. * * This routine produces a new hadron. It * also sets the momentum and vertex to the values given. */ PPair produceHadron(tcPDPtr hadron, tPPtr newPtr, const Lorentz5Momentum &a, const LorentzPoint &b) const; protected: /** * Produces a cluster from the flavours passed in. * * This routine produces a new cluster with the flavours given by ptrQ and newPtr. * The new 5 momentum is a and the parent momentum are c and d. C is for the * ptrQ and d is for the new particle newPtr. rem specifies whether the existing * particle is a beam remnant or not. */ PPair produceCluster(tPPtr ptrQ, tPPtr newPtr, const Lorentz5Momentum &a, const LorentzPoint &b, const Lorentz5Momentum &c, const Lorentz5Momentum &d, const bool rem, tPPtr spect=tPPtr(), bool remSpect=false) const; /** * Returns the new quark-antiquark pair * needed for fission of a heavy cluster. Equal probabilities * are assumed for producing u, d, or s pairs. */ void drawNewFlavourQuarks(PPtr& newPtrPos,PPtr& newPtrNeg) const; /** * Returns the new quark-antiquark pair or diquark - * antidiquark pair needed for fission of a heavy cluster. */ void drawNewFlavourDiquarks(PPtr& newPtrPos,PPtr& newPtrNeg, const ClusterPtr & clu) const; /** * Returns the new quark-antiquark pair * needed for fission of a heavy cluster. Equal probabilities * are assumed for producing u, d, or s pairs. * Extra argument is used when performing strangeness enhancement */ void drawNewFlavourEnhanced(PPtr& newPtrPos,PPtr& newPtrNeg, Energy2 mass2) const; /** * Produces the mass of a child cluster. * * Draw the masses \f$M'\f$ of the the cluster child produced * by the fission of an heavy cluster (of mass M). m1, m2 are the masses * of the constituents of the cluster; m is the mass of the quark extract * from the vacuum (together with its antiparticle). The algorithm produces * the mass of the cluster formed with consituent m1. * Two mass distributions can be used for the child cluster mass: * -# power-like mass distribution ("normal" mass) with power exp * \f[ M' = {\rm rnd}((M-m_1-m_2-m)^P, m^p)^{1/P} + m_1 \f] * where \f$ P \f$ is a parameter of the model and \f$ \rm{rnd} \f$ is * the function: * \f[ \rm{rnd}(a,b) = (1-r)a + r b \f] * and here \f$ r \f$ is a random number [0,1]. * -# fast-decreasing exponential mass distribution ("soft" mass) with * rmin. rmin is given by * \f[ r_{\rm min} = \exp(-b (M - m_1 - m_2 - 2 m)) \f] * where \f$ b \f$ is a parameter of the model. The generated mass is * given by * \f[ M' = m_1 + m - \frac{\log\left( * {\rm rnd}(r_{\rm min}, 1-r_{\rm min})\right)}{b} \f]. * * The choice of which mass distribution should be used for each of the two * cluster children is dictated by the parameter soft. */ Energy drawChildMass(const Energy M, const Energy m1, const Energy m2, const Energy m, const double exp, const bool soft) const; /** * Determine the positions of the two children clusters. * * This routine generates the momentum of the decay products. It also * generates the momentum in the lab frame of the partons drawn out of * the vacuum. */ void calculatePositions(const Lorentz5Momentum &pClu, const LorentzPoint & positionClu, const Lorentz5Momentum & pClu1, const Lorentz5Momentum & pClu2, LorentzPoint & positionClu1, LorentzPoint & positionClu2 ) const; protected: /** * Dimension used to calculate phase space weights */ double dim() const {return _dim;} /** * Access to soft-cluster parameter */ Energy btClM() const {return _btClM;} /** * Function that returns either the cluster mass or the lambda measure */ Energy2 clustermass(const ClusterPtr & cluster) const; /** * Draw a new flavour for the given cluster; currently defaults to * the default model */ virtual void drawNewFlavour(PPtr& newPtr1, PPtr& newPtr2, const ClusterPtr & cluster) const { if (_enhanceSProb == 0){ if (_diquarkClusterFission>=0) drawNewFlavourDiquarks(newPtr1,newPtr2,cluster); else drawNewFlavourQuarks(newPtr1,newPtr2); } else { drawNewFlavourEnhanced(newPtr1,newPtr2,clustermass(cluster)); } } /** * Calculate the masses and possibly kinematics of the cluster * fission at hand; if claculateKineamtics is perfomring non-trivial * steps kinematics claulcated here will be overriden. Currentl;y resorts to the default * @return the potentially non-trivial distribution weight=f(M1,M2) * On Failure we return 0 */ virtual double drawNewMasses(const Energy Mc, const bool soft1, const bool soft2, Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, tcPPtr ptrQ1, const Lorentz5Momentum& pQ1, tcPPtr, const Lorentz5Momentum& pQone, tcPPtr, const Lorentz5Momentum& pQtwo, tcPPtr ptrQ2, const Lorentz5Momentum& pQ2) const; /** * Calculate the masses and possibly kinematics of the cluster * fission at hand; if claculateKineamtics is perfomring non-trivial * steps kinematics claulcated here will be overriden. Currentl;y resorts to the default */ // virtual pair<Energy,Energy> drawNewMasses(Energy Mc, bool soft1, bool soft2, // Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, // tcPPtr ptrQ1, const Lorentz5Momentum& pQ1, // tcPPtr, const Lorentz5Momentum& pQone, // tcPPtr, const Lorentz5Momentum& pQtwo, // tcPPtr ptrQ2, const Lorentz5Momentum& pQ2) const { // pair<Energy,Energy> result; // // power for splitting // double exp1 = !spectrum()->isExotic(ptrQ1->dataPtr()) ? _pSplitLight : _pSplitExotic; // double exp2 = !spectrum()->isExotic(ptrQ2->dataPtr()) ? _pSplitLight : _pSplitExotic; // for ( const long& id : spectrum()->heavyHadronizingQuarks() ) { // assert(_pSplitHeavy.find(id) != _pSplitHeavy.end()); // if ( spectrum()->hasHeavy(id,ptrQ1->dataPtr()) ) exp1 = _pSplitHeavy.find(id)->second; // if ( spectrum()->hasHeavy(id,ptrQ2->dataPtr()) ) exp2 = _pSplitHeavy.find(id)->second; // } // result.first = drawChildMass(Mc,pQ1.mass(),pQ2.mass(),pQone.mass(),exp1,soft1); // result.second = drawChildMass(Mc,pQ2.mass(),pQ1.mass(),pQtwo.mass(),exp2,soft2); // pClu1.setMass(result.first); // pClu2.setMass(result.second); // return result; // } /** * Calculate the final kinematics of a heavy cluster decay C->C1 + * C2, if not already performed by drawNewMasses */ virtual void 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; protected: /** @name Access members for child classes. */ //@{ /** * Access to the hadron selector */ HadronSpectrumPtr hadronSpectrum() const {return _hadronSpectrum;} + /** + * Access for fission Pwts + */ + const map<long,double> fissionPwt() const { return _fissionPwt;} + //@} protected: /** @name Standard Interfaced functions. */ //@{ /** * Initialize this object after the setup phase before saving an * EventGenerator to disk. * @throws InitException if object could not be initialized properly. */ virtual void doinit(); //@} private: /** * Smooth probability for dynamic threshold cuts: * @scale the current scale, e.g. the mass of the cluster, * @threshold the physical threshold, */ bool ProbablityFunction(double scale, double threshold); bool ProbablityFunctionTest(double Mass, double threshold); /** * Check if a cluster is heavy enough to split again */ bool isHeavy(tcClusterPtr ); /** * Check if a cluster is heavy enough to be at least kinematically able to split */ bool canSplitMinimally(tcClusterPtr, Energy); /** * Check if can't make a hadron from the partons */ inline bool cantMakeHadron(tcPPtr p1, tcPPtr p2) { return ! spectrum()->canBeHadron(p1->dataPtr(), p2->dataPtr()); } /** * Flat PhaseSpace weight for ClusterFission */ double weightFlatPhaseSpace(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, tcPPtr pQ, tcPPtr pQ1, tcPPtr pQ2) const { switch (_phaseSpaceWeights) { case 1: return weightPhaseSpaceConstituentMasses(Mc, Mc1, Mc2, m, m1, m2, 0.0); case 2: return weightFlatPhaseSpaceHadronMasses(Mc, Mc1, Mc2, pQ, pQ1, pQ2); case 3: return weightFlatPhaseSpaceNoConstituentMasses(Mc, Mc1, Mc2); default: assert(false); } }; /** * PhaseSpace weight for ClusterFission using constituent masses */ double weightPhaseSpaceConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, const double power=0.0) const; /** * Flat PhaseSpace weight for ClusterFission using lightest hadron masses */ double weightFlatPhaseSpaceHadronMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, tcPPtr pQ, tcPPtr pQ1, tcPPtr pQ2) const; double weightFlatPhaseSpaceNoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2) const; /** * Calculate a veto for the phase space weight */ bool phaseSpaceVeto(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, tcPPtr pQ1=tcPPtr(), tcPPtr pQ2=tcPPtr(), tcPPtr pQ=tcPPtr(), const double power = 0.0) const; bool phaseSpaceVetoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, const double power = 0.0) const; bool phaseSpaceVetoNoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2) const; bool phaseSpaceVetoHadronPairs(const Energy Mc, const Energy Mc1, const Energy Mc2, tcPPtr pQ1, tcPPtr pQ2, tcPPtr pQconst) const; /** * A pointer to a Herwig::HadronSpectrum object for generating hadrons. */ HadronSpectrumPtr _hadronSpectrum; /** * @name The Cluster max mass,dependant on which quarks are involved, used to determine when * fission will occur. */ //@{ Energy _clMaxLight; Energy _clMaxDiquark; map<long,Energy> _clMaxHeavy; Energy _clMaxExotic; //@} /** * @name The power used to determine when cluster fission will occur. */ //@{ double _clPowLight; double _clPowDiquark; map<long,double> _clPowHeavy; double _clPowExotic; //@} /** * @name The power, dependant on whic quarks are involved, used in the cluster mass generation. */ //@{ double _pSplitLight; map<long,double> _pSplitHeavy; double _pSplitExotic; /** * Weights for alternative cluster fission */ map<long,double> _fissionPwt; /** * Include phase space weights */ int _phaseSpaceWeights; /** * Dimensionality of phase space weight */ double _dim; /** * Flag used to determine between normal cluster fission and alternative cluster fission */ int _fissionCluster; /** * Flag to choose static or dynamic kinematic thresholds in cluster splittings */ int _kinematicThresholdChoice; /** * Pwt weight for drawing diquark */ double _pwtDIquark; /** * allow clusters to fission to 1 (or 2) diquark clusters or not */ int _diquarkClusterFission; //@} /** * Parameter used (2/b) for the beam cluster mass generation. * Currently hard coded value. */ Energy _btClM; /** * Flag used to determine what distributions to use for the cluster masses. */ int _iopRem; /** * The string constant */ Tension _kappa; /** * Flag that switches between no strangeness enhancement, scaling enhancement, * and exponential enhancement (in numerical order) */ int _enhanceSProb; /** * Parameter that governs the strangeness enhancement scaling */ Energy _m0Fission; /** * Flag that switches between mass measures used in strangeness enhancement: * cluster mass, or the lambda measure - ( m_{clu}^2 - (m_q + m_{qbar})^2 ) */ int _massMeasure; /** * Constant variable which stops the scale from being to large, and not worth * calculating */ const double _maxScale = 20.; /** * Power factor in ClausterFissioner bell probablity function */ double _probPowFactor; /** * Shifts from the center in ClausterFissioner bell probablity function */ double _probShift; /** * Shifts from the kinetic threshold in ClausterFissioner */ Energy2 _kinThresholdShift; /** * Flag for strict diquark selection according to kinematics */ int _strictDiquarkKinematics; /** * Use Covariant boost in SoftClusterFissioner */ bool _covariantBoost; /** * Power for MassPreSampler = PowerLaw */ double _powerLawPower; int _writeOut; /* * flag for allowing strange Diquarks to be produced during * Cluster Fission * */ unsigned int _hadronizingStrangeDiquarks; }; } #endif /* HERWIG_ClusterFissioner_H */ diff --git a/Hadronization/SoftClusterFissioner.cc b/Hadronization/SoftClusterFissioner.cc --- a/Hadronization/SoftClusterFissioner.cc +++ b/Hadronization/SoftClusterFissioner.cc @@ -1,3683 +1,3074 @@ // -*- C++ -*- // // SoftClusterFissioner.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 SoftClusterFissioner class. // #include "SoftClusterFissioner.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 "Herwig/Utilities/AlphaS.h" #include <boost/numeric/ublas/matrix.hpp> #include <boost/numeric/ublas/io.hpp> #include <boost/numeric/ublas/lu.hpp> #include <cassert> #include <vector> using namespace Herwig; -DescribeClass<SoftClusterFissioner,Interfaced> +DescribeClass<SoftClusterFissioner,ClusterFissioner> describeSoftClusterFissioner("Herwig::SoftClusterFissioner","Herwig.so"); // Initialize counters unsigned int SoftClusterFissioner::_counterMaxLoopViolations=0; unsigned int SoftClusterFissioner::_counterPaccGreater1=0; unsigned int SoftClusterFissioner::_counterFissionMatrixElement=0; SoftClusterFissioner::SoftClusterFissioner() : - _clMaxLight(3.35*GeV), - _clMaxDiquark(3.35*GeV), - _clMaxExotic(3.35*GeV), - _clPowLight(2.0), - _clPowDiquark(2.0), - _clPowExotic(2.0), - _pSplitLight(1.0), - _pSplitExotic(1.0), - _phaseSpaceWeights(0), - _dim(4), - _fissionCluster(0), - _kinematicThresholdChoice(0), - _pwtDIquark(0.0), - _diquarkClusterFission(0), - _btClM(1.0*GeV), - _iopRem(1), - _kappa(1.0e15*GeV/meter), - _enhanceSProb(0), - _m0Fission(2.*GeV), - _massMeasure(0), - _probPowFactor(4.0), - _probShift(0.0), - _kinThresholdShift(1.0*sqr(GeV)), - _strictDiquarkKinematics(0), - _covariantBoost(false), _allowHadronFinalStates(2), _massSampler(0), _phaseSpaceSamplerCluster(0), _phaseSpaceSamplerConstituent(0), _matrixElement(0), _fissionApproach(0), _powerLawPower(0.0), _maxLoopFissionMatrixElement(5000000), _safetyFactorMatrixElement(10.0), _epsilonIR(1.0), _failModeMaxLoopMatrixElement(0), _writeOut(0), _hadronizingStrangeDiquarks(2) { } SoftClusterFissioner::~SoftClusterFissioner(){ if (_counterMaxLoopViolations){ std::cout << "WARNING: There have been " << _counterMaxLoopViolations << "/" << _counterFissionMatrixElement << " Maximum tries violations during the Simulation! (" << 100.0*double(_counterMaxLoopViolations)/double(_counterFissionMatrixElement) << " %)\n" << "You may want to reduce SoftClusterFissioner:SafetyFactorOverEst (=>potentially Pacc>=1) " << "and/or increase SoftClusterFissioner:MaxLoopMatrixElement (=>slower performance)\n"; } if (_counterPaccGreater1){ std::cout << "WARNING: There have been " << _counterPaccGreater1 << "/" << _counterFissionMatrixElement << " Pacc>=1 exceptions during the Simulation! (" << 100.0*double(_counterPaccGreater1)/double(_counterFissionMatrixElement) << " %)\n" << "You may want to increase SoftClusterFissioner:SafetyFactorOverEst to reduce this " << "\nNOTE: look at the log file and look for the larges Pacc accepted and multiply " << "\n SoftClusterFissioner:SafetyFactorOverEst by this factor"; } } IBPtr SoftClusterFissioner::clone() const { return new_ptr(*this); } IBPtr SoftClusterFissioner::fullclone() const { return new_ptr(*this); } void SoftClusterFissioner::persistentOutput(PersistentOStream & os) const { - os << ounit(_clMaxLight,GeV) << ounit(_clMaxHeavy,GeV) << ounit(_clMaxDiquark,GeV) << ounit(_clMaxExotic,GeV) - << _clPowLight << _clPowHeavy << _clPowDiquark << _clPowExotic - << _pSplitLight << _pSplitHeavy << _pSplitExotic - << _fissionCluster << _fissionPwt - << _pwtDIquark - << _diquarkClusterFission - << ounit(_btClM,GeV) - << _iopRem << ounit(_kappa, GeV/meter) - << _enhanceSProb << ounit(_m0Fission,GeV) << _massMeasure - << _dim << _phaseSpaceWeights - << _hadronSpectrum << _kinematicThresholdChoice - << _probPowFactor << _probShift << ounit(_kinThresholdShift,sqr(GeV)) - << _strictDiquarkKinematics - << _covariantBoost - << _allowHadronFinalStates - << _massSampler + os << _massSampler << _phaseSpaceSamplerCluster << _phaseSpaceSamplerConstituent << _matrixElement << _fissionApproach << _powerLawPower << _maxLoopFissionMatrixElement << _safetyFactorMatrixElement << _epsilonIR << _failModeMaxLoopMatrixElement << _writeOut - << _hadronizingStrangeDiquarks ; } - void SoftClusterFissioner::persistentInput(PersistentIStream & is, int) { - is >> iunit(_clMaxLight,GeV) >> iunit(_clMaxHeavy,GeV) >> iunit(_clMaxDiquark,GeV) >> iunit(_clMaxExotic,GeV) - >> _clPowLight >> _clPowHeavy >> _clPowDiquark >> _clPowExotic - >> _pSplitLight >> _pSplitHeavy >> _pSplitExotic - >> _fissionCluster >> _fissionPwt - >> _pwtDIquark - >> _diquarkClusterFission - >> iunit(_btClM,GeV) - >> _iopRem >> iunit(_kappa, GeV/meter) - >> _enhanceSProb >> iunit(_m0Fission,GeV) >> _massMeasure - >> _dim >> _phaseSpaceWeights - >> _hadronSpectrum >> _kinematicThresholdChoice - >> _probPowFactor >> _probShift >> iunit(_kinThresholdShift,sqr(GeV)) - >> _strictDiquarkKinematics - >> _covariantBoost - >> _allowHadronFinalStates - >> _massSampler + is >> _massSampler >> _phaseSpaceSamplerCluster >> _phaseSpaceSamplerConstituent >> _matrixElement >> _fissionApproach >> _powerLawPower >> _maxLoopFissionMatrixElement >> _safetyFactorMatrixElement >> _epsilonIR >> _failModeMaxLoopMatrixElement >> _writeOut - >> _hadronizingStrangeDiquarks ; } /* namespace{ void printV(Lorentz5Momentum p) { std::cout << "("<<p.e()/GeV<<"|"<<p.vect().x()/GeV<<","<<p.vect().y()/GeV<<","<<p.vect().z()/GeV<<") Mass = "<<p.mass()/GeV<<" m = "<<p.m()/GeV<<"\n"; } } */ void SoftClusterFissioner::doinit() { - Interfaced::doinit(); - if (_writeOut){ - std::ofstream out("data_CluFis.dat", std::ios::out); - out.close(); - } - // Energy m1=1.2*GeV; - // Energy m2=2.3*GeV; - // std::cout <<std::setprecision(18)<< sqr(m1+m2)/GeV2 << std::endl; - // std::cout <<std::setprecision(18)<< ((m1+m2)*(m1+m2))/GeV2 << std::endl; - // std::cout <<std::setprecision(18)<< ((sqr(m1+m2))-((m1+m2)*(m1+m2)))/GeV2 << std::endl; - /* TESTs for Two particle boost - * TODO - * */ - /* - Energy M,M1,M2; - Energy m,m1,m2; - - M=20.1*GeV; - M1=10.1*GeV; - M2=5.1*GeV; - m2=0.325*GeV; - m1=0.625*GeV; - m=1.1*GeV; - - // double cosTheta1=0.3; - // double phi1=0.7*3.14; - // double cosTheta2=-0.4; - // double phi2=-0.2*3.14; - - // Axis LABz(0,0,1); - // Energy Plab=80.1*GeV; - - // Lorentz5Momentum P(M,Plab*LABz); - // Energy PCOM=P.boost(-P.boostVector()).vect().mag(); - - - double cosTheta=0.0; - double phi=0.7*3.14; - - Energy P1=1.2*GeV; - Energy P2=10.2*GeV; - Energy Q=1.2*GeV; - Lorentz5Momentum p1(m1,P1*Axis(cosTheta*cos(phi),cosTheta*sin(phi),sin(acos(cosTheta)))); - Lorentz5Momentum p2(m1*2,Momentum3(-p1.vect().x(),-p1.vect().y(),P2)); - Lorentz5Momentum qOld(m,Q*sampleDirectionIsotropic()); - Lorentz5Momentum qNew=qOld; - Lorentz5Momentum pC=p1+p2; - printV(p1); - printV(p2); - printV(p1+p2); - printV(pC); - printV(qOld); - printV(qNew); - // p1.boost(-pC.boostVector()); - // p2.boost(-pC.boostVector()); - // q.boost(-pC.boostVector()); - // printV(q); - std::vector<Lorentz5Momentum *> v; - v.push_back(&qNew); - Kinematics::BoostIntoTwoParticleFrame(pC.m(),p1,p2,v); - // Kinematics::twoBodDecay(pC,); - printV(qOld); - printV(qNew); - // std::cout << p1 << std::endl; - // std::cout << p2 << std::endl; - // std::cout << p1+p2 << std::endl; - // std::cout << Q << std::endl; - - - - -*/ - - - - - - - - + ClusterFissioner::doinit(); // TODO: Some User warnings/errors but not complete list if (_matrixElement!=0 && _fissionApproach==0) generator()->logWarning( Exception( "For non-trivial MatrixElement you need to enable FissionApproach=New or Hybrid\n", Exception::warning)); - 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 ( const long& id : spectrum()->lightHadronizingDiquarks() ) { - // if ( - // _clPowDiquark.find(id) == _clPowDiquark.end() || - // _clMaxDiquark.find(id) == _clMaxDiquark.end() ) - // throw InitException() << "not all parameters have been set for diquarks 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 better solution for this magic number alternative - _fissionPwt[1103] = _pwtDIquark * pwtDquark * pwtDquark; - _fissionPwt[2101] = 0.5 * _pwtDIquark * pwtUquark * pwtDquark; - _fissionPwt[2203] = _pwtDIquark * pwtUquark * pwtUquark; - if (_hadronizingStrangeDiquarks>0) { - _fissionPwt[3101] = 0.5 * _pwtDIquark * pwtSquark * pwtDquark; - _fissionPwt[3201] = 0.5 * _pwtDIquark * pwtSquark * pwtUquark; - if (_hadronizingStrangeDiquarks==2) { - _fissionPwt[3303] = _pwtDIquark* pwtSquark * pwtSquark; - } - } } void SoftClusterFissioner::Init() { static ClassDocumentation<SoftClusterFissioner> documentation ("Class responsibles for chopping up the clusters"); - static Reference<SoftClusterFissioner,HadronSpectrum> interfaceHadronSpectrum - ("HadronSpectrum", - "Set the Hadron spectrum for this cluster fissioner.", - &SoftClusterFissioner::_hadronSpectrum, false, false, true, false); - - // ClMax for light, Bottom, Charm and exotic (e.g. Susy) quarks - static Parameter<SoftClusterFissioner,Energy> - interfaceClMaxLight ("ClMaxLight","cluster max mass for light quarks (unit [GeV])", - &SoftClusterFissioner::_clMaxLight, GeV, 3.35*GeV, ZERO, 100.0*GeV, - false,false,false); - static Parameter<SoftClusterFissioner,Energy> - interfaceClMaxDiquark ("ClMaxDiquark","cluster max mass for light hadronizing diquarks (unit [GeV])", - &SoftClusterFissioner::_clMaxDiquark, GeV, 3.35*GeV, ZERO, 100.0*GeV, - false,false,false); - static ParMap<SoftClusterFissioner,Energy> interfaceClMaxHeavy - ("ClMaxHeavy", - "ClMax for heavy quarks", - &SoftClusterFissioner::_clMaxHeavy, GeV, -1, 3.35*GeV, ZERO, 100.0*GeV, - false, false, Interface::upperlim); - // static ParMap<SoftClusterFissioner,Energy> interfaceClMaxDiquark - // ("ClMaxDiquark", - // "ClMax for light hadronizing diquarks", - // &SoftClusterFissioner::_clMaxDiquark, GeV, -1, 3.35*GeV, ZERO, 100.0*GeV, - // false, false, Interface::upperlim); - static Parameter<SoftClusterFissioner,Energy> - interfaceClMaxExotic ("ClMaxExotic","cluster max mass for exotic quarks (unit [GeV])", - &SoftClusterFissioner::_clMaxExotic, GeV, 3.35*GeV, ZERO, 100.0*GeV, - false,false,false); - - // ClPow for light, Bottom, Charm and exotic (e.g. Susy) quarks - static Parameter<SoftClusterFissioner,double> - interfaceClPowLight ("ClPowLight","cluster mass exponent for light quarks", - &SoftClusterFissioner::_clPowLight, 0, 2.0, 0.0, 10.0,false,false,false); - static ParMap<SoftClusterFissioner,double> interfaceClPowHeavy - ("ClPowHeavy", - "ClPow for heavy quarks", - &SoftClusterFissioner::_clPowHeavy, -1, 1.0, 0.0, 10.0, - false, false, Interface::upperlim); - // static ParMap<SoftClusterFissioner,double> interfaceClPowDiquark - // ("ClPowDiquark", - // "ClPow for light hadronizing diquarks", - // &SoftClusterFissioner::_clPowDiquark, -1, 1.0, 0.0, 10.0, - // false, false, Interface::upperlim); - static Parameter<SoftClusterFissioner,double> - interfaceClPowDiquark ("ClPowDiquark","cluster mass exponent for light hadronizing diquarks", - &SoftClusterFissioner::_clPowDiquark, 0, 2.0, 0.0, 10.0,false,false,false); - static Parameter<SoftClusterFissioner,double> - interfaceClPowExotic ("ClPowExotic","cluster mass exponent for exotic quarks", - &SoftClusterFissioner::_clPowExotic, 0, 2.0, 0.0, 10.0,false,false,false); - - // PSplit for light, Bottom, Charm and exotic (e.g. Susy) quarks - static Parameter<SoftClusterFissioner,double> - interfacePSplitLight ("PSplitLight","cluster mass splitting param for light quarks", - &SoftClusterFissioner::_pSplitLight, 0, 1.0, 0.0, 10.0,false,false,false); - static ParMap<SoftClusterFissioner,double> interfacePSplitHeavy - ("PSplitHeavy", - "PSplit for heavy quarks", - &SoftClusterFissioner::_pSplitHeavy, -1, 1.0, 0.0, 10.0, - false, false, Interface::upperlim); - static Parameter<SoftClusterFissioner,double> - interfacePSplitExotic ("PSplitExotic","cluster mass splitting param for exotic quarks", - &SoftClusterFissioner::_pSplitExotic, 0, 1.0, 0.0, 10.0,false,false,false); - - - static Switch<SoftClusterFissioner,int> interfaceFission - ("Fission", - "Option for different Fission options", - &SoftClusterFissioner::_fissionCluster, 1, false, false); - static SwitchOption interfaceFissionDefault - (interfaceFission, - "Default", - "Normal cluster fission which depends on the hadron spectrum class.", - 0); - static SwitchOption interfaceFissionNew - (interfaceFission, - "New", - "Alternative cluster fission which does not depend on the hadron spectrum class", - 1); - static SwitchOption interfaceFissionNewDiquarkSuppression - (interfaceFission, - "NewDiquarkSuppression", - "Alternative cluster fission which does not depend on the hadron spectrum class" - " and includes a suppression of AlphaS^2(Mc) for Diquark Production during " - "Cluster Fission", - -1); - - static Switch<SoftClusterFissioner,int> interfaceFissionApproach ("FissionApproach", "Option for different Cluster Fission approaches", &SoftClusterFissioner::_fissionApproach, 0, false, false); static SwitchOption interfaceFissionApproachDefault (interfaceFissionApproach, "Default", "Default Herwig-7.3.0 cluster fission without restructuring", 0); static SwitchOption interfaceFissionApproachNew (interfaceFissionApproach, "New", "New cluster fission which allows to choose MassSampler" ", PhaseSpaceSampler and MatrixElement", 1); static SwitchOption interfaceFissionApproachHybrid (interfaceFissionApproach, "Hybrid", "New cluster fission which allows to choose MassSampler" ", PhaseSpaceSampler and MatrixElement, but uses Default" " Approach for BeamClusters", 2); static SwitchOption interfaceFissionApproachPreservePert (interfaceFissionApproach, "PreservePert", "New cluster fission which allows to choose MassSampler" ", PhaseSpaceSampler and MatrixElement, but uses Default" " Approach for BeamClusters", 3); static SwitchOption interfaceFissionApproachPreserveNonPert (interfaceFissionApproach, "PreserveNonPert", "New cluster fission which allows to choose MassSampler" ", PhaseSpaceSampler and MatrixElement, but uses Default" " Approach for BeamClusters", 4); static SwitchOption interfaceFissionApproachPreserveFirstPert (interfaceFissionApproach, "PreserveFirstPert", "New cluster fission which allows to choose MassSampler" ", PhaseSpaceSampler and MatrixElement, but uses Default" " Approach for BeamClusters", 5); // Switch C->H1,C2 C->H1,H2 on or off static Switch<SoftClusterFissioner,int> interfaceAllowHadronFinalStates ("AllowHadronFinalStates", "Option for allowing hadron final states of cluster fission", &SoftClusterFissioner::_allowHadronFinalStates, 0, false, false); static SwitchOption interfaceAllowHadronFinalStatesNone (interfaceAllowHadronFinalStates, "None", "Option for disabling hadron final states of cluster fission", 0); static SwitchOption interfaceAllowHadronFinalStatesSemiHadronicOnly (interfaceAllowHadronFinalStates, "SemiHadronicOnly", "Option for allowing hadron final states of cluster fission of type C->H1,C2 " "(NOT YET USABLE WITH MatrixElement only use option None)", 1); static SwitchOption interfaceAllowHadronFinalStatesAll (interfaceAllowHadronFinalStates, "All", "Option for allowing hadron final states of cluster fission " "of type C->H1,C2 or C->H1,H2 " "(NOT YET USABLE WITH MatrixElement only use option None)", 2); // Mass Sampler Switch static Switch<SoftClusterFissioner,int> interfaceMassSampler ("MassSampler", "Option for different Mass sampling options", &SoftClusterFissioner::_massSampler, 0, false, false); static SwitchOption interfaceMassSamplerDefault (interfaceMassSampler, "Default", "Choose H7.2.3 default mass sampling using PSplitX", 0); static SwitchOption interfaceMassSamplerUniform (interfaceMassSampler, "Uniform", "Choose Uniform Mass sampling in M1,M2 space", 1); static SwitchOption interfaceMassSamplerFlatPhaseSpace (interfaceMassSampler, "FlatPhaseSpace", "Choose Flat Phase Space sampling of Mass in M1,M2 space (Recommended)", 2); static SwitchOption interfaceMassSamplerPhaseSpacePowerLaw (interfaceMassSampler, "PhaseSpacePowerLaw", "Choose Phase Space times a power law sampling of Mass in M1,M2 space.", 3); static Parameter<SoftClusterFissioner,double> interfaceMassPowerLaw("MassPowerLaw", "power of mass power law modulation of FlatPhaseSpace", &SoftClusterFissioner::_powerLawPower, 0.0, -10.0, 0.0, 10.0, false,false,false); // Phase Space Sampler Switch static Switch<SoftClusterFissioner,int> interfacePhaseSpaceSamplerCluster ("PhaseSpaceSamplerCluster", "Option for different phase space sampling options", &SoftClusterFissioner::_phaseSpaceSamplerCluster, 0, false, false); static SwitchOption interfacePhaseSpaceSamplerClusterAligned (interfacePhaseSpaceSamplerCluster, "Aligned", "Draw the momentum of child clusters to be aligned" " the relative momentum of the mother cluster", 0); static SwitchOption interfacePhaseSpaceSamplerClusterIsotropic (interfacePhaseSpaceSamplerCluster, "Isotropic", "Draw the momentum of child clusters to be isotropic" " the relative momentum of the mother cluster", 1); static SwitchOption interfacePhaseSpaceSamplerClusterTchannel (interfacePhaseSpaceSamplerCluster, "Tchannel", "Draw the momentum of child clusters to be t-channel like" " the relative momentum of the mother cluster" " i.e. cosTheta ~ 1/(1+A-cosTheta)^2", 2); static Switch<SoftClusterFissioner,int> interfacePhaseSpaceSamplerConstituents ("PhaseSpaceSamplerConstituents", "Option for different phase space sampling options", &SoftClusterFissioner::_phaseSpaceSamplerConstituent, 0, false, false); static SwitchOption interfacePhaseSpaceSamplerConstituentsAligned (interfacePhaseSpaceSamplerConstituents, "Aligned", "Draw the momentum of the constituents of the child clusters " "to be aligned relative to the direction of the child cluster", 0); static SwitchOption interfacePhaseSpaceSamplerConstituentsIsotropic (interfacePhaseSpaceSamplerConstituents, "Isotropic", "Draw the momentum of the constituents of the child clusters " "to be isotropic relative to the direction of the child cluster", 1); static SwitchOption interfacePhaseSpaceSamplerConstituentsTchannel (interfacePhaseSpaceSamplerConstituents, "Exponential", "Draw the momentum of the constituents of the child clusters " "to be exponential relative to the direction of the child cluster" " i.e. cosTheta ~ exp(lam*(cosTheta-1)", 2); // Matrix Element Choice Switch static Switch<SoftClusterFissioner,int> interfaceMatrixElement ("MatrixElement", "Option for different Matrix Element options", &SoftClusterFissioner::_matrixElement, 0, false, false); static SwitchOption interfaceMatrixElementDefault (interfaceMatrixElement, "Default", "Choose trivial matrix element i.e. whatever comes from the mass and " "phase space sampling", 0); static SwitchOption interfaceMatrixElementSoftQQbarFinalFinal (interfaceMatrixElement, "SoftQQbarFinalFinal", "Choose Soft p1,p2->q1,q2,g*->q1,q2,q,qbar matrix element" "NOTE: Here the matrix element depends on qi.q(bar)", 1); static SwitchOption interfaceMatrixElementSoftQQbarInitialFinal (interfaceMatrixElement, "SoftQQbarInitialFinal", "Choose Soft p1,p2->q1,q2,g*->q1,q2,q,qbar matrix element " "NOTE: Here the matrix element depends on pi.q(bar)", 2); static SwitchOption interfaceMatrixElementTest (interfaceMatrixElement, "Test", "Choose Soft p1,p2->q1,q2,g*->q1,q2,q,qbar matrix element " "NOTE: Here the matrix element depends on pi.q(bar)", 4); static SwitchOption interfaceMatrixElementSoftQQbarInitialFinalTchannel (interfaceMatrixElement, "SoftQQbarInitialFinalTchannel", "Choose Soft p1,p2->q1,q2,g*->q1,q2,q,qbar matrix element " "NOTE: Here the matrix element depends on pi.q(bar)", 5); // Technical Max loop parameter for New ClusterFission approach and Matrix Element // Rejection sampling static Parameter<SoftClusterFissioner,int> interfaceMaxLoopMatrixElement ("MaxLoopMatrixElement", "Technical Parameter for how many tries are allowed to sample the " "Cluster Fission matrix element before reverting to fissioning " "using the default Fission Aproach", &SoftClusterFissioner::_maxLoopFissionMatrixElement, 5000000, 100, 1e8, false, false, Interface::limited); static Switch<SoftClusterFissioner,int> interfaceFailModeMaxLoopMatrixElement ("FailModeMaxLoopMatrixElement", "How to fail if we try more often than MaxLoopMatrixElement", &SoftClusterFissioner::_failModeMaxLoopMatrixElement, 0, false, false); static SwitchOption interfaceDiquarkClusterFissionOldFission (interfaceFailModeMaxLoopMatrixElement, "OldFission", "Use old Cluster Fission Aproach if we try more often than MaxLoopMatrixElement", 0); static SwitchOption interfaceDiquarkClusterFissionRejectEvent (interfaceFailModeMaxLoopMatrixElement, "RejectEvent", "Reject Event if we try more often than MaxLoopMatrixElement", 1); static Switch<SoftClusterFissioner,int> interfaceDiquarkClusterFission ("DiquarkClusterFission", "Allow clusters to fission to 1 or 2 diquark Clusters or Turn off diquark fission completely", &SoftClusterFissioner::_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 SwitchOption interfaceDiquarkClusterFissionOff (interfaceDiquarkClusterFission, "Off", "Don't allow clusters fission to draw diquarks ", -1); - static ParMap<SoftClusterFissioner,double> interfaceFissionPwt - ("FissionPwt", - "The weights for quarks in the fission process.", - &SoftClusterFissioner::_fissionPwt, -1, 1.0, 0.0, 10.0, - false, false, Interface::upperlim); - - static Switch<SoftClusterFissioner,int> interfaceRemnantOption - ("RemnantOption", - "Option for the treatment of remnant clusters", - &SoftClusterFissioner::_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<SoftClusterFissioner,Energy> interfaceBTCLM - ("SoftClusterFactor", - "Parameter for the mass spectrum of remnant clusters", - &SoftClusterFissioner::_btClM, GeV, 1.*GeV, 0.1*GeV, 10.0*GeV, - false, false, Interface::limited); - - - static Parameter<SoftClusterFissioner,Tension> interfaceStringTension - ("StringTension", - "String tension used in vertex displacement calculation", - &SoftClusterFissioner::_kappa, GeV/meter, - 1.0e15*GeV/meter, ZERO, ZERO, - false, false, Interface::lowerlim); - - static Switch<SoftClusterFissioner,int> interfaceEnhanceSProb - ("EnhanceSProb", - "Option for enhancing strangeness", - &SoftClusterFissioner::_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<SoftClusterFissioner,int> interfaceMassMeasure - ("MassMeasure", - "Option to use different mass measures", - &SoftClusterFissioner::_massMeasure,0,false,false); - static SwitchOption interfaceMassMeasureMass - (interfaceMassMeasure, - "Mass", - "Mass Measure", - 0); - static SwitchOption interfaceMassMeasureLambda - (interfaceMassMeasure, - "Lambda", - "Lambda Measure", - 1); - - static Parameter<SoftClusterFissioner,Energy> interfaceFissionMassScale - ("FissionMassScale", - "Cluster fission mass scale", - &SoftClusterFissioner::_m0Fission, GeV, 2.0*GeV, 0.1*GeV, 50.*GeV, - false, false, Interface::limited); - - static Parameter<SoftClusterFissioner,double> interfaceProbPowFactor - ("ProbablityPowerFactor", - "Power factor in ClausterFissioner bell probablity function", - &SoftClusterFissioner::_probPowFactor, 2.0, 0.001, 20.0, - false, false, Interface::limited); - - static Parameter<SoftClusterFissioner,double> interfaceProbShift - ("ProbablityShift", - "Shifts from the center in ClausterFissioner bell probablity function", - &SoftClusterFissioner::_probShift, 0.0, -10.0, 10.0, - false, false, Interface::limited); - - static Parameter<SoftClusterFissioner,Energy2> interfaceKineticThresholdShift - ("KineticThresholdShift", - "Shifts from the kinetic threshold in SoftClusterFissioner", - &SoftClusterFissioner::_kinThresholdShift, sqr(GeV), 0.*sqr(GeV), -10.0*sqr(GeV), 10.0*sqr(GeV), - false, false, Interface::limited); - - static Switch<SoftClusterFissioner,int> interfaceKinematicThreshold - ("KinematicThreshold", - "Option for using static or dynamic kinematic thresholds in cluster splittings", - &SoftClusterFissioner::_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 SwitchOption interfaceKinematicThresholdProbabilistic - (interfaceKinematicThreshold, - "Probabilistic", - "Set Probabilistic kinematic threshold for cluster fission.", - 2); - - - - static Switch<SoftClusterFissioner,bool> interfaceCovariantBoost - ("CovariantBoost", - "Use single Covariant Boost for Cluster Fission", - &SoftClusterFissioner::_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 Switch<SoftClusterFissioner,int> interfaceStrictDiquarkKinematics - ("StrictDiquarkKinematics", - "Option for selecting different selection criterions of diquarks for ClusterFission", - &SoftClusterFissioner::_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<SoftClusterFissioner,double> interfacePwtDIquark - ("PwtDIquark", - "specific probability for choosing a d diquark", - &SoftClusterFissioner::_pwtDIquark, 0.0, 0.0, 10.0, - false, false, Interface::limited); - - static Switch<SoftClusterFissioner,int> interfacePhaseSpaceWeights - ("PhaseSpaceWeights", - "Include phase space weights.", - &SoftClusterFissioner::_phaseSpaceWeights, 0, false, false); - static SwitchOption interfacePhaseSpaceWeightsNo - (interfacePhaseSpaceWeights, - "No", - "Do not include the effect of cluster phase space", - 0); - static SwitchOption interfacePhaseSpaceWeightsYes - (interfacePhaseSpaceWeights, - "Yes", - "Do include the effect of cluster fission phase space " - "related to constituent masses." - "Note: Need static Threshold choice", - 1); - static SwitchOption interfacePhaseSpaceWeightsUseHadronMasses - (interfacePhaseSpaceWeights, - "UseHadronMasses", - "Do include the effect of cluster fission phase space " - "related to hadron masses." - "Note: Need static Threshold choice", - 2); - static SwitchOption interfacePhaseSpaceWeightsNoConstituentMasses - (interfacePhaseSpaceWeights, - "NoConstituentMasses", - "Do not include the effect of cluster fission phase space " - "related to constituent masses." - "Note: Need static Threshold choice", - 3); - - static Parameter<SoftClusterFissioner,double> - interfaceDim ("Dimension","Dimension in which phase space weights are calculated", - &SoftClusterFissioner::_dim, 0, 4.0, 0.0, 10.0,false,false,false); - // Matrix Element Choice Switch static Parameter<SoftClusterFissioner,double> interfaceSafetyFactorOverEst("SafetyFactorOverEst","Safety factor with which the numerical overestimate is calculated", &SoftClusterFissioner::_safetyFactorMatrixElement, 0, 10.0, 1.0, 1000.0,false,false,false); // Matrix Element IR cutoff static Parameter<SoftClusterFissioner,double> interfaceMatrixElementIRCutOff("MatrixElementIRCutOff","IR CutOff for MatrixElement with IR divergences. " "for MatrixElement SoftQQbarInitialFinalTchannel e.g. such that 1/(t^2)->1/((t-_epsilonIR*mGluonConst^2)^2)", &SoftClusterFissioner::_epsilonIR, 0, 0.01, 1e-6, 1000.0,false,false,false); // Matrix Element Choice Switch static Switch<SoftClusterFissioner,int> interfaceWriteOut ("WriteOut", "Option for different Matrix Element options", &SoftClusterFissioner::_writeOut, 0, false, false); static SwitchOption interfaceWriteOutNo (interfaceWriteOut, "No", "Choose trivial matrix element i.e. whatever comes from the mass and " "phase space sampling", 0); static SwitchOption interfaceWriteOutYes (interfaceWriteOut, "Yes", "Choose Soft q1,q2->q1,q2,g*->q1,q2,q,qbar matrix element", 1); - - // Allowing for strange diquarks in the ClusterFission - static Switch<SoftClusterFissioner,unsigned int> interfaceHadronizingStrangeDiquarks - ("HadronizingStrangeDiquarks", - "Option for adding strange diquarks to Cluster Fission (if Fission = New or Hybrid is enabled)", - &SoftClusterFissioner::_hadronizingStrangeDiquarks, 0, false, false); - static SwitchOption interfaceHadronizingStrangeDiquarksNo - (interfaceHadronizingStrangeDiquarks, - "No", - "No strangeness containing diquarks during Cluster Fission", - 0); - static SwitchOption interfaceHadronizingStrangeDiquarksOnlySingleStrange - (interfaceHadronizingStrangeDiquarks, - "OnlySingleStrange", - "Only one strangeness containing diquarks during Cluster Fission i.e. su,sd", - 1); - static SwitchOption interfaceHadronizingStrangeDiquarksAll - (interfaceHadronizingStrangeDiquarks, - "All", - "All strangeness containing diquarks during Cluster Fission i.e. su,sd,ss", - 2); - } - tPVector SoftClusterFissioner::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 SoftClusterFissioner::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); } } } SoftClusterFissioner::cutType SoftClusterFissioner::cutTwo(ClusterPtr & cluster, tPVector & finalhadrons, bool softUEisOn) { switch (_fissionApproach) { case 0: return cutTwoDefault(cluster, finalhadrons, softUEisOn); break; case 1: return cutTwoNew(cluster, finalhadrons, softUEisOn); break; case 2: if (cluster->isBeamCluster()) { return cutTwoDefault(cluster, finalhadrons, softUEisOn); } else { return cutTwoNew(cluster, finalhadrons, softUEisOn); } break; case 3: { bool isPert=false; for (unsigned int i = 0; i < cluster->numComponents(); i++) { if (cluster->isPerturbative(i)) isPert=true; } if (isPert) return cutTwoDefault(cluster, finalhadrons, softUEisOn); else return cutTwoNew(cluster, finalhadrons, softUEisOn); break; } case 4: { bool isPert=false; for (unsigned int i = 0; i < cluster->numComponents(); i++) { if (cluster->isPerturbative(i)) isPert=true; } if (!isPert) return cutTwoDefault(cluster, finalhadrons, softUEisOn); else return cutTwoNew(cluster, finalhadrons, softUEisOn); break; } case 5: { bool isPert=true; for (unsigned int i = 0; i < cluster->numComponents(); i++) { if (!cluster->isPerturbative(i)) { isPert=false; break; } } if (isPert) return cutTwoDefault(cluster, finalhadrons, softUEisOn); else return cutTwoNew(cluster, finalhadrons, softUEisOn); break; } default: assert(false); } } SoftClusterFissioner::cutType SoftClusterFissioner::cutTwoDefault(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(); 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()); if(cantMakeHadron(ptrQ1, newPtr1) || cantMakeHadron(ptrQ2, newPtr2)) { swap(newPtr1, newPtr2); // check again if(cantMakeHadron(ptrQ1, newPtr1) || cantMakeHadron(ptrQ2, newPtr2)) { throw Exception() << "SoftClusterFissioner cannot split the cluster (" << ptrQ1->PDGName() << ' ' << ptrQ2->PDGName() << ") into hadrons.\n" << Exception::runerror; } } // 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); double weightMasses = drawNewMassesDefault(Mc, soft1, soft2, pClu1, pClu2, ptrQ1, pQ1, newPtr1, pQone, newPtr2, pQtwo, ptrQ2, pQ2); if (weightMasses==0.0) continue; // derive the masses of the children Mc1 = pClu1.mass(); Mc2 = pClu2.mass(); // static kinematic threshold if(_kinematicThresholdChoice == 0) { if (Mc1 < m1+m || Mc2 < m+m2 || Mc1+Mc2 > Mc) continue; if (_phaseSpaceWeights==2 && ( Mc1 < spectrum()->massLightestHadronPair(ptrQ1->dataPtr(),newPtr1->dataPtr()) || Mc2 < spectrum()->massLightestHadronPair(ptrQ2->dataPtr(),newPtr2->dataPtr()) )) 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 && phaseSpaceVeto(Mc,Mc1,Mc2,m,m1,m2, ptrQ1, ptrQ2, newPtr1, _powerLawPower) ) { // reduce counter as it regards only the mass sampling counter--; 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); + toHadron1 = spectrum()->chooseSingleHadron(ptrQ1->dataPtr(), newPtr1->dataPtr(),Mc1); if(toHadron1) { Mc1 = toHadron1->mass(); pClu1.setMass(Mc1); } - toHadron2 = _hadronSpectrum->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc2); + toHadron2 = spectrum()->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc2); if(toHadron2) { Mc2 = toHadron2->mass(); pClu2.setMass(Mc2); } // 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); + toHadron1 = spectrum()->chooseSingleHadron(ptrQ1->dataPtr(), newPtr1->dataPtr(),Mc-Mc2); if(toHadron1) { Mc1 = toHadron1->mass(); pClu1.setMass(Mc1); } } else if(!toHadron2) { - toHadron2 = _hadronSpectrum->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc-Mc1); + toHadron2 = spectrum()->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc-Mc1); if(toHadron2) { 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) 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; } namespace { int areNotSame(const Lorentz5Momentum & p1,const Lorentz5Momentum & p2){ double tol=1e-5; if (abs(p1.vect().x()-p2.vect().x())>tol*GeV){ std::cout << "disagreeing x " <<std::setprecision(-log10(tol)+2) << abs(p1.vect().x()-p2.vect().x())/GeV<< std::endl; return 1; } if (abs(p1.vect().y()-p2.vect().y())>tol*GeV){ std::cout << "disagreeing y " <<std::setprecision(-log10(tol)+2)<< abs(p1.vect().y()-p2.vect().y())/GeV<< std::endl; return 2; } if (abs(p1.vect().z()-p2.vect().z())>tol*GeV){ std::cout << "disagreeing z " <<std::setprecision(-log10(tol)+2)<< abs(p1.vect().z()-p2.vect().z())/GeV<< std::endl; return 3; } if (abs(p1.e()-p2.e())>tol*GeV){ std::cout << "disagreeing e " <<std::setprecision(-log10(tol)+2)<< abs(p1.e()-p2.e())/GeV<< std::endl; return 4; } if (abs(p1.m()-p2.m())>tol*GeV){ std::cout << "disagreeing m " <<std::setprecision(-log10(tol)+2)<< abs(p1.m()-p2.m())/GeV<< std::endl; return 4; } if (abs(p1.m()-p1.mass())>tol*GeV){ std::cout << "disagreeing p1 m - mass " <<std::setprecision(-log10(tol)+2)<< abs(p1.m()-p1.mass())/GeV<< std::endl; return 4; } if (abs(p2.m()-p2.mass())>tol*GeV){ std::cout << "disagreeing p2 m - mass " <<std::setprecision(-log10(tol)+2)<< abs(p2.m()-p2.mass())/GeV<< std::endl; return 4; } return 0; } } SoftClusterFissioner::cutType SoftClusterFissioner::cutTwoNew(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(); // TODO BEGIN Changed comp to default if ( Mc < spectrum()->massLightestHadronPair(ptrQ1->dataPtr(),ptrQ2->dataPtr())) { static const PPtr null = PPtr(); return cutType(PPair(null,null),PPair(null,null)); } // TODO END Changed comp to default 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. int counter_MEtry = 0; Energy Mc1 = ZERO; Energy Mc2 = ZERO; Energy m = ZERO; Energy m1 = ptrQ1->data().constituentMass(); Energy m2 = ptrQ2->data().constituentMass(); Energy mMin = getParticleData(ParticleID::d)->constituentMass(); // Minimal threshold for non-zero Mass PhaseSpace if ( Mc < (m1 + m2 + 2*mMin )) { static const PPtr null = PPtr(); return cutType(PPair(null,null),PPair(null,null)); } tcPDPtr toHadron1, toHadron2; PPtr newPtr1 = PPtr (); PPtr newPtr2 = PPtr (); Lorentz5Momentum pClu1, pClu2, pQ1, pQone, pQtwo, pQ2; Lorentz5Momentum pClu = cluster->momentum(); // known const Lorentz5Momentum p0Q1 = ptrQ1->momentum(); // known (mom Q1 before fission) const Lorentz5Momentum p0Q2 = ptrQ2->momentum(); // known (mom Q2 before fission) // where to return to in case of rejected sample enum returnTo { FlavourSampling, MassSampling, PhaseSpaceSampling, MatrixElementSampling, Done }; // start with FlavourSampling returnTo retTo=FlavourSampling; int letFissionToXHadrons = _allowHadronFinalStates; // if a beam cluster not allowed to decay to hadrons if (cluster->isBeamCluster() && softUEisOn) letFissionToXHadrons = 0; // ### Flavour, Mass, PhaseSpace and MatrixElement Sampling loop until accepted: ### bool escape = false; double weightMasses,weightPhaseSpaceAngles; do { switch (retTo) { case FlavourSampling: { // ## Flavour sampling and kinematic constraints ## drawNewFlavour(newPtr1,newPtr2,cluster); // get a flavour for the qqbar pair // check for right ordering assert (ptrQ2); assert (newPtr2); assert (ptrQ2->dataPtr()); assert (newPtr2->dataPtr()); // 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; // DEBUG only: // std::cout << "Make Clusters: ( " << ptrQ1->PDGName() << " " << newPtr1->PDGName() << " ), ( " // << ptrQ2->PDGName() << " " << newPtr2->PDGName() << " )\n"; // check if Hadron formation is possible if (!( diqClu1 || diqClu2 ) && (cantMakeHadron(ptrQ1, newPtr1) || cantMakeHadron(ptrQ2, newPtr2))) { swap(newPtr1, newPtr2); // check again if(cantMakeHadron(ptrQ1, newPtr1) || cantMakeHadron(ptrQ2, newPtr2)) { throw Exception() << "SoftClusterFissioner 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); } 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 m = newPtr1->data().constituentMass(); // Do not split in the case there is no phase space available + permille security double eps=0.1; if(Mc < (1+eps)*(m1 + m + m2 + m)) { retTo = FlavourSampling; // escape if no flavour phase space possibile without fission if (fabs((m - mMin)/GeV) < 1e-3) { escape = true; retTo = Done; } continue; } pQ1.setMass(m1); pQone.setMass(m); pQtwo.setMass(m); pQ2.setMass(m2); // Determined the (5-components) momenta (all in the LAB frame) // p0Q1.setMass(ptrQ1->mass()); // known (mom Q1 before fission) // p0Q1.rescaleEnergy(); // TODO check if needed // p0Q2.setMass(ptrQ2->mass()); // known (mom Q2 before fission) // p0Q2.rescaleEnergy();// TODO check if needed pClu.rescaleMass(); Energy MLHP1 = spectrum()->hadronPairThreshold(ptrQ1->dataPtr(),newPtr1->dataPtr()); Energy MLHP2 = spectrum()->hadronPairThreshold(ptrQ2->dataPtr(),newPtr2->dataPtr()); - Energy MLH1 = _hadronSpectrum->lightestHadron(ptrQ1->dataPtr(),newPtr1->dataPtr())->mass(); - Energy MLH2 = _hadronSpectrum->lightestHadron(ptrQ2->dataPtr(),newPtr2->dataPtr())->mass(); + Energy MLH1 = spectrum()->lightestHadron(ptrQ1->dataPtr(),newPtr1->dataPtr())->mass(); + Energy MLH2 = spectrum()->lightestHadron(ptrQ2->dataPtr(),newPtr2->dataPtr())->mass(); bool canBeSingleHadron1 = (m1 + m) < MLHP1; bool canBeSingleHadron2 = (m2 + m) < MLHP2; Energy mThresh1 = (m1 + m); Energy mThresh2 = (m2 + m); if (canBeSingleHadron1) mThresh1 = MLHP1; if (canBeSingleHadron2) mThresh2 = MLHP2; switch (letFissionToXHadrons) { case 0: { // Option None: only C->C1,C2 allowed // check if mass phase space is non-zero // resample or escape if only allowed mass phase space is for C->H1,H2 or C->H1,C2 if ( Mc < (mThresh1 + mThresh2)) { // escape if not even the lightest flavour phase space is possibile // TODO make this independent of explicit u/d quark if ( fabs((m - getParticleData(ThePEG::ParticleID::u)->constituentMass())/GeV) < 1e-3 || fabs((m - getParticleData(ThePEG::ParticleID::d)->constituentMass())/GeV) < 1e-3 ) { escape = true; retTo = Done; continue; } else { retTo = FlavourSampling; continue; } } break; } case 1: { // Option SemiHadronicOnly: C->H,C allowed // NOTE: TODO implement matrix element for this // resample or escape if only allowed mass phase space is for C->H1,H2 // First case is for ensuring the enough mass to be available and second one rejects disjoint mass regions if ( ( (canBeSingleHadron1 && canBeSingleHadron2) && Mc < (mThresh1 + mThresh2) ) || ( (canBeSingleHadron1 || canBeSingleHadron2) && (canBeSingleHadron1 ? Mc-(m2+m) < MLH1:false || canBeSingleHadron2 ? Mc-(m1+m) < MLH2:false) ) ){ // escape if not even the lightest flavour phase space is possibile // TODO make this independent of explicit u/d quark if ( fabs((m - getParticleData(ThePEG::ParticleID::u)->constituentMass())/GeV) < 1e-3 || fabs((m - getParticleData(ThePEG::ParticleID::d)->constituentMass())/GeV) < 1e-3 ) { escape = true; retTo = Done; continue; } else { retTo = FlavourSampling; continue; } } break; } case 2: { // Option All: C->H,C and C->H,H allowed // NOTE: TODO implement matrix element for this // Mass Phase space for all option can always be found if cluster massive enough to go // to the lightest 2 hadrons break; } default: assert(false); } // Note: want to fallthrough (in C++17 one could uncomment // the line below to show that this is intentional) [[fallthrough]]; } /* * MassSampling choices: * - Default (default) * - Uniform * - FlatPhaseSpace * - SoftMEPheno * */ case MassSampling: { weightMasses = drawNewMasses(Mc, soft1, soft2, pClu1, pClu2, ptrQ1, pQ1, newPtr1, pQone, newPtr2, pQtwo, ptrQ2, pQ2); // TODO IF C->C1,C2 (and not C->C,H or H1,H2) masses sampled and is in PhaseSpace must push through // because otherwise no matrix element if(weightMasses==0.0) { // TODO check which option is better retTo = FlavourSampling; // retTo = MassSampling; continue; } // derive the masses of the children Mc1 = pClu1.mass(); Mc2 = pClu2.mass(); // static kinematic threshold if(_kinematicThresholdChoice != 1 ) { // NOTE: that the squared thresholds below are // numerically more stringent which is needed // kaellen function calculation if(sqr(Mc1) < sqr(m1+m) || sqr(Mc2) < sqr(m+m2) || sqr(Mc1+Mc2) > sqr(Mc)){ // TODO check which option is better // retTo = FlavourSampling; retTo = MassSampling; 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 ) { // TODO check which option is better // retTo = FlavourSampling; retTo = MassSampling; continue; } } else assert(false); /************************** * 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); + toHadron1 = spectrum()->chooseSingleHadron(ptrQ1->dataPtr(), newPtr1->dataPtr(),Mc1); if ( letFissionToXHadrons == 0 && toHadron1 ) { // reject mass samples which would force C->C,H or C->H1,H2 fission if desired // // Check if Mc1max < MLHP1, in which case we might need to choose a different flavour // else resampling the masses should be sufficient - Energy MLHP1 = _hadronSpectrum->massLightestHadronPair(ptrQ1->dataPtr(), newPtr1->dataPtr()); - Energy MLHP2 = _hadronSpectrum->massLightestHadronPair(ptrQ2->dataPtr(), newPtr2->dataPtr()); + Energy MLHP1 = spectrum()->massLightestHadronPair(ptrQ1->dataPtr(), newPtr1->dataPtr()); + Energy MLHP2 = spectrum()->massLightestHadronPair(ptrQ2->dataPtr(), newPtr2->dataPtr()); Energy Mc1max = (m2+m) > MLHP2 ? (Mc-(m2+m)):(Mc-MLHP2); // for avoiding inf loops set min threshold if ( Mc1max - MLHP1 < 1e-2*GeV ) { retTo = FlavourSampling; } else { retTo = MassSampling; } continue; } if(toHadron1) { Mc1 = toHadron1->mass(); pClu1.setMass(Mc1); } - toHadron2 = _hadronSpectrum->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc2); + toHadron2 = spectrum()->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc2); if ( letFissionToXHadrons == 0 && toHadron2 ) { // reject mass samples which would force C->C,H or C->H1,H2 fission if desired // // Check if Mc2max < MLHP2, in which case we might need to choose a different flavour // else resampling the masses should be sufficient - Energy MLHP1 = _hadronSpectrum->massLightestHadronPair(ptrQ1->dataPtr(), newPtr1->dataPtr()); - Energy MLHP2 = _hadronSpectrum->massLightestHadronPair(ptrQ2->dataPtr(), newPtr2->dataPtr()); + Energy MLHP1 = spectrum()->massLightestHadronPair(ptrQ1->dataPtr(), newPtr1->dataPtr()); + Energy MLHP2 = spectrum()->massLightestHadronPair(ptrQ2->dataPtr(), newPtr2->dataPtr()); Energy Mc2max = (m1+m) > MLHP1 ? (Mc-(m1+m)):(Mc-MLHP1); // for avoiding inf loops set min threshold if ( Mc2max - MLHP2 < 1e-2*GeV ) { retTo = FlavourSampling; } else { retTo = MassSampling; } continue; } if(toHadron2) { Mc2 = toHadron2->mass(); pClu2.setMass(Mc2); } if (letFissionToXHadrons == 1 && (toHadron1 && toHadron2) ) { // reject mass samples which would force C->H1,H2 fission if desired // TODO check which option is better // retTo = FlavourSampling; retTo = MassSampling; continue; } // Check if the decay kinematics is still possible: if not then // force the one-hadron decay for the other cluster as well. // NOTE: that the squared thresholds below are // numerically more stringent which is needed // kaellen function calculation if(sqr(Mc1 + Mc2) > sqr(Mc)) { // reject if we would need to create a C->H,H process if letFissionToXHadrons<2 if (letFissionToXHadrons < 2) { // TODO check which option is better // retTo = FlavourSampling; retTo = MassSampling; continue; } // TODO forbid other cluster!!!! to be also at hadron if(!toHadron1) { - toHadron1 = _hadronSpectrum->chooseSingleHadron(ptrQ1->dataPtr(), newPtr1->dataPtr(),Mc-Mc2); - // toHadron1 = _hadronSpectrum->chooseSingleHadron(ptrQ1->dataPtr(), newPtr1->dataPtr(),ZERO); + toHadron1 = spectrum()->chooseSingleHadron(ptrQ1->dataPtr(), newPtr1->dataPtr(),Mc-Mc2); + // toHadron1 = spectrum()->chooseSingleHadron(ptrQ1->dataPtr(), newPtr1->dataPtr(),ZERO); if(toHadron1) { Mc1 = toHadron1->mass(); pClu1.setMass(Mc1); } } else if(!toHadron2) { - toHadron2 = _hadronSpectrum->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc-Mc1); - // toHadron2 = _hadronSpectrum->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),ZERO); + toHadron2 = spectrum()->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),Mc-Mc1); + // toHadron2 = spectrum()->chooseSingleHadron(ptrQ2->dataPtr(), newPtr2->dataPtr(),ZERO); if(toHadron2) { Mc2 = toHadron2->mass(); pClu2.setMass(Mc2); } } } // NOTE: that the squared thresholds below are // numerically more stringent which is needed // kaellen function calculation if (sqr(Mc) <= sqr(Mc1+Mc2)){ // escape if already lightest quark drawn if ( fabs((m - getParticleData(ThePEG::ParticleID::u)->constituentMass())/GeV) < 1e-3 || fabs((m - getParticleData(ThePEG::ParticleID::d)->constituentMass())/GeV) < 1e-3 ) { // escape = true; retTo = Done; } else { // Try again with lighter quark retTo = FlavourSampling; } continue; } // Note: want to fallthrough (in C++17 one could uncomment // the line below to show that this is intentional) [[fallthrough]]; } /* * PhaseSpaceSampling choices: * - FullyAligned (default) * - AlignedIsotropic * - FullyIsotropic * */ case PhaseSpaceSampling: { // ### Sample the Phase Space with respect to Matrix Element: ### // TODO insert here PhaseSpace sampler weightPhaseSpaceAngles = drawKinematics(pClu,p0Q1,p0Q2,toHadron1,toHadron2, pClu1,pClu2,pQ1,pQone,pQtwo,pQ2); { //sanity if (areNotSame(pClu1+pClu2,pClu)) { std::cout << "PCLU" << std::endl; } if (areNotSame(pClu1,pQ1+pQone)) { std::cout << "PCLU1" << std::endl; } if (areNotSame(pClu2,pQ2+pQtwo)) { std::cout << "PCLU2" << std::endl; } if (areNotSame(pClu,pQ1+pQone+pQ2+pQtwo)) { std::cout << "PTOT" << std::endl; } } if(weightPhaseSpaceAngles==0.0) { // TODO check which option is better retTo = MassSampling; continue; } // Should be precise i.e. no rejection expected // Note: want to fallthrough (in C++17 one could uncomment // the line below to show that this is intentional) [[fallthrough]]; } /* * MatrixElementSampling choices: * - Default (default) * - SoftQQbar * */ case MatrixElementSampling: { counter_MEtry++; // TODO maybe bridge this to work more neatly // Ignore matrix element for C->C,H or C->H1,H2 fission if (toHadron1 || toHadron2) { retTo = Done; break; } // Actual MatrixElement evaluated at sampled PhaseSpace point double SQME = calculateSQME(p0Q1,p0Q2,pQ1,pQone,pQ2,pQtwo); // Total overestimate of MatrixElement independent // of the PhaseSpace point and independent of M1,M2 double SQMEoverEstimate = calculateSQME_OverEstimate(Mc,m1,m2,m); // weight for MatrixElement*PhaseSpace must be in [0:1] double weightSQME = SQME/SQMEoverEstimate; assert(weightSQME>0.0); // weight(M1,M2) for M1*M2*(Two body PhaseSpace)**3 should be in [0,1] double weightFlatPS = weightFlatPhaseSpace(Mc, Mc1, Mc2, m, m1, m2, ptrQ1, ptrQ2, newPtr1); if (weightFlatPS<0 || weightFlatPS>1.0){ throw Exception() << "weightFlatPS = "<< weightFlatPS << " > 1 or negative in SoftClusterFissioner::cutTwo" << "Mc = " << Mc/GeV << "Mc1 = " << Mc1/GeV << "Mc2 = " << Mc2/GeV << "m1 = " << m1/GeV << "m2 = " << m2/GeV << "m = " << m/GeV << "SQME = " << SQME << "SQME_OE = " << SQMEoverEstimate << "PS = " << weightFlatPS << Exception::runerror; } // current phase space point is distributed according to weightSamp double Pacc = weightFlatPS * weightSQME; double SamplingWeight = weightMasses * weightPhaseSpaceAngles; if (!(SamplingWeight >= 0 ) || std::isnan(SamplingWeight) || std::isinf(SamplingWeight)){ throw Exception() << "SamplingWeight = "<< SamplingWeight << " in SoftClusterFissioner::cutTwo" << "Mc = " << Mc/GeV << "Mc1 = " << Mc1/GeV << "Mc2 = " << Mc2/GeV << "m1 = " << m1/GeV << "m2 = " << m2/GeV << "m = " << m/GeV << "weightMasses = " << weightMasses << "weightPhaseSpaceAngles = " << weightPhaseSpaceAngles << "PS = " << weightFlatPS << Exception::runerror; } Pacc/=SamplingWeight; if (!(Pacc >= 0 ) || std::isnan(Pacc) || std::isinf(Pacc)){ throw Exception() << "Pacc = "<< Pacc << " < 0 in SoftClusterFissioner::cutTwo" << "Mc = " << Mc/GeV << "Mc1 = " << Mc1/GeV << "Mc2 = " << Mc2/GeV << "m1 = " << m1/GeV << "m2 = " << m2/GeV << "m = " << m/GeV << "SQME = " << SQME << "SQME_OE = " << SQMEoverEstimate << "PS = " << weightFlatPS << Exception::runerror; } assert(Pacc >= 0.0); if (_matrixElement!=0) { if ( Pacc > 1.0){ countPaccGreater1(); throw Exception() << "Pacc = "<< Pacc << " > 1 in SoftClusterFissioner::cutTwo" << "Mc = " << Mc/GeV << "Mc1 = " << Mc1/GeV << "Mc2 = " << Mc2/GeV << "m1 = " << m1/GeV << "m2 = " << m2/GeV << "m = " << m/GeV << Exception::warning; } } static int first=_writeOut; if (_matrixElement==0 || UseRandom::rnd()<Pacc) { //Always accept a sample if trivial matrix element if (_writeOut) { // std::cout << "\nAccept Pacc = "<<Pacc<<"\n"; std::ofstream out("data_CluFis.dat", std::ios::app | std::ios::out); Lorentz5Momentum p0Q1tmp(p0Q1); Lorentz5Momentum pClu1tmp(pClu1); Lorentz5Momentum pClu2tmp(pClu2); p0Q1tmp.boost(-pClu.boostVector()); pClu1tmp.boost(-pClu.boostVector()); double cosThetaP1C1=p0Q1tmp.vect().cosTheta(pClu1tmp.vect()); out << Pacc << "\t" << Mc/GeV << "\t" << pClu1.mass()/GeV << "\t" << pClu2.mass()/GeV << "\t" << pQone*pQtwo/(pQone.mass()*pQtwo.mass()) << "\t" << pQ1*pQ2/(pQ1.mass()*pQ2.mass()) << "\t" << pQ1*pQtwo/(pQ1.mass()*pQtwo.mass()) << "\t" << pQ2*pQone/(pQ2.mass()*pQone.mass()) << "\t" << p0Q1*(pQone+pQtwo)/(p0Q1.mass()*(pQone.mass()+pQtwo.mass())) << "\t" << p0Q2*(pQone+pQtwo)/(p0Q2.mass()*(pQone.mass()+pQtwo.mass())) << "\t" << m/GeV << "\t" << cosThetaP1C1 << "\n"; out.close(); Energy MbinStart=2.0*GeV; Energy MbinEnd=91.0*GeV; Energy dMbin=1.0*GeV; Energy MbinLow; Energy MbinHig; if ( fabs((m-getParticleData(ParticleID::d)->constituentMass())/GeV)<1e-5 && fabs((m1-getParticleData(ParticleID::d)->constituentMass())/GeV)<1e-5 && fabs((m2-getParticleData(ParticleID::d)->constituentMass())/GeV)<1e-5) { int cnt = 0; for (Energy MbinIt=MbinStart; MbinIt < MbinEnd; MbinIt+=dMbin) { if (first){ first=0; std::cout << "\nFirst\n" << std::endl; int ctr=0; for (Energy MbinIt=MbinStart; MbinIt < MbinEnd; MbinIt+=dMbin) { std::string name = "WriteOut/data_CluFis_BinM_"+std::to_string(ctr)+".dat"; std::ofstream out2(name,std::ios::out); out2 << "# Binned from "<<MbinIt/GeV << "\tto\t" << (MbinIt+dMbin)/GeV<<"\n"; out2 << "# m=m1=m2=0.325\n"; out2 << "# (1) Pacc\t" << "(2) Mc/GeV\t" << "(3) M1/GeV\t" << "(4) M2/GeV\t" << "(5) q.qbar/(mq^2)\t" << "(6) q1.q2/(m1*m2)\t" << "(7) q1.qbar/(m1*mq)\t" << "(8) q2.q/(m2*mq)\t" << "(9) p1.(q+qbar)/(m1*2*mq)\t" << "(10) p2.(q+qbar)/(m2*2*mq)\t" << "(11) m/GeV\t" << "(12) cos(theta_p1_Q1)\n"; out2.close(); ctr++; } // first=0; } MbinLow = MbinIt; MbinHig = MbinLow+dMbin; if (Mc>MbinLow && Mc<MbinHig) { std::string name = "WriteOut/data_CluFis_BinM_"+std::to_string(cnt)+".dat"; std::ofstream out2(name, std::ios::app ); Lorentz5Momentum p0Q1tmp(p0Q1); Lorentz5Momentum pClu1tmp(pClu1); Lorentz5Momentum pClu2tmp(pClu2); p0Q1tmp.boost(-pClu.boostVector()); pClu1tmp.boost(-pClu.boostVector()); double cosThetaP1C1=p0Q1tmp.vect().cosTheta(pClu1tmp.vect()); out2 << Pacc << "\t" << Mc/GeV << "\t" << pClu1.mass()/GeV << "\t" << pClu2.mass()/GeV << "\t" << pQone*pQtwo/(pQone.mass()*pQtwo.mass()) << "\t" << pQ1*pQ2/(pQ1.mass()*pQ2.mass()) << "\t" << pQ1*pQtwo/(pQ1.mass()*pQtwo.mass()) << "\t" << pQ2*pQone/(pQ2.mass()*pQone.mass()) << "\t" << p0Q1*(pQone+pQtwo)/(p0Q1.mass()*(pQone.mass()+pQtwo.mass())) << "\t" << p0Q2*(pQone+pQtwo)/(p0Q2.mass()*(pQone.mass()+pQtwo.mass())) << "\t" << m/GeV << "\t" << cosThetaP1C1 << "\n"; out2.close(); break; } // if (Mc<MbinIt) break; cnt++; } } } retTo=Done; break; } retTo = MassSampling; continue; } default: { assert(false); } } } while (retTo!=Done && !escape && counter_MEtry < _maxLoopFissionMatrixElement); if(escape) { // happens if we get at too light cluster to begin with static const PPtr null = PPtr(); return cutType(PPair(null,null),PPair(null,null)); } if(counter_MEtry >= _maxLoopFissionMatrixElement) { countMaxLoopViolations(); // happens if we get too massive clusters where the matrix element // is very inefficiently sampled std::stringstream warning; warning << "Matrix Element rejection sampling tried more than " << counter_MEtry << " times.\nMcluster = " << Mc/GeV << "GeV\nm1 = " << m1/GeV << "GeV\nm2 = " << m2/GeV << "GeV\nm = " << m/GeV << "GeV\n" << "isBeamCluster = " << cluster->isBeamCluster() <<"Using default as SoftClusterFissioner::cutTwoDefault as a fallback.\n" << "***This Exception should not happen too often!*** "; generator()->logWarning( Exception(warning.str(),Exception::warning)); switch (_failModeMaxLoopMatrixElement) { case 0: // OldFission return cutTwoDefault(cluster,finalhadrons,softUEisOn); break; case 1: // RejectEvent throw Exception() << warning.str() << Exception::eventerror; break; default: assert(false); } } assert(abs(Mc1-pClu1.m())<1e-2*GeV); assert(abs(Mc2-pClu2.m())<1e-2*GeV); assert(abs(Mc1-pClu1.mass())<1e-2*GeV); assert(abs(Mc2-pClu2.mass())<1e-2*GeV); { Lorentz5Momentum pp1(p0Q1); Lorentz5Momentum pclu1(pClu1); pclu1.boost(-pClu.boostVector()); pp1.boost(-pClu.boostVector()); // std::ofstream out("testCF.dat", std::ios::app); // out << pclu1.vect().cosTheta(pp1.vect())<<"\n"; // out.close(); } // Count successfull ClusterFission countFissionMatrixElement(); // ==> full sample generated /****************** * 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; } SoftClusterFissioner::cutType SoftClusterFissioner::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() << "SoftClusterFissioner 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); double weightMasses = drawNewMassesDefault(mmax, soft1, soft2, pClu1, pClu2, ptrQ[iq1], pQ1, newPtr1, pQone, newPtr2, pQtwo, ptrQ[iq1], pQ2); if (weightMasses == 0.0) continue; Mc1 = pClu1.mass(); Mc2 = pClu2.mass(); if(Mc1 < m1+m || Mc2 < m+m2 || Mc1+Mc2 > mmax) continue; if ( _phaseSpaceWeights && phaseSpaceVeto(mmax,Mc1,Mc2,m,m1,m2) ) { // reduce counter as it regards only the mass sampling counter--; continue; } // check if need to force meson clster to hadron - toHadron = _hadronSpectrum->chooseSingleHadron(ptrQ[iq1]->dataPtr(), newPtr1->dataPtr(),Mc1); + toHadron = spectrum()->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); + toHadron = spectrum()->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); + Mc2 = spectrum()->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; } SoftClusterFissioner::PPair SoftClusterFissioner::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(); + rval.first = (spectrum()->lightestHadron(hadron,newPtr->dataPtr()))->produceParticle(); } else rval.first = hadron->produceParticle(); rval.second = newPtr; rval.first->set5Momentum(a); rval.first->setVertex(b); return rval; } SoftClusterFissioner::PPair SoftClusterFissioner::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; } /** * Calculate the phase space weight for M1*M2*(2 body PhaseSpace) ignore constituent masses */ double SoftClusterFissioner::weightFlatPhaseSpaceNoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2) const { double M_temp = Mc/GeV; double M1_temp = Mc1/GeV; double M2_temp = Mc2/GeV; if (sqr(M_temp)<sqr(M1_temp+M2_temp)) { // This should be checked before throw Exception() << "SoftClusterFissioner has not checked Masses properly\n" << "Mc = " << M_temp << "\n" << "Mc1 = " << M1_temp << "\n" << "Mc2 = " << M2_temp << "\n" << Exception::warning; return 0.0; } double lam = Kinematics::kaellen(M_temp, M1_temp, M2_temp); double ratio; // old weight of Jan without the Jacobi factor M1*M2 of the Mass integration // double PSweight = pow(lam,_dim-3.)*pow(M1_temp*M2_temp,2.-_dim); // new weight with the Jacobi factor M1*M2 of the Mass integration double PSweight = M1_temp*M2_temp*pow(sqrt(lam),_dim-3.); // overestimate only possible for dim>=3.0 assert(_dim>=3.0); // old overestimate of Jan without the Jacobi factor M1*M2 of the Mass integration // double overEstimate = _dim>=4.0 ? pow(M_temp,4.*_dim-14.):pow(M_temp,2*(_dim-3.0))/pow((m1_temp+m_temp)*(m2_temp+m_temp),4.0-_dim); // new improved overestimate with the Jacobi factor M1*M2 of the Mass integration double overEstimate = pow(6.0*sqrt(3.0), 3.0 - _dim)*pow(M_temp, 2.*(_dim-2.)); ratio = PSweight/overEstimate; if (!(ratio>=0)) std::cout << "ratio = " <<ratio<<" M "<<M_temp<<" M1 "<<M1_temp<<" M2 "<<M2_temp<<"\t"<<_dim<<"\t" << lam <<"\t"<< overEstimate<<"\n\n"; if (!(ratio<=1)) std::cout << "ratio = " <<ratio<<" M "<<M_temp<<" M1 "<<M1_temp<<" M2 "<<M2_temp<<"\t"<<_dim<<"\t" << lam <<"\t"<< overEstimate<<"\n\n"; // if (ratio > 0.9) std::cout << "ratio = " << ratio <<"\n"; assert (ratio >= 0); assert (ratio <= 1); return ratio; } /** * Calculate the phase space weight for M1*M2*(2 body PhaseSpace)^3 */ double SoftClusterFissioner::weightPhaseSpaceConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, const double power) const { double M_temp = Mc/GeV; double M1_temp = Mc1/GeV; double M2_temp = Mc2/GeV; double m_temp = m/GeV; double m1_temp = m1/GeV; double m2_temp = m2/GeV; if (sqr(M_temp)<sqr(M1_temp+M2_temp) || sqr(M1_temp)<sqr(m1_temp+m_temp) || sqr(M2_temp)<sqr(m2_temp+m_temp) ) { // This should be checked before throw Exception() << "SoftClusterFissioner has not checked Masses properly\n" << "Mc = " << M_temp << "\n" << "Mc1 = " << M1_temp << "\n" << "Mc2 = " << M2_temp << "\n" << "m1 = " << m1_temp << "\n" << "m2 = " << m2_temp << "\n" << "m = " << m_temp << "\n" << Exception::warning; return 0.0; } double lam1 = Kinematics::kaellen(M1_temp, m1_temp, m_temp); double lam2 = Kinematics::kaellen(M2_temp, m2_temp, m_temp); double lam3 = Kinematics::kaellen(M_temp, M1_temp, M2_temp); double ratio; // old weight of Jan without the Jacobi factor M1*M2 of the Mass integration // double PSweight = pow(lam1*lam2*lam3,_dim-3.)*pow(M1_temp*M2_temp,2.-_dim); // new weight with the Jacobi factor M1*M2 of the Mass integration double PSweight = pow(lam1*lam2*lam3,(_dim-3.)/2.0)*pow(M1_temp*M2_temp,3.-_dim); // overestimate only possible for dim>=3.0 assert(_dim>=3.0); // old overestimate of Jan without the Jacobi factor M1*M2 of the Mass integration // double overEstimate = _dim>=4.0 ? pow(M_temp,4.*_dim-14.):pow(M_temp,2*(_dim-3.0))/pow((m1_temp+m_temp)*(m2_temp+m_temp),4.0-_dim); // new improved overestimate with the Jacobi factor M1*M2 of the Mass integration double overEstimate = pow(6.0*sqrt(3.0), 3.0 - _dim)*pow(M_temp, 4.*_dim-12.); ratio = PSweight/overEstimate; if (!(ratio>=0)) std::cout << "ratio = " <<ratio<<" M "<<M_temp<<" M1 "<<M1_temp<<" M2 "<<M2_temp<<" m1 "<<m1_temp<<" m2 "<<m2_temp<<" m "<<m_temp<<"\t"<<_dim<<"\t" << lam1<<"\t"<< lam2<<"\t" << lam3 <<"\t"<< overEstimate<<"\n\n"; if (power) { double powerLawOver = power<0 ? pow(Mc1*Mc2/((m1+m)*(m2+m)),power):pow(Mc1*Mc2/((Mc-(m1+m))*(Mc-(m2+m))),power); ratio*=powerLawOver; } // if (ratio > 0.9) std::cout << "ratio = " << ratio <<"\n"; assert (ratio >= 0); assert (ratio <= 1); return ratio; } /** * Calculate the phase space weight for M1*M2*(2 body PhaseSpace)^3 * using Hadron Masses */ double SoftClusterFissioner::weightFlatPhaseSpaceHadronMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, tcPPtr pQ, tcPPtr pQ1, tcPPtr pQ2) const { auto LHP1 = spectrum()->lightestHadronPair(pQ1->dataPtr(),pQ->dataPtr()); auto LHP2 = spectrum()->lightestHadronPair(pQ2->dataPtr(),pQ->dataPtr()); if (sqr(Mc1)<sqr(LHP1.first->mass()+LHP1.second->mass())) return true; if (sqr(Mc2)<sqr(LHP2.first->mass()+LHP2.second->mass())) return true; // double weigthHadrons = Kinematics::pstarTwoBodyDecay(Mc1,m1,m)*Kinematics::pstarTwoBodyDecay(Mc2,m2,m)/(Kinematics::pstarTwoBodyDecay(Mc1,LHP1.first->mass(),LHP1.second->mass())*Kinematics::pstarTwoBodyDecay(Mc2,LHP2.first->mass(),LHP2.second->mass())); // double tot= weightFlatPhaseSpace(Mc, Mc1, Mc2, m, m1, m2)/weigthHadrons; // if (std::isinf(tot) || std::isnan(tot) || tot<0.0 || tot>1.0) // std::cout << "tot = " << double lam1 = sqrt(Kinematics::kaellen(Mc1/GeV, LHP1.first->mass()/GeV, LHP1.second->mass()/GeV)); double lam2 = sqrt(Kinematics::kaellen(Mc2/GeV, LHP2.first->mass()/GeV, LHP2.second->mass()/GeV)); double lam3 = sqrt(Kinematics::kaellen(Mc/GeV, Mc1/GeV, Mc2/GeV)); double ratio; // old weight of Jan without the Jacobi factor M1*M2 of the Mass integration // double PSweight = pow(lam1*lam2*lam3,_dim-3.)*pow(M1_temp*M2_temp,2.-_dim); // new weight with the Jacobi factor M1*M2 of the Mass integration double PSweight = pow(lam1*lam2*lam3,_dim-3.)*pow(Mc1*Mc2/GeV2,3.-_dim); // overestimate only possible for dim>=3.0 assert(_dim>=3.0); // old overestimate of Jan without the Jacobi factor M1*M2 of the Mass integration // double overEstimate = _dim>=4.0 ? pow(M_temp,4.*_dim-14.):pow(M_temp,2*(_dim-3.0))/pow((m1_temp+m_temp)*(m2_temp+m_temp),4.0-_dim); // new improved overestimate with the Jacobi factor M1*M2 of the Mass integration double overEstimate = pow(6.0*sqrt(3.0), 3.0 - _dim)*pow(Mc/GeV, 4.*_dim-12.); ratio = PSweight/overEstimate; // if (!(ratio>=0)) std::cout << "M "<<Mc/GeV<<" M1 "<<Mc1/GeV<<" M2 "<<Mc2/GeV<<" m1 "<<m1_temp<<" m2 "<<m2_temp<<" m "<<m_temp<<"\n\n"; // if (ratio > 0.9) std::cout << "ratio = " << ratio <<"\n"; if (std::isinf(ratio) || std::isnan(ratio) || ratio<0.0 || ratio>1.0) std::cout << "ratio = " << ratio<<std::endl; assert (ratio >= 0); assert (ratio <= 1); return ratio; } /** * Veto for the phase space weight * returns true if proposed Masses are rejected * else returns false */ bool SoftClusterFissioner::phaseSpaceVeto(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, tcPPtr pQ1, tcPPtr pQ2, tcPPtr pQ, const double power) const { switch (_phaseSpaceWeights) { case 1: return phaseSpaceVetoConstituentMasses(Mc, Mc1, Mc2, m, m1, m2, power); case 2: return phaseSpaceVetoHadronPairs(Mc, Mc1, Mc2, pQ, pQ1, pQ2); case 3: return phaseSpaceVetoNoConstituentMasses(Mc, Mc1, Mc2); default: assert(false); } } /** * Veto for the phase space weight * returns true if proposed Masses are rejected * else returns false */ bool SoftClusterFissioner::phaseSpaceVetoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, const double power) const { return (UseRandom::rnd()>weightPhaseSpaceConstituentMasses(Mc, Mc1, Mc2, m, m1, m2, power)); } bool SoftClusterFissioner::phaseSpaceVetoNoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2) const { return (UseRandom::rnd()>weightFlatPhaseSpaceNoConstituentMasses(Mc, Mc1, Mc2)); } bool SoftClusterFissioner::phaseSpaceVetoHadronPairs(const Energy Mc, const Energy Mc1, const Energy Mc2, tcPPtr pQ, tcPPtr pQ1, tcPPtr pQ2) const { return (UseRandom::rnd()>weightFlatPhaseSpaceHadronMasses(Mc, Mc1, Mc2, pQ, pQ1, pQ2)); } /** * Calculate the masses and possibly kinematics of the cluster * fission at hand; if claculateKineamtics is perfomring non-trivial * steps kinematics calulcated here will be overriden. Currently resorts to the default * @return the potentially non-trivial distribution weight=f(M1,M2) * On Failure we return 0 */ double SoftClusterFissioner::drawNewMasses(const Energy Mc, const bool soft1, const bool soft2, Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, tcPPtr ptrQ1, const Lorentz5Momentum& pQ1, tcPPtr newPtr1, const Lorentz5Momentum& pQone, tcPPtr, const Lorentz5Momentum& pQtwo, tcPPtr ptrQ2, const Lorentz5Momentum& pQ2) const { // TODO add precise weightMS that could be used used for improving the rejection Sampling switch (_massSampler) { case 0: return drawNewMassesDefault(Mc, soft1, soft2, pClu1, pClu2, ptrQ1, pQ1, tcPPtr(), pQone, tcPPtr(),pQtwo, ptrQ2, pQ2); break; case 1: return drawNewMassesUniform(Mc, pClu1, pClu2, pQ1, pQone, pQ2); break; case 2: return drawNewMassesPhaseSpace(Mc, pClu1, pClu2, pQ1, pQone, pQ2, ptrQ1, newPtr1, ptrQ2); break; case 3: return drawNewMassesPhaseSpacePowerLaw(Mc, pClu1, pClu2, pQ1, pQone, pQ2, ptrQ1, newPtr1, ptrQ2); break; default: assert(false); } return 0;// failure } -/** - * Calculate the masses and possibly kinematics of the cluster - * fission at hand; if claculateKineamtics is perfomring non-trivial - * steps kinematics claulcated here will be overriden. Currentl;y resorts to the default - */ -double SoftClusterFissioner::drawNewMassesDefault(const Energy Mc, const bool soft1, const bool soft2, - Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, - tcPPtr ptrQ1, const Lorentz5Momentum& pQ1, - tcPPtr, const Lorentz5Momentum& pQone, - tcPPtr, const Lorentz5Momentum& pQtwo, - tcPPtr ptrQ2, const Lorentz5Momentum& pQ2) const { - // power for splitting - double exp1 = !spectrum()->isExotic(ptrQ1->dataPtr()) ? _pSplitLight : _pSplitExotic; - double exp2 = !spectrum()->isExotic(ptrQ2->dataPtr()) ? _pSplitLight : _pSplitExotic; - for ( const long& id : spectrum()->heavyHadronizingQuarks() ) { - assert(_pSplitHeavy.find(id) != _pSplitHeavy.end()); - if ( spectrum()->hasHeavy(id,ptrQ1->dataPtr()) ) exp1 = _pSplitHeavy.find(id)->second; - if ( spectrum()->hasHeavy(id,ptrQ2->dataPtr()) ) exp2 = _pSplitHeavy.find(id)->second; - } - - Energy M1 = drawChildMass(Mc,pQ1.mass(),pQ2.mass(),pQone.mass(),exp1,soft1); - Energy M2 = drawChildMass(Mc,pQ2.mass(),pQ1.mass(),pQtwo.mass(),exp2,soft2); - - pClu1.setMass(M1); - pClu2.setMass(M2); - - return 1.0; // succeeds -} /** * Sample the masses for flat phase space * */ double SoftClusterFissioner::drawNewMassesUniform(const Energy Mc, Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, const Lorentz5Momentum& pQ1, const Lorentz5Momentum& pQ, const Lorentz5Momentum& pQ2) const { Energy M1,M2; const Energy m1 = pQ1.mass(); const Energy m2 = pQ2.mass(); const Energy m = pQ.mass(); const Energy M1min = m1 + m; const Energy M2min = m2 + m; const Energy M1max = Mc - M2min; const Energy M2max = Mc - M1min; assert(M1max-M1min>ZERO); assert(M2max-M2min>ZERO); double r1; double r2; int counter = 0; const int max_counter = 100; while (counter < max_counter) { r1 = UseRandom::rnd(); r2 = UseRandom::rnd(); M1 = (M1max-M1min)*r1 + M1min; M2 = (M2max-M2min)*r2 + M2min; counter++; if ( Mc > M1 + M2) break; } if (counter==max_counter || Mc < M1 + M2 || M1 <= M1min || M2 <= M2min ) return 0.0; // failure pClu1.setMass(M1); pClu2.setMass(M2); return 1.0; // succeeds } /** * Sample the masses for flat phase space * */ double SoftClusterFissioner::drawNewMassesPhaseSpacePowerLaw(const Energy Mc, Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, const Lorentz5Momentum& pQ1, const Lorentz5Momentum& pQ, const Lorentz5Momentum& pQ2, tcPPtr ptrQ1, tcPPtr ptrQ2, tcPPtr ptrQ) const { Energy M1,M2,MuS; const Energy m1 = pQ1.mass(); const Energy m2 = pQ2.mass(); const Energy m = pQ.mass(); const Energy M1min = m1 + m; const Energy M2min = m2 + m; const Energy M1max = Mc - M2min; const Energy M2max = Mc - M1min; assert(M1max-M1min>ZERO); assert(M2max-M2min>ZERO); double r1; double r2; int counter = 0; const int max_counter = 200; while (counter < max_counter) { r1 = UseRandom::rnd(); r2 = UseRandom::rnd(); M1 = (M1max-M1min)*r1 + M1min; M2 = (M2max-M2min)*r2 + M2min; counter++; if (sqr(M1+M2)>sqr(Mc)) continue; // if (!phaseSpaceVeto(Mc,M1,M2,m,m1,m2) ) break; // For FlatPhaseSpace sampling vetoing if (!phaseSpaceVeto(Mc,M1,M2,m,m1,m2, ptrQ1, ptrQ2, ptrQ,_powerLawPower) ) break; // For FlatPhaseSpace sampling vetoing } if (counter==max_counter) return 0.0; // failure pClu1.setMass(M1); pClu2.setMass(M2); return weightPhaseSpaceConstituentMasses(Mc, M1, M2, m, m1, m2, _powerLawPower); // succeeds return weight } /** * Sample the masses for flat phase space * */ double SoftClusterFissioner::drawNewMassesPhaseSpace(const Energy Mc, Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, const Lorentz5Momentum& pQ1, const Lorentz5Momentum& pQ, const Lorentz5Momentum& pQ2, tcPPtr ptrQ1, tcPPtr ptrQ2, tcPPtr ptrQ ) const { Energy M1,M2,MuS; const Energy m1 = pQ1.mass(); const Energy m2 = pQ2.mass(); const Energy m = pQ.mass(); const Energy M1min = m1 + m; const Energy M2min = m2 + m; const Energy M1max = Mc - M2min; const Energy M2max = Mc - M1min; assert(M1max-M1min>ZERO); assert(M2max-M2min>ZERO); double r1; double r2; int counter = 0; const int max_counter = 200; while (counter < max_counter) { r1 = UseRandom::rnd(); r2 = UseRandom::rnd(); M1 = (M1max-M1min)*r1 + M1min; M2 = (M2max-M2min)*r2 + M2min; counter++; if (sqr(M1+M2)>sqr(Mc)) continue; // if (!phaseSpaceVeto(Mc,M1,M2,m,m1,m2) ) break; // For FlatPhaseSpace sampling vetoing if (!phaseSpaceVeto(Mc,M1,M2,m,m1,m2, ptrQ1, ptrQ2, ptrQ) ) break; // For FlatPhaseSpace sampling vetoing } if (counter==max_counter) return 0.0; // failure pClu1.setMass(M1); pClu2.setMass(M2); return weightFlatPhaseSpace(Mc, M1, M2, m, m1, m2, ptrQ1, ptrQ2, ptrQ); // succeeds return weight } void SoftClusterFissioner::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; double weight; // double factorPS; 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++; minMass=spectrum()->massLightestHadronPair(pD1,pD2); - if (_fissionCluster==0) choice.insert(_hadronSpectrum->pwtQuark(id),id); - else if (abs(_fissionCluster)==1) choice.insert(_fissionPwt.find(id)->second,id); + if (_fissionCluster==0) choice.insert(spectrum()->pwtQuark(id),id); + else if (abs(_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); Energy mH1=spectrum()->massLightestHadron(pD2,cand); Energy mH2=spectrum()->massLightestHadron(cand,pD1); // factorPS = Kinematics::pstarTwoBodyDecay(Mc,mH1,mH2)/(Mc/2.0); // factorPS = 1.0; minMass = mH1 + mH2; } else { minMass = spectrum()->massLightestBaryonPair(pD1,pD2); // factorPS = 1.0; } if (Mc < minMass) continue; // countDiQ++; - if (_fissionCluster==0) weight = _hadronSpectrum->pwtQuark(id); - else if (abs(_fissionCluster)==1) weight = _fissionPwt.find(id)->second; + if (_fissionCluster==0) weight = spectrum()->pwtQuark(id); + else if (abs(_fissionCluster)==1) weight = fissionPwt().find(id)->second; else assert(false); if (_fissionCluster==-1) weight*=sqr(Herwig::Math::alphaS(Mc, 0.25*GeV,3, 2)); // weight/=factorPS; choice.insert(weight,id); } 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) weight = _hadronSpectrum->pwtQuark(id); - else if (abs(_fissionCluster)==1) weight = _fissionPwt.find(id)->second; + if (_fissionCluster==0) weight = spectrum()->pwtQuark(id); + else if (abs(_fissionCluster)==1) weight = fissionPwt().find(id)->second; else assert(false); if (_fissionCluster==-1) weight*=sqr(Herwig::Math::alphaS(Mc, 0.25*GeV,3, 2)); choice.insert(weight,id); } 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) weight = _hadronSpectrum->pwtQuark(id); - else if (abs(_fissionCluster)==1) weight = _fissionPwt.find(id)->second; + if (_fissionCluster==0) weight = spectrum()->pwtQuark(id); + else if (abs(_fissionCluster)==1) weight = fissionPwt().find(id)->second; else assert(false); if (_fissionCluster==-1) weight*=sqr(Herwig::Math::alphaS(Mc, 0.25*GeV,3, 2)); choice.insert(weight,id); } 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 SoftClusterFissioner::drawNewFlavourQuarks(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(abs(_fissionCluster)){ case 0: for ( const long& id : spectrum()->lightHadronizingQuarks() ) - choice.insert(_hadronSpectrum->pwtQuark(id),id); + choice.insert(spectrum()->pwtQuark(id),id); break; case 1: for ( const long& id : spectrum()->lightHadronizingQuarks() ) - choice.insert(_fissionPwt.find(id)->second,id); + 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 SoftClusterFissioner::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(abs(_fissionCluster)){ // 0: SoftClusterFissioner and ClusterDecayer use the same weights case 0: - prob_d = _hadronSpectrum->pwtQuark(ParticleID::d); - prob_u = _hadronSpectrum->pwtQuark(ParticleID::u); + prob_d = spectrum()->pwtQuark(ParticleID::d); + prob_u = spectrum()->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); + prob_s = (_maxScale < scale) ? 0. : pow(spectrum()->pwtQuark(ParticleID::s),scale); else if (_enhanceSProb == 2) prob_s = (_maxScale < scale) ? 0. : exp(-scale); break; /* 1: SoftClusterFissioner 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; + 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); + 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 SoftClusterFissioner::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 SoftClusterFissioner::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; } } std::pair<Axis,double> SoftClusterFissioner::sampleDirectionConstituents( const Lorentz5Momentum & pClu, const Energy Mcluster) const { switch (_phaseSpaceSamplerConstituent) { case 0: { // Aligned return std::make_pair(pClu.vect().unit(),1.0); } case 1: { // Isotropic return std::make_pair(sampleDirectionIsotropic(),1.0); } case 2: { // Exponential // New // double C_est_Exponential=1.18; // double power_est_Exponential=0.65; // Old double C_est_Exponential=0.63; double power_est_Exponential=0.89; double lambda=C_est_Exponential*pow(Mcluster/GeV,power_est_Exponential); return sampleDirectionExponential(pClu.vect().unit(), lambda); } default: assert(false); break; } return std::make_pair(Axis(),0.0); // Failure } std::pair<Axis,double> SoftClusterFissioner::sampleDirectionCluster( const Lorentz5Momentum & pQ, const Lorentz5Momentum & pClu) const { switch (_phaseSpaceSamplerCluster) { case 0: { // Aligned Axis dir; if (_covariantBoost) // in Covariant Boost the positive z-Axis is defined as the direction of // the pQ vector in the Cluster rest frame dir = Axis(0,0,1); else dir = sampleDirectionAligned(pQ, pClu); return std::make_pair(dir,1.0); } case 1: { // Isotropic return std::make_pair(sampleDirectionIsotropic(),1.0); } case 2: { // Tchannel Energy M=pClu.mass(); // New // double C_est_Tchannel=3.66; // double power_est_Tchannel=-1.95; // Old double C_est_Tchannel=9.78; double power_est_Tchannel=-2.5; double A = C_est_Tchannel*pow(M/GeV,power_est_Tchannel); double Ainv = 1.0/(1.0+A); return sampleDirectionTchannel(sampleDirectionAligned(pQ,pClu),Ainv); } default: assert(false); } return std::make_pair(Axis(),0.0); // Failure } std::pair<Axis,double> SoftClusterFissioner::sampleDirectionExponential(const Axis dirQ, const double lambda) const { Axis FinalDir = dirQ; double cosTheta = Kinematics::sampleCosExp(lambda); double phi; // std::cout << "cosThetaSamp = "<< cosTheta <<"\tlambda = " <<lambda << std::endl; // If no change in angle keep the direction fixed if (fabs(cosTheta-1.0)>1e-14) { // rotate to sampled angles FinalDir.rotate(acos(cosTheta),dirQ.orthogonal()); phi = UseRandom::rnd(-Constants::pi,Constants::pi); FinalDir.rotate(phi,dirQ); } // std::cout << "cosThetaTrue = "<< FinalDir.cosTheta(dirQ) <<"\tlambda = " <<lambda << std::endl; double weight = exp(lambda*(cosTheta-1.0)); if (std::isnan(weight) ||std::isinf(weight) ) std::cout << "lambda = " << lambda << "\t cos " << cosTheta<< std::endl; assert(!std::isnan(weight)); assert(!std::isinf(weight)); if (fabs(FinalDir.cosTheta(dirQ)-cosTheta)>1e-8) std::cout << "cosThetaTrue = "<< FinalDir.cosTheta(dirQ) <<"\tlambda = " <<lambda << std::endl; return std::make_pair(FinalDir,weight); } std::pair<Axis,double> SoftClusterFissioner::sampleDirectionTchannel(const Axis dirQ, const double Ainv) const { double cosTheta = Kinematics::sampleCosTchannel(Ainv); double phi; Axis FinalDir = dirQ; // If no change in angle keep the direction fixed if (fabs(cosTheta-1.0)>1e-14) { // rotate to sampled angles FinalDir.rotate(acos(cosTheta),dirQ.orthogonal()); phi = UseRandom::rnd(-Constants::pi,Constants::pi); FinalDir.rotate(phi,dirQ); } double weight = 0.0; if (1.0-Ainv>1e-10) weight=1.0/pow(1.0/Ainv-cosTheta,2.0); else weight=1.0/pow((1.0-cosTheta)+(1.0-Ainv),2.0); if (std::isnan(weight) ||std::isinf(weight) ) std::cout << "Ainv = " << Ainv << "\t cos " << cosTheta<< std::endl; assert(!std::isnan(weight)); assert(!std::isinf(weight)); if (fabs(FinalDir.cosTheta(dirQ)-cosTheta)>1e-8) std::cout << "cosThetaTrue = "<< FinalDir.cosTheta(dirQ) <<"\tAinv = " <<Ainv << std::endl; return std::make_pair(FinalDir,weight); } Axis SoftClusterFissioner::sampleDirectionAligned(const Lorentz5Momentum & pQ, const Lorentz5Momentum & pClu) const { Lorentz5Momentum pQinCOM(pQ); pQinCOM.setMass(pQ.m()); pQinCOM.boost( -pClu.boostVector() ); // boost from LAB to C return pQinCOM.vect().unit(); } Axis SoftClusterFissioner::sampleDirectionAlignedSmeared(const Lorentz5Momentum & pQ, const Lorentz5Momentum & pClu) const { Lorentz5Momentum pQinCOM(pQ); pQinCOM.setMass(pQ.m()); pQinCOM.boost( -pClu.boostVector() ); // boost from LAB to C if (pQinCOM.vect().mag2()<=ZERO){ return sampleDirectionAlignedSmeared(pClu-pQ,pClu); } const Axis dir = pQinCOM.vect().unit(); double cluSmear = 0.5; double cosTheta; do { cosTheta = 1.0 + cluSmear*log( UseRandom::rnd() ); } while (fabs(cosTheta)>1.0 || std::isnan(cosTheta) || std::isinf(cosTheta)); if (!(dir.mag2()>ZERO)){ std::cout << "\nDRI = 0"<< dir.mag2() << std::endl; } Axis dirSmeared = dir; double phi; if (fabs(cosTheta-1.0)>1e-10) { // rotate to sampled angles dirSmeared.rotate(acos(cosTheta),dir.orthogonal()); phi = UseRandom::rnd(-M_PI,M_PI); dirSmeared.rotate(phi,dir); } if (!(dirSmeared.mag2()>ZERO)){ std::cout << "\nDRI SMR = 0" <<cosTheta<< "\t" <<acos(cosTheta)<< "\t"<< phi<< "\t"<< dirSmeared.mag2() << std::endl; std::cout << dir.orthogonal() << std::endl; } return dirSmeared; } Axis SoftClusterFissioner::sampleDirectionIsotropic() 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)*sinTheta, sin(Phi)*sinTheta, cosTheta); return uClusterUniform.unit(); } Axis SoftClusterFissioner::sampleDirectionSemiUniform(const Lorentz5Momentum & pQ, const Lorentz5Momentum & pClu) const { Axis dir = sampleDirectionAligned(pQ,pClu); Axis res; do { res=sampleDirectionIsotropic(); } while (dir*res<0); return res; } namespace { double SoftFunction( const Lorentz5Momentum & pi, const Lorentz5Momentum & pj, const Lorentz5Momentum & q, const Lorentz5Momentum & qbar) { Energy2 mq2 = q*q; double numerator = -(pi*pj)*(q*qbar+mq2)/sqr(GeV2); double denominator = sqr(q*qbar+mq2)*(pi*(q+qbar))*(pj*(q+qbar))/sqr(GeV2*GeV2); int cataniScheme = 1; // Different expressions which should yield similar results switch (cataniScheme) { case 0: numerator += ((pi*q)*(pj*qbar) + (pj*q)*(pi*qbar))/sqr(GeV2); break; case 1: numerator += - (0.5*(pi*(q-qbar))*(pj*(q-qbar)))/sqr(GeV2); break; default: assert(false); } double Iij = numerator/denominator; return Iij; } } /* SQME for p1,p2->C1(q1,q),C2(q2,qbar) * Note that: * p0Q1 -> p1 * p0Q2 -> p2 * pQ1 -> q1 * pQone -> q * pQ2 -> q2 * pQone -> qbar * */ double SoftClusterFissioner::calculateSQME( const Lorentz5Momentum & p1, const Lorentz5Momentum & p2, const Lorentz5Momentum & q1, const Lorentz5Momentum & q, const Lorentz5Momentum & q2, const Lorentz5Momentum & qbar) const { double SQME; switch (_matrixElement) { case 0: SQME = 1.0; break; case 1: { /* // Energy2 p1p2 = p1*p2; Energy2 q1q2 = q1 * q2; Energy2 q1q = q1 * q ; Energy2 q2qbar = q2 * qbar; Energy2 q2q = q2 * q ; Energy2 q1qbar = q1 * qbar; Energy2 qqbar = q * qbar; Energy2 mq2 = q.mass2(); double Numerator = q1q2 * (qqbar + mq2)/sqr(GeV2); Numerator += 0.5 * (q1q - q1qbar)*(q2q - q2qbar)/sqr(GeV2); double Denominator = sqr(qqbar + mq2)*(q1q + q1qbar)*(q2q + q2qbar)/sqr(sqr(GeV2)); SQME = Numerator/Denominator; */ double I11 = SoftFunction(q1,q1,q,qbar); double I22 = SoftFunction(q2,q2,q,qbar); double I12 = SoftFunction(q1,q2,q,qbar); SQME = (I11+I22-2.0*I12); if (SQME<0.0) { std::cout << "I11 = " << I11<< std::endl; std::cout << "I22 = " << I22<< std::endl; std::cout << "2*I12 = " << I12<< std::endl; std::cout << "soft = " << I11+I22-2.0*I12<< std::endl; std::cout << "M = " << (p1+p2).m()/GeV<< std::endl; std::cout << "M1 = " << (q1+q).m()/GeV<< std::endl; std::cout << "M2 = " << (q2+qbar).m()/GeV<< std::endl; std::cout << "m1 = " << (q1).m()/GeV<< std::endl; std::cout << "m2 = " << (q2).m()/GeV<< std::endl; std::cout << "m = " << (q).m()/GeV<< std::endl; } break; } case 2: { /* Energy2 p1p2 = p1 * p2; Energy2 p1q = p1 * q ; Energy2 p2qbar = p2 * qbar; Energy2 p2q = p2 * q ; Energy2 p1qbar = p1 * qbar; Energy2 qqbar = q * qbar; Energy2 mq2 = q.mass2(); double Numerator = p1p2 * (qqbar + mq2)/sqr(GeV2); Numerator += 0.5 * (p1q - p1qbar)*(p2q - p2qbar)/sqr(GeV2); double Denominator = sqr(qqbar + mq2)*(p1q + p1qbar)*(p2q + p2qbar)/sqr(sqr(GeV2)); SQME = Numerator/Denominator; */ double I11 = SoftFunction(p1,p1,q,qbar); double I22 = SoftFunction(p2,p2,q,qbar); double I12 = SoftFunction(p1,p2,q,qbar); SQME = (I11+I22-2.0*I12); if (SQME<0.0) { std::cout << "I11 = " << I11<< std::endl; std::cout << "I22 = " << I22<< std::endl; std::cout << "2*I12 = " << I12<< std::endl; std::cout << "soft = " << I11+I22-2.0*I12<< std::endl; std::cout << "M = " << (p1+p2).m()/GeV<< std::endl; std::cout << "M1 = " << (q1+q).m()/GeV<< std::endl; std::cout << "M2 = " << (q2+qbar).m()/GeV<< std::endl; std::cout << "m1 = " << (q1).m()/GeV<< std::endl; std::cout << "m2 = " << (q2).m()/GeV<< std::endl; std::cout << "m = " << (q).m()/GeV<< std::endl; } break; } case 4: { SQME = sqr(sqr(GeV2))/(sqr(q*qbar-q*q)*(p1*(q1+q)-p1*p1-sqrt((p1*p1)*(q*q)))*(p2*(q2+qbar)-p2*p2-sqrt((p2*p2)*(q*q)))); break; } case 5: { /* Energy2 p1p2 = p1 * p2; Energy2 p1q = p1 * q ; Energy2 p2qbar = p2 * qbar; Energy2 p2q = p2 * q ; Energy2 p1qbar = p1 * qbar; Energy2 qqbar = q * qbar; Energy2 mq2 = q.mass2(); double Numerator = p1p2 * (qqbar + mq2)/sqr(GeV2); Numerator += 0.5 * (p1q - p1qbar)*(p2q - p2qbar)/sqr(GeV2); double Denominator = sqr(qqbar + mq2)*(p1q + p1qbar)*(p2q + p2qbar)/sqr(sqr(GeV2)); // add test Tchannel Matrix Element interference with gluon mass regulated // Denominator *= sqr(sqr(p1-q1)-_epsilonIR*sqrt((p1*p1)*(p2*p2)))/sqr(GeV2); */ Energy mg=getParticleData(spectrum()->gluonId())->constituentMass(); double Denominator = (sqr(p1-q1)-_epsilonIR*mg*mg)/(GeV2); Denominator *= (sqr(p2-q2)-_epsilonIR*mg*mg)/(GeV2); double Numerator = ((q1*q2)*(p1*p2)+(p2*q1)*(p1*q2))/sqr(GeV2); // double I11 = SoftFunction(p1,p1,q,qbar); // double I22 = SoftFunction(p2,p2,q,qbar); // double I12 = SoftFunction(p1,p2,q,qbar); double I11 = SoftFunction(q1,q1,q,qbar); double I22 = SoftFunction(q2,q2,q,qbar); double I12 = SoftFunction(q1,q2,q,qbar); SQME = Numerator*(I11+I22-2.0*I12)/Denominator; if (SQME<0.0) { std::cout << "I11 = " << I11<< std::endl; std::cout << "I22 = " << I22<< std::endl; std::cout << "2*I12 = " << I12<< std::endl; std::cout << "soft = " << I11+I22-2.0*I12<< std::endl; std::cout << "Numerator = " << Numerator<< std::endl; std::cout << "Denominator = " << Denominator<< std::endl; std::cout << "M = " << (p1+p2).m()/GeV<< std::endl; std::cout << "M1 = " << (q1+q).m()/GeV<< std::endl; std::cout << "M2 = " << (q2+qbar).m()/GeV<< std::endl; std::cout << "m1 = " << (q1).m()/GeV<< std::endl; std::cout << "m2 = " << (q2).m()/GeV<< std::endl; std::cout << "m = " << (q).m()/GeV<< std::endl; } break; } default: assert(false); } if (SQME < 0) throw Exception() << "Squared Matrix Element = "<< SQME <<" < 0 in SoftClusterFissioner::calculateSQME() " << Exception::runerror; return SQME; } /* Overestimate for SQME for p1,p2->C1(q1,q),C2(q2,qbar) * Note that: * p0Q1 -> p1 where Mass -> m1 * p0Q2 -> p2 where Mass -> m2 * pQ1 -> q1 where Mass -> m1 * pQone -> q where Mass -> mq * pQ2 -> q2 where Mass -> m2 * pQone -> qbar where Mass -> mq * */ double SoftClusterFissioner::calculateSQME_OverEstimate( const Energy& Mc, const Energy& m1, const Energy& m2, const Energy& mq ) const { double SQME_OverEstimate; switch (_matrixElement) { case 0: SQME_OverEstimate = 1.0; break; case 1: { // Fit factor for guess of best overestimate double A = 0.25; SQME_OverEstimate = _safetyFactorMatrixElement*A*pow(mq/GeV,-4); break; } case 2: { // Fit factor for guess of best overestimate double A = 0.25; SQME_OverEstimate = _safetyFactorMatrixElement*A*pow(mq/GeV,-4); break; } case 4: { // Fit factor for guess of best overestimate SQME_OverEstimate = _safetyFactorMatrixElement*sqr(sqr(GeV2))/(sqr(mq*mq)*m1*m2*(m1+mq)*(m2+mq)); break; } case 5: { // Fit factor for guess of best overestimate // Energy MassMax = 8.550986578849006037e+00*GeV; // mass max of python // double wTotMax = 5.280507222727160297e+03; // at MassMax // double argExp = 55.811; // from fit of python package // double powLog = 0.08788; // from fit of python package Energy MassMax = 5.498037126562503119e+01*GeV; // mass max of python double wTotMax = 1.570045806367627112e+06; // at MassMax double argExp = 16.12; // from fit of python package double powLog = 0.3122; // from fit of python package // Energy2 p1p2=0.5*(Mc*Mc-m1*m1-m2*m2); // SQME_OverEstimate = _safetyFactorMatrixElement*A*pow(mq/GeV,-4)*(2*sqr(p1p2))/sqr(_epsilonIR*(m1*m2)); Energy Mmin = (m1+m2+2*mq); // Energy MdblPrec = Mmin*exp(pow(pow(log(MassMax/Mmin),powLog)+600.0/argExp,1.0/powLog)); // if (Mc >MdblPrec) // std::cout << "errroRRRRR R MC = " << Mc/GeV << "\t Mcmax "<< MdblPrec/GeV<< std::endl; if (MassMax<Mmin) MassMax=Mmin; double Overestimate = wTotMax*exp(argExp*(pow(log(Mc/Mmin),powLog)-pow(log(MassMax/Mmin),powLog))); SQME_OverEstimate = _safetyFactorMatrixElement*Overestimate;//*A*pow(mq/GeV,-4)*(2*sqr(p1p2))/sqr(_epsilonIR*(m1*m2)); if (SQME_OverEstimate==0 || std::isinf(SQME_OverEstimate)|| std::isnan(SQME_OverEstimate)) { std::cout << "SQME_OverEstimate is " << SQME_OverEstimate << std::endl; std::cout << "Overestimate is " << Overestimate << std::endl; std::cout << "MC " << Mc/GeV << std::endl; std::cout << "m " << mq/GeV << std::endl; std::cout << "m1 " << m1/GeV << std::endl; std::cout << "m2 " << m2/GeV << std::endl; } break; } default: assert(false); } return SQME_OverEstimate; } double SoftClusterFissioner::drawKinematics( const Lorentz5Momentum & pClu, const Lorentz5Momentum & p0Q1, const Lorentz5Momentum & p0Q2, const bool toHadron1, const bool toHadron2, Lorentz5Momentum & pClu1, Lorentz5Momentum & pClu2, Lorentz5Momentum & pQ1, Lorentz5Momentum & pQbar, Lorentz5Momentum & pQ, Lorentz5Momentum & pQ2bar) const { double weightTotal=0.0; if (pClu.mass() < pClu1.mass() + pClu2.mass() || pClu1.mass()<ZERO || pClu2.mass()<ZERO ) { throw Exception() << "Impossible Kinematics in SoftClusterFissioner::drawKinematics() (A)\n" << "Mc = "<< pClu.mass()/GeV <<" GeV\n" << "Mc1 = "<< pClu1.mass()/GeV <<" GeV\n" << "Mc2 = "<< pClu2.mass()/GeV <<" GeV\n" << Exception::eventerror; } // Sample direction of the daughter clusters std::pair<Axis,double> directionCluster = sampleDirectionCluster(p0Q1, pClu); Axis DirToClu = directionCluster.first; weightTotal = directionCluster.second; if (0 && _writeOut) { ofstream out("WriteOut/test_Cluster.dat",std::ios::app); Lorentz5Momentum pQinCOM(p0Q1); pQinCOM.setMass(p0Q1.m()); pQinCOM.boost( -pClu.boostVector() ); // boost from LAB to C out << DirToClu.cosTheta(pQinCOM.vect()) << "\n"; } if (_covariantBoost) { const Energy M = pClu.mass(); const Energy M1 = pClu1.mass(); const Energy M2 = pClu2.mass(); const Energy PcomClu=Kinematics::pstarTwoBodyDecay(M,M1,M2); Momentum3 pClu1sampVect( PcomClu*DirToClu); Momentum3 pClu2sampVect(-PcomClu*DirToClu); pClu1.setMass(M1); pClu1.setVect(pClu1sampVect); pClu1.rescaleEnergy(); pClu2.setMass(M2); pClu2.setVect(pClu2sampVect); pClu2.rescaleEnergy(); } else { Lorentz5Momentum pClutmp(pClu); pClutmp.boost(-pClu.boostVector()); Kinematics::twoBodyDecay(pClutmp, pClu1.mass(), pClu2.mass(),DirToClu, pClu1, pClu2); } // 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 SoftClusterFissioner::drawKinematics() (B)" << Exception::eventerror; } std::pair<Axis,double> direction1 = sampleDirectionConstituents(pClu1,pClu.mass()); // Need to boost constituents first into the pClu rest frame // boost from Cluster1 rest frame to Cluster COM Frame // Kinematics::twoBodyDecay(pClu1, pQ1.mass(), pQbar.mass(), DirClu1, pQ1, pQbar); Kinematics::twoBodyDecay(pClu1, pQ1.mass(), pQbar.mass(), direction1.first, pQ1, pQbar); weightTotal*=direction1.second; if (0 && _writeOut) { ofstream out("WriteOut/test_Constituents1.dat",std::ios::app); Lorentz5Momentum pQ1inCOM(pQ1); pQ1inCOM.setMass(pQ1.m()); pQ1inCOM.boost( -pClu1.boostVector() ); // boost from LAB to C out << pQ1inCOM.vect().cosTheta(pClu1.vect()) << "\n"; } } // 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 SoftClusterFissioner::drawKinematics() (C)" << Exception::eventerror; } std::pair<Axis,double> direction2 = sampleDirectionConstituents(pClu2,pClu.mass()); // boost from Cluster2 rest frame to Cluster COM Frame Kinematics::twoBodyDecay(pClu2, pQ2bar.mass(), pQ.mass(), direction2.first, pQ2bar, pQ); weightTotal*=direction2.second; if (0 && _writeOut) { ofstream out("WriteOut/test_Constituents2.dat",std::ios::app); Lorentz5Momentum pQ2inCOM(pQ2bar); pQ2inCOM.setMass(pQ2bar.m()); pQ2inCOM.boost( -pClu2.boostVector() ); // boost from LAB to C out << pQ2inCOM.vect().cosTheta(pClu2.vect()) << "\n"; } } // Boost all momenta from the Cluster COM frame to the Lab frame if (_covariantBoost) { std::vector<Lorentz5Momentum *> momenta; momenta.push_back(&pClu1); momenta.push_back(&pClu2); if (!toHadron1) { momenta.push_back(&pQ1); momenta.push_back(&pQbar); } if (!toHadron2) { momenta.push_back(&pQ); momenta.push_back(&pQ2bar); } Kinematics::BoostIntoTwoParticleFrame(pClu.mass(),p0Q1, p0Q2, momenta); } else { pClu1.boost(pClu.boostVector()); pClu2.boost(pClu.boostVector()); pQ1.boost(pClu.boostVector()); pQ2bar.boost(pClu.boostVector()); pQ.boost(pClu.boostVector()); pQbar.boost(pClu.boostVector()); } return weightTotal; // success } void SoftClusterFissioner::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 **************************/ // Calculate the unit three-vector, in the C frame, along which // all of the constituents and children clusters move. Lorentz5Momentum u(p0Q1); u.boost( -pClu.boostVector() ); // boost from LAB to C // the unit three-vector is then u.vect().unit() // Calculate the momenta of C1 and C2 in the (parent) C frame first, // where the direction of C1 is u.vect().unit(), and then boost back in the // LAB frame. if (pClu.m() < pClu1.mass() + pClu2.mass() ) { throw Exception() << "Impossible Kinematics in SoftClusterFissioner::calculateKinematics() (A)" << Exception::eventerror; } Kinematics::twoBodyDecay(pClu, pClu1.mass(), pClu2.mass(), u.vect().unit(), pClu1, pClu2); // 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 SoftClusterFissioner::calculateKinematics() (B)" << Exception::eventerror; } Kinematics::twoBodyDecay(pClu1, pQ1.mass(), pQbar.mass(), u.vect().unit(), 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 SoftClusterFissioner::calculateKinematics() (C)" << Exception::eventerror; } Kinematics::twoBodyDecay(pClu2, pQ.mass(), pQ2bar.mass(), u.vect().unit(), pQ, pQ2bar); } } void SoftClusterFissioner::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 SoftClusterFissioner::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 SoftClusterFissioner::ProbablityFunctionTest(double Mass, double threshold) { - double cut = UseRandom::rnd(0.0,1.0); - // double arg = scale/threshold; - // double epsilon = 1e-5; - // if (arg<(1.0+epsilon) ){ - // // std::cout << "arg = "<<arg << std::endl; - // arg=1.0+epsilon; - // } - // return 1./(1.+_probPowFactor*pow(log(arg),-2.0)) > cut ? true : false; - if ((Mass-threshold)<=0) - return false; - return 1.0/(1.0 + _probPowFactor*pow(1.0/(Mass-threshold),_clPowLight)) > cut ? true : false; -} - - -bool SoftClusterFissioner::isHeavy(tcClusterPtr clu) { - // particle data for constituents - tcPDPtr cptr[3]={tcPDPtr(),tcPDPtr(),tcPDPtr()}; - bool hasDiquark=0; - for(size_t ix=0;ix<min(clu->numComponents(),3);++ix) { - cptr[ix]=clu->particle(ix)->dataPtr(); - // Assuming diquark masses are ordered with larger id corresponding to larger masses - if (DiquarkMatcher::Check(*(cptr[ix]))) { - hasDiquark=true; - break; - } - } - // 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; - // if no heavy quark is found in the cluster, but diquarks are present use - // different ClMax and ClPow - if ( hasDiquark) { - clpow = _clPowDiquark; - clmax = _clMaxDiquark; - } - - 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); - } - // probablistic kinematic threshold - else if(_kinematicThresholdChoice == 2) { - //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); - - double Mass = clu->mass()/GeV; - double threshold = clu->sumConstituentMasses()/GeV + 2.0 * minmass/GeV; - aboveCutoff = ProbablityFunctionTest(Mass,threshold + clmax/GeV); - - canSplitMinimally = Mass - threshold>ZERO; - } - - return aboveCutoff && canSplitMinimally; -} diff --git a/Hadronization/SoftClusterFissioner.h b/Hadronization/SoftClusterFissioner.h --- a/Hadronization/SoftClusterFissioner.h +++ b/Hadronization/SoftClusterFissioner.h @@ -1,736 +1,698 @@ // -*- C++ -*- // // SoftClusterFissioner.h is a part of Herwig - A multi-purpose Monte Carlo event generator // Copyright (C) 2002-2019 The Herwig Collaboration // // Herwig is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // #ifndef HERWIG_SoftClusterFissioner_H #define HERWIG_SoftClusterFissioner_H #include <ThePEG/Interface/Interfaced.h> #include "CluHadConfig.h" #include "ClusterFissioner.h" -#include "SoftClusterFissioner.h" #include "HadronSpectrum.h" namespace Herwig { using namespace ThePEG; /** \ingroup Hadronization * \class SoftClusterFissioner * \brief This class handles clusters which are too heavy by a semi-perturbative cluster fission matrix element. * \author Stefan Kiebacher * * @see \ref SoftClusterFissionerInterfaces "The interfaces" * defined for SoftClusterFissioner. */ class SoftClusterFissioner: public ClusterFissioner { public: /** @name Standard constructors and destructors. */ //@{ /** * Default constructor. */ SoftClusterFissioner(); /** * Default destructor. */ virtual ~SoftClusterFissioner(); //@} virtual tPVector fission(ClusterVector & clusters, bool softUEisOn); public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * Standard Init function used to initialize the interfaces. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * Private and non-existent assignment operator. */ SoftClusterFissioner & operator=(const SoftClusterFissioner &) = delete; void cut(stack<ClusterPtr> &, ClusterVector&, tPVector & finalhadrons, bool softUEisOn); public: /** * Definition for easy passing of two particles. */ typedef pair<PPtr,PPtr> PPair; /** * Definition for use in the cut function. */ typedef pair<PPair,PPair> cutType; /** * Splits the input cluster. * * Split the input cluster (which can be either an heavy non-beam * cluster or a beam cluster). The result is two pairs of particles. The * first element of each pair is new cluster/hadron, while the second * element of each pair is the particle drawn from the vacuum to create * the new cluster/hadron. * Notice that this method treats also beam clusters by using a different * mass spectrum used to generate the cluster child masses (see method * drawChildMass). */ //@{ /** * Split two-component cluster */ virtual cutType cutTwo(ClusterPtr &, tPVector & finalhadrons, bool softUEisOn); /** * Split two-component cluster using Default FissionApproach */ virtual cutType cutTwoDefault(ClusterPtr &, tPVector & finalhadrons, bool softUEisOn); /** * Split two-component cluster using New FissionApproach */ virtual cutType cutTwoNew(ClusterPtr &, tPVector & finalhadrons, bool softUEisOn); /** * Split three-component cluster */ virtual cutType cutThree(ClusterPtr &, tPVector & finalhadrons, bool softUEisOn); //@} public: /** * Produces a hadron and returns the flavour drawn from the vacuum. * * This routine produces a new hadron. It * also sets the momentum and vertex to the values given. */ PPair produceHadron(tcPDPtr hadron, tPPtr newPtr, const Lorentz5Momentum &a, const LorentzPoint &b) const; protected: /** * Produces a cluster from the flavours passed in. * * This routine produces a new cluster with the flavours given by ptrQ and newPtr. * The new 5 momentum is a and the parent momentum are c and d. C is for the * ptrQ and d is for the new particle newPtr. rem specifies whether the existing * particle is a beam remnant or not. */ PPair produceCluster(tPPtr ptrQ, tPPtr newPtr, const Lorentz5Momentum &a, const LorentzPoint &b, const Lorentz5Momentum &c, const Lorentz5Momentum &d, const bool rem, tPPtr spect=tPPtr(), bool remSpect=false) const; /** * Returns the new quark-antiquark pair * needed for fission of a heavy cluster. Equal probabilities * are assumed for producing u, d, or s pairs. */ void drawNewFlavourQuarks(PPtr& newPtrPos,PPtr& newPtrNeg) const; /** * Returns the new quark-antiquark pair or diquark - * antidiquark pair needed for fission of a heavy cluster. */ void drawNewFlavourDiquarks(PPtr& newPtrPos,PPtr& newPtrNeg, const ClusterPtr & clu) const; /** * Returns the new quark-antiquark pair * needed for fission of a heavy cluster. Equal probabilities * are assumed for producing u, d, or s pairs. * Extra argument is used when performing strangeness enhancement */ void drawNewFlavourEnhanced(PPtr& newPtrPos,PPtr& newPtrNeg, Energy2 mass2) const; /** * Produces the mass of a child cluster. * * Draw the masses \f$M'\f$ of the the cluster child produced * by the fission of an heavy cluster (of mass M). m1, m2 are the masses * of the constituents of the cluster; m is the mass of the quark extract * from the vacuum (together with its antiparticle). The algorithm produces * the mass of the cluster formed with consituent m1. * Two mass distributions can be used for the child cluster mass: * -# power-like mass distribution ("normal" mass) with power exp * \f[ M' = {\rm rnd}((M-m_1-m_2-m)^P, m^p)^{1/P} + m_1 \f] * where \f$ P \f$ is a parameter of the model and \f$ \rm{rnd} \f$ is * the function: * \f[ \rm{rnd}(a,b) = (1-r)a + r b \f] * and here \f$ r \f$ is a random number [0,1]. * -# fast-decreasing exponential mass distribution ("soft" mass) with * rmin. rmin is given by * \f[ r_{\rm min} = \exp(-b (M - m_1 - m_2 - 2 m)) \f] * where \f$ b \f$ is a parameter of the model. The generated mass is * given by * \f[ M' = m_1 + m - \frac{\log\left( * {\rm rnd}(r_{\rm min}, 1-r_{\rm min})\right)}{b} \f]. * * The choice of which mass distribution should be used for each of the two * cluster children is dictated by the parameter soft. */ Energy drawChildMass(const Energy M, const Energy m1, const Energy m2, const Energy m, const double exp, const bool soft) const; /** * Determine the positions of the two children clusters. * * This routine generates the momentum of the decay products. It also * generates the momentum in the lab frame of the partons drawn out of * the vacuum. */ void calculatePositions(const Lorentz5Momentum &pClu, const LorentzPoint & positionClu, const Lorentz5Momentum & pClu1, const Lorentz5Momentum & pClu2, LorentzPoint & positionClu1, LorentzPoint & positionClu2 ) const; protected: /** * Dimension used to calculate phase space weights */ double dim() const {return _dim;} /** * Access to soft-cluster parameter */ Energy btClM() const {return _btClM;} /** * Function that returns either the cluster mass or the lambda measure */ Energy2 clustermass(const ClusterPtr & cluster) const; /** * old calculateKinematics function for Default FissionApproach */ void 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; /** * Draw a new flavour for the given cluster; currently defaults to * the default model */ virtual void drawNewFlavour(PPtr& newPtr1, PPtr& newPtr2, const ClusterPtr & cluster) const { if (_enhanceSProb == 0){ if (_diquarkClusterFission>=0) drawNewFlavourDiquarks(newPtr1,newPtr2,cluster); else drawNewFlavourQuarks(newPtr1,newPtr2); } else { drawNewFlavourEnhanced(newPtr1,newPtr2,clustermass(cluster)); } } /** * Calculate the masses and possibly kinematics of the cluster * fission at hand; if claculateKineamtics is perfomring non-trivial * steps kinematics claulcated here will be overriden. Currentl;y resorts to the default * @return the potentially non-trivial distribution weight=f(M1,M2) * On Failure we return 0 */ double drawNewMasses(const Energy Mc, const bool soft1, const bool soft2, Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, tcPPtr ptrQ1, const Lorentz5Momentum& pQ1, tcPPtr, const Lorentz5Momentum& pQone, tcPPtr, const Lorentz5Momentum& pQtwo, tcPPtr ptrQ2, const Lorentz5Momentum& pQ2) const; /** * Calculate the masses and possibly kinematics of the cluster * fission at hand; if claculateKineamtics is perfomring non-trivial * steps kinematics claulcated here will be overriden. Currentl;y resorts to the default */ double drawNewMassesDefault(const Energy Mc, const bool soft1, const bool soft2, Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, tcPPtr ptrQ1, const Lorentz5Momentum& pQ1, tcPPtr, const Lorentz5Momentum& pQone, tcPPtr, const Lorentz5Momentum& pQtwo, tcPPtr ptrQ2, const Lorentz5Momentum& pQ2) const; /** * Sample the masses for flat phase space * */ double drawNewMassesUniform(const Energy Mc, Lorentz5Momentum & pClu1, Lorentz5Momentum & pClu2, const Lorentz5Momentum & pQ1, const Lorentz5Momentum & pQ, const Lorentz5Momentum & pQ2) const; /** * Sample the masses for flat phase space * */ double drawNewMassesPhaseSpace(const Energy Mc, Lorentz5Momentum & pClu1, Lorentz5Momentum & pClu2, const Lorentz5Momentum & pQ1, const Lorentz5Momentum & pQ, const Lorentz5Momentum & pQ2, tcPPtr ptrQ1, tcPPtr ptrQ2, tcPPtr ptrQ ) const; /** * Sample the masses for flat phase space modulated by a power law * */ double drawNewMassesPhaseSpacePowerLaw(const Energy Mc, Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2, const Lorentz5Momentum& pQ1, const Lorentz5Momentum& pQ, const Lorentz5Momentum& pQ2, tcPPtr ptrQ1, tcPPtr ptrQ2, tcPPtr ptrQ) const; /** * Calculate the final kinematics of a heavy cluster decay C->C1 + * C2, if not already performed by drawNewMasses * @return returns false if failes */ double drawKinematics(const Lorentz5Momentum &pClu, const Lorentz5Momentum &p0Q1, const Lorentz5Momentum &p0Q2, const bool toHadron1, const bool toHadron2, Lorentz5Momentum &pClu1, Lorentz5Momentum &pClu2, Lorentz5Momentum &pQ1, Lorentz5Momentum &pQb, Lorentz5Momentum &pQ, Lorentz5Momentum &pQ2b) const; /** * Calculation of the squared matrix element from the drawn * momenta * @return value of the squared matrix element in units of * 1/GeV4 * */ double calculateSQME( const Lorentz5Momentum & p1, const Lorentz5Momentum & p2, const Lorentz5Momentum & q1, const Lorentz5Momentum & q, const Lorentz5Momentum & q2, const Lorentz5Momentum & qbar) const; /** * Calculation of the overestimate for the squared matrix * element independent on M1 and M2 * @return value of the overestimate squared matrix element * in units of 1/GeV4 * */ double calculateSQME_OverEstimate( const Energy& Mc, const Energy& m1, const Energy& m2, const Energy& mq) const; protected: /** @name Access members for child classes. */ //@{ /** * Access to the hadron selector */ - HadronSpectrumPtr hadronSpectrum() const {return _hadronSpectrum;} + // HadronSpectrumPtr hadronSpectrum() const {return _hadronSpectrum;} //@} protected: /** @name Standard Interfaced functions. */ //@{ /** * Initialize this object after the setup phase before saving an * EventGenerator to disk. * @throws InitException if object could not be initialized properly. */ virtual void doinit(); //@} private: std::pair<Axis,double> sampleDirectionCluster(const Lorentz5Momentum & pQ, const Lorentz5Momentum & pClu) const; std::pair<Axis,double> sampleDirectionConstituents(const Lorentz5Momentum & pClu, const Energy Mcluster) const; /** * Samples the direction of Cluster Fission products uniformly **/ Axis sampleDirectionIsotropic() const; /** * Samples the direction of Cluster Fission products uniformly * but only accepts those flying in the direction of pRelCOM **/ Axis sampleDirectionSemiUniform(const Lorentz5Momentum & pQ, const Lorentz5Momentum & pClu) const; /** * Samples the direction of Cluster Fission products according to Default * fully aligned D = 1+1 Fission * */ Axis sampleDirectionAligned(const Lorentz5Momentum & pQ, const Lorentz5Momentum & pClu) const; /** * Samples the direction of Cluster Fission products according to Default * Tchannel like distribution * */ std::pair<Axis,double> sampleDirectionTchannel(const Axis dirQ, const double Ainv) const; /** * Samples the direction of direction from an exponential distribution * */ std::pair<Axis,double> sampleDirectionExponential(const Axis dirQ, const double lambda) const; /** * Samples the direction of Cluster Fission products according to smeared direction along the cluster * */ Axis sampleDirectionAlignedSmeared(const Lorentz5Momentum & pQ, const Lorentz5Momentum & pClu) const; /** * Smooth probability for dynamic threshold cuts: * @scale the current scale, e.g. the mass of the cluster, * @threshold the physical threshold, */ bool ProbablityFunction(double scale, double threshold); bool ProbablityFunctionTest(double Mass, double threshold); /** * Check if a cluster is heavy enough to split again */ bool isHeavy(tcClusterPtr ); /** * Check if a cluster is heavy enough to be at least kinematically able to split */ bool canSplitMinimally(tcClusterPtr, Energy); /** * Check if can't make a hadron from the partons */ inline bool cantMakeHadron(tcPPtr p1, tcPPtr p2) { return ! spectrum()->canBeHadron(p1->dataPtr(), p2->dataPtr()); } /** * Flat PhaseSpace weight for ClusterFission */ double weightFlatPhaseSpace(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, tcPPtr pQ, tcPPtr pQ1, tcPPtr pQ2) const { switch (_phaseSpaceWeights) { case 1: return weightPhaseSpaceConstituentMasses(Mc, Mc1, Mc2, m, m1, m2, 0.0); case 2: return weightFlatPhaseSpaceHadronMasses(Mc, Mc1, Mc2, pQ, pQ1, pQ2); case 3: return weightFlatPhaseSpaceNoConstituentMasses(Mc, Mc1, Mc2); default: assert(false); } }; /** * PhaseSpace weight for ClusterFission using constituent masses */ double weightPhaseSpaceConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, const double power=0.0) const; /** * Flat PhaseSpace weight for ClusterFission using lightest hadron masses */ double weightFlatPhaseSpaceHadronMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, tcPPtr pQ, tcPPtr pQ1, tcPPtr pQ2) const; double weightFlatPhaseSpaceNoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2) const; /** * Calculate a veto for the phase space weight */ bool phaseSpaceVeto(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, tcPPtr pQ1=tcPPtr(), tcPPtr pQ2=tcPPtr(), tcPPtr pQ=tcPPtr(), const double power = 0.0) const; bool phaseSpaceVetoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2, const Energy m, const Energy m1, const Energy m2, const double power = 0.0) const; bool phaseSpaceVetoNoConstituentMasses(const Energy Mc, const Energy Mc1, const Energy Mc2) const; bool phaseSpaceVetoHadronPairs(const Energy Mc, const Energy Mc1, const Energy Mc2, tcPPtr pQ1, tcPPtr pQ2, tcPPtr pQconst) const; void countPaccGreater1(){ _counterPaccGreater1++; }; void countMaxLoopViolations(){ _counterMaxLoopViolations++; }; void countFissionMatrixElement(){ _counterFissionMatrixElement++; }; /** - * A pointer to a Herwig::HadronSpectrum object for generating hadrons. - */ - HadronSpectrumPtr _hadronSpectrum; - - /** - * @name The Cluster max mass,dependant on which quarks are involved, used to determine when - * fission will occur. - */ - //@{ - Energy _clMaxLight; - Energy _clMaxDiquark; - map<long,Energy> _clMaxHeavy; - Energy _clMaxExotic; - //@} - /** - * @name The power used to determine when cluster fission will occur. - */ - //@{ - double _clPowLight; - double _clPowDiquark; - map<long,double> _clPowHeavy; - double _clPowExotic; - //@} - /** - * @name The power, dependant on whic quarks are involved, used in the cluster mass generation. - */ - //@{ - double _pSplitLight; - map<long,double> _pSplitHeavy; - double _pSplitExotic; - - /** - * Weights for alternative cluster fission - */ - map<long,double> _fissionPwt; - - /** * Include phase space weights */ int _phaseSpaceWeights; /** * Dimensionality of phase space weight */ double _dim; /** * Flag used to determine between normal cluster fission and alternative cluster fission */ int _fissionCluster; /** * Flag to choose static or dynamic kinematic thresholds in cluster splittings */ int _kinematicThresholdChoice; /** * Pwt weight for drawing diquark */ double _pwtDIquark; /** * allow clusters to fission to 1 (or 2) diquark clusters or not */ int _diquarkClusterFission; //@} /** * Parameter used (2/b) for the beam cluster mass generation. * Currently hard coded value. */ Energy _btClM; /** * Flag used to determine what distributions to use for the cluster masses. */ int _iopRem; /** * The string constant */ Tension _kappa; /** * Width of the gaussian sampling for the FluxTube Kinematics */ double _fluxTubeWidth; /** * Flag that switches between no strangeness enhancement, scaling enhancement, * and exponential enhancement (in numerical order) */ int _enhanceSProb; /** * Parameter that governs the strangeness enhancement scaling */ Energy _m0Fission; /** * Flag that switches between mass measures used in strangeness enhancement: * cluster mass, or the lambda measure - ( m_{clu}^2 - (m_q + m_{qbar})^2 ) */ int _massMeasure; /** * Constant variable which stops the scale from being to large, and not worth * calculating */ const double _maxScale = 20.; /** * Power factor in ClausterFissioner bell probablity function */ double _probPowFactor; /** * Shifts from the center in ClausterFissioner bell probablity function */ double _probShift; /** * Shifts from the kinetic threshold in ClausterFissioner */ Energy2 _kinThresholdShift; /** * Flag for strict diquark selection according to kinematics */ int _strictDiquarkKinematics; /** * Use Covariant boost in SoftClusterFissioner */ bool _covariantBoost; /** * Flag for allowing Hadron Final states in Cluster Fission */ int _allowHadronFinalStates; /** * Choice of Mass sampling for SoftClusterFissioner * i.e. rejection sampling starting from MassPresampling * Chooses the to be sampled Mass distribution * Note: This ideal distribution would be ideally * exactly the integral over the angles as a * function of M1,M2 */ int _massSampler; /** * Choice of Phase Space sampling for SoftClusterFissioner * for child cluster directions and constituent directions */ int _phaseSpaceSamplerCluster; int _phaseSpaceSamplerConstituent; /** * Choice of Matrix Element for SoftClusterFissioner * Note: The choice of the matrix element requires * to provide one overestimate as a function * of M1,M2 and another overestimate of the * previous overestimate independent of * M1 and M2 i.e. only dependent on M,m1,m2,m */ int _matrixElement; /** * Choice of SoftClusterFissioner Approach */ int _fissionApproach; /** * Power for MassPreSampler = PowerLaw */ double _powerLawPower; /** * Choice of SoftClusterFissioner Approach * Technical Parameter for how many tries are allowed to sample the * Cluster Fission matrix element before reverting to fissioning * using the default Fission Aproach */ int _maxLoopFissionMatrixElement; /* * Safety factor for a better overestimate of the matrix Element * * */ double _safetyFactorMatrixElement; /* * IR cutOff for IR divergent diagramms * */ double _epsilonIR; /* * Failing mode for MaxLoopMatrixElement * */ int _failModeMaxLoopMatrixElement; int _writeOut; /* * flag for allowing strange Diquarks to be produced during * Cluster Fission * */ unsigned int _hadronizingStrangeDiquarks; private: // For MatrixElement only: // Counter for how many times we accept events with Pacc>1 static unsigned int _counterPaccGreater1; // Counter for how many times we escape the sampling by // tries > _maxLoopFissionMatrixElement // NOTE: This maxing out either rejects event or uses old // ClusterFission static unsigned int _counterMaxLoopViolations; static unsigned int _counterFissionMatrixElement; }; } #endif /* HERWIG_SoftClusterFissioner_H */ diff --git a/src/defaults/Hadronization.in b/src/defaults/Hadronization.in --- a/src/defaults/Hadronization.in +++ b/src/defaults/Hadronization.in @@ -1,146 +1,182 @@ # -*- ThePEG-repository -*- ############################################################ # Setup of default hadronization # # There are no user servicable parts inside. # # Anything that follows below should only be touched if you # know what you're doing. ############################################################# cd /Herwig/Particles create ThePEG::ParticleData Cluster setup Cluster 81 Cluster 0.00990 0.0 0.0 0.0 0 0 0 1 create ThePEG::ParticleData Remnant setup Remnant 82 Remnant 0.00990 0.0 0.0 0.0 0 0 0 1 mkdir /Herwig/Hadronization cd /Herwig/Hadronization create Herwig::ClusterHadronizationHandler ClusterHadHandler create Herwig::PartonSplitter PartonSplitter create Herwig::ClusterFinder ClusterFinder create Herwig::ColourReconnector ColourReconnector create Herwig::ClusterFissioner ClusterFissioner +create Herwig::SoftClusterFissioner SoftClusterFissioner create Herwig::LightClusterDecayer LightClusterDecayer create Herwig::ClusterDecayer ClusterDecayer create Herwig::HwppSelector SMHadronSpectrum newdef ClusterHadHandler:PartonSplitter PartonSplitter newdef ClusterHadHandler:ClusterFinder ClusterFinder newdef ClusterHadHandler:ColourReconnector ColourReconnector newdef ClusterHadHandler:ClusterFissioner ClusterFissioner newdef ClusterHadHandler:LightClusterDecayer LightClusterDecayer newdef ClusterHadHandler:ClusterDecayer ClusterDecayer do ClusterHadHandler:UseHandlersForInteraction QCD newdef ClusterHadHandler:MinVirtuality2 0.1*GeV2 newdef ClusterHadHandler:MaxDisplacement 1.0e-10*millimeter newdef ClusterHadHandler:UnderlyingEventHandler NULL newdef PartonSplitter:HadronSpectrum SMHadronSpectrum newdef ClusterFinder:HadronSpectrum SMHadronSpectrum newdef ClusterFissioner:HadronSpectrum SMHadronSpectrum newdef ClusterDecayer:HadronSpectrum SMHadronSpectrum newdef LightClusterDecayer:HadronSpectrum SMHadronSpectrum # ColourReconnector Default Parameters newdef ColourReconnector:HadronSpectrum SMHadronSpectrum newdef ColourReconnector:ColourReconnection Yes newdef ColourReconnector:Algorithm Baryonic # Statistical CR Parameters: newdef ColourReconnector:AnnealingFactor 0.9 newdef ColourReconnector:AnnealingSteps 50 newdef ColourReconnector:TriesPerStepFactor 5.0 newdef ColourReconnector:InitialTemperature 0.1 # Plain and Baryonic CR Paramters newdef ColourReconnector:ReconnectionProbability 0.95 newdef ColourReconnector:ReconnectionProbabilityBaryonic 0.7 # BaryonicMesonic and BaryonicMesonic CR Paramters newdef ColourReconnector:ReconnectionProbability3Mto3M 0.5 newdef ColourReconnector:ReconnectionProbability3MtoBBbar 0.5 newdef ColourReconnector:ReconnectionProbabilityBbarBto3M 0.5 newdef ColourReconnector:ReconnectionProbability2Bto2B 0.05 newdef ColourReconnector:ReconnectionProbabilityMBtoMB 0.5 newdef ColourReconnector:StepFactor 1.0 newdef ColourReconnector:MesonToBaryonFactor 1.333 # General Parameters and switches newdef ColourReconnector:MaxDistance 1.0e50 newdef ColourReconnector:OctetTreatment Final newdef ColourReconnector:CR2BeamClusters No newdef ColourReconnector:Junction Yes newdef ColourReconnector:LocalCR No newdef ColourReconnector:CausalCR No # Debugging newdef ColourReconnector:Debug No # set ClusterFissioner parameters -set /Herwig/Hadronization/ClusterFissioner:KinematicThreshold Dynamic -set /Herwig/Hadronization/ClusterFissioner:KineticThresholdShift 0.08844 -set /Herwig/Hadronization/ClusterFissioner:ProbablityPowerFactor 6.486 -set /Herwig/Hadronization/ClusterFissioner:ProbablityShift -0.87875 +newdef ClusterFissioner:KinematicThreshold Dynamic +newdef ClusterFissioner:KineticThresholdShift 0.08844 +newdef ClusterFissioner:ProbablityPowerFactor 6.486 +newdef ClusterFissioner:ProbablityShift -0.87875 # Clustering parameters for light quarks newdef ClusterFissioner:ClMaxLight 3.528693*GeV newdef ClusterFissioner:ClPowLight 1.849375 newdef ClusterFissioner:PSplitLight 0.914156 insert ClusterFissioner:FissionPwt 1 1.0 insert ClusterFissioner:FissionPwt 2 1.0 insert ClusterFissioner:FissionPwt 3 0.374094 newdef ClusterDecayer:ClDirLight 1 newdef ClusterDecayer:ClSmrLight 0.78 # Clustering parameters for b-quarks insert ClusterFissioner:ClMaxHeavy 5 3.757*GeV insert ClusterFissioner:ClPowHeavy 5 0.547 insert ClusterFissioner:PSplitHeavy 5 0.625 insert ClusterDecayer:ClDirHeavy 5 1 insert ClusterDecayer:ClSmrHeavy 5 0.078 newdef SMHadronSpectrum:SingleHadronLimitBottom 0.000 # Clustering parameters for c-quarks insert ClusterFissioner:ClMaxHeavy 4 3.950*GeV insert ClusterFissioner:ClPowHeavy 4 2.559 insert ClusterFissioner:PSplitHeavy 4 0.994 insert ClusterDecayer:ClDirHeavy 4 1 insert ClusterDecayer:ClSmrHeavy 4 0.163 newdef SMHadronSpectrum:SingleHadronLimitCharm 0.000 # Cluster Paramters for light Diquark Cluster # currently set according to Light quark defaults newdef ClusterFissioner:ClMaxDiquark 3.528693*GeV newdef ClusterFissioner:ClPowDiquark 1.849375 # Clustering parameters for exotic quarks # (e.g. hadronizing Susy particles) newdef ClusterFissioner:ClMaxExotic 2.7*GeV newdef ClusterFissioner:ClPowExotic 1.46 newdef ClusterFissioner:PSplitExotic 1.00 newdef ClusterDecayer:ClDirExotic 1 newdef ClusterDecayer:ClSmrExotic 0. newdef SMHadronSpectrum:SingleHadronLimitExotic 0. +################################################ +# BEG SoftClusterFissioner initialization: # +################################################ +newdef SoftClusterFissioner:HadronSpectrum SMHadronSpectrum +newdef SoftClusterFissioner:KinematicThreshold Dynamic +newdef SoftClusterFissioner:KineticThresholdShift 0.08844 +newdef SoftClusterFissioner:ProbablityPowerFactor 6.486 +newdef SoftClusterFissioner:ProbablityShift -0.87875 +# Clustering parameters for light quarks +newdef SoftClusterFissioner:ClMaxLight 3.528693*GeV +newdef SoftClusterFissioner:ClPowLight 1.849375 +newdef SoftClusterFissioner:PSplitLight 0.914156 +insert SoftClusterFissioner:FissionPwt 1 1.0 +insert SoftClusterFissioner:FissionPwt 2 1.0 +insert SoftClusterFissioner:FissionPwt 3 0.374094 +# Clustering parameters for b-quarks +insert SoftClusterFissioner:ClMaxHeavy 5 3.757*GeV +insert SoftClusterFissioner:ClPowHeavy 5 0.547 +insert SoftClusterFissioner:PSplitHeavy 5 0.625 +# Clustering parameters for c-quarks +insert SoftClusterFissioner:ClMaxHeavy 4 3.950*GeV +insert SoftClusterFissioner:ClPowHeavy 4 2.559 +insert SoftClusterFissioner:PSplitHeavy 4 0.994 +# Cluster Paramters for light Diquark Cluster +# currently set according to Light quark defaults +newdef SoftClusterFissioner:ClMaxDiquark 3.528693*GeV +newdef SoftClusterFissioner:ClPowDiquark 1.849375 +# Clustering parameters for exotic quarks +# (e.g. hadronizing Susy particles) +newdef SoftClusterFissioner:ClMaxExotic 2.7*GeV +newdef SoftClusterFissioner:ClPowExotic 1.46 +newdef SoftClusterFissioner:PSplitExotic 1.00 +################################################ +# END SoftClusterFissioner initialization: # +################################################ # insert PartonSplitter:SplitPwt 1 1.0 insert PartonSplitter:SplitPwt 2 1.0 insert PartonSplitter:SplitPwt 3 0.824135 newdef PartonSplitter:Split Light # newdef SMHadronSpectrum:PwtDquark 1.0 newdef SMHadronSpectrum:PwtUquark 1.0 newdef SMHadronSpectrum:PwtSquark 0.374094 newdef SMHadronSpectrum:PwtCquark 0.0 newdef SMHadronSpectrum:PwtBquark 0.0 newdef SMHadronSpectrum:PwtDIquark 0.33107 newdef SMHadronSpectrum:SngWt 0.89050 newdef SMHadronSpectrum:DecWt 0.41628 newdef SMHadronSpectrum:Mode 1 newdef SMHadronSpectrum:BelowThreshold All create Herwig::SpinHadronizer SpinHadronizer