Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F9501179
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
55 KB
Subscribers
None
View Options
diff --git a/Hadronization/ClusterDecayer.cc b/Hadronization/ClusterDecayer.cc
--- a/Hadronization/ClusterDecayer.cc
+++ b/Hadronization/ClusterDecayer.cc
@@ -1,807 +1,813 @@
// -*- C++ -*-
//
// ClusterDecayer.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 ClusterDecayer class.
//
#include "ClusterDecayer.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 <ThePEG/Repository/EventGenerator.h>
#include "Herwig/Utilities/Kinematics.h"
#include "Cluster.h"
#include <ThePEG/Utilities/DescribeClass.h>
#include <ThePEG/Repository/UseRandom.h>
#include "ThePEG/Interface/ParMap.h"
#include "Herwig/Utilities/Histogram.h"
using namespace Herwig;
DescribeClass<ClusterDecayer,Interfaced>
describeClusterDecayer("Herwig::ClusterDecayer","Herwig.so");
ClusterDecayer::ClusterDecayer() :
_clDirLight(1),
_clDirExotic(1),
_clSmrLight(0.0),
_clSmrExotic(0.0),
_onshell(false),
_masstry(20),
_covariantBoost(false),
_kinematics(0)
{}
IBPtr ClusterDecayer::clone() const {
return new_ptr(*this);
}
IBPtr ClusterDecayer::fullclone() const {
return new_ptr(*this);
}
void ClusterDecayer::persistentOutput(PersistentOStream & os) const
{
os << _clDirLight << _clDirHeavy
<< _clDirExotic << _clSmrLight << _clSmrHeavy
<< _clSmrExotic << _onshell << _masstry
<< _covariantBoost
<< _kinematics
<< _hadronSpectrum;
}
void ClusterDecayer::persistentInput(PersistentIStream & is, int) {
is >> _clDirLight >> _clDirHeavy
>> _clDirExotic >> _clSmrLight >> _clSmrHeavy
>> _clSmrExotic >> _onshell >> _masstry
>> _covariantBoost
>> _kinematics
>> _hadronSpectrum;
}
void ClusterDecayer::doinit() {
Interfaced::doinit();
for ( const long& id : spectrum()->heavyHadronizingQuarks() ) {
if ( _clSmrHeavy.find(id) == _clSmrHeavy.end() ||
_clDirHeavy.find(id) == _clDirHeavy.end() )
throw InitException() << "not all parameters have been set for heavy quark cluster decay";
}
}
void ClusterDecayer::Init() {
static ClassDocumentation<ClusterDecayer> documentation
("This class is responsible for the two-body decays of normal clusters");
static Reference<ClusterDecayer,HadronSpectrum> interfaceHadronSpectrum
("HadronSpectrum",
"Set the hadron spectrum for this parton splitter.",
&ClusterDecayer::_hadronSpectrum, false, false, true, false);
//ClDir for light, Bottom, Charm and exotic (e.g Susy) quarks
static Switch<ClusterDecayer,bool> interfaceClDirLight
("ClDirLight",
"Cluster direction for light quarks",
&ClusterDecayer::_clDirLight, true, false, false);
static SwitchOption interfaceClDirLightPreserve
(interfaceClDirLight,
"Preserve",
"Preserve the direction of the quark from the perturbative stage"
" as the direction of the meson produced from it",
true);
static SwitchOption interfaceClDirLightIsotropic
(interfaceClDirLight,
"Isotropic",
"Assign the direction of the meson randomly",
false);
static ParMap<ClusterDecayer,bool> interfaceClDirHeavy
("ClDirHeavy",
"Preserve the direction of the given heavy quark from the perturbative stage.",
&ClusterDecayer::_clDirHeavy, -1, true, false, true,
false, false, Interface::limited);
static Switch<ClusterDecayer,bool> interfaceClDirExotic
("ClDirExotic",
"Cluster direction for exotic quarks",
&ClusterDecayer::_clDirExotic, true, false, false);
static SwitchOption interfaceClDirExoticPreserve
(interfaceClDirExotic,
"Preserve",
"Preserve the direction of the quark from the perturbative stage"
" as the direction of the meson produced from it",
true);
static SwitchOption interfaceClDirExoticIsotropic
(interfaceClDirExotic,
"Isotropic",
"Assign the direction of the meson randomly",
false);
static Switch<ClusterDecayer,bool> interfaceCovariantBoost
("CovariantBoost",
"Use single Covariant Boost for Cluster Fission",
&ClusterDecayer::_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<ClusterDecayer,int> interfaceKinematics
("Kinematics",
"Choose kinematics of Cluster Decay",
&ClusterDecayer::_kinematics, 0, true, false);
static SwitchOption interfaceKinematicsDefault
(interfaceKinematics,
"Default",
"default kinematics",
0);
static SwitchOption interfaceKinematicsTchannel
(interfaceKinematics,
"Tchannel",
"t channel like kinematics using the matrix element 1/(p1.p3)^2",
1);
static SwitchOption interfaceKinematicsAllIsotropic
(interfaceKinematics,
"AllIsotropic",
"All clusters decay isotropically",
2);
static SwitchOption interfaceKinematicsAllAligned
(interfaceKinematics,
"AllAligned",
"All clusters decay in the direction of their constituents",
3);
static SwitchOption interfaceKinematicsTchannelHemisphere
(interfaceKinematics,
"TchannelHemisphere",
"t channel like kinematics using the matrix element 1/(p1.p3)^2 "
"But limited the sampling to the hemisphere of the original constituents",
-1);
static SwitchOption interfaceKinematicsAllIsotropicHemisphere
(interfaceKinematics,
"IsotropicHemisphere",
"All clusters decay semi-isotropically "
"limited the sampling to the hemisphere of the original constituents",
-2);
// ClSmr for ligth, Bottom, Charm and exotic (e.g. Susy) quarks
static Parameter<ClusterDecayer,double>
interfaceClSmrLight ("ClSmrLight", "cluster direction Gaussian smearing for light quark",
&ClusterDecayer::_clSmrLight, 0, 0.0 , 0.0 , 2.0,false,false,false);
static ParMap<ClusterDecayer,double> interfaceClSmrHeavy
("ClSmrHeavy",
"cluster direction Gaussian smearing for heavy quarks",
&ClusterDecayer::_clSmrHeavy, -1, 0.0, 0.0, 2.0,
false, false, Interface::limited);
static Parameter<ClusterDecayer,double>
interfaceClSmrExotic ("ClSmrExotic", "cluster direction Gaussian smearing for exotic quark",
&ClusterDecayer::_clSmrExotic, 0, 0.0 , 0.0 , 2.0,false,false,false);
static Switch<ClusterDecayer,bool> interfaceOnShell
("OnShell",
"Whether or not the hadrons produced should by on shell or generated using the"
" mass generator.",
&ClusterDecayer::_onshell, false, false, false);
static SwitchOption interfaceOnShellOnShell
(interfaceOnShell,
"Yes",
"Produce the hadrons on shell",
true);
static SwitchOption interfaceOnShellOffShell
(interfaceOnShell,
"No",
"Generate the masses using the mass generator.",
false);
static Parameter<ClusterDecayer,unsigned int> interfaceMassTry
("MassTry",
"The number attempts to generate the masses of the hadrons produced"
" in the cluster decay.",
&ClusterDecayer::_masstry, 20, 1, 50,
false, false, Interface::limited);
}
void ClusterDecayer::decay(const ClusterVector & clusters, tPVector & finalhadrons) {
// Loop over all clusters, and if they are not too heavy (that is
// intermediate clusters that have undergone to fission) or not
// too light (that is final clusters that have been already decayed
// into single hadron) then decay them into two hadrons.
for (ClusterVector::const_iterator it = clusters.begin();
it != clusters.end(); ++it) {
if ((*it)->isAvailable() && !(*it)->isStatusFinal()
&& (*it)->isReadyToDecay()) {
pair<PPtr,PPtr> prod = decayIntoTwoHadrons(*it);
if(!prod.first)
throw Exception() << "Can't perform decay of cluster ("
<< (*it)->particle(0)->dataPtr()->PDGName() << " "
<< (*it)->particle(1)->dataPtr()->PDGName() << ")\nThreshold = "
<< ounit(spectrum()->massLightestHadronPair((*it)->particle(0)->dataPtr(),
(*it)->particle(1)->dataPtr()),GeV)
<< "\nLightest hadron pair = ( "
<< spectrum()->lightestHadronPair((*it)->particle(0)->dataPtr(),
(*it)->particle(1)->dataPtr()).first->PDGName() << ",\t"
<< spectrum()->lightestHadronPair((*it)->particle(0)->dataPtr(),
(*it)->particle(1)->dataPtr()).second->PDGName() << " )\nCluster =\n"
<< **it
<< "in ClusterDecayer::decay()"
<< Exception::eventerror;
(*it)->addChild(prod.first);
(*it)->addChild(prod.second);
finalhadrons.push_back(prod.first);
finalhadrons.push_back(prod.second);
}
}
}
static double sampleCosTchannel(double Ainv);
static double sampleCosTchannel(double Ainv){
/*
* samples exactly from distribution 1/(1/Ainv-cosTheta)^2
* where cosTheta in [-1:1]
* */
if (fabs(Ainv)<1e-14) return UseRandom::rnd()*2.0-1.0;
double A=1.0/Ainv;
double r=UseRandom::rnd();
double cosTheta = r*2.0-1.0;
return (1.0+A*cosTheta)/(A+cosTheta);
}
/*
static double sampleCosRegular(double A, double R);
static double sampleCosRegular(double A, double R){
double zNeg=(A-1)/R;
double zPos=(A+1)/R;
double r=atan(zPos)+UseRandom::rnd()*(atan(zNeg)-atan(zPos));
double cosTheta=A-R*tan(r);
if (fabs(cosTheta)>1.0 || std::isnan(cosTheta)){
std::cout << "cosTheta = "<< cosTheta << std::endl;
std::cout << "R = "<< R << std::endl;
std::cout << "r = "<< r << std::endl;
std::cout << "zNeg = "<< zNeg << std::endl;
std::cout << "zPos = "<< zPos << std::endl;
std::cout << "A = "<< A << std::endl;
}
return cosTheta;
}
*/
pair<PPtr,PPtr> ClusterDecayer::decayIntoTwoHadrons(tClusterPtr ptr) {
switch (abs(_kinematics))
{
case 0:
// Default
return decayIntoTwoHadronsDefault(ptr);
case 1:
// Tchannel- like
return decayIntoTwoHadronsTchannel(ptr);
case 2:
// AllIsotropic
return decayIntoTwoHadronsTchannel(ptr);
case 3:
// AllAligned
return decayIntoTwoHadronsTchannel(ptr);
default:
assert(false);
}
}
pair<PPtr,PPtr> ClusterDecayer::decayIntoTwoHadronsDefault(tClusterPtr ptr) {
using Constants::pi;
using Constants::twopi;
// To decay the cluster into two hadrons one must distinguish between
// constituent quarks (or diquarks) that originate from perturbative
// processes (hard process or parton shower) from those that are generated
// by the non-perturbative gluon splitting or from fission of heavy clusters.
// In the latter case the two body decay is assumed to be *isotropic*.
// In the former case instead, if proper flag are activated, the two body
// decay is assumed to "remember" the direction of the constituents inside
// the cluster, in the cluster frame. The actual smearing of the hadron
// directions around the direction of the constituents, in the cluster
// frame, can be different between non-b hadrons and b-hadrons, but is given
// by the same functional form:
// cosThetaSmearing = 1 + smearFactor * log( rnd() )
// (repeated until cosThetaSmearing > -1)
// where the factor smearFactor is different between b- and non-b hadrons.
//
// We need to require (at least at the moment, maybe in the future we
// could change it) that the cluster has exactly two components.
// If this is not the case, then send a warning because it is not suppose
// to happen, and then return.
if ( ptr->numComponents() != 2 ) {
generator()->logWarning( Exception("ClusterDecayer::decayIntoTwoHadrons "
"***Still cluster with not exactly 2 components*** ",
Exception::warning) );
return pair<PPtr,PPtr>();
}
// Extract the id and particle pointer of the two components of the cluster.
tPPtr ptr1 = ptr->particle(0);
tPPtr ptr2 = ptr->particle(1);
tcPDPtr ptr1data = ptr1->dataPtr();
tcPDPtr ptr2data = ptr2->dataPtr();
bool isHad1FlavSpecial = false;
bool cluDirHad1 = _clDirLight;
double cluSmearHad1 = _clSmrLight;
bool isHad2FlavSpecial = false;
bool cluDirHad2 = _clDirLight;
double cluSmearHad2 = _clSmrLight;
if (spectrum()->isExotic(ptr1data)) {
isHad1FlavSpecial = true;
cluDirHad1 = _clDirExotic;
cluSmearHad1 = _clSmrExotic;
} else {
for ( const long& id : spectrum()->heavyHadronizingQuarks() ) {
if ( spectrum()->hasHeavy(id,ptr1data) ) {
cluDirHad1 = _clDirHeavy[id];
cluSmearHad1 = _clSmrHeavy[id];
}
}
}
if (spectrum()->isExotic(ptr2data)) {
isHad2FlavSpecial = true;
cluDirHad2 = _clDirExotic;
cluSmearHad2 = _clSmrExotic;
} else {
for ( const long& id : spectrum()->heavyHadronizingQuarks() ) {
if ( spectrum()->hasHeavy(id,ptr2data) ) {
cluDirHad2 = _clDirHeavy[id];
cluSmearHad2 = _clSmrHeavy[id];
}
}
}
bool isOrigin1Perturbative = ptr->isPerturbative(0);
bool isOrigin2Perturbative = ptr->isPerturbative(1);
// We have to decide which, if any, of the two hadrons will have
// the momentum, in the cluster parent frame, smeared around the
// direction of its constituent (for Had1 is the one pointed by
// ptr1, and for Had2 is the one pointed by ptr2).
// This happens only if the flag _ClDirX is 1 and the constituent is
// perturbative (that is not coming from nonperturbative gluon splitting
// or cluster fission). In the case that both the hadrons satisfy this
// two requirements (of course only one must be treated, because the other
// one will have the momentum automatically fixed by the momentum
// conservation) then more priority is given in the case of a b-hadron.
// Finally, in the case that the two hadrons have same priority, then
// we choose randomly, with equal probability, one of the two.
int priorityHad1 = 0;
if ( cluDirHad1 && isOrigin1Perturbative ) {
priorityHad1 = isHad1FlavSpecial ? 2 : 1;
}
int priorityHad2 = 0;
if ( cluDirHad2 && isOrigin2Perturbative ) {
priorityHad2 = isHad2FlavSpecial ? 2 : 1;
}
if ( priorityHad2 && priorityHad1 == priorityHad2 && UseRandom::rndbool() ) {
priorityHad2 = 0;
}
Lorentz5Momentum pClu = ptr->momentum();
bool secondHad = false;
Axis uSmear_v3;
if ( priorityHad1 || priorityHad2 ) {
double cluSmear;
Lorentz5Momentum pQ;
Lorentz5Momentum pQbar;
if ( priorityHad1 > priorityHad2 ) {
pQ = ptr1->momentum();
pQbar = ptr2->momentum();
cluSmear = cluSmearHad1;
} else {
pQ = ptr2->momentum();
pQbar = ptr1->momentum();
cluSmear = cluSmearHad2;
secondHad = true;
}
// To find the momenta of the two hadrons in the parent cluster frame
// we proceed as follows. First, we determine the unit vector parallel
// to the direction of the constituent in the cluster frame. Then we
// have to smear such direction using the following prescription:
// --- in theta angle w.r.t. such direction (not the z-axis),
// the drawing of the cosine of such angle is done via:
// 1.0 + cluSmear*log( rnd() )
// (repeated until it gives a value above -1.0)
// --- in phi angle w.r.t. such direction, the drawing is simply flat.
// Then, given the direction in the parent cluster frame of one of the
// two hadrons, it is straighforward to get the momenta of both hadrons
// (always in the same parent cluster frame).
if (_covariantBoost) {
// Use correct boost for a 2 particle constituent cluster
// the direction of the constituents is aligned with the z-Axis
// since the boost is constructed as such
Energy Pcom = Kinematics::pstarTwoBodyDecay(pClu.m(),pQ.m(),pQbar.m());
pQ = secondHad ? Lorentz5Momentum(pQbar.m(), -Pcom*Axis(0,0,1)):Lorentz5Momentum(pQ.m(), Pcom*Axis(0,0,1));
// pQbar = Lorentz5Momentum(pQbar.m(),-Pcom*Axis(0,0,1));
}
else {
pQ.boost( -pClu.boostVector() ); // naive boost from Lab to Cluster frame
}
uSmear_v3 = pQ.vect().unit(); // Direction of the priority quark in COM frame
// skip if cluSmear is too small
if ( cluSmear > 0.001 ) {
// generate the smearing angle
double cosSmear;
do cosSmear = 1.0 + cluSmear*log( UseRandom::rnd() );
while ( cosSmear < -1.0 );
double sinSmear = sqrt( 1.0 - sqr(cosSmear) );
// calculate rotation to z axis
LorentzRotation rot;
double sinth(sqrt(1.-sqr(uSmear_v3.z())));
if(abs(uSmear_v3.perp2()/uSmear_v3.z())>1e-10)
rot.setRotate(-acos(uSmear_v3.z()),
Axis(-uSmear_v3.y()/sinth,uSmear_v3.x()/sinth,0.));
// + random azimuthal rotation
rot.rotateZ(UseRandom::rnd()*twopi);
// set direction in rotated frame
Lorentz5Vector<double> ltemp(0.,sinSmear,cosSmear,0.);
// rotate back
rot.invert();
ltemp *= rot;
uSmear_v3 = ltemp.vect();
}
}
else {
// Isotropic decay: flat in cosTheta and phi.
uSmear_v3 = Axis(1.0, 0.0, 0.0); // just to set the rho to 1
uSmear_v3.setTheta( acos( UseRandom::rnd( -1.0 , 1.0 ) ) );
uSmear_v3.setPhi( UseRandom::rnd( -pi , pi ) );
}
pair<tcPDPtr,tcPDPtr> dataPair
= _hadronSpectrum->chooseHadronPair(ptr->mass(),
ptr1data,
ptr2data);
if(dataPair.first == tcPDPtr() ||
dataPair.second == tcPDPtr()) return pair<PPtr,PPtr>();
// Create the two hadron particle objects with the specified id.
PPtr ptrHad1,ptrHad2;
// produce the hadrons on mass shell
if(_onshell) {
ptrHad1 = dataPair.first ->produceParticle(dataPair.first ->mass());
ptrHad2 = dataPair.second->produceParticle(dataPair.second->mass());
}
// produce the hadrons with mass given by the mass generator
else {
unsigned int ntry(0);
do {
ptrHad1 = dataPair.first ->produceParticle();
ptrHad2 = dataPair.second->produceParticle();
++ntry;
}
while(ntry<_masstry&&ptrHad1->mass()+ptrHad2->mass()>ptr->mass());
// if fails produce on shell and issue warning (should never happen??)
if( ptrHad1->mass() + ptrHad2->mass() > ptr->mass() ) {
generator()->log() << "Failed to produce off-shell hadrons in "
<< "ClusterDecayer::decayIntoTwoHadrons producing hadrons "
<< "on-shell" << endl;
ptrHad1 = dataPair.first ->produceParticle(dataPair.first ->mass());
ptrHad2 = dataPair.second->produceParticle(dataPair.second->mass());
}
}
if (!ptrHad1 || !ptrHad2) {
ostringstream s;
s << "ClusterDecayer::decayIntoTwoHadrons ***Cannot create the two hadrons***"
<< dataPair.first ->PDGName() << " and "
<< dataPair.second->PDGName();
cerr << s.str() << endl;
generator()->logWarning( Exception(s.str(), Exception::warning) );
} else {
Lorentz5Momentum pHad1, pHad2; // 5-momentum vectors that we have to determine
if ( secondHad ) uSmear_v3 *= -1.0;
if (pClu.m() < ptrHad1->mass()+ptrHad2->mass() ) {
throw Exception() << "Impossible Kinematics in ClusterDecayer::decayIntoTwoHadrons()"
<< Exception::eventerror;
}
if (_covariantBoost) {
// Use correct boost for a 2 particle constituent cluster
Energy Pcom=Kinematics::pstarTwoBodyDecay(pClu.m(),ptrHad1->mass(),ptrHad2->mass());
pHad1=Lorentz5Momentum(ptrHad1->mass(), Pcom*uSmear_v3);
pHad2=Lorentz5Momentum(ptrHad2->mass(),-Pcom*uSmear_v3);
const Lorentz5Momentum pQ1(ptr1data->constituentMass(),ptr->particle(0)->momentum().vect());
const Lorentz5Momentum pQ2(ptr2data->constituentMass(),ptr->particle(1)->momentum().vect());
- Kinematics::BoostIntoTwoParticleFrame(pClu.m(),pQ1, pQ2, pHad1, pHad2);
+ std::vector<Lorentz5Momentum *> momenta;
+ momenta.push_back(&pHad1);
+ momenta.push_back(&pHad2);
+ Kinematics::BoostIntoTwoParticleFrame(pClu.m(),pQ1, pQ2, momenta);
}
else {
// 5-momentum vectors that we have to determine
Kinematics::twoBodyDecay(pClu,ptrHad1->mass(),ptrHad2->mass(),uSmear_v3,
pHad1,pHad2);
}
ptrHad1->set5Momentum(pHad1);
ptrHad2->set5Momentum(pHad2);
// Determine the positions of the two children clusters.
LorentzPoint positionHad1 = LorentzPoint();
LorentzPoint positionHad2 = LorentzPoint();
calculatePositions(pClu, ptr->vertex(), pHad1, pHad2, positionHad1, positionHad2);
ptrHad1->setVertex(positionHad1);
ptrHad2->setVertex(positionHad2);
}
return pair<PPtr,PPtr>(ptrHad1,ptrHad2);
}
pair<PPtr,PPtr> ClusterDecayer::decayIntoTwoHadronsTchannel(tClusterPtr ptr) {
using Constants::pi;
using Constants::twopi;
/* New test for cluster decay modelling according to matrix element
* C-> q,qbar->h1,h2 using |M|^2=1/((p1-p3)^2-(m1-m3)^2)^2 which is Tchannel-like
* */
if ( ptr->numComponents() != 2 ) {
generator()->logWarning( Exception("ClusterDecayer::decayIntoTwoHadrons "
"***Still cluster with not exactly 2 components*** ",
Exception::warning) );
return pair<PPtr,PPtr>();
}
// Extract the id and particle pointer of the two components of the cluster.
tPPtr ptr1 = ptr->particle(0);
tPPtr ptr2 = ptr->particle(1);
tcPDPtr ptr1data = ptr1->dataPtr();
tcPDPtr ptr2data = ptr2->dataPtr();
Lorentz5Momentum pClu = ptr->momentum();
pair<tcPDPtr,tcPDPtr> dataPair
= _hadronSpectrum->chooseHadronPair(ptr->mass(),
ptr1data,
ptr2data);
if(dataPair.first == tcPDPtr() ||
dataPair.second == tcPDPtr()) return pair<PPtr,PPtr>();
// Create the two hadron particle objects with the specified id.
PPtr ptrHad1,ptrHad2;
// produce the hadrons on mass shell
if(_onshell) {
ptrHad1 = dataPair.first ->produceParticle(dataPair.first ->mass());
ptrHad2 = dataPair.second->produceParticle(dataPair.second->mass());
}
// produce the hadrons with mass given by the mass generator
else {
unsigned int ntry(0);
do {
ptrHad1 = dataPair.first ->produceParticle();
ptrHad2 = dataPair.second->produceParticle();
++ntry;
}
while(ntry<_masstry&&ptrHad1->mass()+ptrHad2->mass()>ptr->mass());
// if fails produce on shell and issue warning (should never happen??)
if( ptrHad1->mass() + ptrHad2->mass() > ptr->mass() ) {
generator()->log() << "Failed to produce off-shell hadrons in "
<< "ClusterDecayer::decayIntoTwoHadrons producing hadrons "
<< "on-shell" << endl;
ptrHad1 = dataPair.first ->produceParticle(dataPair.first ->mass());
ptrHad2 = dataPair.second->produceParticle(dataPair.second->mass());
}
}
if (!ptrHad1 || !ptrHad2) {
ostringstream s;
s << "ClusterDecayer::decayIntoTwoHadrons ***Cannot create the two hadrons***"
<< dataPair.first ->PDGName() << " and "
<< dataPair.second->PDGName();
cerr << s.str() << endl;
generator()->logWarning( Exception(s.str(), Exception::warning) );
} else {
if (pClu.m() < ptrHad1->mass()+ptrHad2->mass() ) {
throw Exception() << "Impossible Kinematics in ClusterDecayer::decayIntoTwoHadrons()"
<< Exception::eventerror;
}
Lorentz5Momentum pHad1,pHad2;
Axis uSmear_v3;
Lorentz5Momentum pQ = ptr1->momentum();
Lorentz5Momentum pQbar = ptr2->momentum();
if (_covariantBoost) {
// Use correct boost for a 2 particle constituent cluster
// the direction of the constituents is aligned with the z-Axis
// since the boost is constructed as such
Energy Pcom = Kinematics::pstarTwoBodyDecay(pClu.m(),pQ.m(),pQbar.m());
pQ = Lorentz5Momentum(pQ.m(), Pcom*Axis(0,0,1));
pQbar = Lorentz5Momentum(pQbar.m(),-Pcom*Axis(0,0,1));
}
else {
pQ.boost( -pClu.boostVector() ); // naive boost from Lab to Cluster frame
}
const Axis dirQ = pQ.vect().unit(); // Direction of the quark in COM frame
switch (abs(_kinematics))
{
case 1:
{
Energy mHad1 = ptrHad1->mass();
Energy mHad2 = ptrHad2->mass();
Energy mConst1 = ptr1data->constituentMass();
Energy mConst2 = ptr2data->constituentMass();
Energy PcomIni = Kinematics::pstarTwoBodyDecay(pClu.m(),mConst1,mConst2);
Energy PcomFin = Kinematics::pstarTwoBodyDecay(pClu.m(),mHad1,mHad2);
Energy EcomIni1 = sqrt(sqr(mConst1) + sqr(PcomIni));
Energy EcomFin1 = sqrt(sqr(mHad1) + sqr(PcomFin));
// double Ainv = PcomIni*PcomFin/(EcomIni1*EcomFin1);
// New sampling from distribution 1/((p1-p3)^2-(m1-mHad1)^2)
double Ainv = PcomIni*PcomFin/(EcomIni1*EcomFin1-mHad1*mConst1);
double cosTheta,phi;
do {
uSmear_v3 = dirQ;
cosTheta = sampleCosTchannel(Ainv);
if (fabs(cosTheta-1.0)>1e-14) {
// rotate to sampled angles
uSmear_v3.rotate(acos(cosTheta),dirQ.orthogonal());
phi = UseRandom::rnd(-pi,pi);
uSmear_v3.rotate(phi,dirQ);
}
}
// filter out not corresponding hemisphere
while ( _kinematics<0 && dirQ.dot(uSmear_v3)<=0.0);
break;
}
case 2:
{
// uSmear_v3 changed to isotropic direction
Axis iso;
do {
iso = Axis(1.0, 0.0, 0.0); // just to set the rho to 1
iso.setTheta( acos( UseRandom::rnd( -1.0 , 1.0 ) ) );
iso.setPhi( UseRandom::rnd( -pi , pi ) );
}
// filter out not corresponding hemisphere
while ( _kinematics<0 && dirQ.dot(iso)<=0.0);
uSmear_v3 = iso;
break;
}
case 3:
{
// set to be aligned
uSmear_v3 = dirQ;
break;
}
default:
assert(false);
}
// sanity check
if (_kinematics<0 && !(uSmear_v3.dot(dirQ)>0.0)) {
std::cout << "uSmear_v3.dirQ "<< uSmear_v3.dot(dirQ) << std::endl;
}
assert(_kinematics>0 || (_kinematics<0 && uSmear_v3.dot(dirQ)>0.0));
// TODO simple Tchannel 1/(pConst1-pHad1)**4 distribution
/*
//sanity checks
static int firstAsmall=1;
static int firstAlarge=1;
if (firstAsmall && fabs(A)<1){
// Histogram hcosTheta(-1,1,100);
Histogram hcosThetaRegular(-1,1,100);
int N=10000;
double R=1;
std::cout << "\nA = "<<A <<"\n";
std::cout << "\nR = "<<R <<"\n";
for (int i = 0; i < N; i++)
{
double c=sampleCosRegular(A,R);
hcosThetaRegular+=c;
}
ofstream out1("hcosThetaRegular_Asmall.dat", std::ios::out);
hcosThetaRegular.topdrawOutput(out1);
out1.close();
ofstream out2("hcosThetaRegular_Asmall_RIVET.dat", std::ios::out);
hcosThetaRegular.rivetOutput(out2);
out2.close();
firstAsmall=0;
}
static int firstAlarge=1;
Ainv=0.8;
if (firstAlarge && fabs(1./Ainv)>1){
Histogram hcosThetaTchannel(-1,1,100);
double A=1.0/Ainv;
std::cout << "\nA = "<<A <<"\n";
int N=10000;
for (int i = 0; i < N; i++)
{
double c=sampleCosTchannel(Ainv);
hcosThetaTchannel+=c;
}
ofstream out1("hcosThetaTchannel_Alarge.dat", std::ios::out);
hcosThetaTchannel.topdrawOutput(out1);
out1.close();
ofstream out2("hcosThetaTchannel_Alarge_RIVET.dat", std::ios::out);
hcosThetaTchannel.rivetOutput(out2);
out2.close();
firstAlarge=0;
}
*/
if (_covariantBoost) {
// Use correct boost for a 2 particle constituent cluster
Energy PcomFin=Kinematics::pstarTwoBodyDecay(pClu.m(),ptrHad1->mass(),ptrHad2->mass());
pHad1=Lorentz5Momentum(ptrHad1->mass(), PcomFin*uSmear_v3);
pHad2=Lorentz5Momentum(ptrHad2->mass(),-PcomFin*uSmear_v3);
const Lorentz5Momentum pQ1(ptr1data->constituentMass(),ptr->particle(0)->momentum().vect());
const Lorentz5Momentum pQ2(ptr2data->constituentMass(),ptr->particle(1)->momentum().vect());
- Kinematics::BoostIntoTwoParticleFrame(pClu.m(),pQ1, pQ2, pHad1, pHad2);
+ std::vector<Lorentz5Momentum *> momenta;
+ momenta.push_back(&pHad1);
+ momenta.push_back(&pHad2);
+ Kinematics::BoostIntoTwoParticleFrame(pClu.m(),pQ1, pQ2, momenta);
}
else {
// 5-momentum vectors that we have to determine
Kinematics::twoBodyDecay(pClu,ptrHad1->mass(),ptrHad2->mass(),uSmear_v3,
pHad1,pHad2);
}
ptrHad1->set5Momentum(pHad1);
ptrHad2->set5Momentum(pHad2);
// Determine the positions of the two children clusters.
LorentzPoint positionHad1 = LorentzPoint();
LorentzPoint positionHad2 = LorentzPoint();
calculatePositions(pClu, ptr->vertex(), pHad1, pHad2, positionHad1, positionHad2);
ptrHad1->setVertex(positionHad1);
ptrHad2->setVertex(positionHad2);
}
return pair<PPtr,PPtr>(ptrHad1,ptrHad2);
}
void ClusterDecayer::
calculatePositions(const Lorentz5Momentum &pClu,
const LorentzPoint &positionClu,
const Lorentz5Momentum &,
const Lorentz5Momentum &,
LorentzPoint &positionHad1,
LorentzPoint &positionHad2 ) const {
// First, determine the relative positions of the children hadrons
// with respect to their parent cluster, in the cluster reference frame,
// assuming gaussian smearing with width inversely proportional to the
// parent cluster mass.
Length smearingWidth = hbarc / pClu.m();
LorentzDistance distanceHad[2];
for ( int i = 0; i < 2; i++ ) { // i==0 is the first hadron; i==1 is the second one
Length delta[4]={ZERO,ZERO,ZERO,ZERO};
// smearing of the four components of the LorentzDistance, two at the same time to improve speed
for ( int j = 0; j < 3; j += 2 ) {
delta[j] = UseRandom::rndGauss(smearingWidth, Length(ZERO));
delta[j+1] = UseRandom::rndGauss(smearingWidth, Length(ZERO));
}
// set the distance
delta[0] = abs(delta[0]) +sqrt(sqr(delta[1])+sqr(delta[2])+sqr(delta[3]));
distanceHad[i] = LorentzDistance(delta[1],delta[2],delta[3],delta[0]);
// Boost such relative positions of the children hadrons,
// with respect to their parent cluster,
// from the cluster reference frame to the Lab frame.
distanceHad[i].boost(pClu.boostVector());
}
// Finally, determine the absolute positions of the children hadrons
// in the Lab frame.
positionHad1 = distanceHad[0] + positionClu;
positionHad2 = distanceHad[1] + positionClu;
}
diff --git a/Utilities/Kinematics.cc b/Utilities/Kinematics.cc
--- a/Utilities/Kinematics.cc
+++ b/Utilities/Kinematics.cc
@@ -1,545 +1,514 @@
// -*- C++ -*-
//
// Kinematics.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 Kinematics class.
//
#include "Kinematics.h"
#include <ThePEG/Vectors/Lorentz5Vector.h>
#include <ThePEG/Vectors/LorentzVector.h>
#include <ThePEG/Vectors/LorentzRotation.h>
#include <ThePEG/Repository/EventGenerator.h>
#include <ThePEG/Repository/CurrentGenerator.h>
#include <ThePEG/EventRecord/Event.h>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/lu.hpp>
using namespace Herwig;
using namespace std;
using namespace ThePEG;
/**
* Boost consistently the Lorentz5Momenta in momenta (given in the pi and pj COM frame)
* into the p0Q1 p0Q2 frame.
* */
void Kinematics::BoostIntoTwoParticleFrame(const Energy M, const Lorentz5Momentum & piLab,
const Lorentz5Momentum & pjLab,
std::vector<Lorentz5Momentum * > momenta) {
double cosPhi=piLab.vect().cosTheta(pjLab.vect());
double Phi=acos(cosPhi);
double sinPhi=sin(Phi);
// If Phi==0 use regular 1+1D boost
double epsilon=std::numeric_limits<double>::epsilon();
if (fabs(cosPhi-1.0)<=epsilon || fabs(cosPhi+1.0)<=epsilon) {
Lorentz5Momentum pClu(M,(piLab+pjLab).vect());
Boost bv = pClu.boostVector();
for (unsigned int it = 0; it < momenta.size(); it++) {
momenta[it]->boost(bv);
}
return;
}
Energy Ei=piLab.e();
Energy Ej=pjLab.e();
if (std::isnan(Phi) || std::isinf(Phi)) throw Exception() << "NAN or INF in Phi in Kinematics::BoostIntoTwoParticleFrame\n"
<< Exception::runerror;
Energy mi=piLab.mass();
Energy mj=pjLab.mass();
Energy2 mi2=mi*mi;
Energy2 mj2=mj*mj;
Energy Pi=piLab.vect().mag();
Energy Pj=pjLab.vect().mag();
assert(Pi>ZERO);
std::vector<boost::numeric::ublas::vector<double>> momentaHat;
std::vector<Energy> Masses;
for (unsigned int it = 0; it < momenta.size(); it++) {
Masses.push_back(momenta[it]->mass());
momentaHat.push_back(boost::numeric::ublas::vector<double>(3));
momentaHat[it](0) = momenta[it]->e()/GeV;
momentaHat[it](1) = momenta[it]->x()/GeV;
momentaHat[it](2) = momenta[it]->z()/GeV;
}
// Lorentz Matrix Lambda maps:
// piHat = (Ecomi,0,0, Pcom) to piLab = (Ei, 0,0,Pi )
// pjHat = (Ecomj,0,0,-Pcom) to pjLab = (Ej,Pj*sin(phi),0,Pj*cos(phi))
// and therefore maps also correctly momentaHat to momentaOut into the Lab frame
boost::numeric::ublas::matrix<double> Lambda(3,3);
Energy pstar=Kinematics::pstarTwoBodyDecay(M,mi,mj);
Energy2 A2=pstar*M;
Energy2 B2=Pi*Pj*sinPhi;
Energy B2divPi=Pj*sinPhi;
Energy2 Deltaij=Pi*Ej-Ei*Pj*cosPhi;
double delta2=mi2*Pj*Pj*sinPhi*sinPhi/(Deltaij*Deltaij);
double Lambda11=0;
// better numerics
if (delta2<1e-13) Lambda11 = Deltaij>=ZERO ? (1.0-0.5*delta2):-(1.0-0.5*delta2);
else if (Deltaij!=ZERO) Lambda11= Deltaij>=ZERO ? 1.0/sqrt(1.0+delta2):-1.0/sqrt(1.0+delta2);
if (std::isnan(A2/GeV2) || std::isinf(A2/GeV2)) throw Exception() << "NAN in A2/GeV2\n"
<< Exception::runerror;
Lambda(0,0) = (Ei+Ej)/M;
Lambda(0,1) = B2/A2;
Lambda(0,2) = (Ei-Ej)/(2.0*pstar)-((mi2-mj2)*(Ei+Ej))/(2.0*M*A2);
Lambda(1,0) = B2divPi/M;
- Lambda(1,1) = Lambda11;
+ Lambda(1,1) = Lambda11; // This should be just Deltaij/(M*pstar)
Lambda(1,2) = -(M*M-(mj2-mi2))*B2divPi/(2.0*M*A2);
Lambda(2,0) = (Pi+Pj*cosPhi)/M;
Lambda(2,1) = Ei*B2divPi/A2;
Lambda(2,2) = (A2*A2-0.5*Ei*(Ej*(M*M-(mj2-mi2))-Ei*(M*M-(mi2-mj2))))/(Pi*M*A2);
Axis zAxis(0,0,1);
Axis xAxis(1,0,0);
Lorentz5Momentum piRes(mi,Pi*zAxis);
Lorentz5Momentum pjRes(mj,Pj*(xAxis*sinPhi+zAxis*cosPhi));
std::vector<Lorentz5Momentum> momentaRes;
Lorentz5Momentum pClu1,pClu2;
- // TODO FIX THIS ERROR consistently
- // Horrible fix below but works partially
- // throw Exception() << "Phi = "<< std::setprecision(16)<<fabs(pClu1.vect()*Axis(0,0,1)/pClu1.vect().mag())-1.0<<" in Kinematics::BoostIntoTwoParticleFrame\n" << Exception::warning;
- // TODO in this case just rescale piLab pjLab correspondingly, but the directions shall not change
- // pClu1 aligned with piLab
boost::numeric::ublas::vector<double> momentaOut(3);
- /*
- if (
- fabs(momenta[0]->x()/GeV) < 1e-8
- && fabs(momenta[0]->y()/GeV) < 1e-8
- && fabs(momenta[1]->x()/GeV) < 1e-8
- && fabs(momenta[1]->y()/GeV) < 1e-8
- && fabs((momenta[0]->z()+momenta[1]->z())/GeV) < 1e-8
- ) {
- isAligned = 2;
- double factor = fabs(momenta[0]->z()/pstar);
- double otherFactor[2];
- if (momenta[0]->z() > ZERO) {
- otherFactor[0] = (momenta[0]->e()-factor*sqrt(mi2+sqr(pstar)))/M;
- otherFactor[1] = (momenta[1]->e()-factor*sqrt(mj2+sqr(pstar)))/M;
- // doing the Boost into the Lab frame analytically:
- pClu1 = factor*piRes + otherFactor[0]*(piRes + pjRes);
- pClu2 = factor*pjRes + otherFactor[1]*(piRes + pjRes);
- }
- else {
- otherFactor[0] = (momenta[1]->e()-factor*sqrt(mi2+sqr(pstar)))/M;
- otherFactor[1] = (momenta[0]->e()-factor*sqrt(mj2+sqr(pstar)))/M;
- // doing the Boost into the Lab frame analytically:
- pClu2 = factor*piRes + otherFactor[0]*(piRes + pjRes);
- pClu1 = factor*pjRes + otherFactor[1]*(piRes + pjRes);
- }
- momentaRes.push_back(pClu1);
- momentaRes.push_back(pClu2);
- } */
-
unsigned int iter = 0;
bool isAligned;
Momentum3 piHat(ZERO, ZERO, pstar);
Momentum3 pjHat(ZERO, ZERO, -pstar);
Momentum3 pAligned(ZERO, ZERO, ZERO);
+ // TODO FIX THIS ERROR consistently
+ // Horrible fix below but works partially
+ // TODO in this case just rescale piLab pjLab correspondingly, but the directions shall not change
+ // pClu1 aligned with piLab
for (auto & pHat : momentaHat)
{
isAligned = false;
if (momenta[iter]->vect().mag()>ZERO) {
if (
fabs(1.0 - momenta[iter]->vect().cosTheta(piHat)) < 1.0e-14
) {
isAligned = true;
double factor = momenta[iter]->z()/pstar;
assert(momenta[iter]->z()/pstar > 0);
double otherFactor = (momenta[iter]->e()-factor*sqrt(mi2+sqr(pstar)))/M;
// doing the Boost into the Lab frame analytically:
pAligned = (factor*piRes.vect() + otherFactor*(piRes.vect() + pjRes.vect()));
} else if (
fabs(1.0 - momenta[iter]->vect().cosTheta(pjHat)) < 1.0e-14
) {
isAligned = true;
double factor = fabs(momenta[iter]->z()/pstar);
double otherFactor = (momenta[iter]->e()-factor*sqrt(mj2+sqr(pstar)))/M;
// doing the Boost into the Lab frame analytically:
pAligned = (factor*pjRes.vect() + otherFactor*(piRes.vect() + pjRes.vect()));
}
}
// doing the Boost into the Lab frame:
if (!isAligned) {
momentaOut = boost::numeric::ublas::prod(Lambda,pHat);
momentaRes.push_back(Lorentz5Momentum(Masses[iter],
GeV*Axis(momentaOut(1), double(momenta[iter]->y()/GeV), momentaOut(2))));
}
else {
momentaRes.push_back(Lorentz5Momentum(Masses[iter], pAligned));
}
iter++;
}
// Computing the correct rotation, which maps pi/jRes into pi/jLab
Axis omega1=piRes.vect().unit().cross(piLab.vect().unit());
double cosAngle1=piRes.vect().unit()*piLab.vect().unit();
double angle1=acos(cosAngle1);
// Rotate piRes into piLab
piRes.rotate(angle1, omega1);
pjRes.rotate(angle1, omega1);
// Correspondingly do the actual rotation on all momenta
for(auto & pRes : momentaRes)
pRes.rotate(angle1, omega1);
Axis omega2=piRes.vect().unit();
Momentum3 r1dim=(pjLab.vect()-piRes.vect().unit()*(pjLab.vect()*piRes.vect().unit()));
Momentum3 r2dim=(pjRes.vect()-piRes.vect().unit()*(pjRes.vect()*piRes.vect().unit()));
if (r1dim.mag()==ZERO || r2dim.mag()==ZERO || fabs(sinPhi)<1e-14) //trivial rotation so we are done
{
for (unsigned int i = 0; i < momentaRes.size(); i++) {
// copy the final momenta
*(momenta[i]) = momentaRes[i];
}
return;
}
Axis r1=r1dim.unit();
Axis r2=r2dim.unit();
// signs for 2nd rotation
int signToPi = (piRes.vect()*pjLab.vect())/GeV2 > 0 ? 1:-1;
int signToR1R2 = signToPi*(r2.unit().cross(r1.unit())*piRes.vect())/GeV> 0 ? 1:-1;
double angle2=acos(r1.unit()*r2.unit());
if (signToR1R2<0) angle2=-angle2;
// Rotate pjRes into pjLab
pjRes.rotate(angle2, signToPi*omega2);
// Correspondingly do the actual rotation on all momenta
for (unsigned int i = 0; i < momentaRes.size(); i++) {
momentaRes[i].rotate(angle2, signToPi*omega2);
// copy the final momenta
*(momenta[i]) = momentaRes[i];
}
}
-
/**
* Boost consistently the Lorentz5Momenta pClu1 and pClu2 (given in their COM frame)
* into the p0Q1 p0Q2 frame.
* */
+/*
void Kinematics::BoostIntoTwoParticleFrame(const Energy M, const Lorentz5Momentum & piLab,
const Lorentz5Momentum & pjLab,
Lorentz5Momentum & pClu1,
Lorentz5Momentum & pClu2) {
double cosPhi=piLab.vect().cosTheta(pjLab.vect());
double Phi=acos(cosPhi);
double sinPhi=sin(Phi);
// If Phi==0 use regular 1+1D boost
double epsilon=std::numeric_limits<double>::epsilon();
if (fabs(cosPhi-1.0)<=epsilon || fabs(cosPhi+1.0)<=epsilon) {
Lorentz5Momentum pClu(M,(piLab+pjLab).vect());
Boost bv = pClu.boostVector();
pClu1.boost(bv);
pClu2.boost(bv);
return;
}
Energy Ei=piLab.e();
Energy Ej=pjLab.e();
if (std::isnan(Phi) || std::isinf(Phi)) throw Exception() << "NAN or INF in Phi in Kinematics::BoostIntoTwoParticleFrame\n"
<< Exception::runerror;
Energy mi=piLab.mass();
Energy mj=pjLab.mass();
Energy2 mi2=mi*mi;
Energy2 mj2=mj*mj;
Energy Pi=piLab.vect().mag();
Energy Pj=pjLab.vect().mag();
assert(Pi>ZERO);
boost::numeric::ublas::vector<double> Pclu1Hat(3);
boost::numeric::ublas::vector<double> Pclu2Hat(3);
const Energy M1 = pClu1.mass();
const Energy M2 = pClu2.mass();
Pclu1Hat(0)=pClu1.e()/GeV;
Pclu1Hat(1)=pClu1.x()/GeV;
Pclu1Hat(2)=pClu1.z()/GeV;
Pclu2Hat(0)=pClu2.e()/GeV;
Pclu2Hat(1)=pClu2.x()/GeV;
Pclu2Hat(2)=pClu2.z()/GeV;
// Lorentz Matrix Lambda maps:
// piHat = (Ecomi,0,0, Pcom) to piLab = (Ei, 0,0,Pi )
// pjHat = (Ecomj,0,0,-Pcom) to pjLab = (Ej,Pj*sin(phi),0,Pj*cos(phi))
// and therefore maps also correctly Pclu1/2Hat to Pclu1/2 into the Lab frame
boost::numeric::ublas::matrix<double> Lambda(3,3);
Energy pstar=Kinematics::pstarTwoBodyDecay(M,mi,mj);
Energy2 A2=pstar*M;
Energy2 B2=Pi*Pj*sinPhi;
Energy B2divPi=Pj*sinPhi;
Energy2 Deltaij=Pi*Ej-Ei*Pj*cosPhi;
double delta2=mi2*Pj*Pj*sinPhi*sinPhi/(Deltaij*Deltaij);
double Lambda11=0;
// better numerics
if (delta2<1e-13) Lambda11 = Deltaij>=ZERO ? (1.0-0.5*delta2):-(1.0-0.5*delta2);
else if (Deltaij!=ZERO) Lambda11= Deltaij>=ZERO ? 1.0/sqrt(1.0+delta2):-1.0/sqrt(1.0+delta2);
if (std::isnan(A2/GeV2) || std::isinf(A2/GeV2)) throw Exception() << "NAN in A2/GeV2\n"
<< Exception::runerror;
Lambda(0,0) = (Ei+Ej)/M;
Lambda(0,1) = B2/A2;
Lambda(0,2) = (Ei-Ej)/(2.0*pstar)-((mi2-mj2)*(Ei+Ej))/(2.0*M*A2);
Lambda(1,0) = B2divPi/M;
Lambda(1,1) = Lambda11;
Lambda(1,2) = -(M*M-(mj2-mi2))*B2divPi/(2.0*M*A2);
Lambda(2,0) = (Pi+Pj*cosPhi)/M;
Lambda(2,1) = Ei*B2divPi/A2;
Lambda(2,2) = (A2*A2-0.5*Ei*(Ej*(M*M-(mj2-mi2))-Ei*(M*M-(mi2-mj2))))/(Pi*M*A2);
- /* Determinant calculation, but this is often far from 1 due to rounding errors
- * */
+ // Determinant calculation, but this is often far from 1 due to rounding errors
// double det=0;
// det += Lambda(0,0)*Lambda(1,1)*Lambda(3,2);
// det += Lambda(1,0)*Lambda(2,1)*Lambda(0,2);
// det += Lambda(2,0)*Lambda(0,1)*Lambda(1,2);
// det -= Lambda(0,2)*Lambda(1,1)*Lambda(2,0);
// det -= Lambda(1,0)*Lambda(0,1)*Lambda(2,2);
// det -= Lambda(1,2)*Lambda(2,1)*Lambda(0,0);
// if (fabs(det-1.0)>1e-3 || std::isnan(det)) {
// std::cout << "A2 = "<<std::setprecision(18)<< A2/(GeV2) << std::endl;
// std::cout << "B2 = "<< B2/(GeV2)<< std::endl;
// std::cout << "DET-1 = "<< det-1.0 << std::setprecision(3)<< std::endl;
// std::cout << "Lambda:" << std::endl;
// for (int i = 0; i < 3; i++)
// {
// for (int j = 0; j < 3; j++)
// {
// std::cout<< std::setprecision(18) << Lambda(i,j)<< std::setprecision(3) << "\t";
// }
// std::cout << "\n";
// }
// std::cout << "pi,pj in Lab" << std::endl;
// std::cout << "Phi = " << std::setprecision(18) << Phi << std::endl;
// std::cout << "Phi - pi = " << std::setprecision(18) << Phi - M_PI << std::endl;
// }
// TODO FIX THIS ERROR consistently
// throw Exception() << "Phi = "<< std::setprecision(16)<<fabs(pClu1.vect()*Axis(0,0,1)/pClu1.vect().mag())-1.0<<" in Kinematics::BoostIntoTwoParticleFrame\n" << Exception::warning;
// TODO in this case just rescale piLab pjLab correspondingly, but the directions shall not change
// if ( pClu1.vect()*Axis(0,0,1)/pClu1.vect().mag() > 0) {
// pClu1 aligned with piLab
// double factor = pClu1;
// Lorentz5Momenta pClu1Lab(pClu1.mass(),piLab.vect()*factor);
// Lorentz5Momentum pClu(M,piLab+pjLab);
// Boost bv = pClu.boostVector();
// pClu1.boost(bv);
// pClu2.boost(bv);
// std::cout << "Phi " << std::endl;
// return;
// doing the Boost into the Lab frame:
boost::numeric::ublas::vector<double> Pclu1=boost::numeric::ublas::prod(Lambda,Pclu1Hat);
boost::numeric::ublas::vector<double> Pclu2=boost::numeric::ublas::prod(Lambda,Pclu2Hat);
// Computing the correct rotation, which maps pi/jRes into pi/jLab
Axis zAxis(0,0,1);
Axis xAxis(1,0,0);
Lorentz5Momentum piRes(mi,Pi*zAxis);
Lorentz5Momentum pjRes(mj,Pj*(xAxis*sinPhi+zAxis*cosPhi));
Lorentz5Momentum pClu1Res(M1,GeV*Axis(Pclu1[1],double(pClu1.y()/GeV),Pclu1[2]));
Lorentz5Momentum pClu2Res(M2,GeV*Axis(Pclu2[1],double(pClu2.y()/GeV),Pclu2[2]));
Axis omega1=piRes.vect().unit().cross(piLab.vect().unit());
double cosAngle1=piRes.vect().unit()*piLab.vect().unit();
double angle1=acos(cosAngle1);
// Rotate piRes into piLab
piRes.rotate(angle1, omega1);
pjRes.rotate(angle1, omega1);
// Correspondingly do the actual rotation on pClu1Res and pClu2Res
pClu1Res.rotate(angle1, omega1);
pClu2Res.rotate(angle1, omega1);
Axis omega2=piRes.vect().unit();
Momentum3 r1dim=(pjLab.vect()-piRes.vect().unit()*(pjLab.vect()*piRes.vect().unit()));
Momentum3 r2dim=(pjRes.vect()-piRes.vect().unit()*(pjRes.vect()*piRes.vect().unit()));
if (r1dim.mag()==ZERO || r2dim.mag()==ZERO || fabs(sinPhi)<1e-14) //trivial rotation so we are done
{
pClu1=pClu1Res;
pClu2=pClu2Res;
return;
}
Axis r1=r1dim.unit();
Axis r2=r2dim.unit();
// signs for 2nd rotation
int signToPi = (piRes.vect()*pjLab.vect())/GeV2 > 0 ? 1:-1;
int signToR1R2 = signToPi*(r2.unit().cross(r1.unit())*piRes.vect())/GeV> 0 ? 1:-1;
double angle2=acos(r1.unit()*r2.unit());
if (signToR1R2<0) angle2=-angle2;
// Rotate pjRes into pjLab
pjRes.rotate(angle2, signToPi*omega2);
// Correspondingly do the actual rotation on pClu1Res and pClu2Res
pClu1Res.rotate(angle2, signToPi*omega2);
pClu2Res.rotate(angle2, signToPi*omega2);
pClu1=pClu1Res;
pClu2=pClu2Res;
}
-
+*/
bool Kinematics::twoBodyDecayNoBoost(const Lorentz5Momentum & p,
const Energy m1, const Energy m2,
const double cosThetaK, const double phiK,
Lorentz5Momentum & p1, Lorentz5Momentum & p2) {
Energy M = p.mass();
Energy2 M2 = M*M;
Energy2 m12 = m1*m1;
Energy2 m22 = m2*m2;
double cph = cos(phiK);
double sph = sin(phiK);
Energy Q = p.vect().mag();
Energy EQ = sqrt(M2+Q*Q);
double B = sqr(M/Q);
double sinThetaK = sqrt(1.-cosThetaK*cosThetaK);
Energy Pcom = sqrt(Kinematics::kaellenV(M,m1,m2))/(2.0*M);
double eta1 = (M2+m12-m22)/(2.0*M2);
double eta2 = (M2-m12+m22)/(2.0*M2);
Energy k = EQ*Pcom/(Q*sqrt(1.0+B-cosThetaK*cosThetaK));
Energy k0 = -EQ*eta1;
Energy E1 = sqrt(k*k+m12+2.0*cosThetaK*Q*k*eta1+sqr(Q*eta1));
k0 += E1;
Momentum3 kvec3(k*cph*sinThetaK,k*sph*sinThetaK,k*cosThetaK);
Lorentz5Momentum kvec5(kvec3,k0);
Axis xAx(1.0,0.0,0.0);
Axis yAx(0.0,1.0,0.0);
Axis zAx(0.0,0.0,1.0);
Lorentz5Momentum pz (Q*zAx,EQ);
p1 = pz*eta1+kvec5;
p2 = pz*eta2-kvec5;
// double Norm = sqrt(B)*(1.0+B)/2.0;
// double PhaseSpaceVol = (1.0+B)*Pcom/(8.0*Q*Norm);
double theta = acos(zAx.cosTheta(p.vect()));
double phi = theta==0 ? 0.0:atan2(p.vect().y()/GeV,p.vect().x()/GeV);
p1.rotate(theta, yAx);
p1.rotate(phi, zAx);
p2.rotate(theta, yAx);
p2.rotate(phi, zAx);
if (fabs((p1.m()-m1)/GeV)>1e-5) std::cout << "p1.m = "<<p1.m()/GeV << " m1 = "<<m1/GeV << std::endl;
if (fabs((p2.m()-m2)/GeV)>1e-5) std::cout << "p2.m = "<<p2.m()/GeV << " m2 = "<<m2/GeV << std::endl;
if (fabs(((p1+p2).m()-M)/GeV)>1e-5) std::cout << "(p1+p2).m = "<<(p1+p2).m()/GeV << " M = "<<M/GeV << std::endl;
return true;
}
bool Kinematics::twoBodyDecay(const Lorentz5Momentum & p,
const Energy m1, const Energy m2,
const Axis & unitDir1,
Lorentz5Momentum & p1, Lorentz5Momentum & p2) {
Energy min=p.mass();
if ( min >= m1 + m2 && m1 >= ZERO && m2 >= ZERO ) {
Momentum3 pstarVector = unitDir1 * Kinematics::pstarTwoBodyDecay(min,m1,m2);
p1 = Lorentz5Momentum(m1, pstarVector);
p2 = Lorentz5Momentum(m2,-pstarVector);
// boost from CM to LAB
Boost bv = p.boostVector();
double gammarest = p.e()/p.mass();
p1.boost( bv, gammarest );
p2.boost( bv, gammarest );
return true;
}
return false;
}
/*****
* This function, as the name implies, performs a three body decay. The decay
* products are distributed uniformly in all three directions.
****/
bool Kinematics::threeBodyDecay(Lorentz5Momentum p0, Lorentz5Momentum &p1,
Lorentz5Momentum &p2, Lorentz5Momentum &p3,
double (*fcn)(Energy2,Energy2,Energy2,InvEnergy4)) {
// Variables needed in calculation...named same as fortran version
Energy a = p0.mass() + p1.mass();
Energy b = p0.mass() - p1.mass();
Energy c = p2.mass() + p3.mass();
if(b < c) {
CurrentGenerator::log()
<< "Kinematics::threeBodyDecay() phase space problem\n"
<< p0.mass()/GeV << " -> "
<< p1.mass()/GeV << ' '
<< p2.mass()/GeV << ' '
<< p3.mass()/GeV << '\n';
return false;
}
Energy d = abs(p2.mass()-p3.mass());
Energy2 aa = sqr(a);
Energy2 bb = sqr(b);
Energy2 cc = sqr(c);
Energy2 dd = sqr(d);
Energy2 ee = (b-c)*(a-d);
Energy2 a1 = 0.5 * (aa+bb);
Energy2 b1 = 0.5 * (cc+dd);
InvEnergy4 c1 = 4./(sqr(a1-b1));
Energy2 ff;
double ww;
Energy4 pp,qq,rr;
// Choose mass of subsystem 23 with prescribed distribution
const unsigned int MAXTRY = 100;
unsigned int ntry=0;
do {
// ff is the mass squared of the 23 subsystem
ff = UseRandom::rnd()*(cc-bb)+bb;
// pp is ((m0+m1)^2 - m23^2)((m0-m1)^2-m23)
pp = (aa-ff)*(bb-ff);
// qq is ((m2+m3)^2 - m23^2)(|m2-m3|^2-m23^2)
qq = (cc-ff)*(dd-ff);
// weight
ww = (fcn != NULL) ? (*fcn)(ff,a1,b1,c1) : 1.0;
ww = sqr(ww);
rr = ee*ff*UseRandom::rnd();
++ntry;
}
while(pp*qq*ww < rr*rr && ntry < MAXTRY );
if(ntry >= MAXTRY) {
CurrentGenerator::log() << "Kinematics::threeBodyDecay can't generate momenta"
<< " after " << MAXTRY << " attempts\n";
return false;
}
// ff is the mass squared of subsystem 23
// do 2 body decays 0->1+23, 23->2+3
double CosAngle, AzmAngle;
Lorentz5Momentum p23;
p23.setMass(sqrt(ff));
generateAngles(CosAngle,AzmAngle);
bool status = twoBodyDecay(p0,p1.mass(),p23.mass(),CosAngle,AzmAngle,p1,p23);
generateAngles(CosAngle,AzmAngle);
status &= twoBodyDecay(p23,p2.mass(),p3.mass(),CosAngle,AzmAngle,p2,p3);
return status;
}
diff --git a/Utilities/Kinematics.h b/Utilities/Kinematics.h
--- a/Utilities/Kinematics.h
+++ b/Utilities/Kinematics.h
@@ -1,151 +1,152 @@
// -*- C++ -*-
//
// Kinematics.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_Kinematics_H
#define HERWIG_Kinematics_H
// This is the declaration of the Kinematics class.
#include <ThePEG/Config/ThePEG.h>
#include "ThePEG/Vectors/ThreeVector.h"
#include "ThePEG/Vectors/LorentzRotation.h"
#include "ThePEG/Repository/UseRandom.h"
#include <ThePEG/Vectors/Lorentz5Vector.h>
namespace Herwig {
using namespace ThePEG;
/** \ingroup Utilities
* This is a namespace which provides some useful methods
* for kinematics computation, as the two body decays.
*
* NB) For other useful kinematical methods (and probably even those
* implemented in Kinematics class!):
* @see UtilityBase
*/
namespace Kinematics {
/**
* Calculate the momenta for a two body decay
* The return value indicates success or failure.
* @param p The momentum of the decaying particle
* @param m1 The mass of the first decay product
* @param m2 The mass of the second decay product
* @param unitDir1 Direction for the products in the rest frame of
* the decaying particle
* @param p1 The momentum of the first decay product
* @param p2 The momentum of the second decay product
*/
bool twoBodyDecay(const Lorentz5Momentum & p,
const Energy m1, const Energy m2,
const Axis & unitDir1,
Lorentz5Momentum & p1, Lorentz5Momentum & p2);
/**
* Calculate the momenta for a two body decay without performing a boost
* The return value indicates success or failure.
* @param p The momentum of the decaying particle
* @param m1 The mass of the first decay product
* @param m2 The mass of the second decay product
* @param unitDir1 Direction for the products in the rest frame of
* the decaying particle
* @param p1 The momentum of the first decay product
* @param p2 The momentum of the second decay product
*/
bool twoBodyDecayNoBoost(const Lorentz5Momentum & p,
const Energy m1, const Energy m2,
const double cosThetaK, const double phiK,
Lorentz5Momentum & p1, Lorentz5Momentum & p2);
/*
* Kaellen functions
* */
inline double kaellen(double x, double y, double z){
return (x*x-(y+z)*(y+z))*(x*x-(y-z)*(y-z));
}
inline Energy4 kaellenV(Energy x, Energy y, Energy z){
return (x*x-(y+z)*(y+z))*(x*x-(y-z)*(y-z));
}
/**
* Boost consistently the Lorentz5Momenta pClu1 and pClu2 (given in their COM frame)
* into the p0Q1 p0Q2 frame.
* */
+ /*
void BoostIntoTwoParticleFrame(const Energy M, const Lorentz5Momentum & piLab,
const Lorentz5Momentum & pjLab,
Lorentz5Momentum & pClu1,
- Lorentz5Momentum & pClu2);
+ Lorentz5Momentum & pClu2);*/
void BoostIntoTwoParticleFrame(const Energy M, const Lorentz5Momentum & piLab,
const Lorentz5Momentum & pjLab,
std::vector<Lorentz5Momentum * > momenta);
/**
* It returns the unit 3-vector with the given cosTheta and phi.
*/
inline Axis unitDirection(const double cosTheta, const double phi) {
return ( fabs( cosTheta ) <= 1.0 ?
Axis( cos(phi)*sqrt(1.0-cosTheta*cosTheta) ,
sin(phi)*sqrt(1.0-cosTheta*cosTheta) , cosTheta) : Axis() );
}
/**
* Calculate the momenta for a two body decay
* The return value indicates success or failure.
* @param p The momentum of the decaying particle
* @param m1 The mass of the first decay product
* @param m2 The mass of the second decay product
* @param cosThetaStar1 Polar angle in rest frame
* @param phiStar1 Azimuthal angle in rest frame
* @param p1 The momentum of the first decay product
* @param p2 The momentum of the second decay product
*/
inline bool twoBodyDecay(const Lorentz5Momentum & p,
const Energy m1, const Energy m2,
const double cosThetaStar1,
const double phiStar1,
Lorentz5Momentum & p1, Lorentz5Momentum & p2) {
return twoBodyDecay(p,m1,m2,unitDirection(cosThetaStar1,phiStar1),p1,p2);
}
/**
* As the name implies, this takes the momentum p0 and does a flat three
* body decay into p1..p3. The argument fcn is used to add additional
* weights. If it is not used, the default is just flat in phasespace.
* The return value indicates success or failure.
*/
bool threeBodyDecay(Lorentz5Momentum p0, Lorentz5Momentum &p1,
Lorentz5Momentum &p2, Lorentz5Momentum &p3,
double (*fcn)(Energy2,Energy2,Energy2,InvEnergy4) = NULL);
/**
* For the two body decay M -> m1 + m2 it gives the module of the
* 3-momentum of the decay product in the rest frame of M.
*/
inline Energy pstarTwoBodyDecay(const Energy M,
const Energy m1, const Energy m2) {
return ( M > ZERO && m1 >=ZERO && m2 >= ZERO && M > m1+m2 ?
Energy(sqrt(( sqr(M) - sqr(m1+m2) )*( sqr(M) - sqr(m1-m2) ))
/ (2.0*M) ) : ZERO);
}
/**
* This just generates angles. First flat -1..1, second flat 0..2Pi
*/
inline void generateAngles(double & ct, double & az) {
ct = UseRandom::rnd()*2.0 - 1.0; // Flat from -1..1
az = UseRandom::rnd()*Constants::twopi;
}
}
}
#endif /* HERWIG_Kinematics_H */
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 23, 2:01 PM (1 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4479888
Default Alt Text
(55 KB)
Attached To
rHERWIGHG herwighg
Event Timeline
Log In to Comment