Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F8309965
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
101 KB
Subscribers
None
View Options
diff --git a/Hadronization/ClusterFissioner.cc b/Hadronization/ClusterFissioner.cc
--- a/Hadronization/ClusterFissioner.cc
+++ b/Hadronization/ClusterFissioner.cc
@@ -1,1672 +1,1723 @@
// -*- 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"
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),
_writeOut(0)
{
}
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
<< _writeOut
;
}
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
>> _writeOut
;
}
void ClusterFissioner::doinit() {
Interfaced::doinit();
if (_writeOut){
std::ofstream out("data_CluFis.dat", std::ios::out);
out.close();
}
for ( const long& id : spectrum()->heavyHadronizingQuarks() ) {
if ( _pSplitHeavy.find(id) == _pSplitHeavy.end() ||
_clPowHeavy.find(id) == _clPowHeavy.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 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";
}
double pwtDquark=_fissionPwt.find(ParticleID::d)->second;
double pwtUquark=_fissionPwt.find(ParticleID::u)->second;
double pwtSquark=_fissionPwt.find(ParticleID::s)->second;
// 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 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 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 SwitchOption interfaceEnhanceSProbNoLightStrangeness
+ (interfaceEnhanceSProb,
+ "NoLightStrangeness",
+ "Forbid Strangeness production for cluster masses below FissionMassScale",
+ 3);
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,
+ &ClusterFissioner::_m0Fission, GeV, 2.0*GeV, 0.1*GeV, 500.*GeV,
false, false, Interface::limited);
static Parameter<ClusterFissioner,double> interfaceProbPowFactor
("ProbabilityPowerFactor",
"Power factor in ClusterFissioner bell probablity function",
&ClusterFissioner::_probPowFactor, 2.0, 0.001, 20.0,
false, false, Interface::limited);
static Parameter<ClusterFissioner,double> interfaceProbShift
("ProbabilityShift",
"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);
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, 0.0) ) {
// 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()
<< "ERROR in ClusterFissioner::weightFlatPhaseSpaceNoConstituentMasses\n"
<< "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;
// 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);
// 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) || !(ratio<=1)) {
throw Exception()
<< "ERROR in ClusterFissioner::weightFlatPhaseSpaceNoConstituentMasses\n"
<< "ratio = " <<ratio
<<" M "<<M_temp
<<" M1 "<<M1_temp
<<" M2 "<<M2_temp <<"\t"<<_dim<<"\t" << lam
<<"\t"<< overEstimate<<"\n\n"
<< Exception::runerror;
}
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()
<< "ERROR in ClusterFissioner::weightPhaseSpaceConstituentMasses\n"
<< "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;
// 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);
// 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 (!(ratio>=0) || !(ratio<=1)) {
throw Exception()
<< "ERROR in ClusterFissioner::weightPhaseSpaceConstituentMasses\n"
<< "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"
<< Exception::runerror;
}
// multiply by overestimate of power of matrix element to modulate the phase space with (M1*M2)^power
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;
}
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 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;
// 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);
// 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) || !(ratio<=1)) {
throw Exception()
<< "ERROR in ClusterFissioner::weightFlatPhaseSpaceHadronMasses\n"
<< "ratio = " <<ratio
<<" M "<<Mc/GeV
<<" M1 "<<Mc1/GeV
<<" M2 "<<Mc2/GeV <<"\t"<<_dim<<"\t" << lam1<<"\t" << lam2 <<"\t" << lam3
<<"\t"<< overEstimate<<"\n\n"
<< Exception::runerror;
}
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 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());
Energy minMass;
double weight;
Selector<long> choice;
// adding quark-antiquark pairs to the selection list
for ( const long& id : spectrum()->lightHadronizingQuarks() ) {
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);
minMass = mH1 + mH2;
}
else {
minMass = spectrum()->massLightestBaryonPair(pD1,pD2);
}
if (Mc < minMass) continue;
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 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;
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 = "
<< Mc/GeV <<" GeV MassLightestBaryonPair = "
<< spectrum()->massLightestBaryonPair(pD1,pD2)/GeV
<< " GeV cannot decay" << Exception::eventerror;
}
minMass = spectrum()->massLightestBaryonPair(pD1,diq)
+ spectrum()->massLightestBaryonPair(diq,pD2);
if (Mc < minMass) continue;
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 {
+ const ClusterPtr & clu) 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.
+ if ( spectrum()->gluonId() != ParticleID::g )
+ throw Exception() << "strange enhancement only working with Standard Model hadronization"
+ << Exception::runerror;
- 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);
- }
+ Energy2 mass2 = clustermass(clu);
+ // 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.
- 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());
+ double prob_d = 0.;
+ double prob_u = 0.;
+ double prob_s = 0.;
+ double pwts;
+ 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_u = _hadronSpectrum->pwtQuark(ParticleID::u);
+ prob_d = _hadronSpectrum->pwtQuark(ParticleID::d);
+ pwts = _hadronSpectrum->pwtQuark(ParticleID::s);
+ break;
+ }
+ /* Strangeness enhancement:
+ Case 1: probability scaling
+ Case 2: Exponential scaling
+ Case 3: NoLightStrangeness
+ */
+ // 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_u = _fissionPwt.find(ParticleID::u)->second;
+ prob_d = _fissionPwt.find(ParticleID::d)->second;
+ pwts = _fissionPwt.find(ParticleID::s)->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;
+ break;
+ }
+ default:
+ assert(false);
+ }
+
+ switch (_enhanceSProb)
+ {
+ case 1:
+ {
+ // Scaling strangeness enhancement
+ prob_s = (_maxScale < scale) ? 0. : pow(pwts, scale);
+ break;
+ }
+ case 2:
+ {
+ // Exponential strangeness enhancement
+ prob_s = (_maxScale < scale) ? 0. : exp(-scale);
+ break;
+ }
+ case 3:
+ {
+ // Strongly ordered strangeness: only if one of the constituents
+ // are of higher mass than the strange quark allow for the strange
+ // quark production
+
+ // _m0Fission is here the forbidden strangeness hard threshold
+ if (sqrt(mass2) > _m0Fission) {
+ prob_s = pwts;
+ break;
+ }
+ assert(clu->numComponents()==2);
+ unsigned int flav1 = abs(clu->particle(0)->dataPtr()->id());
+ unsigned int flav2 = abs(clu->particle(1)->dataPtr()->id());
+ // Light && u,d clusters are forbidden from producing strangeness
+ prob_s = (flav1>=3 || flav2>=3) ? pwts:0.0;
+ 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::ProbabilityFunction(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::ProbabilityFunctionPower(double Mass, double threshold) {
double cut = UseRandom::rnd(0.0,1.0);
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 = ProbabilityFunction(scale,threshold);
scale = clu->mass()/GeV;
threshold = clu->sumConstituentMasses()/GeV + 2.0 * minmass/GeV;
canSplitMinimally = ProbabilityFunction(scale,threshold);
}
// probablistic kinematic threshold
else if(_kinematicThresholdChoice == 2) {
// Consistent power law for CF probability
double Mass = clu->mass()/GeV;
double threshold = clu->sumConstituentMasses()/GeV + 2.0 * minmass/GeV;
aboveCutoff = ProbabilityFunctionPower(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,617 +1,617 @@
// -*- 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;
+ void drawNewFlavourEnhanced(PPtr& newPtrPos,PPtr& newPtrNeg, const ClusterPtr & clu) 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));
+ drawNewFlavourEnhanced(newPtr1,newPtr2,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 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();
/**
* 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;
//@}
protected:
/**
* Smooth probability for dynamic threshold cuts:
* @scale the current scale, e.g. the mass of the cluster,
* @threshold the physical threshold,
*/
bool ProbabilityFunction(double scale, double threshold);
bool ProbabilityFunctionPower(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());
}
/**
* 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 MatrixElementClusterFissioner
*/
bool _covariantBoost;
/**
* Power for MassPreSampler = PowerLaw
*/
double _powerLawPower;
/*
* flag for allowing strange Diquarks to be produced during
* Cluster Fission
* */
unsigned int _hadronizingStrangeDiquarks;
private:
/*
* DEBUG output */
int _writeOut;
};
}
#endif /* HERWIG_ClusterFissioner_H */
diff --git a/Hadronization/HwppSelector.cc b/Hadronization/HwppSelector.cc
--- a/Hadronization/HwppSelector.cc
+++ b/Hadronization/HwppSelector.cc
@@ -1,313 +1,350 @@
// -*- C++ -*-
//
// HwppSelector.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2019 The Herwig Collaboration
//
// Herwig is licenced under version 3 of the GPL, see COPYING for details.
// Please respect the MCnet academic guidelines, see GUIDELINES for details.
//
//
// This is the implementation of the non-inlined, non-templated member
// functions of the HwppSelector class.
//
#include "HwppSelector.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Interface/Parameter.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/Utilities/Kinematics.h"
#include "ThePEG/Utilities/Selector.h"
#include "ThePEG/Repository/UseRandom.h"
#include <cassert>
#include <ThePEG/Utilities/DescribeClass.h>
#include "Herwig/Utilities/AlphaS.h"
using namespace Herwig;
DescribeClass<HwppSelector,StandardModelHadronSpectrum>
describeHwppSelector("Herwig::HwppSelector","Herwig.so");
IBPtr HwppSelector::clone() const {
return new_ptr(*this);
}
IBPtr HwppSelector::fullclone() const {
return new_ptr(*this);
}
void HwppSelector::doinit() {
// the default partons allowed
// the quarks
for ( int ix=1; ix<=5; ++ix ) {
_partons.push_back(getParticleData(ix));
}
// the diquarks
for(unsigned int ix=1;ix<=5;++ix) {
for(unsigned int iy=1; iy<=ix;++iy) {
if(ix==iy)
_partons.push_back(getParticleData(makeDiquarkID(ix,iy,long(3))));
else
_partons.push_back(getParticleData(makeDiquarkID(ix,iy,long(1))));
}
}
// weights for the different quarks etc
for(unsigned int ix=0; ix<partons().size(); ++ix) {
_pwt[partons()[ix]->id()]=0.;
}
_pwt[1] = _pwtDquark;
_pwt[2] = _pwtUquark;
_pwt[3] = _pwtSquark;
_pwt[4] = _pwtCquark;
_pwt[5] = _pwtBquark;
_pwt[1103] = _pwtDIquark * _pwtDquark * _pwtDquark;
_pwt[2101] = 0.5 * _pwtDIquark * _pwtUquark * _pwtDquark;
_pwt[2203] = _pwtDIquark * _pwtUquark * _pwtUquark;
_pwt[3101] = 0.5 * _pwtDIquark * _pwtSquark * _pwtDquark;
_pwt[3201] = 0.5 * _pwtDIquark * _pwtSquark * _pwtUquark;
_pwt[3303] = _pwtDIquark * _pwtSquark * _pwtSquark;
StandardModelHadronSpectrum::doinit();
// lightest members (baryons)
for(const PDPtr & p1 : partons()) {
if(DiquarkMatcher::Check(p1->id())) continue;
for(const PDPtr & p2 : partons()) {
if(DiquarkMatcher::Check(p2->id())) continue;
lightestBaryons_[make_pair(p1->id(),p2->id())] = lightestBaryonPair(p1,p2);
}
}
}
void HwppSelector::persistentOutput(PersistentOStream & os) const {
os << _pwtDIquark
<< _mode << _enhanceSProb << ounit(_m0Decay,GeV) << _massMeasure
<< _scHadronWtFactor << _sbHadronWtFactor << lightestBaryons_;
}
void HwppSelector::persistentInput(PersistentIStream & is, int) {
is >> _pwtDIquark
>> _mode >> _enhanceSProb >> iunit(_m0Decay,GeV) >> _massMeasure
>> _scHadronWtFactor >> _sbHadronWtFactor >> lightestBaryons_;
}
void HwppSelector::Init() {
static ClassDocumentation<HwppSelector> documentation
("The HwppSelector class implements the Herwig algorithm for selecting"
" the hadrons",
"The hadronization used the selection algorithm described in \\cite{Kupco:1998fx}.",
"%\\cite{Kupco:1998fx}\n"
"\\bibitem{Kupco:1998fx}\n"
" A.~Kupco,\n"
" ``Cluster hadronization in HERWIG 5.9,''\n"
" arXiv:hep-ph/9906412.\n"
" %%CITATION = HEP-PH/9906412;%%\n"
);
static Parameter<HwppSelector,double>
interfacePwtDIquark("PwtDIquark","Weight for choosing a DIquark",
&HwppSelector::_pwtDIquark, 0, 1.0, 0.0, 100.0,
false,false,false);
static Switch<HwppSelector,unsigned int> interfaceMode
("Mode",
"Which algorithm to use",
&HwppSelector::_mode, 1, false, false);
static SwitchOption interfaceModeKupco
(interfaceMode,
"Kupco",
"Use the Kupco approach",
0);
static SwitchOption interfaceModeHwpp
(interfaceMode,
"Hwpp",
"Use the Herwig approach",
1);
static SwitchOption interfaceModeBaryonic
(interfaceMode,
"Baryonic",
"Use alphaS^2 suppression for Baryon production.",
2);
static SwitchOption interfaceModeBaryonicLimit
(interfaceMode,
"BaryonicLimit",
"Use alphaS^2 suppression for Baryon production.",
3);
static Switch<HwppSelector,int> interfaceEnhanceSProb
("EnhanceSProb",
"Option for enhancing strangeness",
&HwppSelector::_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 SwitchOption interfaceEnhanceSProbNoLightStrangeness
+ (interfaceEnhanceSProb,
+ "NoLightStrangeness",
+ "Forbid strangeness production for u,d cluster masses < DecayMassScale",
+ 3);
+
+
static Switch<HwppSelector,int> interfaceMassMeasure
("MassMeasure",
"Option to use different mass measures",
&HwppSelector::_massMeasure,0,false,false);
static SwitchOption interfaceMassMeasureMass
(interfaceMassMeasure,
"Mass",
"Mass Measure",
0);
static SwitchOption interfaceMassMeasureLambda
(interfaceMassMeasure,
"Lambda",
"Lambda Measure",
1);
static Parameter<HwppSelector,double> interfacescHadronWtFactor
("scHadronWtFactor",
- "Wight factor for strenge-charm heavy hadrns",
+ "Weight factor for strange-charm heavy hadrons",
&HwppSelector::_scHadronWtFactor, 1., 0., 10.,
false, false, Interface::limited);
static Parameter<HwppSelector,double> interfacesbHadronWtFactor
("sbHadronWtFactor",
- "Wight factor for strenge-bottom heavy hadrns",
+ "Weight factor for strange-bottom heavy hadrons",
&HwppSelector::_sbHadronWtFactor, 1., 0., 10.,
false, false, Interface::limited);
static Parameter<HwppSelector,Energy> interfaceDecayMassScale
("DecayMassScale",
"Cluster decay mass scale",
- &HwppSelector::_m0Decay, GeV, 1.0*GeV, 0.1*GeV, 50.*GeV,
+ &HwppSelector::_m0Decay, GeV, 1.0*GeV, 0.1*GeV, 500.*GeV,
false, false, Interface::limited);
}
double HwppSelector::baryonWeight(long id) const {
const int pspin = id % 10;
if(pspin == 2) {
// Singlet (Lambda-like) baryon
if( (id/100)%10 < (id/10 )%10 ) return sqr(_sngWt);
}
// Decuplet baryon
else if (pspin == 4) return sqr(_decWt);
return 1.;
}
std::tuple<bool,bool,bool> HwppSelector::selectBaryon(const Energy cluMass, tcPDPtr par1, tcPDPtr par2) const {
useMe();
std::tuple<bool,bool,bool> output(true,true,true);
switch (_mode)
{
case 0:
return output;
case 1:
{
if(UseRandom::rnd() > 1./(1.+_pwtDIquark) && cluMass > massLightestBaryonPair(par1,par2)) {
std::get<0>(output) = false;
}
else {
std::get<1>(output) = false;
std::get<2>(output) = false;
}
break;
}
case 2:
{
double wB=_pwtDIquark*sqr(Herwig::Math::alphaS(cluMass, 0.25*GeV,3, 2));
if (wB>1.0) wB=1.0;
if(UseRandom::rnd() < wB && cluMass > massLightestBaryonPair(par1,par2)) {
std::get<0>(output) = false;
}
else {
std::get<1>(output) = false;
std::get<2>(output) = false;
}
break;
}
case 3:
{
double wB=_pwtDIquark*sqr(Herwig::Math::alphaS(cluMass, 0.25*GeV,3, 2));
wB = wB/(1.0+wB);
if (wB>1.0) wB=1.0;
if(UseRandom::rnd() < wB && cluMass > massLightestBaryonPair(par1,par2)) {
std::get<0>(output) = false;
}
else {
std::get<1>(output) = false;
std::get<2>(output) = false;
}
break;
}
default:
assert(false);
}
return output;
}
double HwppSelector::strangeWeight(const Energy cluMass, tcPDPtr par1, tcPDPtr par2) const {
- // Decoupling the weight of heavy strenge hadrons
- if(_enhanceSProb == 0 && abs(par1->id()) == 4) {
- return pwt(3)*_scHadronWtFactor;
- }
- else if(_enhanceSProb == 0 && abs(par1->id()) == 5) {
- return pwt(3)*_sbHadronWtFactor;
- }
- // Scaling strangeness enhancement
- else if(_enhanceSProb == 1) {
- double scale = double(sqr(_m0Decay/cluMass));
- return (_maxScale < scale) ? 0. : pow(pwt(3),scale);
- }
- // Exponential strangeness enhancement
- else if(_enhanceSProb == 2) {
- Energy2 mass2;
- Energy endpointmass = par1->mass() + par2->mass();
- // Choose to use either the cluster mass
- // or to use the lambda measure
- mass2 = (_massMeasure == 0) ? sqr(cluMass) :
- sqr(cluMass) - sqr(endpointmass);
- double scale = double(sqr(_m0Decay)/mass2);
- return (_maxScale < scale) ? 0. : exp(-scale);
- }
- return pwt(3);
+ switch (_enhanceSProb)
+ {
+ case 0:
+ {
+ // Decoupling the weight of heavy strenge hadrons
+ if(abs(par1->id()) == 4) {
+ return pwt(3)*_scHadronWtFactor;
+ }
+ else if(abs(par1->id()) == 5) {
+ return pwt(3)*_sbHadronWtFactor;
+ }
+ else {
+ return pwt(3);
+ }
+ break;
+ }
+ case 1:
+ {
+ // Scaling strangeness enhancement
+ double scale = double(sqr(_m0Decay/cluMass));
+ return (_maxScale < scale) ? 0. : pow(pwt(3),scale);
+ break;
+ }
+ case 2:
+ {
+ // Exponential strangeness enhancement
+ Energy2 mass2;
+ Energy endpointmass = par1->mass() + par2->mass();
+ // Choose to use either the cluster mass
+ // or to use the lambda measure
+ mass2 = (_massMeasure == 0) ? sqr(cluMass) :
+ sqr(cluMass) - sqr(endpointmass);
+ double scale = double(sqr(_m0Decay)/mass2);
+ return (_maxScale < scale) ? 0. : exp(-scale);
+ break;
+ }
+ case 3:
+ {
+ // Strongly ordered strangeness: only if one of the constituents
+ // are of higher mass than the strange quark allow for the strange
+ // quark production
+
+ // _m0Decay is here the forbidden strangeness hard threshold
+ if (cluMass > _m0Decay)
+ return pwt(3);
+ // Light && u,d clusters are forbidden from producing strangeness
+ return (abs(par1->id())>=3 || abs(par2->id())>=3) ? pwt(3):0.0;
+ break;
+ }
+ default:
+ assert(false);
+ }
+ assert(false);
+ return 0;
}
tcPDPair HwppSelector::lightestBaryonPair(tcPDPtr ptr1, tcPDPtr ptr2) const {
// Make sure that we don't have any diquarks as input, return arbitrarily
// large value if we do
Energy currentSum = Constants::MaxEnergy;
tcPDPair output;
for(unsigned int ix=0; ix<partons().size(); ++ix) {
if(!DiquarkMatcher::Check(partons()[ix]->id())) continue;
HadronTable::const_iterator
tit1=table().find(make_pair(abs(ptr1->id()),partons()[ix]->id())),
tit2=table().find(make_pair(partons()[ix]->id(),abs(ptr2->id())));
if( tit1==table().end() || tit2==table().end()) continue;
if(tit1->second.empty()||tit2->second.empty()) continue;
Energy s = tit1->second.begin()->mass + tit2->second.begin()->mass;
if(currentSum > s) {
currentSum = s;
output.first = tit1->second.begin()->ptrData;
output.second = tit2->second.begin()->ptrData;
}
}
return output;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Dec 21, 4:59 PM (15 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3962525
Default Alt Text
(101 KB)
Attached To
rHERWIGHG herwighg
Event Timeline
Log In to Comment