Page MenuHomeHEPForge

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -1,46 +1,47 @@
168ae2110e964d62fbc1331a1c2e095952a67748 release-2-5-2
3abb4fa42e20e332796c2572334c2d77204cd0e0 release-2-4-2
4796ca080aafd5daa3b7349b015cb1df944428a2 release-2-5-0
76da042f056eb153981b4d005d5474ffb90a5e88 release-2-4-1
81a684a558413c69df314365eabf09893ffd43d8 release-2-6-0
bd75cd00d99f4bdbaed992daf98f0a73c0f91e9b release-2-4-0
ff6ecc8d49ce10299303b050394bd5cb5837f1c3 release-2-5-1
d0389f5453b2c210923e1adc7b872b18269de668 release-2-6-1
f8998033021185942533b824607285feb3fbd2dc release-2-6-1a
cead23e428b9aacaf2d709e722624e54f844498b release-2-6-1b
191db4655439045f912cb21bd905e729d59ec7bc release-2-6-2
edb538156e9c3d64bb842934b4cebf0126aeb9ea release-2-6-3
eb4a104591859ecac18746b1ad54d6aa0c2a5d1a release-2-7-0
568971ac5b3c1d044c9259f2280a8304fc5a62e9 trunk-before-QED
6e3edb6cfeb4ee48687eb4eb3d016026fc59d602 trunk-after-QED
633abb80b571aa23088957df60e9b0000bbb8a22 release-2-7-1
1bdde095d2346c15ee548e5406a96f0fc6d6e0f1 beforeHQ
a0f9fb821396092bdbeee532bcb0bd624f58335b before_MB_merge
270c1e6b34aa7f758f1d9868c4d3e1ec4bf4e709 herwig-7-0-0
6e0f198c1c2603ecd1a0b6cfe40105cda4bd58c5 herwig-7-0-1
566c1de845a8070559cda45b1bdb40afa18cb2cc herwig-7-0-2
f5c4aa956880f2def763ebd57de7b5bfa55cb1db herwig-7-0-3
65282dedfc2e4bec184e68678dbf4c553c968f38 herwig-7-0-4
541e7790b65ed423c86780bf66ec30e6b99b5a18 herwig-7-1-0
dd35a1c12d57c047169e8c5fb18644972d49c6ac herwig-7-1-1
0d651b079756b63713e32a1341d81e4dfc7eeb7b herwig-7-1-2
4b97934bc41c861c4be04f563ffa68a94a982560 herwig-7-1-3
97aca5398cfa1f3273804f03fa96fa0fa23eca61 herwig-7-1-4
3d69fbe18c682c98891c5f9204947f2eb7a72686 herwig-7-1-5
392e0bdc94f11d067dc24792a4b470b09eb8fdf7 herwig-7-2-0
392e0bdc94f11d067dc24792a4b470b09eb8fdf7 herwig-7-2-0
af22cb052ed5e3fd5323ec9a24693962efe8144d herwig-7-2-0
f3047b8819217a3ea264adf423ab183c9ca9b3a1 herwig-7-1-6
f3047b8819217a3ea264adf423ab183c9ca9b3a1 herwig-7-1-6
c9519d355aeab14d43e4655faf78e2c7324b34a6 herwig-7-1-6
af22cb052ed5e3fd5323ec9a24693962efe8144d herwig-7-2-0
51f480cba67c56c2b6d5a65383254d6aafcd5cbd herwig-7-2-0
b76dedb1f1f1bc42d676bca00019852b32003502 herwig-7-2-1
474898bfc7f68305c654d6323f844c6cbd0921fc herwig-7.2.2
6178a87dde19c2547c33d44c66f17e429977cab2 herwig-7-2-3
efb456a0668e87b03bc4b1ae68d53ec4fdd777b6 validated_with_herwighg-default
9c12a19ed7fd602882ce332fe35ec0ef55d18d6e herwig-7-3-0
9c12a19ed7fd602882ce332fe35ec0ef55d18d6e herwig-7-3-0
0000000000000000000000000000000000000000 herwig-7-3-0
0000000000000000000000000000000000000000 herwig-7-3-0
d7f1293681a15a5c8bc77849c5d2b83014b6684b herwig-7-3-0
+815012e091bbf0f0197f1f336142a4e2e2edbfe9 validated_with_herwighg-default-9413f438d49c
diff --git a/Decay/General/GeneralTwoBodyDecayer.cc b/Decay/General/GeneralTwoBodyDecayer.cc
--- a/Decay/General/GeneralTwoBodyDecayer.cc
+++ b/Decay/General/GeneralTwoBodyDecayer.cc
@@ -1,828 +1,838 @@
// -*- C++ -*-
//
// GeneralTwoBodyDecayer.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 GeneralTwoBodyDecayer class.
//
#include "GeneralTwoBodyDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/PDT/DecayMode.h"
#include "ThePEG/Utilities/Exception.h"
#include "Herwig/Shower/RealEmissionProcess.h"
#include "Herwig/Utilities/Kinematics.h"
+#include "Herwig/Shower/QTilde/Dark/HiddenValleyModel.h"
using namespace Herwig;
ParticleVector GeneralTwoBodyDecayer::decay(const Particle & parent,
const tPDVector & children) const {
// return empty vector if products heavier than parent
Energy mout(ZERO);
for(tPDVector::const_iterator it=children.begin();
it!=children.end();++it) mout+=(**it).massMin();
if(mout>parent.mass()) return ParticleVector();
// generate the decay
bool cc;
int imode=modeNumber(cc,parent.dataPtr(),children);
// generate the kinematics
ParticleVector decay=generate(generateIntermediates(),cc,imode,parent);
// make the colour connections
colourConnections(parent, decay);
// return the answer
return decay;
}
void GeneralTwoBodyDecayer::doinit() {
PerturbativeDecayer::doinit();
assert( incoming_ && outgoing_.size()==2);
//create phase space mode
addMode(new_ptr(PhaseSpaceMode(incoming_,{outgoing_[0],outgoing_[1]},
maxWeight_)));
}
int GeneralTwoBodyDecayer::modeNumber(bool & cc, tcPDPtr parent,
const tPDVector & children) const {
long parentID = parent->id();
long id1 = children[0]->id();
long id2 = children[1]->id();
cc = false;
long out1 = outgoing_[0]->id();
long out2 = outgoing_[1]->id();
if( parentID == incoming_->id() &&
((id1 == out1 && id2 == out2) ||
(id1 == out2 && id2 == out1)) ) {
return 0;
}
else if(incoming_->CC() && parentID == incoming_->CC()->id()) {
cc = true;
if( outgoing_[0]->CC()) out1 = outgoing_[0]->CC()->id();
if( outgoing_[1]->CC()) out2 = outgoing_[1]->CC()->id();
if((id1 == out1 && id2 == out2) ||
(id1 == out2 && id2 == out1)) return 0;
}
return -1;
}
void GeneralTwoBodyDecayer::
colourConnections(const Particle & parent,
const ParticleVector & out) const {
PDT::Colour incColour(parent.data().iColour());
PDT::Colour outaColour(out[0]->data().iColour());
PDT::Colour outbColour(out[1]->data().iColour());
//incoming colour singlet
if(incColour == PDT::Colour0) {
// colour triplet-colourantitriplet
if((outaColour == PDT::Colour3 && outbColour == PDT::Colour3bar) ||
(outaColour == PDT::Colour3bar && outbColour == PDT::Colour3)) {
bool ac(out[0]->id() < 0);
out[0]->colourNeighbour(out[1],!ac);
}
//colour octet
else if(outaColour == PDT::Colour8 && outbColour == PDT::Colour8) {
out[0]->colourNeighbour(out[1]);
out[0]->antiColourNeighbour(out[1]);
}
// colour singlets
else if(outaColour == PDT::Colour0 && outbColour == PDT::Colour0) {
}
// unknown
else
throw Exception() << "Unknown outgoing colours for decaying "
<< "colour singlet in "
<< "GeneralTwoBodyDecayer::colourConnections "
<< outaColour << " " << outbColour
<< Exception::runerror;
}
//incoming colour triplet
else if(incColour == PDT::Colour3) {
// colour triplet + singlet
if(outaColour == PDT::Colour3 && outbColour == PDT::Colour0) {
out[0]->incomingColour(const_ptr_cast<tPPtr>(&parent));
}
//opposite order
else if(outaColour == PDT::Colour0 && outbColour == PDT::Colour3) {
out[1]->incomingColour(const_ptr_cast<tPPtr>(&parent));
}
// octet + triplet
else if(outaColour == PDT::Colour8 && outbColour == PDT::Colour3) {
out[0]->incomingColour(const_ptr_cast<tPPtr>(&parent));
out[1]->antiColourNeighbour(out[0]);
}
//opposite order
else if(outaColour == PDT::Colour3 && outbColour == PDT::Colour8) {
out[1]->incomingColour(const_ptr_cast<tPPtr>(&parent));
out[0]->antiColourNeighbour(out[1]);
}
else if(outaColour == PDT::Colour3bar && outaColour == PDT::Colour3bar) {
tColinePtr col[2] = {ColourLine::create(out[0],true),
ColourLine::create(out[1],true)};
parent.colourLine()->setSinkNeighbours(col[0],col[1]);
}
else
throw Exception() << "Unknown outgoing colours for decaying "
<< "colour triplet in "
<< "GeneralTwoBodyDecayer::colourConnections() "
<< outaColour << " " << outbColour
<< Exception::runerror;
}
// incoming colour anti triplet
else if(incColour == PDT::Colour3bar) {
// colour antitriplet +singlet
if(outaColour == PDT::Colour3bar && outbColour == PDT::Colour0) {
out[0]->incomingAntiColour(const_ptr_cast<tPPtr>(&parent));
}
//opposite order
else if(outaColour == PDT::Colour0 && outbColour == PDT::Colour3bar) {
out[1]->incomingAntiColour(const_ptr_cast<tPPtr>(&parent));
}
//octet + antitriplet
else if(outaColour == PDT::Colour3bar && outbColour == PDT::Colour8) {
out[1]->incomingAntiColour(const_ptr_cast<tPPtr>(&parent));
out[0]->colourNeighbour(out[1]);
}
//opposite order
else if(outaColour == PDT::Colour8 && outbColour == PDT::Colour3bar) {
out[0]->incomingAntiColour(const_ptr_cast<tPPtr>(&parent));
out[1]->colourNeighbour(out[0]);
}
else if(outaColour == PDT::Colour3 && outbColour == PDT::Colour3) {
tColinePtr col[2] = {ColourLine::create(out[0]),
ColourLine::create(out[1])};
parent.antiColourLine()->setSourceNeighbours(col[0],col[1]);
}
else
throw Exception() << "Unknown outgoing colours for decaying "
<< "colour antitriplet "
<< "in GeneralTwoBodyDecayer::colourConnections() "
<< outaColour << " " << outbColour
<< Exception::runerror;
}
//incoming colour octet
else if(incColour == PDT::Colour8) {
// triplet-antitriplet
if(outaColour == PDT::Colour3&&outbColour == PDT::Colour3bar) {
out[0]->incomingColour(const_ptr_cast<tPPtr>(&parent));
out[1]->incomingAntiColour(const_ptr_cast<tPPtr>(&parent));
}
// opposite order
else if(outbColour == PDT::Colour3&&outaColour == PDT::Colour3bar) {
out[0]->incomingAntiColour(const_ptr_cast<tPPtr>(&parent));
out[1]->incomingColour(const_ptr_cast<tPPtr>(&parent));
}
// neutral octet
else if(outaColour == PDT::Colour0&&outbColour == PDT::Colour8) {
out[1]->incomingColour(const_ptr_cast<tPPtr>(&parent));
out[1]->incomingAntiColour(const_ptr_cast<tPPtr>(&parent));
}
else if(outbColour == PDT::Colour0&&outaColour == PDT::Colour8) {
out[0]->incomingColour(const_ptr_cast<tPPtr>(&parent));
out[0]->incomingAntiColour(const_ptr_cast<tPPtr>(&parent));
}
else
throw Exception() << "Unknown outgoing colours for decaying "
<< "colour octet "
<< "in GeneralTwoBodyDecayer::colourConnections() "
<< outaColour << " " << outbColour
<< Exception::runerror;
}
else if(incColour == PDT::Colour6) {
if(outaColour == PDT::Colour3 && outbColour == PDT::Colour3) {
tPPtr tempParent = const_ptr_cast<tPPtr>(&parent);
Ptr<MultiColour>::pointer parentColour =
dynamic_ptr_cast<Ptr<MultiColour>::pointer>
(tempParent->colourInfo());
tColinePtr line1 = const_ptr_cast<tColinePtr>(parentColour->colourLines()[0]);
line1->addColoured(dynamic_ptr_cast<tPPtr>(out[0]));
tColinePtr line2 = const_ptr_cast<tColinePtr>(parentColour->colourLines()[1]);
line2->addColoured(dynamic_ptr_cast<tPPtr>(out[1]));
}
else
throw Exception() << "Unknown outgoing colours for decaying "
<< "colour sextet "
<< "in GeneralTwoBodyDecayer::colourConnections() "
<< outaColour << " " << outbColour
<< Exception::runerror;
}
else if(incColour == PDT::Colour6bar) {
if(outaColour == PDT::Colour3bar && outbColour == PDT::Colour3bar) {
tPPtr tempParent = const_ptr_cast<tPPtr>(&parent);
Ptr<MultiColour>::pointer parentColour =
dynamic_ptr_cast<Ptr<MultiColour>::pointer>
(tempParent->colourInfo());
tColinePtr line1 = const_ptr_cast<tColinePtr>(parentColour->antiColourLines()[0]);
line1->addAntiColoured(dynamic_ptr_cast<tPPtr>(out[0]));
tColinePtr line2 = const_ptr_cast<tColinePtr>(parentColour->antiColourLines()[1]);
line2->addAntiColoured(dynamic_ptr_cast<tPPtr>(out[1]));
}
else
throw Exception() << "Unknown outgoing colours for decaying "
<< "colour anti-sextet "
<< "in GeneralTwoBodyDecayer::colourConnections() "
<< outaColour << " " << outbColour
<< Exception::runerror;
}
else
throw Exception() << "Unknown incoming colour in "
<< "GeneralTwoBodyDecayer::colourConnections() "
<< incColour
<< Exception::runerror;
}
bool GeneralTwoBodyDecayer::twoBodyMEcode(const DecayMode & dm, int & mecode,
double & coupling) const {
assert(dm.parent()->id() == incoming_->id());
ParticleMSet::const_iterator pit = dm.products().begin();
long id1 = (*pit)->id();
++pit;
long id2 = (*pit)->id();
long id1t(outgoing_[0]->id()), id2t(outgoing_[1]->id());
mecode = -1;
coupling = 1.;
if( id1 == id1t && id2 == id2t ) {
return true;
}
else if( id1 == id2t && id2 == id1t ) {
return false;
}
else
assert(false);
return false;
}
void GeneralTwoBodyDecayer::persistentOutput(PersistentOStream & os) const {
os << incoming_ << outgoing_ << maxWeight_;
}
void GeneralTwoBodyDecayer::persistentInput(PersistentIStream & is, int) {
is >> incoming_ >> outgoing_ >> maxWeight_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeAbstractClass<GeneralTwoBodyDecayer,PerturbativeDecayer>
describeHerwigGeneralTwoBodyDecayer("Herwig::GeneralTwoBodyDecayer", "Herwig.so");
void GeneralTwoBodyDecayer::Init() {
static ClassDocumentation<GeneralTwoBodyDecayer> documentation
("This class is designed to be a base class for all 2 body decays"
"in a general model");
}
double GeneralTwoBodyDecayer::brat(const DecayMode &, const Particle & p,
double oldbrat) const {
ParticleVector children = p.children();
if( children.size() != 2 || !p.data().widthGenerator() )
return oldbrat;
// partial width for this mode
Energy scale = p.mass();
Energy pwidth =
partialWidth( make_pair(p.dataPtr(), scale),
make_pair(children[0]->dataPtr(), children[0]->mass()),
make_pair(children[1]->dataPtr(), children[1]->mass()) );
Energy width = p.data().widthGenerator()->width(p.data(), scale);
return pwidth/width;
}
void GeneralTwoBodyDecayer::doinitrun() {
PerturbativeDecayer::doinitrun();
for(unsigned int ix=0;ix<numberModes();++ix) {
double fact = pow(1.5,int(mode(ix)->incoming().first->iSpin())-1);
mode(ix)->maxWeight(fact*mode(ix)->maxWeight());
}
}
double GeneralTwoBodyDecayer::colourFactor(tcPDPtr in, tcPDPtr out1,
tcPDPtr out2) const {
// identical particle symmetry factor
double output = out1->id()==out2->id() ? 0.5 : 1.;
// colour neutral incoming particle
if(in->iColour()==PDT::Colour0) {
// both colour neutral
if(out1->iColour()==PDT::Colour0 && out2->iColour()==PDT::Colour0)
output *= 1.;
// colour triplet/ antitriplet
else if((out1->iColour()==PDT::Colour3 && out2->iColour()==PDT::Colour3bar) ||
(out1->iColour()==PDT::Colour3bar && out2->iColour()==PDT::Colour3 ) ) {
output *= 3.;
}
// colour octet colour octet
else if(out1->iColour()==PDT::Colour8 && out2->iColour()==PDT::Colour8 ) {
output *= 8.;
}
+ else if((out1->iColour()==PDT::DarkColourFundamental
+ && out2->iColour()==PDT::DarkColourAntiFundamental) ||
+ (out1->iColour()==PDT::DarkColourAntiFundamental
+ && out2->iColour()==PDT::DarkColourFundamental)) {
+ throw Exception() << "The GeneralTwoBodyDecayer cannot yet handle "
+ << "decays to dark coloured particles. Please include these "
+ << "decays in the hard process"
+ << Exception::runerror;
+ }
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for decay colour neutral particle in "
<< "GeneralTwoBodyDecayer::colourFactor() for "
<< in->PDGName() << " -> "
<< out1->PDGName() << " " << out2->PDGName()
<< Exception::runerror;
}
// triplet
else if(in->iColour()==PDT::Colour3) {
// colour triplet + neutral
if((out1->iColour()==PDT::Colour0 && out2->iColour()==PDT::Colour3) ||
(out1->iColour()==PDT::Colour3 && out2->iColour()==PDT::Colour0) ) {
output *= 1.;
}
// colour triplet + octet
else if((out1->iColour()==PDT::Colour8 && out2->iColour()==PDT::Colour3) ||
(out1->iColour()==PDT::Colour3 && out2->iColour()==PDT::Colour8) ) {
output *= 4./3.;
}
// colour anti triplet anti triplet
else if(out1->iColour()==PDT::Colour3bar &&
out2->iColour()==PDT::Colour3bar) {
output *= 2.;
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for decay colour triplet particle in "
<< "GeneralTwoBodyDecayer::colourFactor() for "
<< in->PDGName() << " -> "
<< out1->PDGName() << " " << out2->PDGName()
<< Exception::runerror;
}
// anti triplet
else if(in->iColour()==PDT::Colour3bar) {
// colour anti triplet + neutral
if((out1->iColour()==PDT::Colour0 && out2->iColour()==PDT::Colour3bar ) ||
(out1->iColour()==PDT::Colour3bar && out2->iColour()==PDT::Colour0 ) ) {
output *= 1.;
}
// colour anti triplet + octet
else if((out1->iColour()==PDT::Colour8 && out2->iColour()==PDT::Colour3bar ) ||
(out1->iColour()==PDT::Colour3bar && out2->iColour()==PDT::Colour8 ) ) {
output *= 4./3.;
}
// colour triplet triplet
else if(out1->iColour()==PDT::Colour3 &&
out2->iColour()==PDT::Colour3) {
output *= 2.;
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for decay colour anti triplet particle in "
<< "GeneralTwoBodyDecayer::colourFactor() for "
<< in->PDGName() << " -> "
<< out1->PDGName() << " " << out2->PDGName()
<< Exception::runerror;
}
else if(in->iColour()==PDT::Colour8) {
// colour octet + neutral
if((out1->iColour()==PDT::Colour0 && out2->iColour()==PDT::Colour8 ) ||
(out1->iColour()==PDT::Colour8 && out2->iColour()==PDT::Colour0 ) ) {
output *= 1.;
}
// colour triplet/antitriplet
else if((out1->iColour()==PDT::Colour3 && out2->iColour()==PDT::Colour3bar) ||
(out1->iColour()==PDT::Colour3bar && out2->iColour()==PDT::Colour3 ) ) {
output *= 0.5;
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for decay colour octet particle in "
<< "GeneralTwoBodyDecayer::colourFactor() for "
<< in->PDGName() << " -> "
<< out1->PDGName() << " " << out2->PDGName()
<< Exception::runerror;
}
else if(in->iColour()==PDT::Colour6) {
// colour sextet -> triplet triplet
if( out1->iColour()==PDT::Colour3 && out2->iColour()==PDT::Colour3 ) {
output *= 1.;
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for decay colour sextet particle in "
<< "GeneralTwoBodyDecayer::colourFactor() for "
<< in->PDGName() << " -> "
<< out1->PDGName() << " " << out2->PDGName()
<< Exception::runerror;
}
else if(in->iColour()==PDT::Colour6bar) {
// colour sextet -> triplet triplet
if( out1->iColour()==PDT::Colour3bar && out2->iColour()==PDT::Colour3bar ) {
output *= 1.;
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for decay colour anti-sextet particle in "
<< "GeneralTwoBodyDecayer::colourFactor() for "
<< in->PDGName() << " -> "
<< out1->PDGName() << " " << out2->PDGName()
<< Exception::runerror;
}
else
throw Exception() << "Unknown colour "
<< in->iColour() << " for the decaying particle in "
<< "GeneralTwoBodyDecayer::colourFactor() for "
<< in->PDGName() << " -> "
<< out1->PDGName() << " " << out2->PDGName()
<< Exception::runerror;
return output;
}
Energy GeneralTwoBodyDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
// select the number of the mode
tPDVector children;
children.push_back(const_ptr_cast<PDPtr>(outa.first));
children.push_back(const_ptr_cast<PDPtr>(outb.first));
bool cc;
int nmode=modeNumber(cc,inpart.first,children);
tcPDPtr newchild[2] = {mode(nmode)->outgoing()[0],
mode(nmode)->outgoing()[1]};
// make the particles
Lorentz5Momentum pparent = Lorentz5Momentum(inpart.second);
PPtr parent = inpart.first->produceParticle(pparent);
vector<Lorentz5Momentum> pout(2);
double ctheta,phi;
Kinematics::generateAngles(ctheta,phi);
Kinematics::twoBodyDecay(pparent, outa.second, outb.second,
ctheta, phi,pout[0],pout[1]);
if( ( !cc && outa.first!=newchild[0]) ||
( cc && !(( outa.first->CC() && outa.first->CC() == newchild[0])||
( !outa.first->CC() && outa.first == newchild[0]) )))
swap(pout[0],pout[1]);
tPDVector out = {const_ptr_cast<tPDPtr>(outa.first),
const_ptr_cast<tPDPtr>(outb.first)};
double me = me2(-1,*parent,out,pout,Initialize);
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second,
outa.second, outb.second);
return me/(8.*Constants::pi)*pcm;
}
void GeneralTwoBodyDecayer::decayInfo(PDPtr incoming, PDPair outgoing) {
incoming_=incoming;
outgoing_.clear();
outgoing_.push_back(outgoing.first );
outgoing_.push_back(outgoing.second);
}
double GeneralTwoBodyDecayer::matrixElementRatio(const Particle & inpart,
const ParticleVector & decay2,
const ParticleVector & decay3,
MEOption meopt,
ShowerInteraction inter) {
// calculate R/B
tPDVector outgoing = {const_ptr_cast<tPDPtr>(decay2[0]->dataPtr()),
const_ptr_cast<tPDPtr>(decay2[1]->dataPtr())};
const vector<Lorentz5Momentum> mom = {decay2[0]->momentum(),
decay2[1]->momentum()};
double B = me2 (0, inpart, outgoing,mom, meopt);
double R = threeBodyME(0, inpart, decay3, inter, meopt);
return R/B;
}
const vector<DVector> & GeneralTwoBodyDecayer::getColourFactors(const Particle & inpart,
const ParticleVector & decay,
unsigned int & nflow) {
// calculate the colour factors for the three-body decay
vector<int> sing,trip,atrip,oct,sex,asex;
for(unsigned int it=0;it<decay.size();++it) {
if (decay[it]->dataPtr()->iColour() == PDT::Colour0 ) sing. push_back(it);
else if(decay[it]->dataPtr()->iColour() == PDT::Colour3 ) trip. push_back(it);
else if(decay[it]->dataPtr()->iColour() == PDT::Colour3bar ) atrip.push_back(it);
else if(decay[it]->dataPtr()->iColour() == PDT::Colour8 ) oct. push_back(it);
else if(decay[it]->dataPtr()->iColour() == PDT::Colour6 ) sex. push_back(it);
else if(decay[it]->dataPtr()->iColour() == PDT::Colour6bar ) asex. push_back(it);
}
// identical particle symmetry factor
double symFactor=1.;
if (( sing.size()==2 && decay[ sing[0]]->id()==decay[ sing[1]]->id()) ||
( trip.size()==2 && decay[ trip[0]]->id()==decay[ trip[1]]->id()) ||
(atrip.size()==2 && decay[atrip[0]]->id()==decay[atrip[1]]->id()) ||
( oct.size()==2 && decay[ oct[0]]->id()==decay[ oct[1]]->id()) ||
( sex.size()==2 && decay[ sex[0]]->id()==decay[ sex[1]]->id()) ||
( asex.size()==2 && decay[ asex[0]]->id()==decay[ asex[1]]->id()))
symFactor /= 2.;
else if (oct.size()==3 &&
decay[oct[0]]->id()==decay[oct[1]]->id() &&
decay[oct[0]]->id()==decay[oct[2]]->id())
symFactor /= 6.;
colour_ = vector<DVector>(1,DVector(1,symFactor*1.));
// decaying colour singlet
if(inpart.dataPtr()->iColour() == PDT::Colour0) {
if(trip.size()==1 && atrip.size()==1 && oct.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor*4.));
}
else if(trip.size()==1 && atrip.size()==1 && sing.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor*3.));
}
else if (oct.size()==3){
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor*24.));
}
else if(sing.size()==3) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor));
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for decay colour scalar particle in "
<< "GeneralTwoBodyDecayer::getColourFactors() for "
<< inpart. dataPtr()->PDGName() << " -> "
<< decay[0]->dataPtr()->PDGName() << " "
<< decay[1]->dataPtr()->PDGName() << " "
<< decay[2]->dataPtr()->PDGName()
<< Exception::runerror;
}
// decaying colour triplet
else if(inpart.dataPtr()->iColour() == PDT::Colour3) {
if(trip.size()==1 && sing.size()==1 && oct.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor*4./3.));
}
else if(trip.size()==1 && sing.size()==2) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor));
}
else if(trip.size()==1 && oct.size()==2) {
nflow = 2;
colour_.clear();
colour_.resize(2,DVector(2,0.));
colour_[0][0] = symFactor*16./9.; colour_[0][1] = -symFactor*2./9.;
colour_[1][0] = -symFactor*2./9.; colour_[1][1] = symFactor*16./9.;
}
else if(atrip.size()==2 && sing.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor*2.));
}
else if(atrip.size()==2 && oct.size()==1) {
nflow = 2;
colour_.clear();
colour_.resize(2,DVector(2,0.));
colour_[0][0] = 8./3.*symFactor; colour_[0][1] =-4./3.*symFactor;
colour_[1][0] = -4./3.*symFactor; colour_[1][1] = 8./3.*symFactor;
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for decay colour triplet particle in "
<< "GeneralTwoBodyDecayer::getColourFactors() for "
<< inpart. dataPtr()->PDGName() << " -> "
<< decay[0]->dataPtr()->PDGName() << " "
<< decay[1]->dataPtr()->PDGName() << " "
<< decay[2]->dataPtr()->PDGName()
<< Exception::runerror;
}
// decaying colour anti-triplet
else if(inpart.dataPtr()->iColour() == PDT::Colour3bar) {
if(atrip.size()==1 && sing.size()==1 && oct.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor*4./3.));
}
else if(atrip.size()==1 && sing.size()==2) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor));
}
else if(atrip.size()==1 && oct.size()==2){
nflow = 2;
colour_.clear();
colour_ .resize(2,DVector(2,0.));
colour_[0][0] = symFactor*16./9.; colour_[0][1] = -symFactor*2./9.;
colour_[1][0] = -symFactor*2./9.; colour_[1][1] = symFactor*16./9.;
}
else if(trip.size()==2 && sing.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor*2.));
}
else if(trip.size()==2 && oct.size()==1) {
nflow = 2;
colour_.clear();
colour_.resize(2,DVector(2,0.));
colour_[0][0] = 8./3.*symFactor; colour_[0][1] =-4./3.*symFactor;
colour_[1][0] = -4./3.*symFactor; colour_[1][1] = 8./3.*symFactor;
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for decay colour anti-triplet particle in "
<< "GeneralTwoBodyDecayer::getColourFactors() for "
<< inpart. dataPtr()->PDGName() << " -> "
<< decay[0]->dataPtr()->PDGName() << " "
<< decay[1]->dataPtr()->PDGName() << " "
<< decay[2]->dataPtr()->PDGName()
<< Exception::runerror;
}
// decaying colour octet
else if(inpart.dataPtr()->iColour() == PDT::Colour8) {
if(oct.size()==1 && trip.size()==1 && atrip.size()==1) {
nflow = 2;
colour_.clear();
colour_.resize(2,DVector(2,0.));
colour_[0][0] = symFactor*2./3. ; colour_[0][1] = -symFactor*1./12.;
colour_[1][0] = -symFactor*1./12.; colour_[1][1] = symFactor*2./3. ;
}
else if (sing.size()==1 && trip.size()==1 && atrip.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor*0.5));
}
else if (oct.size()==2 && sing.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor*3.));
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for a decaying colour octet particle in "
<< "GeneralTwoBodyDecayer::getColourFactors() for "
<< inpart. dataPtr()->PDGName() << " -> "
<< decay[0]->dataPtr()->PDGName() << " "
<< decay[1]->dataPtr()->PDGName() << " "
<< decay[2]->dataPtr()->PDGName()
<< Exception::runerror;
}
// Sextet
else if(inpart.dataPtr()->iColour() == PDT::Colour6) {
if(trip.size()==2 && sing.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor));
}
else if(trip.size()==2 && oct.size()==1) {
nflow = 2;
colour_.clear();
colour_.resize(2,DVector(2,0.));
colour_[0][0] = 4./3.*symFactor; colour_[0][1] = 1./3.*symFactor;
colour_[1][0] = 1./3.*symFactor; colour_[1][1] = 4./3.*symFactor;
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for a decaying colour sextet particle in "
<< "GeneralTwoBodyDecayer::getColourFactors() for "
<< inpart. dataPtr()->PDGName() << " -> "
<< decay[0]->dataPtr()->PDGName() << " "
<< decay[1]->dataPtr()->PDGName() << " "
<< decay[2]->dataPtr()->PDGName()
<< Exception::runerror;
}
// anti Sextet
else if(inpart.dataPtr()->iColour() == PDT::Colour6bar) {
if(atrip.size()==2 && sing.size()==1) {
nflow = 1;
colour_ = vector<DVector>(1,DVector(1,symFactor));
}
else if(atrip.size()==2 && oct.size()==1) {
nflow = 2;
colour_.clear();
colour_.resize(2,DVector(2,0.));
colour_[0][0] = 4./3.*symFactor; colour_[0][1] = 1./3.*symFactor;
colour_[1][0] = 1./3.*symFactor; colour_[1][1] = 4./3.*symFactor;
}
else
throw Exception() << "Unknown colour for the outgoing particles"
<< " for a decaying colour anti-sextet particle in "
<< "GeneralTwoBodyDecayer::getColourFactors() for "
<< inpart. dataPtr()->PDGName() << " -> "
<< decay[0]->dataPtr()->PDGName() << " "
<< decay[1]->dataPtr()->PDGName() << " "
<< decay[2]->dataPtr()->PDGName()
<< Exception::runerror;
}
else
throw Exception() << "Unknown colour for the decaying particle in "
<< "GeneralTwoBodyDecayer::getColourFactors() for "
<< inpart. dataPtr()->PDGName() << " -> "
<< decay[0]->dataPtr()->PDGName() << " "
<< decay[1]->dataPtr()->PDGName() << " "
<< decay[2]->dataPtr()->PDGName()
<< Exception::runerror;
return colour_;
}
const GeneralTwoBodyDecayer::CFlow &
GeneralTwoBodyDecayer::colourFlows(const Particle & inpart,
const ParticleVector & decay) {
// static initialization of commonly used colour structures
static const CFlow init = CFlow(3, CFlowPairVec(1, make_pair(0, 1.)));
static CFlow tripflow = init;
static CFlow atripflow = init;
static CFlow octflow = init;
static CFlow sexflow = init;
static CFlow epsflow = init;
static const CFlow fpflow = CFlow(4, CFlowPairVec(1, make_pair(0, 1.)));
static bool initialized = false;
if (! initialized) {
tripflow[2].resize(2, make_pair(0,1.));
tripflow[2][0] = make_pair(0, 1.);
tripflow[2][1] = make_pair(1,-1.);
tripflow[1][0] = make_pair(1, 1.);
atripflow[1].resize(2, make_pair(0,1.));
atripflow[1][0] = make_pair(0, 1.);
atripflow[1][1] = make_pair(1,-1.);
atripflow[2][0] = make_pair(1, 1.);
octflow[0].resize(2, make_pair(0,1.));
octflow[0][0] = make_pair(0,-1.);
octflow[0][1] = make_pair(1, 1.);
octflow[2][0] = make_pair(1, 1.);
sexflow[0].resize(2, make_pair(0,1.));
sexflow[0][1] = make_pair(1,1.);
sexflow[2][0] = make_pair(1,1.);
epsflow[0].resize(2, make_pair(0,-1.));
epsflow[0][0] = make_pair(0,-1.);
epsflow[0][1] = make_pair(1,-1.);
epsflow[2][0] = make_pair(1,1.);
initialized = true;
}
// main function body
int sing=0,trip=0,atrip=0,oct=0,sex=0,asex=0;
for (size_t it=0; it<decay.size(); ++it) {
switch ( decay[it]->dataPtr()->iColour() ) {
case PDT::Colour0: ++sing; break;
case PDT::Colour3: ++trip; break;
case PDT::Colour3bar: ++atrip; break;
case PDT::Colour8: ++oct; break;
case PDT::Colour6: ++sex; break;
case PDT::Colour6bar: ++asex; break;
/// @todo: handle these better
case PDT::ColourUndefined: break;
case PDT::Coloured: break;
case PDT::DarkColoured: break;
case PDT::DarkColourNeutral: break;
case PDT::DarkColourFundamental: break;
case PDT::DarkColourAntiFundamental: break;
case PDT::DarkColourAdjoint: break;
}
}
const CFlow * retval = 0;
// decaying colour triplet
if(inpart.dataPtr()->iColour() == PDT::Colour3 &&
trip==1 && oct==2) {
retval = &tripflow;
}
// decaying colour anti-triplet
else if(inpart.dataPtr()->iColour() == PDT::Colour3bar &&
atrip==1 && oct==2){
retval = &atripflow;
}
// decaying colour octet
else if(inpart.dataPtr()->iColour() == PDT::Colour8 &&
oct==1 && trip==1 && atrip==1) {
retval = &octflow;
}
// decaying colour sextet
else if(inpart.dataPtr()->iColour() ==PDT::Colour6 &&
oct==1 && trip==2) {
retval = &sexflow;
}
// decaying anti colour sextet
else if(inpart.dataPtr()->iColour() ==PDT::Colour6bar &&
oct==1 && atrip==2) {
retval = &sexflow;
}
// decaying colour triplet (eps)
else if(inpart.dataPtr()->iColour() == PDT::Colour3 &&
atrip==2 && oct==1) {
retval = &epsflow;
}
// decaying colour anti-triplet (eps)
else if(inpart.dataPtr()->iColour() == PDT::Colour3bar &&
trip==2 && oct==1){
retval = &epsflow;
}
else {
retval = &fpflow;
}
return *retval;
}
double GeneralTwoBodyDecayer::threeBodyME(const int , const Particle &,
const ParticleVector &,
ShowerInteraction, MEOption) {
throw Exception() << "Base class GeneralTwoBodyDecayer::threeBodyME() "
<< "called, should have an implementation in the inheriting class"
<< Exception::runerror;
return 0.;
}
diff --git a/Hadronization/ClusterFissioner.cc b/Hadronization/ClusterFissioner.cc
--- a/Hadronization/ClusterFissioner.cc
+++ b/Hadronization/ClusterFissioner.cc
@@ -1,1170 +1,1677 @@
// -*- 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(false),
+ _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))
-{}
-
+ _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(_clMaxExotic,GeV) << _clPowLight << _clPowHeavy
- << _clPowExotic << _pSplitLight
- << _pSplitHeavy << _pSplitExotic
+ 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 << _kinematicThresholdChoice << ounit(_kappa, GeV/meter)
- << _enhanceSProb << ounit(_m0Fission,GeV) << _massMeasure << _dim << _phaseSpaceWeights
- << _hadronSpectrum
- << _probPowFactor << _probShift << ounit(_kinThresholdShift,sqr(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(_clMaxExotic,GeV) >> _clPowLight >> _clPowHeavy
- >> _clPowExotic >> _pSplitLight
- >> _pSplitHeavy >> _pSplitExotic
+ is >> iunit(_clMaxLight,GeV) >> iunit(_clMaxHeavy,GeV) >> iunit(_clMaxDiquark,GeV) >> iunit(_clMaxExotic,GeV)
+ >> _clPowLight >> _clPowHeavy >> _clPowDiquark >> _clPowExotic
+ >> _pSplitLight >> _pSplitHeavy >> _pSplitExotic
>> _fissionCluster >> _fissionPwt
- >> iunit(_btClM,GeV) >> _iopRem >> _kinematicThresholdChoice
- >> iunit(_kappa, GeV/meter)
- >> _enhanceSProb >> iunit(_m0Fission,GeV) >> _massMeasure >> _dim >> _phaseSpaceWeights
- >> _hadronSpectrum
- >> _probPowFactor >> _probShift >> iunit(_kinThresholdShift,sqr(GeV));
+ >> _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() )
+ _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, 10.0*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, 10.0*GeV,
+ &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, 10.0*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 selector class.",
+ "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 selector class",
+ "New",
+ "Alternative cluster fission which does not depend on the hadron spectrum class",
1);
+ static SwitchOption interfaceFissionNewDiquarkSuppression
+ (interfaceFission,
+ "NewDiquarkSuppression",
+ "Alternative cluster fission which does not depend on the hadron spectrum class"
+ " and includes a suppression of AlphaS^2(Mc) for Diquark Production during "
+ "Cluster Fission",
+ -1);
+
+
+ static Switch<ClusterFissioner,int> interfaceDiquarkClusterFission
+ ("DiquarkClusterFission",
+ "Allow clusters to fission to 1 or 2 diquark Clusters or Turn off diquark fission completely",
+ &ClusterFissioner::_diquarkClusterFission, 0, false, false);
+ static SwitchOption interfaceDiquarkClusterFissionAll
+ (interfaceDiquarkClusterFission,
+ "All",
+ "Allow diquark clusters and baryon clusters to fission to new diquark Clusters",
+ 2);
+ static SwitchOption interfaceDiquarkClusterFissionOnlyBaryonClusters
+ (interfaceDiquarkClusterFission,
+ "OnlyBaryonClusters",
+ "Allow only baryon clusters to fission to new diquark Clusters",
+ 1);
+ static SwitchOption interfaceDiquarkClusterFissionNo
+ (interfaceDiquarkClusterFission,
+ "No",
+ "Don't allow clusters to fission to new diquark Clusters",
+ 0);
+ static SwitchOption interfaceDiquarkClusterFissionOff
+ (interfaceDiquarkClusterFission,
+ "Off",
+ "Don't allow clusters fission to draw diquarks ",
+ -1);
static ParMap<ClusterFissioner,double> interfaceFissionPwt
("FissionPwt",
"The weights for quarks in the fission process.",
&ClusterFissioner::_fissionPwt, -1, 1.0, 0.0, 10.0,
false, false, Interface::upperlim);
static Switch<ClusterFissioner,int> interfaceRemnantOption
("RemnantOption",
"Option for the treatment of remnant clusters",
&ClusterFissioner::_iopRem, 1, false, false);
static SwitchOption interfaceRemnantOptionSoft
(interfaceRemnantOption,
"Soft",
"Both clusters produced in the fission of the beam cluster"
" are treated as soft clusters.",
0);
static SwitchOption interfaceRemnantOptionHard
(interfaceRemnantOption,
"Hard",
"Only the cluster containing the remnant is treated as a soft cluster.",
1);
static SwitchOption interfaceRemnantOptionVeryHard
(interfaceRemnantOption,
"VeryHard",
"Even remnant clusters are treated as hard, i.e. all clusters the same",
2);
static Parameter<ClusterFissioner,Energy> interfaceBTCLM
("SoftClusterFactor",
"Parameter for the mass spectrum of remnant clusters",
&ClusterFissioner::_btClM, GeV, 1.*GeV, 0.1*GeV, 10.0*GeV,
false, false, Interface::limited);
static Parameter<ClusterFissioner,Tension> interfaceStringTension
("StringTension",
"String tension used in vertex displacement calculation",
&ClusterFissioner::_kappa, GeV/meter,
1.0e15*GeV/meter, ZERO, ZERO,
false, false, Interface::lowerlim);
static Switch<ClusterFissioner,int> interfaceEnhanceSProb
("EnhanceSProb",
"Option for enhancing strangeness",
&ClusterFissioner::_enhanceSProb, 0, false, false);
static SwitchOption interfaceEnhanceSProbNo
(interfaceEnhanceSProb,
"No",
"No strangeness enhancement.",
0);
static SwitchOption interfaceEnhanceSProbScaled
(interfaceEnhanceSProb,
"Scaled",
"Scaled strangeness enhancement",
1);
static SwitchOption interfaceEnhanceSProbExponential
(interfaceEnhanceSProb,
"Exponential",
"Exponential strangeness enhancement",
2);
static Switch<ClusterFissioner,int> interfaceMassMeasure
("MassMeasure",
"Option to use different mass measures",
&ClusterFissioner::_massMeasure,0,false,false);
static SwitchOption interfaceMassMeasureMass
(interfaceMassMeasure,
"Mass",
"Mass Measure",
0);
static SwitchOption interfaceMassMeasureLambda
(interfaceMassMeasure,
"Lambda",
"Lambda Measure",
1);
static Parameter<ClusterFissioner,Energy> interfaceFissionMassScale
("FissionMassScale",
"Cluster fission mass scale",
&ClusterFissioner::_m0Fission, GeV, 2.0*GeV, 0.1*GeV, 50.*GeV,
false, false, Interface::limited);
static Parameter<ClusterFissioner,double> interfaceProbPowFactor
- ("ProbablityPowerFactor",
- "Power factor in ClausterFissioner bell probablity function",
- &ClusterFissioner::_probPowFactor, 2.0, 1.0, 20.0,
+ ("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
- ("ProbablityShift",
+ ("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 ClausterFissioner",
+ "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,bool> interfacePhaseSpaceWeights
+
+ 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, false, false, false);
- static SwitchOption interfacePhaseSpaceWeightsYes
- (interfacePhaseSpaceWeights,
- "Yes",
- "Do include the effect of cluster fission phase space",
- true);
+ &ClusterFissioner::_phaseSpaceWeights, 0, false, false);
static SwitchOption interfacePhaseSpaceWeightsNo
(interfacePhaseSpaceWeights,
"No",
"Do not include the effect of cluster phase space",
- false);
+ 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' rather then C -> C' + C''
+ // Sometimes the clusters decay C -> H + C' or C -> H + H' rather then C -> C' + C''
if(one) {
clusters.push_back(one);
if(one->isBeamCluster() && softUEisOn)
one->isAvailable(false);
if(isHeavy(one) && one->isAvailable())
clusterStack.push(one);
}
if(two) {
clusters.push_back(two);
if(two->isBeamCluster() && softUEisOn)
two->isAvailable(false);
if(isHeavy(two) && two->isAvailable())
clusterStack.push(two);
}
}
}
ClusterFissioner::cutType
ClusterFissioner::cutTwo(ClusterPtr & cluster, tPVector & finalhadrons,
bool softUEisOn) {
// need to make sure only 2-cpt clusters get here
assert(cluster->numComponents() == 2);
tPPtr ptrQ1 = cluster->particle(0);
tPPtr ptrQ2 = cluster->particle(1);
Energy Mc = cluster->mass();
assert(ptrQ1);
assert(ptrQ2);
// And check if those particles are from a beam remnant
bool rem1 = cluster->isBeamRemnant(0);
bool rem2 = cluster->isBeamRemnant(1);
// workout which distribution to use
bool soft1(false),soft2(false);
switch (_iopRem) {
case 0:
soft1 = rem1 || rem2;
soft2 = rem2 || rem1;
break;
case 1:
soft1 = rem1;
soft2 = rem2;
break;
}
// Initialization for the exponential ("soft") mass distribution.
static const int max_loop = 1000;
int counter = 0;
Energy Mc1 = ZERO, Mc2 = ZERO,m1=ZERO,m2=ZERO,m=ZERO;
tcPDPtr toHadron1, toHadron2;
PPtr newPtr1 = PPtr ();
PPtr newPtr2 = PPtr ();
bool succeeded = false;
Lorentz5Momentum pClu1, pClu2, pQ1, pQone, pQtwo, pQ2;
do
{
succeeded = false;
++counter;
// get a flavour for the qqbar pair
drawNewFlavour(newPtr1,newPtr2,cluster);
// check for right ordering
assert (ptrQ2);
assert (newPtr2);
assert (ptrQ2->dataPtr());
assert (newPtr2->dataPtr());
if(cantMakeHadron(ptrQ1, newPtr1) || cantMakeHadron(ptrQ2, newPtr2)) {
swap(newPtr1, newPtr2);
// check again
if(cantMakeHadron(ptrQ1, newPtr1) || cantMakeHadron(ptrQ2, newPtr2)) {
throw Exception()
<< "ClusterFissioner cannot split the cluster ("
<< ptrQ1->PDGName() << ' ' << ptrQ2->PDGName()
<< ") into hadrons.\n" << Exception::runerror;
}
}
// Check that new clusters can produce particles and there is enough
// phase space to choose the drawn flavour
m1 = ptrQ1->data().constituentMass();
m2 = ptrQ2->data().constituentMass();
m = newPtr1->data().constituentMass();
// Do not split in the case there is no phase space available
if(Mc < m1+m + m2+m) continue;
pQ1.setMass(m1);
pQone.setMass(m);
pQtwo.setMass(m);
pQ2.setMass(m2);
- pair<Energy,Energy> res = drawNewMasses(Mc, soft1, soft2, pClu1, pClu2,
+ 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 = res.first;
- Mc2 = res.second;
+ Mc1 = pClu1.mass();
+ Mc2 = pClu2.mass();
// static kinematic threshold
if(_kinematicThresholdChoice == 0) {
- if(Mc1 < m1+m || Mc2 < m+m2 || Mc1+Mc2 > Mc) continue;
+ 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 ) {
- if ( phaseSpaceVeto(Mc,Mc1,Mc2,m,m1,m2) )
+ 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);
- pair<Energy,Energy> res = drawNewMasses(mmax, soft1, soft2, pClu1, pClu2,
+ double weightMasses = drawNewMasses(mmax, soft1, soft2, pClu1, pClu2,
ptrQ[iq1], pQ1, newPtr1, pQone,
newPtr2, pQtwo, ptrQ[iq1], pQ2);
- Mc1 = res.first; Mc2 = res.second;
+ if (weightMasses == 0.0) continue;
+
+ Mc1 = pClu1.mass();
+ Mc2 = pClu2.mass();
if(Mc1 < m1+m || Mc2 < m+m2 || Mc1+Mc2 > mmax) continue;
- if ( _phaseSpaceWeights ) {
- if ( phaseSpaceVeto(mmax,Mc1,Mc2,m,m1,m2) )
+ 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;
}
-void ClusterFissioner::drawNewFlavour(PPtr& newPtrPos,PPtr& newPtrNeg) const {
+/**
+ * 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 = "
+ << ounit(Mc,GeV) <<" GeV MassLightestBaryonPair = "
+ << ounit(spectrum()->massLightestBaryonPair(pD1,pD2) ,GeV)
+ << " GeV cannot decay" << Exception::eventerror;
+ }
+ minMass = spectrum()->massLightestBaryonPair(pD1,diq)
+ + spectrum()->massLightestBaryonPair(diq,pD2);
+ if (Mc < minMass) continue;
+ 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(_fissionCluster){
+ switch(abs(_fissionCluster)){
case 0:
for ( const long& id : spectrum()->lightHadronizingQuarks() )
choice.insert(_hadronSpectrum->pwtQuark(id),id);
break;
case 1:
for ( const long& id : spectrum()->lightHadronizingQuarks() )
choice.insert(_fissionPwt.find(id)->second,id);
break;
default :
assert(false);
}
long idNew = choice.select(UseRandom::rnd());
newPtrPos = getParticle(idNew);
newPtrNeg = getParticle(-idNew);
assert (newPtrPos);
assert(newPtrNeg);
assert (newPtrPos->dataPtr());
assert(newPtrNeg->dataPtr());
}
void ClusterFissioner::drawNewFlavourEnhanced(PPtr& newPtrPos,PPtr& newPtrNeg,
Energy2 mass2) const {
if ( spectrum()->gluonId() != ParticleID::g )
throw Exception() << "strange enhancement only working with Standard Model hadronization"
<< Exception::runerror;
// Flavour is assumed to be only u, d, s, with weights
// (which are not normalized probabilities) given
// by the same weights as used in HadronsSelector for
// the decay of clusters into two hadrons.
double prob_d = 0.;
double prob_u = 0.;
double prob_s = 0.;
double scale = abs(double(sqr(_m0Fission)/mass2));
// Choose which splitting weights you wish to use
-switch(_fissionCluster){
+switch(abs(_fissionCluster)){
// 0: ClusterFissioner and ClusterDecayer use the same weights
case 0:
prob_d = _hadronSpectrum->pwtQuark(ParticleID::d);
prob_u = _hadronSpectrum->pwtQuark(ParticleID::u);
/* Strangeness enhancement:
Case 1: probability scaling
Case 2: Exponential scaling
*/
if (_enhanceSProb == 1)
prob_s = (_maxScale < scale) ? 0. : pow(_hadronSpectrum->pwtQuark(ParticleID::s),scale);
else if (_enhanceSProb == 2)
prob_s = (_maxScale < scale) ? 0. : exp(-scale);
break;
/* 1: ClusterFissioner uses its own unique set of weights,
i.e. decoupled from ClusterDecayer */
case 1:
prob_d = _fissionPwt.find(ParticleID::d)->second;
prob_u = _fissionPwt.find(ParticleID::u)->second;
if (_enhanceSProb == 1)
prob_s = (_maxScale < scale) ? 0. : pow(_fissionPwt.find(ParticleID::s)->second,scale);
else if (_enhanceSProb == 2)
prob_s = (_maxScale < scale) ? 0. : exp(-scale);
break;
default:
assert(false);
}
int choice = UseRandom::rnd3(prob_u, prob_d, prob_s);
long idNew = 0;
switch (choice) {
case 0: idNew = ThePEG::ParticleID::u; break;
case 1: idNew = ThePEG::ParticleID::d; break;
case 2: idNew = ThePEG::ParticleID::s; break;
}
newPtrPos = getParticle(idNew);
newPtrNeg = getParticle(-idNew);
assert (newPtrPos);
assert(newPtrNeg);
assert (newPtrPos->dataPtr());
assert(newPtrNeg->dataPtr());
}
Energy2 ClusterFissioner::clustermass(const ClusterPtr & cluster) const {
Lorentz5Momentum pIn = cluster->momentum();
Energy2 endpointmass2 = sqr(cluster->particle(0)->mass() +
cluster->particle(1)->mass());
Energy2 singletm2 = pIn.m2();
// Return either the cluster mass, or the lambda measure
return (_massMeasure == 0) ? singletm2 : singletm2 - endpointmass2;
}
Energy ClusterFissioner::drawChildMass(const Energy M, const Energy m1,
const Energy m2, const Energy m,
const double expt, const bool soft) const {
/***************************
* This method, given in input the cluster mass Mclu of an heavy cluster C,
* made of consituents of masses m1 and m2, draws the masses Mclu1 and Mclu2
* of, respectively, the children cluster C1, made of constituent masses m1
* and m, and cluster C2, of mass Mclu2 and made of constituent masses m2
* and m. The mass is extracted from one of the two following mass
* distributions:
* --- power-like ("normal" distribution)
* d(Prob) / d(M^exponent) = const
* where the exponent can be different from the two children C1 (exp1)
* and C2 (exponent2).
* --- exponential ("soft" distribution)
* d(Prob) / d(M^2) = exp(-b*M)
* where b = 2.0 / average.
* Such distributions are limited below by the masses of
* the constituents quarks, and above from the mass of decaying cluster C.
* The choice of which of the two mass distributions to use for each of the
* two cluster children is dictated by iRemnant (see below).
* If the number of attempts to extract a pair of mass values that are
* kinematically acceptable is above some fixed number (max_loop, see below)
* the method gives up and returns false; otherwise, when it succeeds, it
* returns true.
*
* These distributions have been modified from HERWIG:
* Before these were:
* Mclu1 = m1 + (Mclu - m1 - m2)*pow( rnd(), 1.0/exponent1 );
* The new one coded here is a more efficient version, same density
* but taking into account 'in phase space from' beforehand
***************************/
// hard cluster
if(!soft) {
return pow(UseRandom::rnd(pow((M-m1-m2-m)*UnitRemoval::InvE, expt),
pow(m*UnitRemoval::InvE, expt)), 1./expt
)*UnitRemoval::E + m1;
}
// Otherwise it uses a soft mass distribution
else {
static const InvEnergy b = 2.0 / _btClM;
Energy max = M-m1-m2-2.0*m;
double rmin = b*max;
rmin = ( rmin < 50 ) ? exp(-rmin) : 0.;
double r1;
do {
r1 = UseRandom::rnd(rmin, 1.0) * UseRandom::rnd(rmin, 1.0);
}
while (r1 < rmin);
return m1 + m - log(r1)/b;
}
}
void ClusterFissioner::calculateKinematics(const Lorentz5Momentum & pClu,
const Lorentz5Momentum & p0Q1,
const bool toHadron1,
const bool toHadron2,
Lorentz5Momentum & pClu1,
Lorentz5Momentum & pClu2,
Lorentz5Momentum & pQ1,
Lorentz5Momentum & pQbar,
Lorentz5Momentum & pQ,
Lorentz5Momentum & pQ2bar) const {
/******************
* This method solves the kinematics of the two body cluster decay:
* C (Q1 Q2bar) ---> C1 (Q1 Qbar) + C2 (Q Q2bar)
* In input we receive the momentum of C, pClu, and the momentum
* of the quark Q1 (constituent of C), p0Q1, both in the LAB frame.
* Furthermore, two boolean variables inform whether the two fission
* products (C1, C2) decay immediately into a single hadron (in which
* case the cluster itself is identify with that hadron) and we do
* not have to solve the kinematics of the components (Q1,Qbar) for
* C1 and (Q,Q2bar) for C2.
* The output is given by the following momenta (all 5-components,
* and all in the LAB frame):
* pClu1 , pClu2 respectively of C1 , C2
* pQ1 , pQbar respectively of Q1 , Qbar in C1
* pQ , pQ2bar respectively of Q , Q2 in C2
* The assumption, suggested from the string model, is that, in C frame,
* C1 and its constituents Q1 and Qbar are collinear, and collinear to
* the direction of Q1 in C (that is before cluster decay); similarly,
* (always in the C frame) C2 and its constituents Q and Q2bar are
* collinear (and therefore anti-collinear with C1,Q1,Qbar).
* The solution is then obtained by using Lorentz boosts, as follows.
* The kinematics of C1 and C2 is solved in their parent C frame,
* and then boosted back in the LAB. The kinematics of Q1 and Qbar
* is solved in their parent C1 frame and then boosted back in the LAB;
* similarly, the kinematics of Q and Q2bar is solved in their parent
* C2 frame and then boosted back in the LAB. In each of the three
* "two-body decay"-like cases, we use the fact that the direction
* of the motion of the decay products is known in the rest frame of
* their parent. This is obvious for the first case in which the
* parent rest frame is C; but it is also true in the other two cases
* where the rest frames are C1 and C2. This is because C1 and C2
* are boosted w.r.t. C in the same direction where their components,
* respectively (Q1,Qbar) and (Q,Q2bar) move in C1 and C2 rest frame
* respectively.
* Of course, although the notation used assumed that C = (Q1 Q2bar)
* where Q1 is a quark and Q2bar an antiquark, indeed everything remain
* unchanged also in all following cases:
* Q1 quark, Q2bar antiquark; --> Q quark;
* Q1 antiquark , Q2bar quark; --> Q antiquark;
* Q1 quark, Q2bar diquark; --> Q quark
* Q1 antiquark, Q2bar anti-diquark; --> Q antiquark
* Q1 diquark, Q2bar quark --> Q antiquark
* Q1 anti-diquark, Q2bar antiquark; --> Q quark
**************************/
// Calculate the unit three-vector, in the C frame, along which
// all of the constituents and children clusters move.
Lorentz5Momentum u(p0Q1);
u.boost( -pClu.boostVector() ); // boost from LAB to C
// the unit three-vector is then u.vect().unit()
// Calculate the momenta of C1 and C2 in the (parent) C frame first,
// where the direction of C1 is u.vect().unit(), and then boost back in the
// LAB frame.
if (pClu.m() < pClu1.mass() + pClu2.mass() ) {
throw Exception() << "Impossible Kinematics in ClusterFissioner::calculateKinematics() (A)"
<< Exception::eventerror;
}
Kinematics::twoBodyDecay(pClu, pClu1.mass(), pClu2.mass(),
u.vect().unit(), pClu1, pClu2);
// In the case that cluster1 does not decay immediately into a single hadron,
// calculate the momenta of Q1 (as constituent of C1) and Qbar in the
// (parent) C1 frame first, where the direction of Q1 is u.vect().unit(),
// and then boost back in the LAB frame.
if(!toHadron1) {
if (pClu1.m() < pQ1.mass() + pQbar.mass() ) {
throw Exception() << "Impossible Kinematics in ClusterFissioner::calculateKinematics() (B)"
<< Exception::eventerror;
}
Kinematics::twoBodyDecay(pClu1, pQ1.mass(), pQbar.mass(),
u.vect().unit(), pQ1, pQbar);
}
// In the case that cluster2 does not decay immediately into a single hadron,
// Calculate the momenta of Q and Q2bar (as constituent of C2) in the
// (parent) C2 frame first, where the direction of Q is u.vect().unit(),
// and then boost back in the LAB frame.
if(!toHadron2) {
if (pClu2.m() < pQ.mass() + pQ2bar.mass() ) {
throw Exception() << "Impossible Kinematics in ClusterFissioner::calculateKinematics() (C)"
<< Exception::eventerror;
}
Kinematics::twoBodyDecay(pClu2, pQ.mass(), pQ2bar.mass(),
u.vect().unit(), pQ, pQ2bar);
}
}
void ClusterFissioner::calculatePositions(const Lorentz5Momentum & pClu,
const LorentzPoint & positionClu,
const Lorentz5Momentum & pClu1,
const Lorentz5Momentum & pClu2,
LorentzPoint & positionClu1,
LorentzPoint & positionClu2) const {
// Determine positions of cluster children.
// See Marc Smith's thesis, page 127, formulas (4.122) and (4.123).
Energy Mclu = pClu.m();
Energy Mclu1 = pClu1.m();
Energy Mclu2 = pClu2.m();
// Calculate the unit three-vector, in the C frame, along which
// children clusters move.
Lorentz5Momentum u(pClu1);
u.boost( -pClu.boostVector() ); // boost from LAB to C frame
// the unit three-vector is then u.vect().unit()
Energy pstarChild = Kinematics::pstarTwoBodyDecay(Mclu,Mclu1,Mclu2);
// First, determine the relative positions of the children clusters
// in the parent cluster reference frame.
Energy2 mag2 = u.vect().mag2();
InvEnergy fact = mag2>ZERO ? 1./sqrt(mag2) : 1./GeV;
Length x1 = ( 0.25*Mclu + 0.5*( pstarChild + (sqr(Mclu2) - sqr(Mclu1))/(2.0*Mclu)))/_kappa;
Length t1 = Mclu/_kappa - x1;
LorentzDistance distanceClu1( x1 * fact * u.vect(), t1 );
Length x2 = (-0.25*Mclu + 0.5*(-pstarChild + (sqr(Mclu2) - sqr(Mclu1))/(2.0*Mclu)))/_kappa;
Length t2 = Mclu/_kappa + x2;
LorentzDistance distanceClu2( x2 * fact * u.vect(), t2 );
// Then, transform such relative positions from the parent cluster
// reference frame to the Lab frame.
distanceClu1.boost( pClu.boostVector() );
distanceClu2.boost( pClu.boostVector() );
// Finally, determine the absolute positions in the Lab frame.
positionClu1 = positionClu + distanceClu1;
positionClu2 = positionClu + distanceClu2;
}
-bool ClusterFissioner::ProbablityFunction(double scale, double threshold) {
+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]) ) {
+ if ( spectrum()->hasHeavy(id,cptr[0],cptr[1],cptr[1])) {
clpow = _clPowHeavy[id];
clmax = _clMaxHeavy[id];
}
}
// required test for SUSY clusters, since aboveCutoff alone
// cannot guarantee (Mc > m1 + m2 + 2*m) in cut()
static const Energy minmass
= getParticleData(ParticleID::d)->constituentMass();
bool aboveCutoff = false, canSplitMinimally = false;
// static kinematic threshold
if(_kinematicThresholdChoice == 0) {
aboveCutoff = (
pow(clu->mass()*UnitRemoval::InvE , clpow)
>
pow(clmax*UnitRemoval::InvE, clpow)
+ pow(clu->sumConstituentMasses()*UnitRemoval::InvE, clpow)
);
canSplitMinimally = clu->mass() > clu->sumConstituentMasses() + 2.0 * minmass;
}
// dynamic kinematic threshold
else if(_kinematicThresholdChoice == 1) {
//some smooth probablity function to create a dynamic thershold
double scale = pow(clu->mass()/GeV , clpow);
double threshold = pow(clmax/GeV, clpow)
+ pow(clu->sumConstituentMasses()/GeV, clpow);
- aboveCutoff = ProbablityFunction(scale,threshold);
+ aboveCutoff = ProbabilityFunction(scale,threshold);
scale = clu->mass()/GeV;
threshold = clu->sumConstituentMasses()/GeV + 2.0 * minmass/GeV;
- canSplitMinimally = ProbablityFunction(scale,threshold);
+ 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,558 +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).
*/
- tPVector fission(ClusterVector & clusters, bool softUEisOn);
+ virtual tPVector fission(ClusterVector & clusters, bool softUEisOn);
/**
* Return the hadron spectrum
*/
- Ptr<HadronSpectrum>::tptr spectrum() const {
+ 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 drawNewFlavour(PPtr& newPtrPos,PPtr& newPtrNeg) const;
+ void drawNewFlavourQuarks(PPtr& newPtrPos,PPtr& newPtrNeg) const;
+
+ /**
+ * Returns the new quark-antiquark pair or diquark -
+ * antidiquark pair needed for fission of a heavy cluster.
+ */
+ void drawNewFlavourDiquarks(PPtr& newPtrPos,PPtr& newPtrNeg,
+ const ClusterPtr & clu) const;
/**
* Returns the new quark-antiquark pair
* needed for fission of a heavy cluster. Equal probabilities
* are assumed for producing u, d, or s pairs.
* Extra argument is used when performing strangeness enhancement
*/
void drawNewFlavourEnhanced(PPtr& newPtrPos,PPtr& newPtrNeg, Energy2 mass2) const;
/**
* Produces the mass of a child cluster.
*
* Draw the masses \f$M'\f$ of the the cluster child produced
* by the fission of an heavy cluster (of mass M). m1, m2 are the masses
* of the constituents of the cluster; m is the mass of the quark extract
* from the vacuum (together with its antiparticle). The algorithm produces
* the mass of the cluster formed with consituent m1.
* Two mass distributions can be used for the child cluster mass:
* -# power-like mass distribution ("normal" mass) with power exp
* \f[ M' = {\rm rnd}((M-m_1-m_2-m)^P, m^p)^{1/P} + m_1 \f]
* where \f$ P \f$ is a parameter of the model and \f$ \rm{rnd} \f$ is
* the function:
* \f[ \rm{rnd}(a,b) = (1-r)a + r b \f]
* and here \f$ r \f$ is a random number [0,1].
* -# fast-decreasing exponential mass distribution ("soft" mass) with
* rmin. rmin is given by
* \f[ r_{\rm min} = \exp(-b (M - m_1 - m_2 - 2 m)) \f]
* where \f$ b \f$ is a parameter of the model. The generated mass is
* given by
* \f[ M' = m_1 + m - \frac{\log\left(
* {\rm rnd}(r_{\rm min}, 1-r_{\rm min})\right)}{b} \f].
*
* The choice of which mass distribution should be used for each of the two
* cluster children is dictated by the parameter soft.
*/
Energy drawChildMass(const Energy M, const Energy m1, const Energy m2,
const Energy m, const double exp, const bool soft) const;
/**
* Determine the positions of the two children clusters.
*
* This routine generates the momentum of the decay products. It also
* generates the momentum in the lab frame of the partons drawn out of
* the vacuum.
*/
void calculatePositions(const Lorentz5Momentum &pClu,
const LorentzPoint & positionClu,
const Lorentz5Momentum & pClu1,
const Lorentz5Momentum & pClu2,
LorentzPoint & positionClu1,
LorentzPoint & positionClu2 ) const;
protected:
/**
* Dimension used to calculate phase space weights
*/
double dim() const {return _dim;}
/**
* Access to soft-cluster parameter
*/
Energy btClM() const {return _btClM;}
/**
* Function that returns either the cluster mass or the lambda measure
*/
Energy2 clustermass(const ClusterPtr & cluster) const;
/**
* Draw a new flavour for the given cluster; currently defaults to
* the default model
*/
virtual void drawNewFlavour(PPtr& newPtr1, PPtr& newPtr2, const ClusterPtr & cluster) const {
if (_enhanceSProb == 0){
- drawNewFlavour(newPtr1,newPtr2);
+ if (_diquarkClusterFission>=0) drawNewFlavourDiquarks(newPtr1,newPtr2,cluster);
+ else drawNewFlavourQuarks(newPtr1,newPtr2);
}
else {
drawNewFlavourEnhanced(newPtr1,newPtr2,clustermass(cluster));
}
}
/**
* Calculate the masses and possibly kinematics of the cluster
* fission at hand; if claculateKineamtics is perfomring non-trivial
* steps kinematics claulcated here will be overriden. Currentl;y resorts to the default
+ * @return the potentially non-trivial distribution weight=f(M1,M2)
+ * On Failure we return 0
*/
- virtual pair<Energy,Energy> drawNewMasses(Energy Mc, bool soft1, bool soft2,
+ virtual double drawNewMasses(const Energy Mc, const bool soft1, const bool soft2,
Lorentz5Momentum& pClu1, Lorentz5Momentum& pClu2,
- tPPtr ptrQ1, Lorentz5Momentum& pQ1,
- tPPtr, Lorentz5Momentum& pQone,
- tPPtr, Lorentz5Momentum& pQtwo,
- tPPtr ptrQ2, Lorentz5Momentum& pQ2) const {
-
- pair<Energy,Energy> result;
-
- // power for splitting
- double exp1 = !spectrum()->isExotic(ptrQ1->dataPtr()) ? _pSplitLight : _pSplitExotic;
- double exp2 = !spectrum()->isExotic(ptrQ2->dataPtr()) ? _pSplitLight : _pSplitExotic;
- for ( const long& id : spectrum()->heavyHadronizingQuarks() ) {
- assert(_pSplitHeavy.find(id) != _pSplitHeavy.end());
- if ( spectrum()->hasHeavy(id,ptrQ1->dataPtr()) ) exp1 = _pSplitHeavy.find(id)->second;
- if ( spectrum()->hasHeavy(id,ptrQ2->dataPtr()) ) exp2 = _pSplitHeavy.find(id)->second;
- }
-
- result.first = drawChildMass(Mc,pQ1.mass(),pQ2.mass(),pQone.mass(),exp1,soft1);
- result.second = drawChildMass(Mc,pQ2.mass(),pQ1.mass(),pQtwo.mass(),exp2,soft2);
-
- pClu1.setMass(result.first);
- pClu2.setMass(result.second);
-
- return result;
-
- }
+ 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 &pQb,
- Lorentz5Momentum &pQ2, Lorentz5Momentum &pQ2b) const;
-
+ 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;
+
+
//@}
-private:
+protected:
/**
* Smooth probability for dynamic threshold cuts:
* @scale the current scale, e.g. the mass of the cluster,
* @threshold the physical threshold,
*/
- bool ProbablityFunction(double scale, double threshold);
+ bool 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());
}
-
- /**
- * Claculate a veto for the phase space weight
- */
- inline bool phaseSpaceVeto(const Energy Mc, const Energy Mc1, const Energy Mc2,
- const Energy m, const Energy m1, const Energy m2) {
- 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;
- double lam1 = sqrt((M1_temp-m1_temp-m_temp)*(M1_temp-m1_temp+m_temp)*(M1_temp+m1_temp+m_temp)*(M1_temp+m1_temp-m_temp));
- double lam2 = sqrt((M2_temp-m2_temp-m_temp)*(M2_temp-m2_temp+m_temp)*(M2_temp+m2_temp+m_temp)*(M2_temp+m2_temp-m_temp));
- double lam3 = sqrt((M_temp-M1_temp-M2_temp)*(M_temp-M1_temp+M2_temp)*(M_temp+M1_temp+M2_temp)*(M_temp+M1_temp-M2_temp));
- double ratio;
- double PSweight = pow(lam1*lam2*lam3,_dim-3.)*pow(M1_temp*M2_temp,2.-_dim);
- double overEstimate =pow(M_temp,4.*_dim-14.);
- ratio = PSweight/overEstimate;
- assert (ratio >= 0);
- assert (ratio <= 1);
- return (UseRandom::rnd()>ratio);
- }
-
/**
* 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
*/
- bool _phaseSpaceWeights;
+ 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/DarkHadronSpectrum.h b/Hadronization/DarkHadronSpectrum.h
--- a/Hadronization/DarkHadronSpectrum.h
+++ b/Hadronization/DarkHadronSpectrum.h
@@ -1,365 +1,372 @@
// -*- C++ -*-
#ifndef Herwig_DarkHadronSpectrum_H
#define Herwig_DarkHadronSpectrum_H
//
// This is the declaration of the DarkHadronSpectrum class.
//
#include "Herwig/Hadronization/HadronSpectrum.h"
#include <ThePEG/PDT/ParticleData.h>
#include <ThePEG/PDT/StandardMatchers.h>
#include <ThePEG/Repository/EventGenerator.h>
#include <ThePEG/PDT/EnumParticles.h>
#include "ThePEG/Repository/CurrentGenerator.h"
namespace Herwig {
using namespace ThePEG;
/**
* Here is the documentation of the DarkHadronSpectrum class.
*
* @see \ref DarkHadronSpectrumInterfaces "The interfaces"
* defined for DarkHadronSpectrum.
*/
class DarkHadronSpectrum: public HadronSpectrum {
public:
/** @name Standard constructors and destructors. */
//@{
/**
* The default constructor.
*/
DarkHadronSpectrum(unsigned int opt);
/**
* The destructor.
*/
virtual ~DarkHadronSpectrum();
//@}
public:
/** @name Partonic content */
//@{
/**
* Return the id of the gluon
*/
virtual long gluonId() const { return ParticleID::darkg; }
/**
* Return the ids of all hadronizing quarks
*/
virtual const vector<long>& hadronizingQuarks() const {
static vector<long> hadronizing = lightHadronizingQuarks();
static vector<long> heavy = heavyHadronizingQuarks();
hadronizing.insert(hadronizing.end(), heavy.begin(), heavy.end());
return hadronizing;
}
/**
* The light hadronizing quarks
*/
virtual const vector<long>& lightHadronizingQuarks() const {
if (long(_lightquarks.size()) != _nlightquarks) {
for (long il=0; il<_nlightquarks; il++) {
_lightquarks.push_back(il+_DarkHadOffset+1);
}
}
return _lightquarks;
}
/**
* The heavy hadronizing quarks
*/
virtual const vector<long>& heavyHadronizingQuarks() const {
if (long(_heavyquarks.size()) != _nheavyquarks) {
for (long il=0; il<_nheavyquarks; il++) {
_heavyquarks.push_back(il+_DarkHadOffset+1+_nlightquarks);
}
}
return _heavyquarks;
}
/**
* The lightest quarks, used for finding the lightest Hadron Pair
*/
virtual const vector<long>& lightestQuarks() const {
// May need to be updated in future for strange-like quarks
return lightHadronizingQuarks();
}
/**
* Return true if any of the possible three input particles contains
* the indicated heavy quark. false otherwise. In the case that
* only the first particle is specified, it can be: an (anti-)quark,
* an (anti-)diquark an (anti-)meson, an (anti-)baryon; in the other
* cases, each pointer is assumed to be either (anti-)quark or
* (anti-)diquark.
*/
virtual bool hasHeavy(long, tcPDPtr, tcPDPtr = PDPtr(), tcPDPtr = PDPtr()) const {
//ToDo: this should work for the heavyHadronizingQuarks
return false;
}
//@}
/**
* Return the threshold for a cluster to split into a pair of hadrons.
* This is normally the mass of the lightest hadron Pair, but can be
* higher for heavy and exotic clusters
*/
virtual Energy hadronPairThreshold(tcPDPtr par1, tcPDPtr par2) const;
/**
* Return the weight for the given flavour
*/
virtual double pwtQuark(const long& id) const {
return pwt(id);
}
/**
+ * Dummy function as it will not be needed
+ */
+ virtual const vector<long>& lightHadronizingDiquarks() const {
+ static vector<long> nothing;
+ return nothing;
+ };
+ /**
* The diquark weight.
*/
double pwtDIquark() const {
return _pwtDIquark;
}
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);
//@}
/**
* The standard Init function used to initialize the interfaces.
* Called exactly once for each class by the class description system
* before the main function starts or
* when this class is dynamically loaded.
*/
static void Init();
protected:
/** @name Standard Interfaced functions. */
//@{
/**
* Initialize this object after the setup phase before saving an
* EventGenerator to disk.
*
* The array _repwt is initialized using the interfaces to set different
* weights for different meson multiplets and the constructHadronTable()
* method called to complete the construction of the hadron tables.
*
* @throws InitException if object could not be initialized properly.
*/
virtual void doinit();
//@}
/**
* Return the id of the diquark (anti-diquark) made by the two
* quarks (antiquarks) of id specified in input (id1, id2).
* Caller must ensure that id1 and id2 are quarks.
*/
long makeDiquarkID(long id1, long id2, long pspin) const;
/**
* Weights for mesons
*/
virtual double mesonWeight(long id) const;
/**
* Return true, if any of the possible input particle pointer is an exotic quark, e.g. Susy quark;
* false otherwise.
*/
bool isExotic(tcPDPtr par1, tcPDPtr par2 = PDPtr(), tcPDPtr par3 = PDPtr()) const;
protected:
/**
* Construct the table of hadron data
* This is the main method to initialize the hadron data (mainly the
* weights associated to each hadron, taking into account its spin,
* eventual isoscalar-octect mixing, singlet-decuplet factor). This is
* the method that one should update when new or updated hadron data is
* available.
*
* This class implements the construction of the basic table but can be
* overridden if needed in inheriting classes.
*
* The rationale for factors used for diquarks involving different quarks can
* be can be explained by taking a prototype example that in the exact SU(2) limit,
* in which:
* \f[m_u=m_d\f]
* \f[M_p=M_n=M_\Delta\f]
* and we will have equal numbers of u and d quarks produced.
* Suppose that we weight 1 the diquarks made of the same
* quark and 1/2 those made of different quarks, the fractions
* of u and d baryons (p, n, Delta) we get are the following:
* - \f$\Delta^{++}\f$: 1 possibility only u uu with weight 1
* - \f$\Delta^- \f$: 1 possibility only d dd with weight 1
* - \f$p,\Delta^+ \f$: 2 possibilities u ud with weight 1/2
* d uu with weight 1
* - \f$n,\Delta^0 \f$: 2 possibilities d ud with weight 1/2
* u dd with weight 1
* In the latter two cases, we have to take into account the
* fact that p and n have spin 1/2 whereas Delta+ and Delta0
* have spin 3/2 therefore from phase space we get a double weight
* for Delta+ and Delta0 relative to p and n respectively.
* Therefore the relative amount of these baryons that is
* produced is the following:
* # p = # n = ( 1/2 + 1 ) * 1/3 = 1/2
* # Delta++ = # Delta- = 1 = ( 1/2 + 1) * 2/3 # Delta+ = # Delta0
* which is correct, and therefore the weight 1/2 for the
* diquarks of different types of quarks is justified (at least
* in this limit of exact SU(2) ).
*/
virtual void constructHadronTable();
/**
* Access the parton weights
*/
double pwt(long pid) const {
map<long,double>::const_iterator it = _pwt.find(abs(pid));
assert( it != _pwt.end() );
return it->second;
}
/**
* Insert a meson in the table
*/
virtual void insertMeson(HadronInfo a, int flav1, int flav2);
/**
* Methods for the mixing of \f$I=0\f$ mesons
*/
//@{
/**
* Return the probability of mixing for Octet-Singlet isoscalar mixing,
* the probability of the
* \f$\frac1{\sqrt{2}}(|u\bar{u}\rangle + |d\bar{d}\rangle)\f$ component
* is returned.
* @param angleMix The mixing angle in degrees (not radians)
* @param order is 0 for no mixing, 1 for the first resonance of a pair,
* 2 for the second one.
* The mixing is defined so that for example with \f$\eta-\eta'\f$ mixing where
* the mixing angle is \f$\theta=-23^0$ with $\eta\f$ as the first particle
* and \f$\eta'\f$ the second one.
* The convention used is
* \f[\eta = \cos\theta|\eta_{\rm octet }\rangle
* -\sin\theta|\eta_{\rm singlet}\rangle\f]
* \f[\eta' = \sin\theta|\eta_{\rm octet }\rangle
* -\cos\theta|\eta_{\rm singlet}\rangle\f]
* with
* \f[|\eta_{\rm singlet}\rangle = \frac1{\sqrt{3}}
* \left[|u\bar{u}\rangle + |d\bar{d}\rangle + |s\bar{s}\rangle\right]\f]
* \f[|\eta_{\rm octet }\rangle = \frac1{\sqrt{6}}
* \left[|u\bar{u}\rangle + |d\bar{d}\rangle - 2|s\bar{s}\rangle\right]\f]
*/
double probabilityMixing(const double angleMix,
const int order) const {
static double convert=Constants::pi/180.0;
if (order == 1)
return sqr( cos( angleMix*convert + atan( sqrt(2.0) ) ) );
else if (order == 2)
return sqr( sin( angleMix*convert + atan( sqrt(2.0) ) ) );
else
return 1.;
}
/**
* Returns the weight of given mixing state.
* @param id The PDG code of the meson
*/
virtual double mixingStateWeight(long id) const;
//@}
virtual double specialQuarkWeight(double quarkWeight, long,
const Energy, tcPDPtr, tcPDPtr) const {
return quarkWeight;
}
/**
* The probability of producting a diquark.
*/
double _pwtDIquark;
/**
* Singlet and Decuplet weights
*/
//@{
/**
* The singlet weight
*/
double _sngWt;
/**
* The decuplet weight
*/
double _decWt;
//@}
/**
* Return true if the two or three particles in input can be the components
* of a baryon; false otherwise.
*/
virtual bool canBeBaryon(tcPDPtr par1, tcPDPtr par2 , tcPDPtr par3 = PDPtr()) const;
private:
/**
* Option for the construction of the tables
*/
unsigned int _topt;
/**
* Which particles to produce for debugging purposes
*/
unsigned int _trial;
/**
* Prefix for Dark Hadron pdgID
*/
int _DarkHadOffset = 4900000;
/**
* The number of light quarks
*/
int _nlightquarks;
/**
* The number of heavy quarks
*/
int _nheavyquarks;
/**
* The pdgIds of the light quarks
*/
mutable vector<long> _lightquarks = {};
/**
* The pdgIds of the heavy quarks
*/
mutable vector<long> _heavyquarks = {};
};
}
#endif /* Herwig_DarkHadronSpectrum_H */
diff --git a/Hadronization/HadronSpectrum.cc b/Hadronization/HadronSpectrum.cc
--- a/Hadronization/HadronSpectrum.cc
+++ b/Hadronization/HadronSpectrum.cc
@@ -1,610 +1,613 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the HadronSpectrum class.
//
#include "HadronSpectrum.h"
#include "ClusterHadronizationHandler.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/EventRecord/Particle.h"
#include "ThePEG/Repository/UseRandom.h"
#include "ThePEG/Repository/EventGenerator.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include <ThePEG/Repository/CurrentGenerator.h>
#include "Herwig/Utilities/Kinematics.h"
#include "ThePEG/Interface/RefVector.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
using namespace Herwig;
namespace {
// debug helper
void dumpTable(const HadronSpectrum::HadronTable & tbl) {
typedef HadronSpectrum::HadronTable::const_iterator TableIter;
for (TableIter it = tbl.begin(); it != tbl.end(); ++it) {
cerr << it->first.first << ' '
<< it->first.second << '\n';
for (HadronSpectrum::KupcoData::const_iterator jt = it->second.begin();
jt != it->second.end(); ++jt) {
cerr << '\t' << *jt << '\n';
}
}
}
}
HadronSpectrum::HadronSpectrum()
: Interfaced(),
belowThreshold_(0),
_repwt(Lmax,vector<vector<double> >(Jmax,vector<double>(Nmax))) {}
HadronSpectrum::~HadronSpectrum() {}
void HadronSpectrum::doinit() {
Interfaced::doinit();
// construct the hadron tables
constructHadronTable();
// lightest members (hadrons)
for(const PDPtr & p1 : partons()) {
for(const PDPtr & p2 : partons()) {
tcPDPair lp = lightestHadronPair(p1,p2);
if(lp.first && lp.second)
lightestHadrons_[make_pair(p1->id(),p2->id())] = lp;
}
}
// for debugging
if (Debug::level >= 10)
dumpTable(table());
}
// If needed, insert default implementations of virtual function defined
// in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs).
void HadronSpectrum::persistentOutput(PersistentOStream & os) const {
os << _table << _partons << _forbidden
<< belowThreshold_ << _repwt << _pwt << lightestHadrons_;
}
void HadronSpectrum::persistentInput(PersistentIStream & is, int) {
is >> _table >> _partons >> _forbidden
>> belowThreshold_ >> _repwt >> _pwt >> lightestHadrons_;
}
// *** Attention *** The following static variable is needed for the type
// description system in ThePEG. Please check that the template arguments
// are correct (the class and its base class), and that the constructor
// arguments are correct (the class name and the name of the dynamically
// loadable library where the class implementation can be found).
DescribeAbstractClass<HadronSpectrum,Interfaced>
describeHerwigHadronSpectrum("Herwig::HadronSpectrum", "Herwig.so");
void HadronSpectrum::Init() {
static ClassDocumentation<HadronSpectrum> documentation
("There is no documentation for the HadronSpectrum class");
static RefVector<HadronSpectrum,ParticleData> interfacePartons
("Partons",
"The partons which are to be considered as the consistuents of the hadrons.",
&HadronSpectrum::_partons, -1, false, false, true, false, false);
static RefVector<HadronSpectrum,ParticleData> interfaceForbidden
("Forbidden",
"The PDG codes of the particles which cannot be produced in the hadronization.",
&HadronSpectrum::_forbidden, -1, false, false, true, false, false);
}
void HadronSpectrum::insertToHadronTable(tPDPtr &particle, int flav1, int flav2) {
// inserting a new Hadron in the hadron table.
long pid = particle->id();
int pspin = particle->iSpin();
HadronInfo a(pid, particle,specialWeight(pid),particle->mass());
// set the weight to the number of spin states
a.overallWeight = pspin*a.swtef;
// mesons
if(pspin%2==1) insertMeson(a,flav1,flav2);
// spin-1/2 baryons
else if(pspin==2) insertOneHalf(a,flav1,flav2);
// spin -3/2 baryons
else if(pspin==4) insertThreeHalf(a,flav1,flav2);
// all other cases
else {
assert(false);
}
}
void HadronSpectrum::insertOneHalf(HadronInfo a, int flav1, int flav2) {
assert(DiquarkMatcher::Check(flav1));
long iq1 = flav1/1000;
long iq2 = (flav1/100)%10;
if(iq1!=iq2 && flav1%10==3) flav1-=2;
if(iq1==iq2) {
if(iq1==flav2) {
a.overallWeight *= 1.5;
_table[make_pair(flav1,flav2)].insert(a);
_table[make_pair(flav2,flav1)].insert(a);
}
else {
_table[make_pair(flav1,flav2)].insert(a);
_table[make_pair(flav2,flav1)].insert(a);
long f3 = makeDiquarkID(iq1,flav2,1);
_table[make_pair(iq1,f3 )].insert(a);
_table[make_pair(f3 ,iq1)].insert(a);
}
}
else if(iq1==flav2) {
// ud1 u type
_table[make_pair(flav1,flav2)].insert(a);
_table[make_pair(flav2,flav1)].insert(a);
// and uu1 d type
long f3 = makeDiquarkID(iq1,iq1,3);
a.overallWeight *= a.wt;
_table[make_pair(f3 ,iq2)].insert(a);
_table[make_pair(iq2, f3)].insert(a);
}
else if(iq2==flav2) assert(false);
else {
_table[make_pair(flav1,flav2)].insert(a);
_table[make_pair(flav2,flav1)].insert(a);
long f3 = makeDiquarkID(iq1,flav2,1);
_table[make_pair(iq2,f3)].insert(a);
_table[make_pair(f3,iq2)].insert(a);
// 3rd perm
f3 = makeDiquarkID(iq2,flav2,1);
_table[make_pair(iq1,f3)].insert(a);
_table[make_pair(f3,iq1)].insert(a);
}
}
void HadronSpectrum::insertThreeHalf(HadronInfo a, int flav1, int flav2) {
assert(DiquarkMatcher::Check(flav1));
long iq1 = flav1/1000;
long iq2 = (flav1/100)%10;
if(iq1!=iq2 && flav1%10==3) flav1-=2;
if(iq1==iq2) {
if(iq1==flav2) {
a.overallWeight *= 1.5;
_table[make_pair(flav1,flav2)].insert(a);
_table[make_pair(flav2,flav1)].insert(a);
}
else {
_table[make_pair(flav1,flav2)].insert(a);
_table[make_pair(flav2,flav1)].insert(a);
long f3 = makeDiquarkID(iq1,flav2,1);
_table[make_pair(iq1,f3 )].insert(a);
_table[make_pair(f3 ,iq1)].insert(a);
}
}
else if(iq1==flav2) {
// ud1 u type
_table[make_pair(flav1,flav2)].insert(a);
_table[make_pair(flav2,flav1)].insert(a);
// and uu1 d type
long f3 = makeDiquarkID(iq1,iq1,3);
a.overallWeight *= a.wt;
_table[make_pair(f3 ,iq2)].insert(a);
_table[make_pair(iq2, f3)].insert(a);
}
else {
_table[make_pair(flav1,flav2)].insert(a);
_table[make_pair(flav2,flav1)].insert(a);
long f3 = makeDiquarkID(iq1,flav2,1);
_table[make_pair(iq2,f3)].insert(a);
_table[make_pair(f3,iq2)].insert(a);
// 3rd perm
f3 = makeDiquarkID(iq2,flav2,1);
_table[make_pair(iq1,f3)].insert(a);
_table[make_pair(f3,iq1)].insert(a);
}
}
tcPDPtr HadronSpectrum::chooseSingleHadron(tcPDPtr par1, tcPDPtr par2,
Energy mass) const {
Energy threshold = hadronPairThreshold(par1,par2);
// only do one hadron decay if mass less than the threshold
if(mass>=threshold) return tcPDPtr();
// select the hadron
tcPDPtr hadron;
// old option pick the lightest hadron
if(belowThreshold_ == 0) {
hadron= lightestHadron(par1,par2);
}
// new option select from those available
else if(belowThreshold_ == 1) {
vector<pair<tcPDPtr,double> > hadrons =
hadronsBelowThreshold(threshold,par1,par2);
if(hadrons.size()==1) {
hadron = hadrons[0].first;
}
else if(hadrons.empty()) {
hadron= lightestHadron(par1,par2);
}
else {
double totalWeight=0.;
for(unsigned int ix=0;ix<hadrons.size();++ix) {
totalWeight += hadrons[ix].second;
}
totalWeight *= UseRandom::rnd();
for(unsigned int ix=0;ix<hadrons.size();++ix) {
if(totalWeight<=hadrons[ix].second) {
hadron = hadrons[ix].first;
break;
}
else
totalWeight -= hadrons[ix].second;
}
assert(hadron);
}
}
else
assert(false);
return hadron;
}
tcPDPair HadronSpectrum::chooseHadronPair(const Energy cluMass,
tcPDPtr par1, tcPDPtr par2) const {
useMe();
// if either of the input partons is a diquark don't allow diquarks to be
// produced
- bool diquark0 = !(DiquarkMatcher::Check(par1->id()) || DiquarkMatcher::Check(par2->id()));
- bool diquark1 = diquark0;
+ bool isDiquark1 = DiquarkMatcher::Check(par1->id());
+ bool isDiquark2 = DiquarkMatcher::Check(par2->id());
+ bool noDiquarkInCluster = !(isDiquark1 || isDiquark2);
+ bool oneDiquarkInCluster = (isDiquark1 != isDiquark2);
bool quark = true;
// decide is baryon or meson production
- if(diquark0) std::tie(quark,diquark0,diquark1) = selectBaryon(cluMass,par1,par2);
+ if(noDiquarkInCluster) std::tie(quark,noDiquarkInCluster,oneDiquarkInCluster)
+ = selectBaryon(cluMass,par1,par2);
// weights for the different possibilities
Energy weight, wgtsum(ZERO);
// loop over all hadron pairs with the allowed flavours
static vector<Kupco> hadrons;
hadrons.clear();
for(unsigned int ix=0;ix<partons().size();++ix) {
tcPDPtr quarktopick = partons()[ix];
if(!quark && std::find(hadronizingQuarks().begin(), hadronizingQuarks().end(),
abs(quarktopick->id())) != hadronizingQuarks().end()) continue;
if(DiquarkMatcher::Check(quarktopick->id()) &&
- ((!diquark0 && quarktopick->iSpin()==1) ||
- (!diquark1 && quarktopick->iSpin()==3))) continue;
+ ((!noDiquarkInCluster && quarktopick->iSpin()==1) ||
+ (!oneDiquarkInCluster && quarktopick->iSpin()==3))) continue;
HadronTable::const_iterator
tit1 = table().find(make_pair(abs(par1->id()),quarktopick->id()));
HadronTable::const_iterator
tit2 = table().find(make_pair(quarktopick->id(),abs(par2->id())));
// If not in table skip
if(tit1 == table().end()||tit2==table().end()) continue;
// tables empty skip
const KupcoData & T1 = tit1->second;
const KupcoData & T2 = tit2->second;
if(T1.empty()||T2.empty()) continue;
// if too massive skip
if(cluMass <= T1.begin()->mass +
T2.begin()->mass) continue;
// quark weight
double quarkWeight = pwt(quarktopick->id());
quarkWeight = specialQuarkWeight(quarkWeight,quarktopick->id(),
cluMass,par1,par2);
// loop over the hadrons
KupcoData::const_iterator H1,H2;
for(H1 = T1.begin();H1 != T1.end(); ++H1) {
for(H2 = T2.begin();H2 != T2.end(); ++H2) {
// break if cluster too light
if(cluMass < H1->mass + H2->mass) break;
weight = quarkWeight * H1->overallWeight * H2->overallWeight *
Kinematics::pstarTwoBodyDecay(cluMass, H1->mass, H2->mass);
int signQ = 0;
assert (par1 && quarktopick);
assert (par2);
assert(quarktopick->CC());
if(canBeHadron(par1, quarktopick->CC())
&& canBeHadron(quarktopick, par2))
signQ = +1;
else if(canBeHadron(par1, quarktopick)
&& canBeHadron(quarktopick->CC(), par2))
signQ = -1;
else {
cerr << "Could not make sign for" << par1->id()<< " " << quarktopick->id()
<< " " << par2->id() << "\n";
assert(false);
}
if (signQ == -1)
quarktopick = quarktopick->CC();
// construct the object with the info
Kupco a(quarktopick, H1->ptrData, H2->ptrData, weight);
hadrons.push_back(a);
wgtsum += weight;
}
}
}
if (hadrons.empty())
return make_pair(tcPDPtr(),tcPDPtr());
// select the hadron
wgtsum *= UseRandom::rnd();
unsigned int ix=0;
do {
wgtsum-= hadrons[ix].weight;
++ix;
}
while(wgtsum > ZERO && ix < hadrons.size());
if(ix == hadrons.size() && wgtsum > ZERO)
return make_pair(tcPDPtr(),tcPDPtr());
--ix;
assert(hadrons[ix].idQ);
int signHad1 = signHadron(par1, hadrons[ix].idQ->CC(), hadrons[ix].hadron1);
int signHad2 = signHadron(par2, hadrons[ix].idQ, hadrons[ix].hadron2);
assert( signHad1 != 0 && signHad2 != 0 );
return make_pair
( signHad1 > 0 ? hadrons[ix].hadron1 : tcPDPtr(hadrons[ix].hadron1->CC()),
signHad2 > 0 ? hadrons[ix].hadron2 : tcPDPtr(hadrons[ix].hadron2->CC()));
}
std::tuple<bool,bool,bool> HadronSpectrum::selectBaryon(const Energy, tcPDPtr, tcPDPtr ) const {
assert(false);
}
tcPDPair HadronSpectrum::lightestHadronPair(tcPDPtr ptr1, tcPDPtr ptr2) const {
Energy currentSum = Constants::MaxEnergy;
tcPDPair output;
for(unsigned int ix=0; ix<partons().size(); ++ix) {
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;
}
tcPDPtr HadronSpectrum::lightestHadron(tcPDPtr ptr1, tcPDPtr ptr2) const {
assert(ptr1 && ptr2);
// find entry in the table
pair<long,long> ids = make_pair(abs(ptr1->id()),abs(ptr2->id()));
HadronTable::const_iterator tit=_table.find(ids);
// throw exception if flavours wrong
if (tit==_table.end())
throw Exception() << "Could not find "
<< ids.first << ' ' << ids.second
<< " in _table. "
<< "In HadronSpectrum::lightestHadron()"
<< Exception::eventerror;
if(tit->second.empty())
throw Exception() << "HadronSpectrum::lightestHadron "
<< "could not find any hadrons containing "
<< ptr1->id() << ' ' << ptr2->id() << '\n'
<< tit->first.first << ' '
<< tit->first.second << Exception::eventerror;
// find the lightest hadron
int sign = signHadron(ptr1,ptr2,tit->second.begin()->ptrData);
tcPDPtr candidate = sign > 0 ?
tit->second.begin()->ptrData : tit->second.begin()->ptrData->CC();
// \todo 20 GeV limit is temporary fudge to let SM particles go through.
// \todo Use isExotic instead?
if (candidate->mass() > 20*GeV
&& candidate->mass() < ptr1->constituentMass() + ptr2->constituentMass()) {
generator()->log() << "HadronSpectrum::lightestHadron: "
<< "chosen candidate " << candidate->PDGName()
<< " is lighter than its constituents "
<< ptr1->PDGName() << ", " << ptr2->PDGName() << '\n'
<< candidate->mass()/GeV << " < " << ptr1->constituentMass()/GeV
<< " + " << ptr2->constituentMass()/GeV << '\n'
<< "Check your particle data tables.\n";
assert(false);
}
return candidate;
}
vector<pair<tcPDPtr,double> >
HadronSpectrum::hadronsBelowThreshold(Energy threshold, tcPDPtr ptr1,
tcPDPtr ptr2) const {
assert(ptr1 && ptr2);
// find entry in the table
pair<long,long> ids = make_pair(abs(ptr1->id()),abs(ptr2->id()));
HadronTable::const_iterator tit=_table.find(ids);
// throw exception if flavours wrong
if (tit==_table.end())
throw Exception() << "Could not find "
<< ids.first << ' ' << ids.second
<< " in _table. "
<< "In HadronSpectrum::hadronsBelowThreshold()"
<< Exception::eventerror;
if(tit->second.empty())
throw Exception() << "HadronSpectrum::hadronsBelowThreshold() "
<< "could not find any hadrons containing "
<< ptr1->id() << ' ' << ptr2->id() << '\n'
<< tit->first.first << ' '
<< tit->first.second << Exception::eventerror;
vector<pair<tcPDPtr,double> > candidates;
KupcoData::const_iterator hit = tit->second.begin();
// find the hadrons
while(hit!=tit->second.end()&&hit->mass<threshold) {
// find the hadron
int sign = signHadron(ptr1,ptr2,hit->ptrData);
tcPDPtr candidate = sign > 0 ? hit->ptrData : hit->ptrData->CC();
// \todo 20 GeV limit is temporary fudge to let SM particles go through.
// \todo Use isExotic instead?
if (candidate->mass() > 20*GeV
&& candidate->mass() < ptr1->constituentMass() + ptr2->constituentMass()) {
generator()->log() << "HadronSpectrum::hadronsBelowTheshold: "
<< "chosen candidate " << candidate->PDGName()
<< " is lighter than its constituents "
<< ptr1->PDGName() << ", " << ptr2->PDGName() << '\n'
<< candidate->mass()/GeV << " < " << ptr1->constituentMass()/GeV
<< " + " << ptr2->constituentMass()/GeV << '\n'
<< "Check your particle data tables.\n";
assert(false);
}
candidates.push_back(make_pair(candidate,hit->overallWeight));
++hit;
}
return candidates;
}
Energy HadronSpectrum::massLightestBaryonPair(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;
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;
}
return currentSum;
}
double HadronSpectrum::mesonWeight(long id) const {
// Total angular momentum
int j = ((id % 10) - 1) / 2;
// related to Orbital angular momentum l
int nl = (id/10000 )%10;
int l = -999;
int n = (id/100000)%10; // Radial excitation
if(j == 0) l = nl;
else if(nl == 0) l = j - 1;
else if(nl == 1 || nl == 2) l = j;
else if(nl == 3) l = j + 1;
// Angular or Radial excited meson
if((l||j||n) && l>=0 && l<Lmax && j<Jmax && n<Nmax) {
return sqr(_repwt[l][j][n]);
}
// rest is not excited or
// has spin >= 5/2 (ispin >= 6), haven't got those
else
return 1.0;
}
int HadronSpectrum::signHadron(tcPDPtr idQ1, tcPDPtr idQ2,
tcPDPtr hadron) const {
// This method receives in input three PDG ids, whose the
// first two have proper signs (corresponding to particles, id > 0,
// or antiparticles, id < 0 ), whereas the third one must
// be always positive (particle not antiparticle),
// corresponding to:
// --- quark-antiquark, or antiquark-quark, or
// quark-diquark, or diquark-quark, or
// antiquark-antidiquark, or antidiquark-antiquark
// for the first two input (idQ1, idQ2);
// --- meson or baryon for the third input (idHad):
// The method returns:
// --- + 1 if the two partons (idQ1, idQ2) are exactly
// the constituents for the hadron idHad;
// --- - 1 if the two partons (idQ1, idQ2) are exactly
// the constituents for the anti-hadron -idHad;
// --- + 0 otherwise.
// The method it is therefore useful to decide the
// sign of the id of the produced hadron as appeared
// in the vector _vecHad (where only hadron idHad > 0 are present)
// given the two constituent partons.
int sign = 0;
long idHad = hadron->id();
assert(idHad > 0);
int chargeIn = idQ1->iCharge() + idQ2->iCharge();
int chargeOut = hadron->iCharge();
// same charge
if( chargeIn == chargeOut && chargeIn !=0 ) sign = +1;
else if(chargeIn == -chargeOut && chargeIn !=0 ) sign = -1;
else if(chargeIn == 0 && chargeOut == 0 ) {
// In the case of same null charge, there are four cases:
// i) K0-like mesons, B0-like mesons, Bs-like mesons
// the PDG convention is to consider them "antiparticle" (idHad < 0)
// if the "dominant" (heavier) flavour (respectively, s, b)
// is a quark (idQ > 0): for instance, B0s = (b, sbar) has id < 0
// Remember that there is an important exception for K0L (id=130) and
// K0S (id=310): they don't have antiparticles, therefore idHad > 0
// always. We use below the fact that K0L and K0S are the unique
// hadrons having 0 the first (less significant) digit of their id.
// 2) D0-like mesons: the PDG convention is to consider them "particle"
// (idHad > 0) if the charm flavour is carried by a c: (c,ubar) has id>0
// 3) the remaining mesons should not have antiparticle, therefore their
// sign is always positive.
// 4) for baryons, that is when one of idQ1 and idQ2 is a (anti-) quark and
// the other one is a (anti-) diquark the sign is negative when both
// constituents are "anti", that is both with id < 0; positive otherwise.
// meson
if(std::find(hadronizingQuarks().begin(), hadronizingQuarks().end(),
abs(idQ1->id())) != hadronizingQuarks().end() &&
std::find(hadronizingQuarks().begin(), hadronizingQuarks().end(),
abs(idQ2->id())) != hadronizingQuarks().end())
{
int idQa = abs(idQ1->id());
int idQb = abs(idQ2->id());
int dominant = idQ2->id();
if(idQa > idQb) {
swap(idQa,idQb);
dominant = idQ1->id();
}
if((idQa==ParticleID::d && idQb==ParticleID::s) ||
(idQa==ParticleID::d && idQb==ParticleID::b) ||
(idQa==ParticleID::s && idQb==ParticleID::b)) {
// idHad%10 is zero for K0L,K0S
if (dominant < 0 || idHad%10 == 0) sign = +1;
else if(dominant > 0) sign = -1;
}
else if((idQa==ParticleID::u && idQb==ParticleID::c) ||
(idQa==ParticleID::u && idQb==ParticleID::t) ||
(idQa==ParticleID::c && idQb==ParticleID::t)) {
if (dominant > 0) sign = +1;
else if(dominant < 0) sign = -1;
}
else if(idQa==idQb) sign = +1;
// sets sign for Susy particles
else sign = (dominant > 0) ? +1 : -1;
}
// baryon
else if(DiquarkMatcher::Check(idQ1->id()) || DiquarkMatcher::Check(idQ2->id())) {
if (idQ1->id() > 0 && idQ2->id() > 0) sign = +1;
else if(idQ1->id() < 0 && idQ2->id() < 0) sign = -1;
}
}
if (sign == 0) {
cerr << "Could not work out sign for "
<< idQ1->PDGName() << ' '
<< idQ2->PDGName() << " => "
<< hadron->PDGName() << '\n';
assert(false);
}
return sign;
}
PDPtr HadronSpectrum::makeDiquark(tcPDPtr par1, tcPDPtr par2) const {
long id1 = par1->id();
long id2 = par2->id();
long pspin = id1==id2 ? 3 : 1;
long idnew = makeDiquarkID(id1,id2, pspin);
assert(!CurrentGenerator::isVoid());
return CurrentGenerator::current().getParticleData(idnew);
}
bool HadronSpectrum::canBeMeson(tcPDPtr par1,tcPDPtr par2) const {
assert(par1 && par2);
long id1 = par1->id();
long id2 = par2->id();
// a Meson must not have any diquarks
if(DiquarkMatcher::Check(id1) || DiquarkMatcher::Check(id2)) return false;
return (std::find(hadronizingQuarks().begin(), hadronizingQuarks().end(),
abs(id1)) != hadronizingQuarks().end() &&
std::find(hadronizingQuarks().begin(), hadronizingQuarks().end(),
abs(id2)) != hadronizingQuarks().end() &&
id1*id2 < 0);
}
diff --git a/Hadronization/HadronSpectrum.h b/Hadronization/HadronSpectrum.h
--- a/Hadronization/HadronSpectrum.h
+++ b/Hadronization/HadronSpectrum.h
@@ -1,650 +1,655 @@
// -*- C++ -*-
#ifndef Herwig_HadronSpectrum_H
#define Herwig_HadronSpectrum_H
//
// This is the declaration of the HadronSpectrum class.
//
#include "ThePEG/Interface/Interfaced.h"
#include "HadronSpectrum.fh"
#include <ThePEG/Persistency/PersistentOStream.h>
#include <ThePEG/Persistency/PersistentIStream.h>
#include <ThePEG/PDT/ParticleData.h>
#include "Kupco.h"
/* These last two imports don't seem to be used here, but are needed for other
classes which import this. Should tidy up at some point*/
#include <ThePEG/PDT/StandardMatchers.h>
#include <ThePEG/Repository/EventGenerator.h>
namespace Herwig {
using namespace ThePEG;
/**
* Here is the documentation of the HadronSpectrum class.
*
* @see \ref HadronSpectrumInterfaces "The interfaces"
* defined for HadronSpectrum.
*/
class HadronSpectrum: public Interfaced {
public:
/** \ingroup Hadronization
* \class HadronInfo
* \brief Class used to store all the hadron information for easy access.
* \author Philip Stephens
*
* Note that:
* - the hadrons in _table can be filled in any ordered
* w.r.t. the mass value, and flavours for different
* groups (for instance, (u,s) hadrons don't need to
* be placed after (d,s) or any other flavour), but
* all hadrons with the same flavours must be consecutive
* ( for instance you cannot alternate hadrons of type
* (d,s) with those of flavour (u,s) ).
* Furthermore, it is assumed that particle and antiparticle
* have the same weights, and therefore only one of them
* must be entered in the table: we have chosen to refer
* to the particle, defined as PDG id > 0, although if
* an anti-particle is provided in input it is automatically
* transform to its particle, simply by taking the modulus
* of its id.
*/
class HadronInfo {
public:
/**
* Constructor
* @param idin The PDG code of the hadron
* @param datain The pointer to the ParticleData object
* @param swtin The singlet/decuplet/orbital factor
* @param massin The mass of the hadron
*/
HadronInfo(long idin=0, tPDPtr datain=tPDPtr(),
double swtin=1., Energy massin=ZERO)
: id(idin), ptrData(datain), swtef(swtin), wt(1.0), overallWeight(0.0),
mass(massin)
{}
/**
* Comparision operator on mass
*/
bool operator<(const HadronInfo &x) const {
if(mass!=x.mass) return mass < x.mass;
else return id < x.id;
}
/**
* The hadrons id.
*/
long id;
/**
* pointer to ParticleData, to get the spin, etc...
*/
tPDPtr ptrData;
/**
* singlet/decuplet/orbital factor
*/
double swtef;
/**
* mixing factor
*/
double wt;
/**
* (2*J+1)*wt*swtef
*/
double overallWeight;
/**
* The hadrons mass
*/
Energy mass;
/**
* Rescale the weight for a given hadron
*/
void rescale(double x) const {
const_cast<HadronInfo*>(this)->overallWeight *= x;
}
/**
* Friend method used to print the value of a table element.
*/
friend PersistentOStream & operator<< (PersistentOStream & os,
const HadronInfo & hi ) {
os << hi.id << hi.ptrData << hi.swtef << hi.wt
<< hi.overallWeight << ounit(hi.mass,GeV);
return os;
}
/**
* debug output
*/
friend ostream & operator<< (ostream & os, const HadronInfo & hi ) {
os << std::scientific << std::showpoint
<< std::setprecision(4)
<< setw(2)
<< hi.id << '\t'
<< hi.swtef << '\t'
<< hi.wt << '\t'
<< hi.overallWeight << '\t'
<< ounit(hi.mass,GeV);
return os;
}
/**
* Friend method used to read in the value of a table element.
*/
friend PersistentIStream & operator>> (PersistentIStream & is,
HadronInfo & hi ) {
is >> hi.id >> hi.ptrData >> hi.swtef >> hi.wt
>> hi.overallWeight >> iunit(hi.mass,GeV);
return is;
}
};
public:
/**
* The helper classes
*/
//@{
/**
* The type is used to contain all the hadrons info of a given flavour.
*/
typedef set<HadronInfo> KupcoData;
//@}
/**
* The hadron table type.
*/
typedef map<pair<long,long>,KupcoData> HadronTable;
public:
/** @name Standard constructors and destructors. */
//@{
/**
* The default constructor.
*/
HadronSpectrum();
/**
* The destructor.
*/
virtual ~HadronSpectrum();
//@}
public:
/** @name Partonic content */
//@{
/**
* Return the id of the gluon
*/
virtual long gluonId() const = 0;
/**
* Return the ids of all hadronizing quarks
*/
virtual const vector<long>& hadronizingQuarks() const = 0;
/**
* The light hadronizing quarks
*/
virtual const vector<long>& lightHadronizingQuarks() const = 0;
/**
+ * The light hadronizing diquarks
+ */
+ virtual const vector<long>& lightHadronizingDiquarks() const = 0;
+
+ /**
* The heavy hadronizing quarks
*/
virtual const vector<long>& heavyHadronizingQuarks() const = 0;
/**
* The lightest quarks, used for finding the lightest Hadron Pair
*/
virtual const vector<long>& lightestQuarks() const = 0;
/**
* Return true if any of the possible three input particles contains
* the indicated heavy quark. false otherwise. In the case that
* only the first particle is specified, it can be: an (anti-)quark,
* an (anti-)diquark an (anti-)meson, an (anti-)baryon; in the other
* cases, each pointer is assumed to be either (anti-)quark or
* (anti-)diquark.
*/
virtual bool hasHeavy(long id, tcPDPtr par1, tcPDPtr par2 = PDPtr(), tcPDPtr par3 = PDPtr()) const = 0;
/**
* Return true, if any of the possible input particle pointer is an
* exotic quark, e.g. Susy quark; false otherwise.
*/
virtual bool isExotic(tcPDPtr par1, tcPDPtr par2 = PDPtr(), tcPDPtr par3 = PDPtr()) const = 0;
//@}
/**
* Access the parton weights
*/
double pwt(long pid) const {
map<long,double>::const_iterator it = _pwt.find(abs(pid));
if( it == _pwt.end() )
throw Exception("Houston, we have a problem",
Exception::eventerror);
return it->second;
}
/**
* Return true if the two or three particles in input can be the components
* of a hadron; false otherwise.
*/
inline bool canBeHadron(tcPDPtr par1, tcPDPtr par2 , tcPDPtr par3 = PDPtr()) const {
return (!par3 && canBeMeson(par1,par2)) || canBeBaryon(par1,par2,par3);
}
/**
* Check if can't make a diquark from the partons
*/
bool canMakeDiQuark(tcPPtr p1, tcPPtr p2) const {
long id1 = p1->id(), id2 = p2->id();
return QuarkMatcher::Check(id1) && QuarkMatcher::Check(id2) && id1*id2>0;
}
/**
* Return the particle data of the diquark (anti-diquark) made by the two
* quarks (antiquarks) par1, par2.
* @param par1 (anti-)quark data pointer
* @param par2 (anti-)quark data pointer
*/
PDPtr makeDiquark(tcPDPtr par1, tcPDPtr par2) const;
/**
* Method to return a pair of hadrons given the PDG codes of
* two or three constituents
* @param cluMass The mass of the cluster
* @param par1 The first constituent
* @param par2 The second constituent
* @param par3 The third constituent
*/
virtual pair<tcPDPtr,tcPDPtr> chooseHadronPair(const Energy cluMass, tcPDPtr par1,
tcPDPtr par2) const;
/**
* Select the single hadron for a cluster decay
* return null pointer if not a single hadron decay
* @param par1 1st constituent
* @param par2 2nd constituent
* @param mass Mass of the cluster
*/
- tcPDPtr chooseSingleHadron(tcPDPtr par1, tcPDPtr par2, Energy mass) const;
+ virtual tcPDPtr chooseSingleHadron(tcPDPtr par1, tcPDPtr par2, Energy mass) const;
/**
* This returns the lightest pair of hadrons given by the flavours.
*
* Given the two (or three) constituents of a cluster, it returns
* the two lightest hadrons with proper flavour numbers.
* Furthermore, the first of the two hadrons must have the constituent with
* par1, and the second must have the constituent with par2.
* \todo At the moment it does *nothing* in the case that also par3 is present.
*
* The method is implemented by calling twice lightestHadron,
* once with (par1,quarktopick->CC()) ,and once with (par2,quarktopick)
* where quarktopick is either the pointer to
* d or u quarks . In fact, the idea is that whatever the flavour of par1
* and par2, no matter if (anti-)quark or (anti-)diquark, the lightest
* pair of hadrons containing flavour par1 and par2 will have either
* flavour d or u, being the lightest quarks.
* The method returns the pair (PDPtr(),PDPtr()) if anything goes wrong.
*
* \todo The method assumes par3 == PDPtr() (otherwise we don't know how to proceed: a
* possible, trivial way would be to randomly select two of the three
* (anti-)quarks and treat them as a (anti-)diquark, reducing the problem
* to two components as treated below.
* In the normal (two components) situation, the strategy is the following:
* treat in the same way the two possibilities: (d dbar) (i=0) and
* (u ubar) (i=1) as the pair quark-antiquark necessary to form a
* pair of hadrons containing the input flavour par1 and par2; finally,
* select the one that produces the lightest pair of hadrons, compatible
* with the charge conservation constraint.
*/
tcPDPair lightestHadronPair(tcPDPtr ptr1, tcPDPtr ptr2) const;
/**
* Returns the mass of the lightest pair of hadrons with the given particles
* @param ptr1 is the first constituent
* @param ptr2 is the second constituent
*/
Energy massLightestHadronPair(tcPDPtr ptr1, tcPDPtr ptr2) const {
map<pair<long,long>,tcPDPair>::const_iterator lightest =
lightestHadrons_.find(make_pair(abs(ptr1->id()),abs(ptr2->id())));
if(lightest!=lightestHadrons_.end())
return lightest->second.first->mass()+lightest->second.second->mass();
else
return ZERO;
}
/**
* Returns the lightest hadron formed by the given particles.
*
* Given the id of two (or three) constituents of a cluster, it returns
* the lightest hadron with proper flavour numbers.
* @param ptr1 is the first constituent
* @param ptr2 is the second constituent
*/
tcPDPtr lightestHadron(tcPDPtr ptr1, tcPDPtr ptr2) const;
/**
* Return the threshold for a cluster to split into a pair of hadrons.
* This is normally the mass of the lightest hadron Pair, but can be
* higher for heavy and exotic clusters
*/
virtual Energy hadronPairThreshold(tcPDPtr par1, tcPDPtr par2) const=0;
/**
* Returns the hadrons below the constituent mass threshold formed by the given particles,
* together with their total weight
*
* Given the id of two (or three) constituents of a cluster, it returns
* the lightest hadron with proper flavour numbers.
* At the moment it does *nothing* in the case that also 'ptr3' present.
* @param threshold The theshold
* @param ptr1 is the first constituent
* @param ptr2 is the second constituent
* @param ptr3 is the third constituent
*/
vector<pair<tcPDPtr,double> > hadronsBelowThreshold(Energy threshold,
tcPDPtr ptr1, tcPDPtr ptr2) const;
/**
* Return the nominal mass of the hadron returned by lightestHadron()
* @param ptr1 is the first constituent
* @param ptr2 is the second constituent
* @param ptr3 is the third constituent
*/
Energy massLightestHadron(tcPDPtr ptr1, tcPDPtr ptr2) const {
// find entry in the table
pair<long,long> ids(abs(ptr1->id()),abs(ptr2->id()));
HadronTable::const_iterator tit=_table.find(ids);
// throw exception if flavours wrong
if(tit==_table.end()||tit->second.empty())
throw Exception() << "HadronSpectrum::massLightestHadron "
<< "failed for particle" << ptr1->id() << " "
<< ptr2->id()
<< Exception::eventerror;
// return the mass
return tit->second.begin()->mass;
}
/**
* Force baryon/meson selection
*/
virtual std::tuple<bool,bool,bool> selectBaryon(const Energy cluMass, tcPDPtr par1, tcPDPtr par2) const;
/**
* Returns the mass of the lightest pair of baryons.
* @param ptr1 is the first constituent
* @param ptr2 is the second constituent
*/
Energy massLightestBaryonPair(tcPDPtr ptr1, tcPDPtr ptr2) const;
/**
* Return the weight for the given flavour
*/
virtual double pwtQuark(const long& id) const = 0;
virtual double specialQuarkWeight(double quarkWeight, long,
const Energy, tcPDPtr, tcPDPtr) const {
return quarkWeight;
}
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);
//@}
/**
* The standard Init function used to initialize the interfaces.
* Called exactly once for each class by the class description system
* before the main function starts or
* when this class is dynamically loaded.
*/
static void Init();
void setGenerator(tEGPtr generator) {
Interfaced::setGenerator(generator);
}
protected:
/** @name Standard Interfaced functions. */
//@{
/**
* Initialize this object after the setup phase before saving an
* EventGenerator to disk.
*
* The array _repwt is initialized using the interfaces to set different
* weights for different meson multiplets and the constructHadronTable()
* method called to complete the construction of the hadron tables.
*
* @throws InitException if object could not be initialized properly.
*/
virtual void doinit();
//@}
/**
* Return the id of the diquark (anti-diquark) made by the two
* quarks (antiquarks) of id specified in input (id1, id2).
* Caller must ensure that id1 and id2 are quarks.
*/
virtual long makeDiquarkID(long id1, long id2, long pspin) const = 0;
protected:
/**
* Return true if the two particles in input can be the components of a meson;
*false otherwise.
*/
bool canBeMeson(tcPDPtr par1,tcPDPtr par2) const;
/**
* Return true if the two or three particles in input can be the components
* of a baryon; false otherwise.
*/
virtual bool canBeBaryon(tcPDPtr par1, tcPDPtr par2 , tcPDPtr par3 = PDPtr()) const = 0;
/**
* A sub-function of HadronSpectrum::constructHadronTable().
* It receives the information of a prospective Hadron and inserts it
* into the hadron table construct.
* @param particle is a particle data pointer to the hadron
* @param flav1 is the first constituent of the hadron
* @param flav2 is the second constituent of the hadron
*/
void insertToHadronTable(tPDPtr &particle, int flav1, int flav2);
/**
* Construct the table of hadron data
* This is the main method to initialize the hadron data (mainly the
* weights associated to each hadron, taking into account its spin,
* eventual isoscalar-octect mixing, singlet-decuplet factor). This is
* the method that one should update when new or updated hadron data is
* available.
*
* This class implements the construction of the basic table but can be
* overridden if needed in inheriting classes.
*
* The rationale for factors used for diquarks involving different quarks can
* be can be explained by taking a prototype example that in the exact SU(2) limit,
* in which:
* \f[m_u=m_d\f]
* \f[M_p=M_n=M_\Delta\f]
* and we will have equal numbers of u and d quarks produced.
* Suppose that we weight 1 the diquarks made of the same
* quark and 1/2 those made of different quarks, the fractions
* of u and d baryons (p, n, Delta) we get are the following:
* - \f$\Delta^{++}\f$: 1 possibility only u uu with weight 1
* - \f$\Delta^- \f$: 1 possibility only d dd with weight 1
* - \f$p,\Delta^+ \f$: 2 possibilities u ud with weight 1/2
* d uu with weight 1
* - \f$n,\Delta^0 \f$: 2 possibilities d ud with weight 1/2
* u dd with weight 1
* In the latter two cases, we have to take into account the
* fact that p and n have spin 1/2 whereas Delta+ and Delta0
* have spin 3/2 therefore from phase space we get a double weight
* for Delta+ and Delta0 relative to p and n respectively.
* Therefore the relative amount of these baryons that is
* produced is the following:
* # p = # n = ( 1/2 + 1 ) * 1/3 = 1/2
* # Delta++ = # Delta- = 1 = ( 1/2 + 1) * 2/3 # Delta+ = # Delta0
* which is correct, and therefore the weight 1/2 for the
* diquarks of different types of quarks is justified (at least
* in this limit of exact SU(2) ).
*/
virtual void constructHadronTable() = 0;
/**
* The table of hadron data
*/
HadronTable _table;
/**
* The PDG codes of the constituent particles allowed
*/
vector<PDPtr> _partons;
/**
* The PDG codes of the hadrons which cannot be produced in the hadronization
*/
vector<PDPtr> _forbidden;
/**
* Access to the table of hadrons
*/
const HadronTable & table() const {
return _table;
}
/**
* Access to the list of partons
*/
const vector<PDPtr> & partons() const {
return _partons;
}
/**
* Calculates a special weight specific to a given hadron.
* @param id The PDG code of the hadron
*/
double specialWeight(long id) const {
const int pspin = id % 10;
// Only K0L and K0S have pspin == 0, should
// not get them until Decay step
assert( pspin != 0 );
// Baryon : J = 1/2 or 3/2
if(pspin%2==0)
return baryonWeight(id);
// Meson
else
return mesonWeight(id);
}
/**
* Weights for mesons
*/
virtual double mesonWeight(long id) const;
/**
* Weights for baryons
*/
virtual double baryonWeight(long id) const = 0;
/**
* This method returns the proper sign ( > 0 hadron; < 0 anti-hadron )
* for the input PDG id idHad > 0, suppose to be made by the
* two constituent particle pointers: par1 and par2 (both with proper sign).
*/
int signHadron(tcPDPtr ptr1, tcPDPtr ptr2, tcPDPtr hadron) const;
/**
* Insert a meson in the table
*/
virtual void insertMeson(HadronInfo a, int flav1, int flav2) = 0;
/**
* Insert a spin\f$\frac12\f$ baryon in the table
*/
virtual void insertOneHalf(HadronInfo a, int flav1, int flav2);
/**
* Insert a spin\f$\frac32\f$ baryon in the table
*/
virtual void insertThreeHalf(HadronInfo a, int flav1, int flav2);
/**
* Option for the selection of hadrons below the pair threshold
*/
unsigned int belowThreshold_;
/**
* The weights for the excited meson multiplets
*/
vector<vector<vector<double> > > _repwt;
/**
* Weights for quarks and diquarks.
*/
map<long,double> _pwt;
/**
* Enums so arrays can be statically allocated
*/
//@{
/**
* Defines values for array sizes. L,J,N max values for excited mesons.
*/
enum MesonMultiplets { Lmax = 3, Jmax = 4, Nmax = 4};
//@}
/**
* Caches of lightest pairs for speed
*/
//@{
/**
* Masses of lightest hadron pair
*/
map<pair<long,long>,tcPDPair> lightestHadrons_;
//@}
private:
/**
* The assignment operator is private and must never be called.
* In fact, it should not even be implemented.
*/
HadronSpectrum & operator=(const HadronSpectrum &) = delete;
};
}
#endif /* Herwig_HadronSpectrum_H */
diff --git a/Hadronization/HwppSelector.cc b/Hadronization/HwppSelector.cc
--- a/Hadronization/HwppSelector.cc
+++ b/Hadronization/HwppSelector.cc
@@ -1,261 +1,313 @@
// -*- 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 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",
&HwppSelector::_scHadronWtFactor, 1., 0., 10.,
false, false, Interface::limited);
static Parameter<HwppSelector,double> interfacesbHadronWtFactor
("sbHadronWtFactor",
"Wight factor for strenge-bottom heavy hadrns",
&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,
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);
- if(_mode ==1) {
+ 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);
}
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;
}
diff --git a/Hadronization/HwppSelector.h b/Hadronization/HwppSelector.h
--- a/Hadronization/HwppSelector.h
+++ b/Hadronization/HwppSelector.h
@@ -1,206 +1,193 @@
// -*- C++ -*-
//
// HwppSelector.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_HwppSelector_H
#define HERWIG_HwppSelector_H
//
// This is the declaration of the HwppSelector class.
//
#include "StandardModelHadronSpectrum.h"
namespace Herwig {
using namespace ThePEG;
/** \ingroup hadronization
* The HwppSelector class selects the hadrons produced in cluster decay using
* the Herwig variant of the cluster model.
*
* @see \ref HwppSelectorInterfaces "The interfaces"
* defined for HwppSelector.
*/
class HwppSelector: public StandardModelHadronSpectrum {
public:
/**
* The default constructor.
*/
HwppSelector() : StandardModelHadronSpectrum(1),
- _pwtDIquark(1.0 ),
_mode(1), _enhanceSProb(0), _m0Decay(1.*GeV),
_scHadronWtFactor(1.), _sbHadronWtFactor(1.)
{}
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);
//@}
/**
* The standard Init function used to initialize the interfaces.
* Called exactly once for each class by the class description system
* before the main function starts or
* when this class is dynamically loaded.
*/
static void Init();
protected:
/**
* Weights for baryons
*/
virtual double baryonWeight(long id) const;
/**
* Whether to select a meson or a baryon
*/
std::tuple<bool,bool,bool> selectBaryon(const Energy cluMass, tcPDPtr par1, tcPDPtr par2) const;
/**
* Strange quark weight
*/
virtual double strangeWeight(const Energy cluMass, tcPDPtr par1, tcPDPtr par2) const;
/**
* Returns the mass of the lightest pair of baryons.
* @param ptr1 is the first constituent
* @param ptr2 is the second constituent
*/
inline Energy massLightestBaryonPair(tcPDPtr ptr1, tcPDPtr ptr2) const {
map<pair<long,long>,tcPDPair>::const_iterator lightest =
lightestBaryons_.find(make_pair(abs(ptr1->id()),abs(ptr2->id())));
assert(lightest!=lightestBaryons_.end());
return lightest->second.first->mass()+lightest->second.second->mass();
}
/**
* Returns the lightest pair of baryons.
* @param ptr1 is the first constituent
* @param ptr2 is the second constituent
*/
tcPDPair lightestBaryonPair(tcPDPtr ptr1, tcPDPtr ptr2) const;
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;
//@}
protected:
/** @name Standard Interfaced functions. */
//@{
/**
* Initialize this object after the setup phase before saving an
* EventGenerator to disk.
* @throws InitException if object could not be initialized properly.
*/
virtual void doinit();
//@}
private:
/**
* The assignment operator is private and must never be called.
* In fact, it should not even be implemented.
*/
HwppSelector & operator=(const HwppSelector &) = delete;
private:
/**
- * The weights for the diquarks
- */
- //@{
- /**
- * The probability of producting a diquark.
- */
- double _pwtDIquark;
- //@}
-
-private:
-
- /**
* Which algorithm to use
*/
unsigned int _mode;
/**
* 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 _m0Decay;
/**
* 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 that stops the scale in strangeness enhancement from
* becoming too large
*/
const double _maxScale = 20.;
/**
* Heavy strange-charm hadron wight coefficient
*/
double _scHadronWtFactor;
/**
* Heavy strange-bottom hadron wight coefficient
*/
double _sbHadronWtFactor;
/**
* Caches of lightest pairs for speed
*/
//@{
/**
* Masses of lightest baryon pair
*/
map<pair<long,long>,tcPDPair> lightestBaryons_;
//@}
};
}
#endif /* HERWIG_HwppSelector_H */
diff --git a/Hadronization/StandardModelHadronSpectrum.cc b/Hadronization/StandardModelHadronSpectrum.cc
--- a/Hadronization/StandardModelHadronSpectrum.cc
+++ b/Hadronization/StandardModelHadronSpectrum.cc
@@ -1,686 +1,711 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the StandardModelHadronSpectrum class.
//
#include "StandardModelHadronSpectrum.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/Parameter.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Interface/ParVector.h"
#include "ThePEG/Interface/RefVector.h"
#include "ThePEG/EventRecord/Particle.h"
#include "ThePEG/Repository/UseRandom.h"
#include "ThePEG/Repository/EventGenerator.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include <ThePEG/PDT/EnumParticles.h>
#include <ThePEG/Repository/EventGenerator.h>
#include <ThePEG/Repository/Repository.h>
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
using namespace Herwig;
namespace {
bool weightIsLess (pair<long,double> a, pair<long,double> b) {
return a.second < b.second;
}
/**
* Return true if the particle pointer corresponds to a diquark
* or anti-diquark carrying b flavour; false otherwise.
*/
inline bool isDiquarkWithB(tcPDPtr par1) {
if (!par1) return false;
long id1 = par1->id();
return DiquarkMatcher::Check(id1) && (abs(id1)/1000)%10 == ParticleID::b;
}
/**
* Return true if the particle pointer corresponds to a diquark
* or anti-diquark carrying c flavour; false otherwise.
*/
inline bool isDiquarkWithC(tcPDPtr par1) {
if (!par1) return false;
long id1 = par1->id();
return ( DiquarkMatcher::Check(id1) &&
( (abs(id1)/1000)%10 == ParticleID::c
|| (abs(id1)/100)%10 == ParticleID::c ) );
}
}
StandardModelHadronSpectrum::StandardModelHadronSpectrum(unsigned int opt)
: HadronSpectrum(),
+ _hadronizingStrangeDiquarks(1),
_pwtDquark( 1.0 ),_pwtUquark( 1.0 ),_pwtSquark( 1.0 ),_pwtCquark( 0.0 ),
_pwtBquark( 0.0 ),
_sngWt( 1.0 ),_decWt( 1.0 ),
_weight1S0(Nmax,1.),_weight3S1(Nmax,1.),_weight1P1(Nmax,1.),_weight3P0(Nmax,1.),
_weight3P1(Nmax,1.),_weight3P2(Nmax,1.),_weight1D2(Nmax,1.),_weight3D1(Nmax,1.),
_weight3D2(Nmax,1.),_weight3D3(Nmax,1.),
_topt(opt),_trial(0),
- _limBottom(), _limCharm(), _limExotic()
+ _limBottom(0.0), _limCharm(0.0), _limExotic(0.0)
{
// The mixing angles
// the ideal mixing angle
const double idealAngleMix = atan( sqrt(0.5) ) * 180.0 / Constants::pi;
// \eta-\eta' mixing angle
_etamix = -23.0;
// phi-omega mixing angle
_phimix = +36.0;
// h_1'-h_1 mixing angle
_h1mix = idealAngleMix;
// f_0(1710)-f_0(1370) mixing angle
_f0mix = idealAngleMix;
// f_1(1420)-f_1(1285)\f$ mixing angle
_f1mix = idealAngleMix;
// f'_2-f_2\f$ mixing angle
_f2mix = +26.0;
// eta_2(1870)-eta_2(1645) mixing angle
_eta2mix = idealAngleMix;
// phi(???)-omega(1650) mixing angle
_omhmix = idealAngleMix;
// phi_3-omega_3 mixing angle
_ph3mix = +28.0;
// eta(1475)-eta(1295) mixing angle
_eta2Smix = idealAngleMix;
// phi(1680)-omega(1420) mixing angle
_phi2Smix = idealAngleMix;
}
StandardModelHadronSpectrum::~StandardModelHadronSpectrum() {}
void StandardModelHadronSpectrum::persistentOutput(PersistentOStream & os) const {
- os << _pwtDquark << _pwtUquark << _pwtSquark
+ os << _hadronizingStrangeDiquarks
+ << _pwtDquark << _pwtUquark << _pwtSquark
<< _pwtCquark << _pwtBquark
<< _etamix << _phimix << _h1mix << _f0mix << _f1mix << _f2mix
<< _eta2mix << _omhmix << _ph3mix << _eta2Smix << _phi2Smix
<< _weight1S0 << _weight3S1 << _weight1P1 << _weight3P0 << _weight3P1
<< _weight3P2 << _weight1D2 << _weight3D1 << _weight3D2 << _weight3D3
<< _sngWt << _decWt << _repwt
<< _limBottom << _limCharm << _limExotic;
}
void StandardModelHadronSpectrum::persistentInput(PersistentIStream & is, int) {
- is >> _pwtDquark >> _pwtUquark >> _pwtSquark
+ is >> _hadronizingStrangeDiquarks
+ >> _pwtDquark >> _pwtUquark >> _pwtSquark
>> _pwtCquark >> _pwtBquark
>> _etamix >> _phimix >> _h1mix >> _f0mix >> _f1mix >> _f2mix
>> _eta2mix >> _omhmix >> _ph3mix >> _eta2Smix >> _phi2Smix
>> _weight1S0 >> _weight3S1 >> _weight1P1 >> _weight3P0 >> _weight3P1
>> _weight3P2 >> _weight1D2 >> _weight3D1 >> _weight3D2 >> _weight3D3
>> _sngWt >> _decWt >> _repwt
>> _limBottom >> _limCharm >> _limExotic;
}
// *** Attention *** The following static variable is needed for the type
// description system in ThePEG. Please check that the template arguments
// are correct (the class and its base class), and that the constructor
// arguments are correct (the class name and the name of the dynamically
// loadable library where the class implementation can be found).
DescribeAbstractClass<StandardModelHadronSpectrum,HadronSpectrum>
describeHerwigStandardModelHadronSpectrum("Herwig::StandardModelHadronSpectrum", "Herwig.so");
void StandardModelHadronSpectrum::Init() {
static ClassDocumentation<StandardModelHadronSpectrum> documentation
("There is no documentation for the StandardModelHadronSpectrum class");
static Parameter<StandardModelHadronSpectrum,double>
interfacePwtDquark("PwtDquark","Weight for choosing a quark D",
&StandardModelHadronSpectrum::_pwtDquark, 0, 1.0, 0.0, 10.0,
false,false,false);
static Parameter<StandardModelHadronSpectrum,double>
interfacePwtUquark("PwtUquark","Weight for choosing a quark U",
&StandardModelHadronSpectrum::_pwtUquark, 0, 1.0, 0.0, 10.0,
false,false,false);
static Parameter<StandardModelHadronSpectrum,double>
interfacePwtSquark("PwtSquark","Weight for choosing a quark S",
&StandardModelHadronSpectrum::_pwtSquark, 0, 1.0, 0.0, 10.0,
false,false,false);
static Parameter<StandardModelHadronSpectrum,double>
interfacePwtCquark("PwtCquark","Weight for choosing a quark C",
&StandardModelHadronSpectrum::_pwtCquark, 0, 0.0, 0.0, 10.0,
false,false,false);
static Parameter<StandardModelHadronSpectrum,double>
interfacePwtBquark("PwtBquark","Weight for choosing a quark B",
&StandardModelHadronSpectrum::_pwtBquark, 0, 0.0, 0.0, 10.0,
false,false,false);
static Parameter<StandardModelHadronSpectrum,double>
interfaceSngWt("SngWt","Weight for singlet baryons",
&StandardModelHadronSpectrum::_sngWt, 0, 1.0, 0.0, 10.0,
false,false,false);
static Parameter<StandardModelHadronSpectrum,double>
interfaceDecWt("DecWt","Weight for decuplet baryons",
&StandardModelHadronSpectrum::_decWt, 0, 1.0, 0.0, 10.0,
false,false,false);
//
// mixing angles
//
// the ideal mixing angle
const double idealAngleMix = atan( sqrt(0.5) ) * 180.0 / Constants::pi;
static Parameter<StandardModelHadronSpectrum,double> interface11S0Mixing
("11S0Mixing",
"The mixing angle for the I=0 mesons from the 1 1S0 multiplet,"
" i.e. eta and etaprime.",
&StandardModelHadronSpectrum::_etamix, -23., -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface13S1Mixing
("13S1Mixing",
"The mixing angle for the I=0 mesons from the 1 3S1 multiplet,"
" i.e. phi and omega.",
&StandardModelHadronSpectrum::_phimix, +36., -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface11P1Mixing
("11P1Mixing",
"The mixing angle for the I=0 mesons from the 1 1P1 multiplet,"
" i.e. h_1' and h_1.",
&StandardModelHadronSpectrum::_h1mix, idealAngleMix, -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface13P0Mixing
("13P0Mixing",
"The mixing angle for the I=0 mesons from the 1 3P0 multiplet,"
" i.e. f_0(1710) and f_0(1370).",
&StandardModelHadronSpectrum::_f0mix, idealAngleMix, -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface13P1Mixing
("13P1Mixing",
"The mixing angle for the I=0 mesons from the 1 3P1 multiplet,"
" i.e. f_1(1420) and f_1(1285).",
&StandardModelHadronSpectrum::_f1mix, idealAngleMix, -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface13P2Mixing
("13P2Mixing",
"The mixing angle for the I=0 mesons from the 1 3P2 multiplet,"
" i.e. f'_2 and f_2.",
&StandardModelHadronSpectrum::_f2mix, 26.0, -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface11D2Mixing
("11D2Mixing",
"The mixing angle for the I=0 mesons from the 1 1D2 multiplet,"
" i.e. eta_2(1870) and eta_2(1645).",
&StandardModelHadronSpectrum::_eta2mix, idealAngleMix, -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface13D0Mixing
("13D0Mixing",
"The mixing angle for the I=0 mesons from the 1 3D0 multiplet,"
" i.e. eta_2(1870) phi(?) and omega(1650).",
&StandardModelHadronSpectrum::_omhmix, idealAngleMix, -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface13D1Mixing
("13D1Mixing",
"The mixing angle for the I=0 mesons from the 1 3D1 multiplet,"
" i.e. phi_3 and omega_3.",
&StandardModelHadronSpectrum::_ph3mix, 28.0, -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface21S0Mixing
("21S0Mixing",
"The mixing angle for the I=0 mesons from the 2 1S0 multiplet,"
" i.e. eta(1475) and eta(1295).",
&StandardModelHadronSpectrum::_eta2Smix, idealAngleMix, -180., 180.,
false, false, Interface::limited);
static Parameter<StandardModelHadronSpectrum,double> interface23S1Mixing
("23S1Mixing",
"The mixing angle for the I=0 mesons from the 1 3S1 multiplet,"
" i.e. phi(1680) and omega(1420).",
&StandardModelHadronSpectrum::_phi2Smix, idealAngleMix, -180., 180.,
false, false, Interface::limited);
//
// the meson weights
//
static ParVector<StandardModelHadronSpectrum,double> interface1S0Weights
("1S0Weights",
"The weights for the 1S0 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight1S0, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static ParVector<StandardModelHadronSpectrum,double> interface3S1Weights
("3S1Weights",
"The weights for the 3S1 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight3S1, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static ParVector<StandardModelHadronSpectrum,double> interface1P1Weights
("1P1Weights",
"The weights for the 1P1 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight1P1, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static ParVector<StandardModelHadronSpectrum,double> interface3P0Weights
("3P0Weights",
"The weights for the 3P0 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight3P0, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static ParVector<StandardModelHadronSpectrum,double> interface3P1Weights
("3P1Weights",
"The weights for the 3P1 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight3P1, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static ParVector<StandardModelHadronSpectrum,double> interface3P2Weights
("3P2Weights",
"The weights for the 3P2 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight3P2, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static ParVector<StandardModelHadronSpectrum,double> interface1D2Weights
("1D2Weights",
"The weights for the 1D2 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight1D2, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static ParVector<StandardModelHadronSpectrum,double> interface3D1Weights
("3D1Weights",
"The weights for the 3D1 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight3D1, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static ParVector<StandardModelHadronSpectrum,double> interface3D2Weights
("3D2Weights",
"The weights for the 3D2 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight3D2, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static ParVector<StandardModelHadronSpectrum,double> interface3D3Weights
("3D3Weights",
"The weights for the 3D3 multiplets start with n=1.",
&StandardModelHadronSpectrum::_weight3D3, Nmax, 1.0, 0.0, 100.0,
false, false, Interface::limited);
static Switch<StandardModelHadronSpectrum,unsigned int> interfaceTrial
("Trial",
"A Debugging option to only produce certain types of hadrons",
&StandardModelHadronSpectrum::_trial, 0, false, false);
static SwitchOption interfaceTrialAll
(interfaceTrial,
"All",
"Produce all the hadrons",
0);
static SwitchOption interfaceTrialPions
(interfaceTrial,
"Pions",
"Only produce pions",
1);
static SwitchOption interfaceTrialSpin2
(interfaceTrial,
"Spin2",
"Only mesons with spin less than or equal to two are produced",
2);
static SwitchOption interfaceTrialSpin3
(interfaceTrial,
"Spin3",
"Only hadrons with spin less than or equal to three are produced",
3);
static Parameter<StandardModelHadronSpectrum,double>
interfaceSingleHadronLimitBottom ("SingleHadronLimitBottom",
"Threshold for one-hadron decay of b-cluster",
&StandardModelHadronSpectrum::_limBottom,
0, 0.0, 0.0, 100.0,false,false,false);
static Parameter<StandardModelHadronSpectrum,double>
interfaceSingleHadronLimitCharm ("SingleHadronLimitCharm",
"threshold for one-hadron decay of c-cluster",
&StandardModelHadronSpectrum::_limCharm,
0, 0.0, 0.0, 100.0,false,false,false);
static Parameter<StandardModelHadronSpectrum,double>
interfaceSingleHadronLimitExotic ("SingleHadronLimitExotic",
"threshold for one-hadron decay of exotic cluster",
&StandardModelHadronSpectrum::_limExotic,
0, 0.0, 0.0, 100.0,false,false,false);
static Switch<StandardModelHadronSpectrum,unsigned int> interfaceBelowThreshold
("BelowThreshold",
"Option fo the selection of the hadrons if the cluster is below the pair threshold",
&StandardModelHadronSpectrum::belowThreshold_, 0, false, false);
static SwitchOption interfaceBelowThresholdLightest
(interfaceBelowThreshold,
"Lightest",
"Force cluster to decay to the lightest hadron with the appropriate flavours",
0);
static SwitchOption interfaceBelowThresholdAll
(interfaceBelowThreshold,
"All",
"Select from all the hadrons below the two hadron threshold according to their spin weights",
1);
+ static Switch<StandardModelHadronSpectrum,unsigned int> interfaceHadronizingStrangeDiquarks
+ ("HadronizingStrangeDiquarks",
+ "Option for adding strange diquarks to Hadronization",
+ &StandardModelHadronSpectrum::_hadronizingStrangeDiquarks, 0, false, false);
+ static SwitchOption interfaceHadronizingStrangeDiquarksNo
+ (interfaceHadronizingStrangeDiquarks,
+ "No",
+ "No strangeness containing diquarks in Hadronization",
+ 0);
+ static SwitchOption interfaceHadronizingStrangeDiquarksOnlySingleStrange
+ (interfaceHadronizingStrangeDiquarks,
+ "OnlySingleStrange",
+ "Only one strangeness containing diquarks in Hadronization i.e. su,sd",
+ 1);
+ static SwitchOption interfaceHadronizingStrangeDiquarksAll
+ (interfaceHadronizingStrangeDiquarks,
+ "All",
+ "All strangeness containing diquarks in Hadronization i.e. su,sd,ss",
+ 2);
+
}
Energy StandardModelHadronSpectrum::hadronPairThreshold(tcPDPtr par1, tcPDPtr par2) const {
// Determine the sum of the nominal masses of the two lightest hadrons
// with the right flavour numbers as the cluster under consideration.
// Notice that we don't need real masses (drawn by a Breit-Wigner
// distribution) because the lightest pair of hadrons does not involve
// any broad resonance.
Energy threshold = massLightestHadronPair(par1,par2);
// Special: it allows one-hadron decays also above threshold.
if (isExotic(par1,par2))
threshold *= (1.0 + UseRandom::rnd()*_limExotic);
else if (hasBottom(par1,par2))
threshold *= (1.0 + UseRandom::rnd()*_limBottom);
else if (hasCharm(par1,par2))
threshold *= (1.0 + UseRandom::rnd()*_limCharm);
return threshold;
}
double StandardModelHadronSpectrum::mixingStateWeight(long id) const {
switch(id) {
case ParticleID::eta: return 0.5*probabilityMixing(_etamix ,1);
case ParticleID::etaprime: return 0.5*probabilityMixing(_etamix ,2);
case ParticleID::phi: return 0.5*probabilityMixing(_phimix ,1);
case ParticleID::omega: return 0.5*probabilityMixing(_phimix ,2);
case ParticleID::hprime_1: return 0.5*probabilityMixing(_h1mix ,1);
case ParticleID::h_1: return 0.5*probabilityMixing(_h1mix ,2);
case 10331: return 0.5*probabilityMixing(_f0mix ,1);
case 10221: return 0.5*probabilityMixing(_f0mix ,2);
case ParticleID::fprime_1: return 0.5*probabilityMixing(_f1mix ,1);
case ParticleID::f_1: return 0.5*probabilityMixing(_f1mix ,2);
case ParticleID::fprime_2: return 0.5*probabilityMixing(_f2mix ,1);
case ParticleID::f_2: return 0.5*probabilityMixing(_f2mix ,2);
case 10335: return 0.5*probabilityMixing(_eta2mix ,1);
case 10225: return 0.5*probabilityMixing(_eta2mix ,2);
case 30333: return 0.5*probabilityMixing(_omhmix ,1);
case 30223: return 0.5*probabilityMixing(_omhmix ,2);
case 337: return 0.5*probabilityMixing(_ph3mix ,1);
case 227: return 0.5*probabilityMixing(_ph3mix ,2);
case 100331: return 0.5*probabilityMixing(_eta2mix ,1);
case 100221: return 0.5*probabilityMixing(_eta2mix ,2);
case 100333: return 0.5*probabilityMixing(_phi2Smix,1);
case 100223: return 0.5*probabilityMixing(_phi2Smix,2);
default: return 1./3.;
}
}
void StandardModelHadronSpectrum::doinit() {
// set the weights for the various excited mesons
// set all to one to start with
for (int l = 0; l < Lmax; ++l ) {
for (int j = 0; j < Jmax; ++j) {
for (int n = 0; n < Nmax; ++n) {
_repwt[l][j][n] = 1.0;
}
}
}
// set the others from the relevant vectors
for( int ix=0;ix<max(int(_weight1S0.size()),int(Nmax));++ix)
_repwt[0][0][ix]=_weight1S0[ix];
for( int ix=0;ix<max(int(_weight3S1.size()),int(Nmax));++ix)
_repwt[0][1][ix]=_weight3S1[ix];
for( int ix=0;ix<max(int(_weight1P1.size()),int(Nmax));++ix)
_repwt[1][1][ix]=_weight1P1[ix];
for( int ix=0;ix<max(int(_weight3P0.size()),int(Nmax));++ix)
_repwt[1][0][ix]=_weight3P0[ix];
for( int ix=0;ix<max(int(_weight3P1.size()),int(Nmax));++ix)
_repwt[1][1][ix]=_weight3P1[ix];
for( int ix=0;ix<max(int(_weight3P2.size()),int(Nmax));++ix)
_repwt[1][2][ix]=_weight3P2[ix];
for( int ix=0;ix<max(int(_weight1D2.size()),int(Nmax));++ix)
_repwt[2][2][ix]=_weight1D2[ix];
for( int ix=0;ix<max(int(_weight3D1.size()),int(Nmax));++ix)
_repwt[2][1][ix]=_weight3D1[ix];
for( int ix=0;ix<max(int(_weight3D2.size()),int(Nmax));++ix)
_repwt[2][2][ix]=_weight3D2[ix];
for( int ix=0;ix<max(int(_weight3D3.size()),int(Nmax));++ix)
_repwt[2][3][ix]=_weight3D3[ix];
// find the maximum
map<long,double>::iterator pit =
max_element(_pwt.begin(),_pwt.end(),weightIsLess);
const double pmax = pit->second;
for(pit=_pwt.begin(); pit!=_pwt.end(); ++pit) {
pit->second/=pmax;
}
HadronSpectrum::doinit();
}
void StandardModelHadronSpectrum::constructHadronTable() {
// initialise the table
_table.clear();
for(unsigned int ix=0; ix<_partons.size(); ++ix) {
for(unsigned int iy=0; iy<_partons.size(); ++iy) {
if (!(DiquarkMatcher::Check(_partons[ix]->id())
&& DiquarkMatcher::Check(_partons[iy]->id())))
_table[make_pair(_partons[ix]->id(),_partons[iy]->id())] = KupcoData();
}
}
// get the particles from the event generator
ParticleMap particles = generator()->particles();
// loop over the particles
//double maxdd(0.),maxss(0.),maxrest(0.);
for(ParticleMap::iterator it=particles.begin();
it!=particles.end(); ++it) {
long pid = it->first;
tPDPtr particle = it->second;
int pspin = particle->iSpin();
// Don't include hadrons which are explicitly forbidden
if(find(_forbidden.begin(),_forbidden.end(),particle)!=_forbidden.end())
continue;
// Don't include non-hadrons or antiparticles
if(pid < 100) continue;
// remove diffractive particles
if(pspin == 0) continue;
// K_0S and K_0L not made make K0 and Kbar0
if(pid==ParticleID::K_S0||pid==ParticleID::K_L0) continue;
// Debugging options
// Only include those with 2J+1 less than...5
if(_trial==2 && pspin >= 5) continue;
// Only include those with 2J+1 less than...7
if(_trial==3 && pspin >= 7) continue;
// Only include pions
if(_trial==1 && pid!=111 && pid!=211) continue;
// shouldn't be coloured
if(particle->coloured()) continue;
// Exclude dark hadrons
if ((pid/100000)==49) continue;
// Get the flavours
const int x4 = (pid/1000)%10;
const int x3 = (pid/100 )%10;
const int x2 = (pid/10 )%10;
const int x7 = (pid/1000000)%10;
const bool wantSusy = x7 == 1 || x7 == 2;
// Skip non-hadrons (susy particles, etc...)
if(x3 == 0 || x2 == 0) continue;
// Skip particles which are neither SM nor SUSY
if(x7 >= 3 && x7 != 9) continue;
int flav1,flav2;
// meson
if(x4 == 0) {
flav1 = x2;
flav2 = x3;
}
// baryon
else {
flav2 = x4;
// insert the spin 1 diquark, sort out the rest later
flav1 = makeDiquarkID(x2,x3,3);
}
if (wantSusy) flav2 += 1000000 * x7;
insertToHadronTable(particle,flav1,flav2);
}
// normalise the weights
if(_topt == 0) {
HadronTable::const_iterator tit;
KupcoData::iterator it;
for(tit=_table.begin();tit!=_table.end();++tit) {
double weight=0;
for(it = tit->second.begin(); it!=tit->second.end(); ++it)
weight=max(weight,it->overallWeight);
weight = 1./weight;
}
// double weight;
// if(tit->first.first==tit->first.second) {
// if(tit->first.first==1||tit->first.first==2) weight=1./maxdd;
// else if (tit->first.first==3) weight=1./maxss;
// else weight=1./maxrest;
// }
// else weight=1./maxrest;
// for(it = tit->second.begin(); it!=tit->second.end(); ++it) {
// it->rescale(weight);
// }
// }
}
}
double StandardModelHadronSpectrum::strangeWeight(const Energy, tcPDPtr, tcPDPtr) const {
assert(false);
}
void StandardModelHadronSpectrum::insertMeson(HadronInfo a, int flav1, int flav2) {
// identical light flavours
if(flav1 == flav2 && flav1<=3) {
// ddbar> uubar> admixture states
if(flav1==1) {
a.overallWeight *= 0.5;
_table[make_pair(1,1)].insert(a);
_table[make_pair(2,2)].insert(a);
}
// load up ssbar> uubar> ddbar> admixture states
else {
// uubar ddbar pieces
a.wt = mixingStateWeight(a.id);
a.overallWeight *= a.wt;
_table[make_pair(1,1)].insert(a);
_table[make_pair(2,2)].insert(a);
a.overallWeight /=a.wt;
// ssbar piece
a.wt = 1.- 2.*a.wt;
if(a.wt > 0) {
a.overallWeight *= a.wt;
_table[make_pair(3,3)].insert(a);
}
}
}
else {
_table[make_pair(flav1,flav2)].insert(a);
if(flav1 != flav2) _table[make_pair(flav2,flav1)].insert(a);
}
}
long StandardModelHadronSpectrum::makeDiquarkID(long id1, long id2, long pspin) const {
assert( id1 * id2 > 0
&& QuarkMatcher::Check(id1)
&& QuarkMatcher::Check(id2)) ;
long ida = abs(id1);
long idb = abs(id2);
if (ida < idb) swap(ida,idb);
if (pspin != 1 && pspin != 3) assert(false);
long idnew = ida*1000 + idb*100 + pspin;
// Diquarks made of quarks of the same type: uu, dd, ss, cc, bb,
// have spin 1, and therefore the less significant digit (which
// corresponds to 2*J+1) is 3 rather than 1 as all other Diquarks.
if (id1 == id2 && pspin == 1) {
//cerr<<"WARNING: spin-0 diquiark of the same type cannot exist."
// <<" Switching to spin-1 diquark.\n";
idnew = ida*1000 + idb*100 + 3;
}
return id1 > 0 ? idnew : -idnew;
}
bool StandardModelHadronSpectrum::hasBottom(tcPDPtr par1, tcPDPtr par2, tcPDPtr par3) const {
long id1 = par1 ? par1->id() : 0;
if ( !par2 && !par3 ) {
return
abs(id1) == ThePEG::ParticleID::b ||
isDiquarkWithB(par1) ||
( MesonMatcher::Check(id1)
&& (abs(id1)/100)%10 == ThePEG::ParticleID::b ) ||
( BaryonMatcher::Check(id1)
&& (abs(id1)/1000)%10 == ThePEG::ParticleID::b );
}
else {
long id2 = par2 ? par2->id() : 0;
long id3 = par3 ? par3->id() : 0;
return
abs(id1) == ThePEG::ParticleID::b || isDiquarkWithB(par1) ||
abs(id2) == ThePEG::ParticleID::b || isDiquarkWithB(par2) ||
abs(id3) == ThePEG::ParticleID::b || isDiquarkWithB(par3);
}
}
bool StandardModelHadronSpectrum::hasCharm(tcPDPtr par1, tcPDPtr par2, tcPDPtr par3) const {
long id1 = par1 ? par1->id(): 0;
if (!par2 && !par3) {
return
abs(id1) == ThePEG::ParticleID::c ||
isDiquarkWithC(par1) ||
( MesonMatcher::Check(id1) &&
((abs(id1)/100)%10 == ThePEG::ParticleID::c ||
(abs(id1)/10)%10 == ThePEG::ParticleID::c) ) ||
( BaryonMatcher::Check(id1) &&
((abs(id1)/1000)%10 == ThePEG::ParticleID::c ||
(abs(id1)/100)%10 == ThePEG::ParticleID::c ||
(abs(id1)/10)%10 == ThePEG::ParticleID::c) );
}
else {
long id2 = par2 ? par1->id(): 0;
long id3 = par3 ? par1->id(): 0;
return
abs(id1) == ThePEG::ParticleID::c || isDiquarkWithC(par1) ||
abs(id2) == ThePEG::ParticleID::c || isDiquarkWithC(par2) ||
abs(id3) == ThePEG::ParticleID::c || isDiquarkWithC(par3);
}
}
bool StandardModelHadronSpectrum::isExotic(tcPDPtr par1, tcPDPtr par2, tcPDPtr par3) const {
/// \todo make this more general
long id1 = par1 ? par1->id(): 0;
long id2 = par2 ? par2->id(): 0;
long id3 = par3 ? par3->id(): 0;
-return
- ( (id1/1000000)% 10 != 0 && (id1/1000000)% 10 != 9 ) ||
+ bool Exotic=( (id1/1000000)% 10 != 0 && (id1/1000000)% 10 != 9 ) ||
( (id2/1000000)% 10 != 0 && (id2/1000000)% 10 != 9 ) ||
( (id3/1000000)% 10 != 0 && (id3/1000000)% 10 != 9 ) ||
abs(id1)==6||abs(id2)==6;
+ if (Exotic)
+ std::cout << "WARNING: found Exotic Clusters ParticleID with id's "<<id1 << "\t"<<id2 <<"\t"<<id3 <<"\n";
+return Exotic;
}
bool StandardModelHadronSpectrum::canBeBaryon(tcPDPtr par1, tcPDPtr par2 , tcPDPtr par3) const {
assert(par1 && par2);
long id1 = par1->id(), id2 = par2->id();
if (!par3) {
if( id1*id2 < 0) return false;
if(DiquarkMatcher::Check(id1))
return abs(int(par2->iColour())) == 3 && !DiquarkMatcher::Check(id2);
if(DiquarkMatcher::Check(id2))
return abs(int(par1->iColour())) == 3;
return false;
}
else {
// In this case, to be a baryon, all three components must be (anti-)quarks
// and with the same sign.
return (par1->iColour() == 3 && par2->iColour() == 3 && par3->iColour() == 3) ||
(par1->iColour() == -3 && par2->iColour() == -3 && par3->iColour() == -3);
}
}
diff --git a/Hadronization/StandardModelHadronSpectrum.h b/Hadronization/StandardModelHadronSpectrum.h
--- a/Hadronization/StandardModelHadronSpectrum.h
+++ b/Hadronization/StandardModelHadronSpectrum.h
@@ -1,536 +1,623 @@
// -*- C++ -*-
#ifndef Herwig_StandardModelHadronSpectrum_H
#define Herwig_StandardModelHadronSpectrum_H
//
// This is the declaration of the StandardModelHadronSpectrum class.
//
#include "Herwig/Hadronization/HadronSpectrum.h"
#include <ThePEG/PDT/ParticleData.h>
#include <ThePEG/PDT/StandardMatchers.h>
#include <ThePEG/Repository/EventGenerator.h>
#include <ThePEG/PDT/EnumParticles.h>
#include "ThePEG/Repository/CurrentGenerator.h"
+#include <ThePEG/Persistency/PersistentOStream.h>
+#include <ThePEG/Persistency/PersistentIStream.h>
namespace Herwig {
using namespace ThePEG;
/**
* Here is the documentation of the StandardModelHadronSpectrum class.
*
* @see \ref StandardModelHadronSpectrumInterfaces "The interfaces"
* defined for StandardModelHadronSpectrum.
*/
class StandardModelHadronSpectrum: public HadronSpectrum {
public:
/** @name Standard constructors and destructors. */
//@{
/**
* The default constructor.
*/
StandardModelHadronSpectrum(unsigned int opt);
/**
* The destructor.
*/
virtual ~StandardModelHadronSpectrum();
//@}
public:
/** @name Partonic content */
//@{
/**
* Return the id of the gluon
*/
virtual long gluonId() const { return ParticleID::g; }
/**
* Return the ids of all hadronizing quarks
*/
virtual const vector<long>& hadronizingQuarks() const {
static vector<long> hadronizing =
{ ParticleID::d, ParticleID::u, ParticleID::s, ParticleID::c, ParticleID::b };
return hadronizing;
}
/**
* The light hadronizing quarks
*/
virtual const vector<long>& lightHadronizingQuarks() const {
static vector<long> light =
{ ParticleID::d, ParticleID::u, ParticleID::s };
return light;
}
/**
+ * The light hadronizing diquarks
+ */
+ virtual const vector<long>& lightHadronizingDiquarks() const {
+ /**
+ * Diquarks q==q_0 are not allowed as they need to have antisymmetric
+ * spin wave-function, which forces the spin to 1
+ * Diquarks q!=q'_1 are not allowed as they need to have antisymmetric
+ * spin wave-function, which forces the spin to 1
+ * */
+
+ // TODO: strange diquarks are turned off for the moment
+ // since in combination with the current ClusterFission
+ // they fail (overshoot) to reproduce the Xi and Lambda
+ // pT spectra.
+ // One may enable these after the ClusterFission
+ // kinematics are settled
+ // TODO why ud_1 not allowed?
+ // exceptions: Could not find 2103 1 in _table
+ // but no problem for 2103 2 ???
+ // ParticleID::ud_1
+ switch(_hadronizingStrangeDiquarks) {
+ case 0:
+ {
+ static vector<long> light = {
+ ParticleID::uu_1,
+ ParticleID::dd_1,
+ ParticleID::ud_0
+ // ParticleID::ud_1,
+ };
+ return light;
+ // break;
+ }
+ case 1:
+ {
+ static vector<long> light = {
+ ParticleID::uu_1,
+ ParticleID::dd_1,
+ ParticleID::ud_0,
+ // ParticleID::ud_1,
+ ParticleID::su_0,
+ // ParticleID::su_1,
+ ParticleID::sd_0
+ // ParticleID::sd_1
+ };
+ return light;
+ }
+ case 2:
+ {
+ static vector<long> light = {
+ ParticleID::uu_1,
+ ParticleID::dd_1,
+ ParticleID::ud_0,
+ // ParticleID::ud_1,
+ ParticleID::su_0,
+ // ParticleID::su_1,
+ ParticleID::sd_0,
+ // ParticleID::sd_1,
+ ParticleID::ss_1
+ };
+ return light;
+ // break;
+ }
+ default:
+ assert(false);
+ }
+ static vector<long> light;
+ return light;
+ }
+
+ /**
* The heavy hadronizing quarks
*/
virtual const vector<long>& heavyHadronizingQuarks() const {
static vector<long> heavy =
{ ParticleID::c, ParticleID::b };
return heavy;
}
/**
* The lightest quarks, used for finding the lightest Hadron Pair
*/
virtual const vector<long>& lightestQuarks() const {
static vector<long> light =
{ ParticleID::d, ParticleID::u};
return light;
}
/**
* Return true if any of the possible three input particles contains
* the indicated heavy quark. false otherwise. In the case that
* only the first particle is specified, it can be: an (anti-)quark,
* an (anti-)diquark an (anti-)meson, an (anti-)baryon; in the other
* cases, each pointer is assumed to be either (anti-)quark or
* (anti-)diquark.
*/
virtual bool hasHeavy(long id, tcPDPtr par1, tcPDPtr par2 = PDPtr(), tcPDPtr par3 = PDPtr()) const {
if ( abs(id) == ParticleID::c )
return hasCharm(par1,par2,par3);
if ( abs(id) == ParticleID::b )
return hasBottom(par1,par2,par3);
return false;
}
//@}
/**
* Return the threshold for a cluster to split into a pair of hadrons.
* This is normally the mass of the lightest hadron Pair, but can be
* higher for heavy and exotic clusters
*/
virtual Energy hadronPairThreshold(tcPDPtr par1, tcPDPtr par2) const;
/**
* Return the weight for the given flavour
*/
virtual double pwtQuark(const long& id) const {
switch(id) {
case ParticleID::d: return pwtDquark(); break;
case ParticleID::u: return pwtUquark(); break;
case ParticleID::s: return pwtSquark(); break;
case ParticleID::c: return pwtCquark(); break;
case ParticleID::b: return pwtBquark(); break;
}
return 0.;
}
/**
* The down quark weight.
*/
double pwtDquark() const {
return _pwtDquark;
}
/**
* The up quark weight.
*/
double pwtUquark() const {
return _pwtUquark;
}
/**
* The strange quark weight.
*/
double pwtSquark() const {
return _pwtSquark;
}
/**
* The charm quark weight.
*/
double pwtCquark() const {
return _pwtCquark;
}
/**
* The bottom quark weight.
*/
double pwtBquark() const {
return _pwtBquark;
}
+ /**
+ * The diquark weight.
+ */
+ double pwtDIquark() const {
+ return _pwtDIquark;
+ }
+
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);
//@}
/**
* The standard Init function used to initialize the interfaces.
* Called exactly once for each class by the class description system
* before the main function starts or
* when this class is dynamically loaded.
*/
static void Init();
protected:
/** @name Standard Interfaced functions. */
//@{
/**
* Initialize this object after the setup phase before saving an
* EventGenerator to disk.
*
* The array _repwt is initialized using the interfaces to set different
* weights for different meson multiplets and the constructHadronTable()
* method called to complete the construction of the hadron tables.
*
* @throws InitException if object could not be initialized properly.
*/
virtual void doinit();
//@}
/**
* Return the id of the diquark (anti-diquark) made by the two
* quarks (antiquarks) of id specified in input (id1, id2).
* Caller must ensure that id1 and id2 are quarks.
*/
long makeDiquarkID(long id1, long id2, long pspin) const;
/**
* Return true if any of the possible three input particles has
* b-flavour;
* false otherwise. In the case that only the first particle is specified,
* it can be: an (anti-)quark, an (anti-)diquark
* an (anti-)meson, an (anti-)baryon; in the other cases, each pointer
* is assumed to be either (anti-)quark or (anti-)diquark.
*/
bool hasBottom(tcPDPtr par1, tcPDPtr par2 = PDPtr(), tcPDPtr par3 = PDPtr()) const;
/**
* Return true if any of the possible three input particles has
* c-flavour;
* false otherwise.In the case that only the first pointer is specified,
* it can be: a (anti-)quark, a (anti-)diquark
* a (anti-)meson, a (anti-)baryon; in the other cases, each pointer
* is assumed to be either (anti-)quark or (anti-)diquark.
*/
bool hasCharm(tcPDPtr par1, tcPDPtr par2 = PDPtr(), tcPDPtr par3 = PDPtr()) const;
/**
* Return true, if any of the possible input particle pointer is an exotic quark, e.g. Susy quark;
* false otherwise.
*/
bool isExotic(tcPDPtr par1, tcPDPtr par2 = PDPtr(), tcPDPtr par3 = PDPtr()) const;
/**
* Return true if the two or three particles in input can be the components
* of a baryon; false otherwise.
*/
virtual bool canBeBaryon(tcPDPtr par1, tcPDPtr par2 , tcPDPtr par3 = PDPtr()) const;
protected:
/**
* Construct the table of hadron data
* This is the main method to initialize the hadron data (mainly the
* weights associated to each hadron, taking into account its spin,
* eventual isoscalar-octect mixing, singlet-decuplet factor). This is
* the method that one should update when new or updated hadron data is
* available.
*
* This class implements the construction of the basic table but can be
* overridden if needed in inheriting classes.
*
* The rationale for factors used for diquarks involving different quarks can
* be can be explained by taking a prototype example that in the exact SU(2) limit,
* in which:
* \f[m_u=m_d\f]
* \f[M_p=M_n=M_\Delta\f]
* and we will have equal numbers of u and d quarks produced.
* Suppose that we weight 1 the diquarks made of the same
* quark and 1/2 those made of different quarks, the fractions
* of u and d baryons (p, n, Delta) we get are the following:
* - \f$\Delta^{++}\f$: 1 possibility only u uu with weight 1
* - \f$\Delta^- \f$: 1 possibility only d dd with weight 1
* - \f$p,\Delta^+ \f$: 2 possibilities u ud with weight 1/2
* d uu with weight 1
* - \f$n,\Delta^0 \f$: 2 possibilities d ud with weight 1/2
* u dd with weight 1
* In the latter two cases, we have to take into account the
* fact that p and n have spin 1/2 whereas Delta+ and Delta0
* have spin 3/2 therefore from phase space we get a double weight
* for Delta+ and Delta0 relative to p and n respectively.
* Therefore the relative amount of these baryons that is
* produced is the following:
* # p = # n = ( 1/2 + 1 ) * 1/3 = 1/2
* # Delta++ = # Delta- = 1 = ( 1/2 + 1) * 2/3 # Delta+ = # Delta0
* which is correct, and therefore the weight 1/2 for the
* diquarks of different types of quarks is justified (at least
* in this limit of exact SU(2) ).
*/
virtual void constructHadronTable();
/**
* Insert a meson in the table
*/
virtual void insertMeson(HadronInfo a, int flav1, int flav2);
/**
* Methods for the mixing of \f$I=0\f$ mesons
*/
//@{
/**
* Return the probability of mixing for Octet-Singlet isoscalar mixing,
* the probability of the
* \f$\frac1{\sqrt{2}}(|u\bar{u}\rangle + |d\bar{d}\rangle)\f$ component
* is returned.
* @param angleMix The mixing angle in degrees (not radians)
* @param order is 0 for no mixing, 1 for the first resonance of a pair,
* 2 for the second one.
* The mixing is defined so that for example with \f$\eta-\eta'\f$ mixing where
* the mixing angle is \f$\theta=-23^0$ with $\eta\f$ as the first particle
* and \f$\eta'\f$ the second one.
* The convention used is
* \f[\eta = \cos\theta|\eta_{\rm octet }\rangle
* -\sin\theta|\eta_{\rm singlet}\rangle\f]
* \f[\eta' = \sin\theta|\eta_{\rm octet }\rangle
* -\cos\theta|\eta_{\rm singlet}\rangle\f]
* with
* \f[|\eta_{\rm singlet}\rangle = \frac1{\sqrt{3}}
* \left[|u\bar{u}\rangle + |d\bar{d}\rangle + |s\bar{s}\rangle\right]\f]
* \f[|\eta_{\rm octet }\rangle = \frac1{\sqrt{6}}
* \left[|u\bar{u}\rangle + |d\bar{d}\rangle - 2|s\bar{s}\rangle\right]\f]
*/
double probabilityMixing(const double angleMix,
const int order) const {
static double convert=Constants::pi/180.0;
if (order == 1)
return sqr( cos( angleMix*convert + atan( sqrt(2.0) ) ) );
else if (order == 2)
return sqr( sin( angleMix*convert + atan( sqrt(2.0) ) ) );
else
return 1.;
}
/**
* Returns the weight of given mixing state.
* @param id The PDG code of the meson
*/
virtual double mixingStateWeight(long id) const;
//@}
virtual double specialQuarkWeight(double quarkWeight, long id,
const Energy cluMass, tcPDPtr par1, tcPDPtr par2) const {
// special for strange
if(abs(id) == 3)
return strangeWeight(cluMass,par1,par2);
else
return quarkWeight;
}
/**
* Strange quark weight
*/
virtual double strangeWeight(const Energy cluMass, tcPDPtr par1, tcPDPtr par2) const;
/**
+ * Strange diquark option for Hadronization
+ */
+ unsigned int _hadronizingStrangeDiquarks;
+ /**
* The weights for the different quarks and diquarks
*/
//@{
/**
* The probability of producting a down quark.
*/
double _pwtDquark;
/**
* The probability of producting an up quark.
*/
double _pwtUquark;
/**
* The probability of producting a strange quark.
*/
double _pwtSquark;
/**
* The probability of producting a charm quark.
*/
double _pwtCquark;
/**
* The probability of producting a bottom quark.
*/
double _pwtBquark;
//@}
/**
+ * The probability of producting a diquark.
+ */
+ double _pwtDIquark;
+ /**
* Singlet and Decuplet weights
*/
//@{
/**
* The singlet weight
*/
double _sngWt;
/**
* The decuplet weight
*/
double _decWt;
//@}
/**
* The mixing angles for the \f$I=0\f$ mesons containing light quarks
*/
//@{
/**
* The \f$\eta-\eta'\f$ mixing angle
*/
double _etamix;
/**
* The \f$\phi-\omega\f$ mixing angle
*/
double _phimix;
/**
* The \f$h_1'-h_1\f$ mixing angle
*/
double _h1mix;
/**
* The \f$f_0(1710)-f_0(1370)\f$ mixing angle
*/
double _f0mix;
/**
* The \f$f_1(1420)-f_1(1285)\f$ mixing angle
*/
double _f1mix;
/**
* The \f$f'_2-f_2\f$ mixing angle
*/
double _f2mix;
/**
* The \f$\eta_2(1870)-\eta_2(1645)\f$ mixing angle
*/
double _eta2mix;
/**
* The \f$\phi(???)-\omega(1650)\f$ mixing angle
*/
double _omhmix;
/**
* The \f$\phi_3-\omega_3\f$ mixing angle
*/
double _ph3mix;
/**
* The \f$\eta(1475)-\eta(1295)\f$ mixing angle
*/
double _eta2Smix;
/**
* The \f$\phi(1680)-\omega(1420)\f$ mixing angle
*/
double _phi2Smix;
//@}
/**
* The weights for the various meson multiplets to be used to supress the
* production of particular states
*/
//@{
/**
* The weights for the \f$\phantom{1}^1S_0\f$ multiplets
*/
vector<double> _weight1S0;
/**
* The weights for the \f$\phantom{1}^3S_1\f$ multiplets
*/
vector<double> _weight3S1;
/**
* The weights for the \f$\phantom{1}^1P_1\f$ multiplets
*/
vector<double> _weight1P1;
/**
* The weights for the \f$\phantom{1}^3P_0\f$ multiplets
*/
vector<double> _weight3P0;
/**
* The weights for the \f$\phantom{1}^3P_1\f$ multiplets
*/
vector<double> _weight3P1;
/**
* The weights for the \f$\phantom{1}^3P_2\f$ multiplets
*/
vector<double> _weight3P2;
/**
* The weights for the \f$\phantom{1}^1D_2\f$ multiplets
*/
vector<double> _weight1D2;
/**
* The weights for the \f$\phantom{1}^3D_1\f$ multiplets
*/
vector<double> _weight3D1;
/**
* The weights for the \f$\phantom{1}^3D_2\f$ multiplets
*/
vector<double> _weight3D2;
/**
* The weights for the \f$\phantom{1}^3D_3\f$ multiplets
*/
vector<double> _weight3D3;
//@}
/**
* Option for the construction of the tables
*/
unsigned int _topt;
/**
* Which particles to produce for debugging purposes
*/
unsigned int _trial;
/**
* @name A parameter used for determining when clusters are too light.
*
* This parameter is used for setting the lower threshold, \f$ t \f$ as
* \f[ t' = t(1 + r B^1_{\rm lim}) \f]
* where \f$ r \f$ is a random number [0,1].
*/
//@{
double _limBottom;
double _limCharm;
double _limExotic;
//@}
};
}
#endif /* Herwig_StandardModelHadronSpectrum_H */
diff --git a/Models/General/HardProcessConstructor.cc b/Models/General/HardProcessConstructor.cc
--- a/Models/General/HardProcessConstructor.cc
+++ b/Models/General/HardProcessConstructor.cc
@@ -1,958 +1,961 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the HardProcessConstructor class.
//
#include "HardProcessConstructor.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
using namespace Herwig;
void HardProcessConstructor::persistentOutput(PersistentOStream & os) const {
os << debug_ << subProcess_ << model_;
}
void HardProcessConstructor::persistentInput(PersistentIStream & is, int) {
is >> debug_ >> subProcess_ >> model_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeAbstractClass<HardProcessConstructor,Interfaced>
describeHerwigHardProcessConstructor("Herwig::HardProcessConstructor", "Herwig.so");
void HardProcessConstructor::Init() {
static ClassDocumentation<HardProcessConstructor> documentation
("Base class for implementation of the automatic generation of hard processes");
static Switch<HardProcessConstructor,bool> interfaceDebugME
("DebugME",
"Print comparison with analytical ME",
&HardProcessConstructor::debug_, false, false, false);
static SwitchOption interfaceDebugMEYes
(interfaceDebugME,
"Yes",
"Print the debug information",
true);
static SwitchOption interfaceDebugMENo
(interfaceDebugME,
"No",
"Do not print the debug information",
false);
}
void HardProcessConstructor::doinit() {
Interfaced::doinit();
EGPtr eg = generator();
model_ = dynamic_ptr_cast<HwSMPtr>(eg->standardModel());
if(!model_)
throw InitException() << "HardProcessConstructor:: doinit() - "
<< "The model pointer is null!"
<< Exception::abortnow;
if(!eg->eventHandler()) {
throw
InitException() << "HardProcessConstructor:: doinit() - "
<< "The eventHandler pointer was null therefore "
<< "could not get SubProcessHandler pointer "
<< Exception::abortnow;
}
string subProcessName =
eg->preinitInterface(eg->eventHandler(), "SubProcessHandlers", "get","");
subProcess_ = eg->getObject<SubProcessHandler>(subProcessName);
if(!subProcess_) {
ostringstream s;
s << "HardProcessConstructor:: doinit() - "
<< "There was an error getting the SubProcessHandler "
<< "from the current event handler. ";
generator()->logWarning( Exception(s.str(), Exception::warning) );
}
}
GeneralHardME::ColourStructure HardProcessConstructor::
colourFlow(const tcPDVector & extpart) const {
PDT::Colour ina = extpart[0]->iColour();
PDT::Colour inb = extpart[1]->iColour();
PDT::Colour outa = extpart[2]->iColour();
PDT::Colour outb = extpart[3]->iColour();
// incoming colour neutral
if(ina == PDT::Colour0 && inb == PDT::Colour0) {
if( outa == PDT::Colour0 && outb == PDT::Colour0 ) {
return GeneralHardME::Colour11to11;
}
else if( outa == PDT::Colour3 && outb == PDT::Colour3bar ) {
return GeneralHardME::Colour11to33bar;
}
+ else if( outa == PDT::DarkColourFundamental && outb == PDT::DarkColourAntiFundamental) {
+ return GeneralHardME::Colour11to33bar;
+ }
else if( outa == PDT::Colour8 && outb == PDT::Colour8 ) {
return GeneralHardME::Colour11to88;
}
else
assert(false);
}
// incoming 3 3
else if(ina == PDT::Colour3 && inb == PDT::Colour3 ) {
if( outa == PDT::Colour3 && outb == PDT::Colour3 ) {
return GeneralHardME::Colour33to33;
}
else if( outa == PDT::Colour6 && outb == PDT::Colour0 ) {
return GeneralHardME::Colour33to61;
}
else if( outa == PDT::Colour0 && outb == PDT::Colour6 ) {
return GeneralHardME::Colour33to16;
}
else if ( outa == PDT::Colour0 && outb == PDT::Colour3bar) {
return GeneralHardME::Colour33to13bar;
}
else if ( outb == PDT::Colour0 && outa == PDT::Colour3bar) {
return GeneralHardME::Colour33to3bar1;
}
else if ( outa == PDT::Colour8 && outb == PDT::Colour3bar) {
return GeneralHardME::Colour33to83bar;
}
else if ( outb == PDT::Colour8 && outa == PDT::Colour3bar) {
return GeneralHardME::Colour33to3bar8;
}
else
assert(false);
}
// incoming 3bar 3bar
else if(ina == PDT::Colour3bar && inb == PDT::Colour3bar ) {
if( outa == PDT::Colour3bar && outb == PDT::Colour3bar ) {
return GeneralHardME::Colour3bar3barto3bar3bar;
}
else if( outa == PDT::Colour6bar && outb == PDT::Colour0) {
return GeneralHardME::Colour3bar3barto6bar1;
}
else if ( outa == PDT::Colour0 && outb == PDT::Colour6bar ) {
return GeneralHardME::Colour3bar3barto16bar;
}
else if ( outa == PDT::Colour0 && outb == PDT::Colour3) {
return GeneralHardME::Colour3bar3barto13;
}
else if ( outb == PDT::Colour0 && outa == PDT::Colour3) {
return GeneralHardME::Colour3bar3barto31;
}
else if ( outa == PDT::Colour8 && outb == PDT::Colour3) {
return GeneralHardME::Colour3bar3barto83;
}
else if ( outb == PDT::Colour8 && outa == PDT::Colour3) {
return GeneralHardME::Colour3bar3barto38;
}
else
assert(false);
}
// incoming 3 3bar
else if(ina == PDT::Colour3 && inb == PDT::Colour3bar ) {
if( outa == PDT::Colour0 && outb == PDT::Colour0 ) {
return GeneralHardME::Colour33barto11;
}
else if( outa == PDT::Colour3 && outb == PDT::Colour3bar ) {
return GeneralHardME::Colour33barto33bar;
}
else if( outa == PDT::Colour8 && outb == PDT::Colour8 ) {
return GeneralHardME::Colour33barto88;
}
else if( outa == PDT::Colour8 && outb == PDT::Colour0 ) {
return GeneralHardME::Colour33barto81;
}
else if( outa == PDT::Colour0 && outb == PDT::Colour8 ) {
return GeneralHardME::Colour33barto18;
}
else if( outa == PDT::Colour6 && outb == PDT::Colour6bar) {
return GeneralHardME::Colour33barto66bar;
}
else if( outa == PDT::Colour6bar && outb == PDT::Colour6) {
return GeneralHardME::Colour33barto6bar6;
}
else
assert(false);
}
// incoming 88
else if(ina == PDT::Colour8 && inb == PDT::Colour8 ) {
if( outa == PDT::Colour0 && outb == PDT::Colour0 ) {
return GeneralHardME::Colour88to11;
}
else if( outa == PDT::Colour3 && outb == PDT::Colour3bar ) {
return GeneralHardME::Colour88to33bar;
}
else if( outa == PDT::Colour8 && outb == PDT::Colour8 ) {
return GeneralHardME::Colour88to88;
}
else if( outa == PDT::Colour8 && outb == PDT::Colour0 ) {
return GeneralHardME::Colour88to81;
}
else if( outa == PDT::Colour0 && outb == PDT::Colour8 ) {
return GeneralHardME::Colour88to18;
}
else if( outa == PDT::Colour6 && outb == PDT::Colour6bar ) {
return GeneralHardME::Colour88to66bar;
}
else
assert(false);
}
// incoming 38
else if(ina == PDT::Colour3 && inb == PDT::Colour8 ) {
if(outa == PDT::Colour3 && outb == PDT::Colour0) {
return GeneralHardME::Colour38to31;
}
else if(outa == PDT::Colour0 && outb == PDT::Colour3) {
return GeneralHardME::Colour38to13;
}
else if(outa == PDT::Colour3 && outb == PDT::Colour8) {
return GeneralHardME::Colour38to38;
}
else if(outa == PDT::Colour8 && outb == PDT::Colour3) {
return GeneralHardME::Colour38to83;
}
else if(outa == PDT::Colour3bar && outb == PDT::Colour6){
return GeneralHardME::Colour38to3bar6;
}
else if(outa == PDT::Colour6 && outb == PDT::Colour3bar) {
return GeneralHardME::Colour38to63bar;
}
else if(outa == PDT::Colour3bar && outb == PDT::Colour3bar) {
return GeneralHardME::Colour38to3bar3bar;
}
else
assert(false);
}
// incoming 3bar8
else if(ina == PDT::Colour3bar && inb == PDT::Colour8 ) {
if(outa == PDT::Colour3bar && outb == PDT::Colour0 ) {
return GeneralHardME::Colour3bar8to3bar1;
}
else if(outa == PDT::Colour0 && outb == PDT::Colour3bar) {
return GeneralHardME::Colour3bar8to13bar;
}
else if(outa == PDT::Colour3bar && outb == PDT::Colour8 ) {
return GeneralHardME::Colour3bar8to3bar8;
}
else if(outa == PDT::Colour8 && outb == PDT::Colour3bar) {
return GeneralHardME::Colour3bar8to83bar;
}
else if(outa == PDT::Colour3 && outb == PDT::Colour3) {
return GeneralHardME::Colour3bar8to33;
}
else
assert(false);
}
// unknown colour flow
else
assert(false);
return GeneralHardME::UNDEFINED;
}
void HardProcessConstructor::fixFSOrder(HPDiagram & diag) {
tcPDPtr psa = getParticleData(diag.incoming.first);
tcPDPtr psb = getParticleData(diag.incoming.second);
tcPDPtr psc = getParticleData(diag.outgoing.first);
tcPDPtr psd = getParticleData(diag.outgoing.second);
//fix a spin order
if( psc->iSpin() < psd->iSpin() ) {
swap(diag.outgoing.first, diag.outgoing.second);
if(diag.channelType == HPDiagram::tChannel) {
diag.ordered.second = !diag.ordered.second;
}
return;
}
if( psc->iSpin() == psd->iSpin() &&
psc->id() < 0 && psd->id() > 0 ) {
swap(diag.outgoing.first, diag.outgoing.second);
if(diag.channelType == HPDiagram::tChannel) {
diag.ordered.second = !diag.ordered.second;
}
return;
}
}
void HardProcessConstructor::assignToCF(HPDiagram & diag) {
if(diag.channelType == HPDiagram::tChannel) {
if(diag.ordered.second) tChannelCF(diag);
else uChannelCF(diag);
}
else if(diag.channelType == HPDiagram::sChannel) {
sChannelCF(diag);
}
else if (diag.channelType == HPDiagram::fourPoint) {
fourPointCF(diag);
}
else
assert(false);
}
void HardProcessConstructor::tChannelCF(HPDiagram & diag) {
tcPDPtr ia = getParticleData(diag.incoming.first );
tcPDPtr ib = getParticleData(diag.incoming.second);
tcPDPtr oa = getParticleData(diag.outgoing.first );
tcPDPtr ob = getParticleData(diag.outgoing.second);
PDT::Colour ina = ia->iColour();
PDT::Colour inb = ib->iColour();
PDT::Colour outa = oa->iColour();
PDT::Colour outb = ob->iColour();
vector<CFPair> cfv(1, make_pair(0, 1.));
if(diag.intermediate->iColour() == PDT::Colour0) {
if(ina==PDT::Colour0) {
cfv[0] = make_pair(0, 1);
}
else if(ina==PDT::Colour3 || ina==PDT::Colour3bar) {
if( inb == PDT::Colour0 ) {
cfv[0] = make_pair(0, 1);
}
else if(inb==PDT::Colour3 || outb==PDT::Colour3bar) {
cfv[0] = make_pair(2, 1);
}
else if(inb==PDT::Colour8) {
cfv[0] = make_pair(2, 1);
}
}
else if(ina==PDT::Colour8) {
if( inb == PDT::Colour0 ) {
cfv[0] = make_pair(0, 1);
}
else if(inb==PDT::Colour3 || outb==PDT::Colour3bar) {
cfv[0] = make_pair(2, 1);
}
else if(inb==PDT::Colour8) {
cfv[0] = make_pair(7, -1);
}
}
}
else if(diag.intermediate->iColour() == PDT::Colour8) {
if(ina==PDT::Colour8&&outa==PDT::Colour8&&
inb==PDT::Colour8&&outb==PDT::Colour8) {
cfv[0]=make_pair(2, 2.);
cfv.push_back(make_pair(3, -2.));
cfv.push_back(make_pair(1, -2.));
cfv.push_back(make_pair(4, 2.));
}
else if(ina==PDT::Colour8&&outa==PDT::Colour0&&
inb==PDT::Colour8&&outb==PDT::Colour8&&
(oa->iSpin()==PDT::Spin0||oa->iSpin()==PDT::Spin1Half||
oa->iSpin()==PDT::Spin3Half)) {
cfv[0] = make_pair(0,-1);
}
else if(ina==PDT::Colour8&&outa==PDT::Colour8&&
inb==PDT::Colour8&&outb==PDT::Colour0&&
(ob->iSpin()==PDT::Spin0||ob->iSpin()==PDT::Spin1Half||
ob->iSpin()==PDT::Spin3Half)) {
cfv[0] = make_pair(0,-1);
}
}
else if(diag.intermediate->iColour() == PDT::Colour3 ||
diag.intermediate->iColour() == PDT::Colour3bar) {
if(outa == PDT::Colour0 || outb == PDT::Colour0) {
if( outa == PDT::Colour6 || outb == PDT::Colour6 ||
outa == PDT::Colour6bar || outb == PDT::Colour6bar) {
cfv[0] = make_pair(0,0.5);
cfv.push_back(make_pair(1,0.5));
}
else if ((ina==PDT::Colour3 && inb == PDT::Colour3 &&
(outa == PDT::Colour3bar || outb == PDT::Colour3bar))||
(ina==PDT::Colour3bar && inb == PDT::Colour3bar &&
(outa == PDT::Colour3 || outb == PDT::Colour3 ))) {
cfv[0] = make_pair(0,1.);
}
else {
cfv[0] = make_pair(0,1.);
}
}
else if(outa==PDT::Colour6 && outb==PDT::Colour3bar) {
cfv[0] = make_pair(4,1.);
cfv.push_back(make_pair(5,1.));
}
else if(outa==PDT::Colour6 && outb==PDT::Colour6bar) {
cfv[0] = make_pair(4, 1.);
for(unsigned int ix=5;ix<8;++ix)
cfv.push_back(make_pair(ix,1.));
}
else if(outa==PDT::Colour6 || outa ==PDT::Colour6bar ||
outb==PDT::Colour6 || outb ==PDT::Colour6bar ) {
assert(false);
}
else if(ina==PDT::Colour3 && inb==PDT::Colour3 ) {
if((outa==PDT::Colour0 && outb==PDT::Colour3bar)||
(outb==PDT::Colour0 && outa==PDT::Colour3bar))
cfv[0] = make_pair(0,1.);
else if((outa==PDT::Colour8 && outb==PDT::Colour3bar)||
(outb==PDT::Colour8 && outa==PDT::Colour3bar)) {
cfv[0] = make_pair(1,1.);
}
}
else if(ina==PDT::Colour3bar && inb==PDT::Colour3bar ) {
if((outa==PDT::Colour0 && outb==PDT::Colour3)||
(outb==PDT::Colour0 && outa==PDT::Colour3))
cfv[0] = make_pair(0,1.);
else if((outa==PDT::Colour8 && outb==PDT::Colour3)||
(outb==PDT::Colour8 && outa==PDT::Colour3)) {
double sign = diag.intermediate->iSpin()==PDT::Spin0 ? -1. : 1.;
cfv[0] = make_pair(1,sign);
}
}
else if((ina==PDT::Colour3 && inb==PDT::Colour8) ||
(ina==PDT::Colour3bar && inb==PDT::Colour8) ||
(inb==PDT::Colour3 && ina==PDT::Colour8) ||
(inb==PDT::Colour3bar && ina==PDT::Colour8) ) {
if((outa==PDT::Colour3 && outb==PDT::Colour3 ) ||
(outa==PDT::Colour3bar && outb==PDT::Colour3bar)) {
cfv[0] = make_pair(1,1.);
}
}
}
else if(diag.intermediate->iColour() == PDT::Colour6 ||
diag.intermediate->iColour() == PDT::Colour6bar) {
if(ina==PDT::Colour8 && inb==PDT::Colour8) {
cfv[0] = make_pair(0, 1.);
for(unsigned int ix=1;ix<4;++ix)
cfv.push_back(make_pair(ix,1.));
for(unsigned int ix=4;ix<8;++ix)
cfv.push_back(make_pair(ix,1.));
}
else if(outa==PDT::Colour3bar && outb==PDT::Colour6) {
cfv[0] = make_pair(0,1.);
for(unsigned int ix=1;ix<4;++ix)
cfv.push_back(make_pair(ix,1.));
}
else if(outa==PDT::Colour6 && outb==PDT::Colour3bar) {
cfv[0] = make_pair(4,1.);
cfv.push_back(make_pair(5,1.));
}
}
diag.colourFlow = cfv;
}
void HardProcessConstructor::uChannelCF(HPDiagram & diag) {
tcPDPtr ia = getParticleData(diag.incoming.first );
tcPDPtr ib = getParticleData(diag.incoming.second);
tcPDPtr oa = getParticleData(diag.outgoing.first );
tcPDPtr ob = getParticleData(diag.outgoing.second);
PDT::Colour ina = ia->iColour();
PDT::Colour inb = ib->iColour();
PDT::Colour outa = oa->iColour();
PDT::Colour outb = ob->iColour();
PDT::Colour offshell = diag.intermediate->iColour();
vector<CFPair> cfv(1, make_pair(1, 1.));
if(offshell == PDT::Colour8) {
if(outa == PDT::Colour0 &&
outb == PDT::Colour0) {
cfv[0].first = 0;
}
else if( outa != outb ) {
if(outa == PDT::Colour0 ||
outb == PDT::Colour0) {
cfv[0].first = 0;
}
else if(ina == PDT::Colour3 && inb == PDT::Colour8 &&
outb == PDT::Colour3 && outa == PDT::Colour8) {
tPDPtr off = diag.intermediate;
if(off->CC()) off=off->CC();
if(off->iSpin()!=PDT::Spin1Half ||
diag.vertices.second->allowed(off->id(),diag.outgoing.first,diag.incoming.second)) {
cfv[0].first = 0;
cfv.push_back(make_pair(1, -1.));
}
else {
cfv[0].first = 1;
cfv.push_back(make_pair(0, -1.));
}
}
else if(ina == PDT::Colour3bar && inb == PDT::Colour8 &&
outb == PDT::Colour3bar && outa == PDT::Colour8) {
tPDPtr off = diag.intermediate;
if(off->CC()) off=off->CC();
if(off->iSpin()!=PDT::Spin1Half ||
diag.vertices.second->allowed(diag.outgoing.first,off->id(),diag.incoming.second)) {
cfv[0].first = 0;
cfv.push_back(make_pair(1, -1.));
}
else {
cfv[0].first = 1;
cfv.push_back(make_pair(0, -1.));
}
}
else {
cfv[0].first = 0;
cfv.push_back(make_pair(1, -1.));
}
}
else if(outa==PDT::Colour8&&ina==PDT::Colour8) {
cfv[0]=make_pair(4, 2.);
cfv.push_back(make_pair(5, -2.));
cfv.push_back(make_pair(0, -2.));
cfv.push_back(make_pair(2, 2.));
}
}
else if(offshell == PDT::Colour3 || offshell == PDT::Colour3bar) {
if( outa == PDT::Colour0 || outb == PDT::Colour0 ) {
if( outa == PDT::Colour6 || outb == PDT::Colour6 ||
outa == PDT::Colour6bar || outb == PDT::Colour6bar) {
cfv[0] = make_pair(0,0.5);
cfv.push_back(make_pair(1,0.5));
}
else if ((ina==PDT::Colour3 && inb == PDT::Colour3 &&
(outa == PDT::Colour3bar || outb == PDT::Colour3bar))||
(ina==PDT::Colour3bar && inb == PDT::Colour3bar &&
(outa == PDT::Colour3 || outb == PDT::Colour3 ))) {
double sign = diag.intermediate->iSpin()==PDT::Spin0 ? -1. : 1.;
cfv[0] = make_pair(0,sign);
}
else {
cfv[0] = make_pair(0,1.);
}
}
else if(outa==PDT::Colour3bar && outb==PDT::Colour6) {
cfv[0] = make_pair(4,1.);
cfv.push_back(make_pair(5,1.));
}
else if(outa==PDT::Colour6 && outb==PDT::Colour3bar) {
cfv[0] = make_pair(0,1.);
for(int ix=0; ix<4;++ix)
cfv.push_back(make_pair(ix,1.));
}
else if(outa==PDT::Colour6bar && outb==PDT::Colour6) {
cfv[0] = make_pair(4,1.);
for(int ix=5; ix<8;++ix)
cfv.push_back(make_pair(ix,1.));
}
else if(ina==PDT::Colour0 && inb==PDT::Colour0) {
cfv[0] = make_pair(0,1.);
}
else if(ina==PDT::Colour3 && inb==PDT::Colour3 ) {
if((outa==PDT::Colour0 && outb==PDT::Colour3bar)||
(outb==PDT::Colour0 && outa==PDT::Colour3bar))
cfv[0] = make_pair(0,1.);
else if((outa==PDT::Colour8 && outb==PDT::Colour3bar)||
(outb==PDT::Colour8 && outa==PDT::Colour3bar)) {
double sign = diag.intermediate->iSpin()==PDT::Spin0 ? -1. : 1.;
cfv[0] = make_pair(2,sign);
}
}
else if(ina==PDT::Colour3bar && inb==PDT::Colour3bar ) {
if((outa==PDT::Colour0 && outb==PDT::Colour3)||
(outb==PDT::Colour0 && outa==PDT::Colour3))
cfv[0] = make_pair(0,1.);
else if((outa==PDT::Colour8 && outb==PDT::Colour3)||
(outb==PDT::Colour8 && outa==PDT::Colour3)) {
cfv[0] = make_pair(2,1.);
}
}
else if(((ina==PDT::Colour3 && inb==PDT::Colour8) ||
(ina==PDT::Colour3bar && inb==PDT::Colour8) ||
(inb==PDT::Colour3 && ina==PDT::Colour8) ||
(inb==PDT::Colour3bar && ina==PDT::Colour8)) &&
((outa==PDT::Colour3 && outb==PDT::Colour3 ) ||
(outa==PDT::Colour3bar && outb==PDT::Colour3bar))) {
cfv[0] = make_pair(2, 1.);
}
else if(( ina==PDT::Colour3 && inb==PDT::Colour3bar &&
outa==PDT::Colour3 && outb==PDT::Colour3bar)) {
cfv[0] = make_pair(2, 1.);
cfv.push_back(make_pair(3,-1.));
}
}
else if( offshell == PDT::Colour0 ) {
if(ina==PDT::Colour0) {
cfv[0] = make_pair(0, 1);
}
else if(ina==PDT::Colour3 || ina==PDT::Colour3bar) {
if( inb == PDT::Colour0 ) {
cfv[0] = make_pair(0, 1);
}
else if(inb==PDT::Colour3 || inb==PDT::Colour3bar) {
cfv[0] = make_pair(3, 1);
}
else if(inb==PDT::Colour8) {
cfv[0] = make_pair(2, 1);
}
}
else if(ina==PDT::Colour8) {
if( inb == PDT::Colour0 ) {
cfv[0] = make_pair(0, 1);
}
else if(inb==PDT::Colour3 || outb==PDT::Colour3bar) {
cfv[0] = make_pair(2, 1);
}
else if(inb==PDT::Colour8) {
cfv[0] = make_pair(8, -1);
}
}
}
else if(diag.intermediate->iColour() == PDT::Colour6 ||
diag.intermediate->iColour() == PDT::Colour6bar) {
if(ina==PDT::Colour8 && inb==PDT::Colour8) {
cfv[0] = make_pair(0, 1.);
for(unsigned int ix=1;ix<4;++ix)
cfv.push_back(make_pair(ix,1.));
for(unsigned int ix=8;ix<12;++ix)
cfv.push_back(make_pair(ix,1.));
}
else if(outa==PDT::Colour3bar && outb==PDT::Colour6) {
cfv[0] = make_pair(4, 1.);
cfv.push_back(make_pair(5,1.));
}
else if(outa==PDT::Colour6 && outb==PDT::Colour3bar) {
cfv[0] = make_pair(0, 1.);
for(unsigned int ix=1;ix<4;++ix)
cfv.push_back(make_pair(ix,1.));
}
}
diag.colourFlow = cfv;
}
void HardProcessConstructor::sChannelCF(HPDiagram & diag) {
tcPDPtr pa = getParticleData(diag.incoming.first);
tcPDPtr pb = getParticleData(diag.incoming.second);
PDT::Colour ina = pa->iColour();
PDT::Colour inb = pb->iColour();
PDT::Colour offshell = diag.intermediate->iColour();
tcPDPtr pc = getParticleData(diag.outgoing.first);
tcPDPtr pd = getParticleData(diag.outgoing.second);
PDT::Colour outa = pc->iColour();
PDT::Colour outb = pd->iColour();
vector<CFPair> cfv(1);
if(offshell == PDT::Colour8) {
if(ina == PDT::Colour0 || inb == PDT::Colour0 ||
outa == PDT::Colour0 || outb == PDT::Colour0) {
cfv[0] = make_pair(0, 1);
}
else {
bool incol = ina == PDT::Colour8 && inb == PDT::Colour8;
bool outcol = outa == PDT::Colour8 && outb == PDT::Colour8;
bool intrip = ina == PDT::Colour3 && inb == PDT::Colour3bar;
bool outtrip = outa == PDT::Colour3 && outb == PDT::Colour3bar;
bool outsex = outa == PDT::Colour6 && outb == PDT::Colour6bar;
bool outsexb = outa == PDT::Colour6bar && outb == PDT::Colour6;
if(incol || outcol) {
// Require an additional minus sign for a scalar/fermion
// 33bar final state due to the way the vertex rules are defined.
int prefact(1);
if( ((pc->iSpin() == PDT::Spin1Half && pd->iSpin() == PDT::Spin1Half) ||
(pc->iSpin() == PDT::Spin0 && pd->iSpin() == PDT::Spin0 ) ||
(pc->iSpin() == PDT::Spin1 && pd->iSpin() == PDT::Spin1 )) &&
(outa == PDT::Colour3 && outb == PDT::Colour3bar) )
prefact = -1;
if(incol && outcol) {
cfv[0] = make_pair(0, -2.);
cfv.push_back(make_pair(1, 2.));
cfv.push_back(make_pair(3, 2.));
cfv.push_back(make_pair(5, -2.));
}
else if(incol && outsex) {
cfv[0].first = 4;
cfv[0].second = prefact;
for(unsigned int ix=1;ix<4;++ix)
cfv.push_back(make_pair(4+ix, prefact));
for(unsigned int ix=0;ix<4;++ix)
cfv.push_back(make_pair(8+ix,-prefact));
}
else {
cfv[0].first = 0;
cfv[0].second = -prefact;
cfv.push_back(make_pair(1, prefact));
}
}
else if( ( intrip && !outtrip ) ||
( !intrip && outtrip ) ) {
if(!outsex)
cfv[0] = make_pair(0, 1);
else {
cfv[0] = make_pair(0, 1.);
for(unsigned int ix=0;ix<3;++ix)
cfv.push_back(make_pair(ix+1, 1.));
}
}
else if((intrip && outsex) || (intrip && outsexb)) {
cfv[0] = make_pair(0,1.);
for(int ix=1; ix<4; ++ix)
cfv.push_back(make_pair(ix,1.));
}
else
cfv[0] = make_pair(1, 1);
}
}
else if(offshell == PDT::Colour0) {
if( ina == PDT::Colour0 ) {
cfv[0] = make_pair(0, 1);
}
else if(ina==PDT::Colour3 || ina==PDT::Colour3bar) {
if( outa == PDT::Colour0 ) {
cfv[0] = make_pair(0, 1);
}
else if(outa==PDT::Colour3 || outa==PDT::Colour3bar) {
cfv[0] = make_pair(3, 1);
}
else if(outa==PDT::Colour8) {
cfv[0] = make_pair(2, 1);
}
else if(outa==PDT::Colour6 || outa==PDT::Colour6bar) {
cfv[0] = make_pair(8, 1.);
cfv.push_back(make_pair(9,1.));
}
else
assert(false);
}
else if(ina==PDT::Colour8) {
if( outa == PDT::Colour0 ) {
cfv[0] = make_pair(0, 1);
}
else if(outa==PDT::Colour3 || outb==PDT::Colour3bar) {
cfv[0] = make_pair(2, 1);
}
else if(outa==PDT::Colour8) {
cfv[0] = make_pair(6, 1);
}
}
}
else if(offshell == PDT::Colour3 || offshell == PDT::Colour3bar) {
if(outa == PDT::Colour6 || outa == PDT::Colour6bar ||
outb == PDT::Colour6bar || outb == PDT::Colour6) {
cfv[0] = make_pair(6, 1.);
cfv.push_back(make_pair(7,1.));
}
else if((ina == PDT::Colour3 && inb == PDT::Colour3) ||
(ina == PDT::Colour3bar && inb == PDT::Colour3bar)) {
if((outa == PDT::Colour3 && outb == PDT::Colour3 ) ||
(outa == PDT::Colour3bar && outb == PDT::Colour3bar)) {
cfv[0] = make_pair(2, 1.);
cfv.push_back(make_pair(3,-1.));
}
else
cfv[0] = make_pair(0,1.);
}
else if(((ina==PDT::Colour3 && inb==PDT::Colour8) ||
(ina==PDT::Colour3bar && inb==PDT::Colour8) ||
(inb==PDT::Colour3 && ina==PDT::Colour8) ||
(inb==PDT::Colour3bar && ina==PDT::Colour8) ) &&
((outa==PDT::Colour3 && outb==PDT::Colour3 ) ||
(outa==PDT::Colour3bar && outb==PDT::Colour3bar))) {
cfv[0] = make_pair(0,1.);
}
else {
if(outa == PDT::Colour0 || outb == PDT::Colour0)
cfv[0] = make_pair(0, 1);
else
cfv[0] = make_pair(1, 1);
}
}
else if( offshell == PDT::Colour6 || offshell == PDT::Colour6bar) {
if((ina == PDT::Colour3 && inb == PDT::Colour3 &&
outa == PDT::Colour3 && outb == PDT::Colour3 ) ||
(ina == PDT::Colour3bar && inb == PDT::Colour3bar &&
outa == PDT::Colour3bar && outb == PDT::Colour3bar)) {
cfv[0] = make_pair(2,0.5);
cfv.push_back(make_pair(3,0.5));
}
else if((ina == PDT::Colour3 && inb == PDT::Colour3 &&
((outa == PDT::Colour6 && outb == PDT::Colour0)||
(outb == PDT::Colour6 && outa == PDT::Colour0))) ||
(ina == PDT::Colour3bar && inb == PDT::Colour3bar &&
((outa == PDT::Colour6bar && outb == PDT::Colour0)||
(outb == PDT::Colour6bar && outa == PDT::Colour0)))) {
cfv[0] = make_pair(0,0.5);
cfv.push_back(make_pair(1,0.5));
}
else
assert(false);
}
else {
if(outa == PDT::Colour0 || outb == PDT::Colour0)
cfv[0] = make_pair(0, 1);
else
cfv[0] = make_pair(1, 1);
}
diag.colourFlow = cfv;
}
void HardProcessConstructor::fourPointCF(HPDiagram & diag) {
using namespace ThePEG::Helicity;
// count the colours
unsigned int noct(0),ntri(0),nsng(0),nsex(0),nf(0);
vector<tcPDPtr> particles;
for(unsigned int ix=0;ix<4;++ix) {
particles.push_back(getParticleData(diag.ids[ix]));
PDT::Colour col = particles.back()->iColour();
if(col==PDT::Colour0) ++nsng;
else if(col==PDT::Colour3||col==PDT::Colour3bar) ++ntri;
else if(col==PDT::Colour8) ++noct;
else if(col==PDT::Colour6||col==PDT::Colour6bar) ++nsex;
if(particles.back()->iSpin()==2) nf+=1;
}
if(nsng==4 || (ntri==2&&nsng==2) ||
(noct==3 && nsng==1) ||
(ntri==2 && noct==1 && nsng==1) ||
(noct == 2 && nsng == 2) ) {
vector<CFPair> cfv(1,make_pair(0,1));
diag.colourFlow = cfv;
}
else if(noct==4) {
// flows for SSVV, VVVV is handled in me class
vector<CFPair> cfv(6);
cfv[0] = make_pair(0, -2.);
cfv[1] = make_pair(1, -2.);
cfv[2] = make_pair(2, +4.);
cfv[3] = make_pair(3, -2.);
cfv[4] = make_pair(4, +4.);
cfv[5] = make_pair(5, -2.);
diag.colourFlow = cfv;
}
else if(ntri==2&&noct==2) {
vector<CFPair> cfv(2);
cfv[0] = make_pair(0, 1);
cfv[1] = make_pair(1, 1);
if(nf==2) cfv[1].second = -1.;
diag.colourFlow = cfv;
}
else if(nsex==2&&noct==2) {
vector<CFPair> cfv;
for(unsigned int ix=0;ix<4;++ix)
cfv.push_back(make_pair(ix ,2.));
for(unsigned int ix=0;ix<8;++ix)
cfv.push_back(make_pair(4+ix,1.));
diag.colourFlow = cfv;
}
else if(ntri==4) {
// get the order from the vertex
vector<long> temp;
for(unsigned int ix=0;ix<4;++ix) {
temp = diag.vertices.first->search(ix,diag.outgoing.first);
if(!temp.empty()) break;
}
// compute the mapping
vector<long> ids;
ids.push_back( particles[0]->CC() ? -diag.incoming.first : diag.incoming.first );
ids.push_back( particles[1]->CC() ? -diag.incoming.second : diag.incoming.second);
ids.push_back( diag.outgoing.first );
ids.push_back( diag.outgoing.second);
vector<unsigned int> order = {0,1,2,3};
vector<bool> matched(4,false);
for(unsigned int ix=0;ix<temp.size();++ix) {
for(unsigned int iy=0;iy<ids.size();++iy) {
if(matched[iy]) continue;
if(temp[ix]==ids[iy]) {
matched[iy] = true;
order[ix]=iy;
break;
}
}
}
// 3 3 -> 3 3
if((particles[0]->iColour()==PDT::Colour3 &&
particles[1]->iColour()==PDT::Colour3) ||
(particles[0]->iColour()==PDT::Colour3bar &&
particles[1]->iColour()==PDT::Colour3bar) ) {
if(diag.vertices.first->colourStructure()==ColourStructure::SU3I12I34) {
if( (order[0]==0 && order[1]==2) || (order[2]==0 && order[3]==2) ||
(order[0]==2 && order[1]==0) || (order[2]==2 && order[3]==0))
diag.colourFlow = vector<CFPair>(1,make_pair(2,1.));
else
diag.colourFlow = vector<CFPair>(1,make_pair(3,1.));
}
else if(diag.vertices.first->colourStructure()==ColourStructure::SU3I14I23) {
if( (order[0]==0 && order[3]==2) || (order[1]==0 && order[2]==2) ||
(order[0]==2 && order[3]==0) || (order[1]==2 && order[2]==0))
diag.colourFlow = vector<CFPair>(1,make_pair(2,1.));
else
diag.colourFlow = vector<CFPair>(1,make_pair(3,1.));
}
else if(diag.vertices.first->colourStructure()==ColourStructure::SU3T21T43) {
if( (order[1]==0 && order[0]==2) || (order[3]==0 && order[2]==2) ||
(order[1]==2 && order[0]==0) || (order[3]==2 && order[2]==0))
diag.colourFlow = vector<CFPair>(1,make_pair(0,1.));
else
diag.colourFlow = vector<CFPair>(1,make_pair(1,1.));
}
else if(diag.vertices.first->colourStructure()==ColourStructure::SU3T23T41) {
if( (order[1]==0 && order[2]==2) || (order[3]==0 && order[0]==2) ||
(order[1]==2 && order[2]==0) || (order[3]==2 && order[0]==0))
diag.colourFlow = vector<CFPair>(1,make_pair(0,1.));
else
diag.colourFlow = vector<CFPair>(1,make_pair(1,1.));
}
else
assert(false);
}
else if((particles[0]->iColour()==PDT::Colour3 &&
particles[1]->iColour()==PDT::Colour3bar) ||
(particles[0]->iColour()==PDT::Colour3bar &&
particles[1]->iColour()==PDT::Colour3)) {
if(diag.vertices.first->colourStructure()==ColourStructure::SU3I12I34) {
if( (order[0]==0 && order[1]==1) || (order[2]==0 && order[3]==0) ||
(order[0]==1 && order[1]==0) || (order[2]==1 && order[3]==1))
diag.colourFlow = vector<CFPair>(1,make_pair(3,1.));
else
diag.colourFlow = vector<CFPair>(1,make_pair(2,1.));
}
else if(diag.vertices.first->colourStructure()==ColourStructure::SU3I14I23) {
if( (order[0]==0 && order[3]==1) || (order[0]==2 && order[3]==3) ||
(order[0]==1 && order[3]==0) || (order[0]==3 && order[3]==2))
diag.colourFlow = vector<CFPair>(1,make_pair(3,1.));
else
diag.colourFlow = vector<CFPair>(1,make_pair(2,1.));
}
else if(diag.vertices.first->colourStructure()==ColourStructure::SU3T21T43) {
if( (order[1]==0 && order[0]==1) || (order[3]==0 && order[2]==1) ||
(order[1]==1 && order[0]==0) || (order[3]==1 && order[2]==0))
diag.colourFlow = vector<CFPair>(1,make_pair(1,1.));
else
diag.colourFlow = vector<CFPair>(1,make_pair(0,1.));
}
else if(diag.vertices.first->colourStructure()==ColourStructure::SU3T23T41) {
if( (order[1]==0 && order[2]==1) || (order[1]==1 && order[2]==0) ||
(order[1]==3 && order[2]==2) || (order[1]==2 && order[2]==3))
diag.colourFlow = vector<CFPair>(1,make_pair(1,1.));
else
diag.colourFlow = vector<CFPair>(1,make_pair(2,1.));
}
else
assert(false);
}
else {
assert(false);
}
}
else {
assert(false);
}
}
namespace {
// Helper functor for find_if in duplicate function.
class SameDiagramAs {
public:
SameDiagramAs(const HPDiagram & diag) : a(diag) {}
bool operator()(const HPDiagram & b) const {
return a == b;
}
private:
HPDiagram a;
};
}
bool HardProcessConstructor::duplicate(const HPDiagram & diag,
const HPDVector & group) const {
//find if a duplicate diagram exists
HPDVector::const_iterator it =
find_if(group.begin(), group.end(), SameDiagramAs(diag));
return it != group.end();
}
bool HardProcessConstructor::checkOrder(const HPDiagram & diag) const {
for(map<string,pair<unsigned int,int> >::const_iterator it=model_->couplings().begin();
it!=model_->couplings().end();++it) {
int order=0;
if(diag.vertices.first ) order += diag.vertices.first ->orderInCoupling(it->second.first);
if(diag.vertices.second&&diag.vertices.first->getNpoint()==3)
order += diag.vertices.second->orderInCoupling(it->second.first);
if(order>it->second.second) return false;
}
return true;
}
diff --git a/Shower/QTilde/Dark/HiddenValleyAlpha.cc b/Shower/QTilde/Dark/HiddenValleyAlpha.cc
--- a/Shower/QTilde/Dark/HiddenValleyAlpha.cc
+++ b/Shower/QTilde/Dark/HiddenValleyAlpha.cc
@@ -1,370 +1,333 @@
// -*- C++ -*-
//
// HiddenValleyAlpha.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2007 The Herwig Collaboration
//
// Herwig is licenced under version 2 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 HiddenValleyAlpha class.
//
#include "HiddenValleyAlpha.h"
#include "HiddenValleyModel.h"
#include "ThePEG/PDT/EnumParticles.h"
#include "ThePEG/PDT/ParticleData.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Interface/Parameter.h"
#include "ThePEG/Interface/Command.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/Utilities/Throw.h"
using namespace Herwig;
IBPtr HiddenValleyAlpha::clone() const {
return new_ptr(*this);
}
IBPtr HiddenValleyAlpha::fullclone() const {
return new_ptr(*this);
}
void HiddenValleyAlpha::persistentOutput(PersistentOStream & os) const {
os << _asType << _asMaxNP << ounit(_qmin,GeV) << _nloop << _thresopt
- << ounit(_lambdain,GeV) << _alphain
- << _tolerance << _maxtry << _alphamin
+ << ounit(_lambdain,GeV) << _tolerance << _maxtry << _alphamin
<< ounit(_thresholds,GeV) << ounit(_lambda,GeV) << _ca << _cf << _tr;
}
void HiddenValleyAlpha::persistentInput(PersistentIStream & is, int) {
is >> _asType >> _asMaxNP >> iunit(_qmin,GeV) >> _nloop >> _thresopt
- >> iunit(_lambdain,GeV) >> _alphain
- >> _tolerance >> _maxtry >> _alphamin
+ >> iunit(_lambdain,GeV) >> _tolerance >> _maxtry >> _alphamin
>> iunit(_thresholds,GeV) >> iunit(_lambda,GeV) >> _ca >> _cf >> _tr;
}
ClassDescription<HiddenValleyAlpha> HiddenValleyAlpha::initHiddenValleyAlpha;
// Definition of the static class description member.
void HiddenValleyAlpha::Init() {
static ClassDocumentation<HiddenValleyAlpha> documentation
("This (concrete) class describes the QCD alpha running.");
static Switch<HiddenValleyAlpha, int> intAsType
("NPAlphaS",
"Behaviour of AlphaS in the NP region",
&HiddenValleyAlpha::_asType, 1, false, false);
static SwitchOption intAsTypeZero
(intAsType, "Zero","zero below Q_min", 1);
static SwitchOption intAsTypeConst
(intAsType, "Const","const as(qmin) below Q_min", 2);
static SwitchOption intAsTypeLin
(intAsType, "Linear","growing linearly below Q_min", 3);
static SwitchOption intAsTypeQuad
(intAsType, "Quadratic","growing quadratically below Q_min", 4);
static SwitchOption intAsTypeExx1
(intAsType, "Exx1", "quadratic from AlphaMaxNP down to as(Q_min)", 5);
static SwitchOption intAsTypeExx2
(intAsType, "Exx2", "const = AlphaMaxNP below Q_min", 6);
// default such that as(qmin) = 1 in the current parametrization.
// min = Lambda3
static Parameter<HiddenValleyAlpha,Energy> intQmin
("Qmin", "Q < Qmin is treated with NP parametrization as of (unit [GeV])",
&HiddenValleyAlpha::_qmin, GeV, 0.630882*GeV, 0.330445*GeV,
100.0*GeV,false,false,false);
static Parameter<HiddenValleyAlpha,double> interfaceAlphaMaxNP
("AlphaMaxNP",
"Max value of alpha in NP region, only relevant if NPAlphaS = 5,6",
&HiddenValleyAlpha::_asMaxNP, 1.0, 0., 100.0,
false, false, Interface::limited);
static Parameter<HiddenValleyAlpha,unsigned int> interfaceNumberOfLoops
("NumberOfLoops",
"The number of loops to use in the alpha_S calculation",
&HiddenValleyAlpha::_nloop, 2, 1, 2,
false, false, Interface::limited);
static Switch<HiddenValleyAlpha,bool> interfaceLambdaOption
("LambdaOption",
"Option for the calculation of the Lambda used in the simulation from the input"
" Lambda_MSbar",
&HiddenValleyAlpha::_lambdaopt, false, false, false);
static SwitchOption interfaceLambdaOptionfalse
(interfaceLambdaOption,
"Same",
"Use the same value",
false);
static SwitchOption interfaceLambdaOptionConvert
(interfaceLambdaOption,
"Convert",
"Use the conversion to the Herwig scheme from NPB349, 635",
true);
- static Parameter<HiddenValleyAlpha,Energy> interfaceLambdaQCD
- ("LambdaQCD",
+ static Parameter<HiddenValleyAlpha,Energy> interfaceLambdaDark
+ ("LambdaDark",
"Input value of Lambda_MSBar",
&HiddenValleyAlpha::_lambdain, GeV, 0.208364*GeV, 100.0*MeV, 100.0*GeV,
false, false, Interface::limited);
- static Parameter<HiddenValleyAlpha,double> interfaceAlphaMZ
- ("AlphaMZ",
- "The input value of the strong coupling at the Z mass ",
- &HiddenValleyAlpha::_alphain, 0.118, 0.1, 0.2,
- false, false, Interface::limited);
-
- static Switch<HiddenValleyAlpha,bool> interfaceInputOption
- ("InputOption",
- "Option for inputing the initial value of the coupling",
- &HiddenValleyAlpha::_inopt, true, false, false);
- static SwitchOption interfaceInputOptionAlphaMZ
- (interfaceInputOption,
- "AlphaMZ",
- "Use the value of alpha at MZ to calculate the coupling",
- true);
- static SwitchOption interfaceInputOptionLambdaQCD
- (interfaceInputOption,
- "LambdaQCD",
- "Use the input value of Lambda to calculate the coupling",
- false);
-
static Parameter<HiddenValleyAlpha,double> interfaceTolerance
("Tolerance",
"The tolerance for discontinuities in alphaS at thresholds.",
&HiddenValleyAlpha::_tolerance, 1e-10, 1e-20, 1e-4,
false, false, Interface::limited);
static Parameter<HiddenValleyAlpha,unsigned int> interfaceMaximumIterations
("MaximumIterations",
"The maximum number of iterations for the Newton-Raphson method to converge.",
&HiddenValleyAlpha::_maxtry, 100, 10, 1000,
false, false, Interface::limited);
static Switch<HiddenValleyAlpha,bool> interfaceThresholdOption
("ThresholdOption",
"Whether to use the consistuent or normal masses for the thresholds",
&HiddenValleyAlpha::_thresopt, true, false, false);
static SwitchOption interfaceThresholdOptionCurrent
(interfaceThresholdOption,
"Current",
"Use the current masses",
true);
static SwitchOption interfaceThresholdOptionConstituent
(interfaceThresholdOption,
"Constituent",
"Use the constitent masses.",
false);
}
double HiddenValleyAlpha::value(const Energy2 scale) const {
pair<short,Energy> nflam;
Energy q = sqrt(scale);
double val(0.);
// special handling if the scale is less than Qmin
if (q < _qmin) {
nflam = getLamNfTwoLoop(_qmin);
double val0 = alphaS(_qmin, nflam.second, nflam.first);
switch (_asType) {
case 1:
// flat, zero; the default type with no NP effects.
val = 0.;
break;
case 2:
// flat, non-zero alpha_s = alpha_s(q2min).
val = val0;
break;
case 3:
// linear in q
val = val0*q/_qmin;
break;
case 4:
// quadratic in q
val = val0*sqr(q/_qmin);
break;
case 5:
// quadratic in q, starting off at asMaxNP, ending on as(qmin)
val = (val0 - _asMaxNP)*sqr(q/_qmin) + _asMaxNP;
break;
case 6:
// just asMaxNP and constant
val = _asMaxNP;
break;
}
}
else {
// the 'ordinary' case
nflam = getLamNfTwoLoop(q);
val = alphaS(q, nflam.second, nflam.first);
}
return scaleFactor() * val;
}
double HiddenValleyAlpha::overestimateValue() const {
return scaleFactor() * _alphamin;
}
double HiddenValleyAlpha::ratio(const Energy2 scale,double) const {
pair<short,Energy> nflam;
Energy q = sqrt(scale);
double val(0.);
// special handling if the scale is less than Qmin
if (q < _qmin) {
nflam = getLamNfTwoLoop(_qmin);
double val0 = alphaS(_qmin, nflam.second, nflam.first);
switch (_asType) {
case 1:
// flat, zero; the default type with no NP effects.
val = 0.;
break;
case 2:
// flat, non-zero alpha_s = alpha_s(q2min).
val = val0;
break;
case 3:
// linear in q
val = val0*q/_qmin;
break;
case 4:
// quadratic in q
val = val0*sqr(q/_qmin);
break;
case 5:
// quadratic in q, starting off at asMaxNP, ending on as(qmin)
val = (val0 - _asMaxNP)*sqr(q/_qmin) + _asMaxNP;
break;
case 6:
// just asMaxNP and constant
val = _asMaxNP;
break;
}
} else {
// the 'ordinary' case
nflam = getLamNfTwoLoop(q);
val = alphaS(q, nflam.second, nflam.first);
}
// denominator
return val/_alphamin;
}
Energy HiddenValleyAlpha::computeLambda(Energy match,
double alpha,
unsigned int nflav) const {
Energy lamtest=200.0*MeV;
double xtest;
unsigned int ntry=0;
do {
++ntry;
xtest=log(sqr(match/lamtest));
xtest+= (alpha-alphaS(match,lamtest,nflav))/derivativealphaS(match,lamtest,nflav);
lamtest=match/exp(0.5*xtest);
}
while(abs(alpha-alphaS(match,lamtest,nflav)) > _tolerance && ntry < _maxtry);
return lamtest;
}
pair<short, Energy> HiddenValleyAlpha::getLamNfTwoLoop(Energy q) const {
unsigned int ix=1;
for(;ix<_thresholds.size();ix++) {
if(q<_thresholds[ix]) break;
}
--ix;
return pair<short,Energy>(ix+_nf_light, _lambda[ix]);
}
void HiddenValleyAlpha::doinit() {
ShowerAlpha::doinit();
// get the model for parameters
tcHiddenValleyPtr model = dynamic_ptr_cast<tcHiddenValleyPtr>
(generator()->standardModel());
if(!model) throw InitException() << "Must be using the HiddenValleyModel"
<< " in HiddenValleyAlpha::doinit()"
<< Exception::runerror;
// get the colour factors
_ca = model->CA();
_cf = model->CF();
_tr = model->TR();
// get the thresholds
_thresholds.push_back(_qmin);
_nf_light = 0;
for(unsigned int ix=1;ix<=model->NF();++ix) {
Energy qmass = getParticleData(ParticleID::darkg+long(ix))->mass();
if (qmass > _qmin) _thresholds.push_back(qmass);
else _nf_light++;
}
_lambda.resize(_thresholds.size());
- Energy mz = getParticleData(ThePEG::ParticleID::Z0)->mass();
- unsigned int nf_heavy;
- for(nf_heavy=0;nf_heavy<_thresholds.size();++nf_heavy) {
- if(mz<_thresholds[nf_heavy]) break;
- }
- nf_heavy-=1;
- unsigned int nf = _nf_light+nf_heavy;
+
+
+ unsigned int nf = _nf_light;
- // value of Lambda from alphas if needed using Newton-Raphson
- if(_inopt) {
- _lambda[nf_heavy] = computeLambda(mz,_alphain,nf-1);
- }
- // otherwise it was an input parameter
- else{_lambda[nf_heavy]=_lambdain;}
+ // Set lambda below heavy quark thresholds to input value
+ _lambda[0]=_lambdain;
// convert lambda to the Monte Carlo scheme if needed
using Constants::pi;
- if(_lambdaopt) _lambda[nf_heavy] *=exp((0.5*_ca*(67./3.-sqr(pi))-_tr*nf*10./3.)/(11*_ca-2*nf))/sqrt(2.);
+ if(_lambdaopt) _lambda[0] *=exp((0.5*_ca*(67./3.-sqr(pi))-_tr*nf*10./3.)/(11*_ca-2*nf))/sqrt(2.);
- // compute the threshold matching
- // above the Z mass
- for(int ix=nf_heavy;ix<int(_thresholds.size())-1;++ix) {
+ // Compute the threshold matching
+ for(int ix=0;ix<int(_thresholds.size())-1;++ix) {
_lambda[ix+1] = computeLambda(_thresholds[ix+1],alphaS(_thresholds[ix+1],
_lambda[ix],ix),ix+1);
}
- // below Z mass
- for(int ix=nf_heavy-1;ix>=0;--ix) {
- _lambda[ix] = computeLambda(_thresholds[ix+1],alphaS(_thresholds[ix+1],
- _lambda[ix+1],ix+1),ix);
- }
// compute the maximum value of as
if ( _asType < 5 ) _alphamin = value(sqr(_qmin)+1.0e-8*sqr(MeV)); // approx as = 1
else _alphamin = max(_asMaxNP, value(sqr(_qmin)+1.0e-8*sqr(MeV)));
// check consistency lambda_3 < qmin
if(_lambda[0]>_qmin)
Throw<InitException>() << "The value of Qmin is less than Lambda in "
<< _qmin/GeV << " < " << _lambda[0]/GeV
<< " HiddenValleyAlpha::doinit " << Exception::abortnow;
}
double HiddenValleyAlpha::derivativealphaS(Energy q, Energy lam, int nf) const {
using Constants::pi;
double lx = log(sqr(q/lam));
// N.B. b_1 is divided by 2 due Hw++ convention
double b0 = 11./3.*_ca - 4./3.*_tr*nf;
double b1 = 17./3.*sqr(_ca) - nf*_tr*(10./3.*_ca+2.*_cf);
if(_nloop==1)
return -4.*pi/(b0*sqr(lx));
else if(_nloop==2)
return -4.*pi/(b0*sqr(lx))*(1.-2.*b1/sqr(b0)/lx*(1.-2.*log(lx)));
else
assert(false);
return 0.;
}
double HiddenValleyAlpha::alphaS(Energy q, Energy lam, int nf) const {
using Constants::pi;
double lx(log(sqr(q/lam)));
// N.B. b_1 is divided by 2 due Hw++ convention
double b0 = 11./3.*_ca - 4./3.*_tr*nf;
double b1 = 17./3.*sqr(_ca) - nf*_tr*(10./3.*_ca+2.*_cf);
// one loop
if(_nloop==1)
return 4.*pi/(b0*lx);
// two loop
else if(_nloop==2) {
return 4.*pi/(b0*lx)*(1.-2.*b1/sqr(b0)*log(lx)/lx);
}
else
assert(false);
return 0.;
}
diff --git a/Shower/QTilde/Dark/HiddenValleyAlpha.h b/Shower/QTilde/Dark/HiddenValleyAlpha.h
--- a/Shower/QTilde/Dark/HiddenValleyAlpha.h
+++ b/Shower/QTilde/Dark/HiddenValleyAlpha.h
@@ -1,338 +1,333 @@
// -*- C++ -*-
//
// HiddenValleyAlpha.h is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2007 The Herwig Collaboration
//
// Herwig is licenced under version 2 of the GPL, see COPYING for details.
// Please respect the MCnet academic guidelines, see GUIDELINES for details.
//
#ifndef HERWIG_HiddenValleyAlpha_H
#define HERWIG_HiddenValleyAlpha_H
//
// This is the declaration of the HiddenValleyAlpha class.
//
#include "Herwig/Shower/ShowerAlpha.h"
#include "ThePEG/Config/Constants.h"
namespace Herwig {
using namespace ThePEG;
/** \ingroup Shower
*
* This concrete class provides the definition of the
* pure virtual function value() and overestimateValue() for the
* strong coupling.
*
* A number of different options for the running of the coupling
* and its initial definition are supported.
*
* @see \ref HiddenValleyAlphaInterfaces "The interfaces"
* defined for HiddenValleyAlpha.
*/
class HiddenValleyAlpha: public ShowerAlpha {
public:
/**
* The default constructor.
*/
HiddenValleyAlpha() : ShowerAlpha(),
_qmin(0.630882*GeV), _asType(1), _asMaxNP(1.0),
_nloop(2),_thresopt(false),
- _lambdain(0.208364*GeV),_alphain(0.118),
+ _lambdain(0.208364*GeV),
_tolerance(1e-10),
_maxtry(100),_alphamin(0.) {}
public:
/**
* Methods to return the coupling
*/
//@{
/**
* It returns the running coupling value evaluated at the input scale
* multiplied by the scale factor scaleFactor().
* @param scale The scale
* @return The coupling
*/
virtual double value(const Energy2 scale) const;
/**
* It returns the running coupling value evaluated at the input scale
* multiplied by the scale factor scaleFactor().
*/
virtual double overestimateValue() const;
/**
* Return the ratio of the coupling at the scale to the overestimated value
*/
virtual double ratio(const Energy2 scale,double factor=1.) const;
/**
* Initialize this coupling.
*/
virtual void initialize() { doinit(); }
//@}
/**
* Get the value of \f$\Lambda_{\rm QCd}\f$
* @param nf number of flavours
*/
Energy lambdaQCD(unsigned int nf) {
if (nf <= 3) return _lambda[0];
else if (nf==4 || nf==5) return _lambda[nf-3];
else return _lambda[3];
}
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);
//@}
/**
* The standard Init function used to initialize the interfaces.
* Called exactly once for each class by the class description system
* before the main function starts or
* when this class is dynamically loaded.
*/
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;
//@}
protected:
/** @name Standard Interfaced functions. */
//@{
/**
* Initialize this object after the setup phase before saving an
* EventGenerator to disk.
* @throws InitException if object could not be initialized properly.
*/
virtual void doinit();
//@}
private:
/**
* Member functions which calculate the coupling
*/
//@{
/**
* The 1,2,3-loop parametrization of \f$\alpha_S\f$.
* @param q The scale
* @param lam \f$\Lambda_{\rm QCD}\f$
* @param nf The number of flavours
*/
double alphaS(Energy q, Energy lam, int nf) const;
/**
* The derivative of \f$\alpha_S\f$ with respect to \f$\ln(Q^2/\Lambda^2)\f$
* @param q The scale
* @param lam \f$\Lambda_{\rm QCD}\f$
* @param nf The number of flavours
*/
double derivativealphaS(Energy q, Energy lam, int nf) const;
/**
* Compute the value of \f$Lambda\f$ needed to get the input value of
* the strong coupling at the scale given for the given number of flavours
* using the Newton-Raphson method
* @param match The scale for the coupling
* @param alpha The input coupling
* @param nflav The number of flavours
*/
Energy computeLambda(Energy match, double alpha, unsigned int nflav) const;
/**
* Return the value of \f$\Lambda\f$ and the number of flavours at the scale.
* @param q The scale
* @return The number of flavours at the scale and \f$\Lambda\f$.
*/
pair<short, Energy> getLamNfTwoLoop(Energy q) const;
//@}
private:
/**
* The static object used to initialize the description of this class.
* Indicates that this is a concrete class with persistent data.
*/
static ClassDescription<HiddenValleyAlpha> initHiddenValleyAlpha;
/**
* The assignment operator is private and must never be called.
* In fact, it should not even be implemented.
*/
HiddenValleyAlpha & operator=(const HiddenValleyAlpha &);
private:
/**
* Minimum value of the scale
*/
Energy _qmin;
/**
* Parameter controlling the behaviour of \f$\alpha_S\f$ in the
* non-perturbative region.
*/
int _asType;
/**
* Another parameter, a possible (maximum) value of alpha in the
* non-perturbative region.
*/
double _asMaxNP;
/**
* Thresholds for the different number of flavours
*/
vector<Energy> _thresholds;
/**
* \f$\Lambda\f$ for the different number of flavours
*/
vector<Energy> _lambda;
/**
* Option for the number of loops
*/
unsigned int _nloop;
/**
* Option for the threshold masses
*/
bool _thresopt;
/**
* Input value of Lambda
*/
Energy _lambdain;
/**
- * Input value of \f$alpha_S(M_Z)\f$
- */
- double _alphain;
-
- /**
* Whether to convert lambda from MSbar scheme
*/
bool _lambdaopt;
/**
* Whether to use input value of alphas(MZ) or lambda
*/
bool _inopt;
/**
* Tolerance for discontinuities at the thresholds
*/
double _tolerance;
/**
* Maximum number of iterations for the Newton-Raphson method to converge
*/
unsigned int _maxtry;
/**
* Number of light flavours (below qmin)
*/
unsigned int _nf_light;
/**
* The minimum value of the coupling
*/
double _alphamin;
/**
* Colour factors
*/
//@{
/**
* \f$C_A\f$
*/
double _ca;
/**
* \f$C_A\f$
*/
double _cf;
/**
* \f$T_R\f$
*/
double _tr;
//@}
};
}
#include "ThePEG/Utilities/ClassTraits.h"
namespace ThePEG {
/** @cond TRAITSPECIALIZATIONS */
/** This template specialization informs ThePEG about the
* base classes of HiddenValleyAlpha. */
template <>
struct BaseClassTrait<Herwig::HiddenValleyAlpha,1> {
/** Typedef of the first base class of HiddenValleyAlpha. */
typedef Herwig::ShowerAlpha NthBase;
};
/** This template specialization informs ThePEG about the name of
* the HiddenValleyAlpha class and the shared object where it is defined. */
template <>
struct ClassTraits<Herwig::HiddenValleyAlpha>
: public ClassTraitsBase<Herwig::HiddenValleyAlpha> {
/** Return a platform-independent class name */
static string className() { return "Herwig::HiddenValleyAlpha"; }
/**
* The name of a file containing the dynamic library where the class
* HiddenValleyAlpha is implemented. It may also include several,
* space-separated, libraries if the class HiddenValleyAlpha depends on
* other classes (base classes excepted). In this case the listed
* libraries will be dynamically linked in the order they are
* specified.
*/
static string library() { return "HwShower.so HwHiddenValley.so"; }
};
/** @endcond */
}
#endif /* HERWIG_HiddenValleyAlpha_H */
diff --git a/Shower/QTilde/SplittingFunctions/PTCutOff.cc b/Shower/QTilde/SplittingFunctions/PTCutOff.cc
--- a/Shower/QTilde/SplittingFunctions/PTCutOff.cc
+++ b/Shower/QTilde/SplittingFunctions/PTCutOff.cc
@@ -1,66 +1,66 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the PTCutOff class.
//
#include "PTCutOff.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/EventRecord/Particle.h"
#include "ThePEG/Repository/UseRandom.h"
#include "ThePEG/Repository/EventGenerator.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/Parameter.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
using namespace Herwig;
IBPtr PTCutOff::clone() const {
return new_ptr(*this);
}
IBPtr PTCutOff::fullclone() const {
return new_ptr(*this);
}
void PTCutOff::persistentOutput(PersistentOStream & os) const {
os << ounit(pTmin_,GeV) << ounit(pT2min_,GeV2);
}
void PTCutOff::persistentInput(PersistentIStream & is, int) {
is >> iunit(pTmin_,GeV) >> iunit(pT2min_,GeV2);
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<PTCutOff,SudakovCutOff>
describeHerwigPTCutOff("Herwig::PTCutOff", "HwShower.so");
void PTCutOff::Init() {
static ClassDocumentation<PTCutOff> documentation
("There is no documentation for the PTCutOff class");
static Parameter<PTCutOff,Energy> interfacepTmin
("pTmin",
"The minimum pT if using a cut-off on the pT",
- &PTCutOff::pTmin_, GeV, 1.0*GeV, ZERO, 100.0*GeV,
+ &PTCutOff::pTmin_, GeV, 1.0*GeV, ZERO, 1000.0*GeV,
false, false, Interface::limited);
}
void PTCutOff::doinit() {
pT2min_ = sqr(pTmin_);
SudakovCutOff::doinit();
}
const vector<Energy> & PTCutOff::virtualMasses(const IdList & ids) {
static vector<Energy> output;
output.clear();
for(auto id : ids)
output.push_back(id->mass());
return output;
}
diff --git a/Utilities/Kinematics.cc b/Utilities/Kinematics.cc
--- a/Utilities/Kinematics.cc
+++ b/Utilities/Kinematics.cc
@@ -1,119 +1,327 @@
// -*- 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 ThePEG;
+/**
+ * Boost consistently the Lorentz5Momenta in momenta (given in the pi and pj COM frame)
+ * into the pi pj LAB frame (piLab,pjLab).
+ * NOTE: the momenta must be given in the COM frame of piLab+pjLab, where piCOM points
+ * in the positive z-Axis and pjCOM in the negative z-Axis
+ * */
+void Kinematics::BoostIntoTwoParticleFrame(const Energy M, const Lorentz5Momentum & piLab,
+ const Lorentz5Momentum & pjLab,
+ std::vector<Lorentz5Momentum * > momenta) {
+ if (piLab.vect().mag()==ZERO || pjLab.vect().mag()==ZERO) {
+ if (piLab.vect().mag()==ZERO && pjLab.vect().mag()==ZERO)
+ // even more trivial case where we are already in the correct frame
+ return;
+ // Trival 1+1 dimensional boost case
+ Lorentz5Momentum pClu(M,(piLab+pjLab).vect());
+ Boost bv = pClu.boostVector();
+ bool PiNonZero = piLab.vect().mag()>ZERO;
+ Lorentz5Momentum pConst = PiNonZero ? Lorentz5Momentum(piLab.m(),piLab.vect()):Lorentz5Momentum(pjLab.m(),pjLab.vect());
+ Axis u=pConst.boost(-bv).vect().unit();
+ for (unsigned int it = 0; it < momenta.size(); it++) {
+ momenta[it]->rotateUz(u.unit());
+ // mirror momenta if pjLab is considered as positive z-Axis
+ if (!PiNonZero) momenta[it]->vect() = - momenta[it]->vect();
+ momenta[it]->boost(bv);
+ }
+ return;
+ }
+ 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();
+ Lorentz5Momentum pConst = Lorentz5Momentum(piLab.m(),piLab.vect());
+ Axis u=pConst.boost(-bv).vect().unit();
+ for (unsigned int it = 0; it < momenta.size(); it++) {
+ // rotate positive z-Axis into the original constituent direction of pi
+ momenta[it]->rotateUz(u.unit());
+ 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;
+ Lambda11 = Pi*Pj*(std::expm1(0.5*std::log1p(mj2/(Pj*Pj)))-std::expm1(0.5*std::log1p(mi2/(Pi*Pi)))+2*pow(sin(Phi/2.0),2)*sqrt(1.0+mi2/(Pi*Pi)))/(A2);
+ 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;
+ boost::numeric::ublas::vector<double> momentaOut(3);
+ 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);
+
+ if (omega1.mag() > ZERO){
+ // 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);
+ }
+ else {
+ std::cout << "OMEGA1 == ZERO = " << omega1.mag()<< std::endl;
+ }
+
+ 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.cross(r1)*piRes.vect())/GeV> 0 ? 1:-1;
+ double angle2=acos(r1*r2);
+ 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];
+ }
+}
+
+
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 * pstarTwoBodyDecay(min,m1,m2);
+ 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,112 +1,160 @@
// -*- 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);
+ /*
+ * 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));
+ }
+
+ /*
+ * samples exactly from distribution 1/(1/Ainv-cosTheta)^2
+ * where cosTheta in [-1:1] and Ainv must be in [-1:1]
+ * */
+ inline double sampleCosTchannel(double Ainv){
+ 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;
+ double res = (1.0+A*cosTheta)/(A+cosTheta);
+ return res;
+ }
+ /*
+ * samples exactly from distribution exp(lambda*(cosTheta-1))
+ * where cosTheta in [-1:1]
+ * */
+ inline double sampleCosExp(double lambda){
+ double r = UseRandom::rnd();
+ // double cosTheta = -1.0 + log(1.0+r*(exp(2*lambda)-1.0))/lambda;
+ // Better numerics for small lambda
+ double cosTheta;
+ // numerics
+ if (2*lambda>700.0)
+ cosTheta = 1.0;
+ else
+ cosTheta = -1.0 + log1p(r*(expm1(2*lambda)))/lambda;
+ assert(cosTheta<=1.0);
+ assert(cosTheta>=-1.0);
+ return cosTheta;
+ }
+ /**
+ * Boost consistently the Lorentz5Momenta momenta (given in the COM frame of pi+pj)
+ * into the piLab pjLab frame. see details at the definition of the function
+ * */
+ 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 */
diff --git a/Utilities/Makefile.am b/Utilities/Makefile.am
--- a/Utilities/Makefile.am
+++ b/Utilities/Makefile.am
@@ -1,47 +1,47 @@
SUBDIRS = XML Statistics
noinst_LTLIBRARIES = libHwUtils.la
libHwUtils_la_SOURCES = \
EnumParticles.h \
Interpolator.tcc Interpolator.h \
Kinematics.cc Kinematics.h \
Progress.h Progress.cc \
Maths.h Maths.cc \
StandardSelectors.cc StandardSelectors.h\
Histogram.cc Histogram.fh Histogram.h \
GaussianIntegrator.cc GaussianIntegrator.h \
GaussianIntegrator.tcc \
Statistic.h HerwigStrategy.cc HerwigStrategy.h \
GSLIntegrator.h GSLIntegrator.tcc \
GSLBisection.h GSLBisection.tcc GSLHelper.h \
Reshuffler.h Reshuffler.cc \
expm-1.h \
HiggsLoopFunctions.h AlphaS.h
nodist_libHwUtils_la_SOURCES = hgstamp.inc
BUILT_SOURCES = hgstamp.inc
CLEANFILES = hgstamp.inc
HGVERSION := $(shell hg -R $(top_srcdir) parents --template '"Herwig {node|short} ({branch})"' 2> /dev/null || echo \"$(PACKAGE_STRING)\" || true )
.PHONY: update_hgstamp
hgstamp.inc: update_hgstamp
@[ -f $@ ] || touch $@
@echo '$(HGVERSION)' | cmp -s $@ - || echo '$(HGVERSION)' > $@
libHwUtils_la_LIBADD = \
XML/libHwXML.la \
Statistics/libHwStatistics.la
check_PROGRAMS = utilities_test
utilities_test_SOURCES = \
tests/utilitiesTestsMain.cc \
tests/utilitiesTestsGlobalFixture.h \
tests/utilitiesTestsKinematics.h \
-tests/utilitiesTestMaths.h \
+tests/utilitiesTestsMaths.h \
tests/utilitiesTestsStatistic.h
utilities_test_LDADD = $(BOOST_UNIT_TEST_FRAMEWORK_LIBS) $(THEPEGLIB) -ldl libHwUtils.la
utilities_test_LDFLAGS = $(AM_LDFLAGS) -export-dynamic $(BOOST_UNIT_TEST_FRAMEWORK_LDFLAGS) $(THEPEGLDFLAGS)
utilities_test_CPPFLAGS = $(AM_CPPFLAGS) $(BOOST_CPPFLAGS)
TESTS = utilities_test
diff --git a/Utilities/expm-1.h b/Utilities/expm-1.h
--- a/Utilities/expm-1.h
+++ b/Utilities/expm-1.h
@@ -1,147 +1,148 @@
//
// Copyright (c) 2007
// Tsai, Dung-Bang
// National Taiwan University, Department of Physics
//
// E-Mail : dbtsai (at) gmail.com
// Begine : 2007/11/20
// Last modify : 2007/11/22
// Version : v0.1
//
// EXPGM_PAD computes the matrix exponential exp(H) for general matrixs,
// including complex and real matrixs using the irreducible (p,p) degree
// rational Pade approximation to the exponential
// exp(z) = r(z)=(+/-)( I+2*(Q(z)/P(z))).
//
// Usage :
//
// U = expm_pad(H)
// U = expm_pad(H, p)
//
// where p is internally set to 6 (recommended and gererally satisfactory).
//
// See also MATLAB supplied functions, EXPM and EXPM1.
//
// Reference :
// EXPOKIT, Software Package for Computing Matrix Exponentials.
// ACM - Transactions On Mathematical Software, 24(1):130-156, 1998
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
// provided that the above copyright notice appear in all copies and
// that both that copyright notice and this permission notice appear
// in supporting documentation. The authors make no representations
// about the suitability of this software for any purpose.
// It is provided "as is" without express or implied warranty.
//
#ifndef _BOOST_UBLAS_EXPM_
#define _BOOST_UBLAS_EXPM_
#include <complex>
#include <boost/numeric/ublas/vector.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/lu.hpp>
+#include <boost/throw_exception.hpp>
namespace boost { namespace numeric { namespace ublas {
template<typename MATRIX> MATRIX expm_pad(const MATRIX &H, const int p = 6) {
typedef typename MATRIX::value_type value_type;
typedef typename MATRIX::size_type size_type;
typedef double real_value_type; // Correct me. Need to modify.
assert(H.size1() == H.size2());
const size_type n = H.size1();
const identity_matrix<value_type> I(n);
matrix<value_type> U(n,n),H2(n,n),P(n,n),Q(n,n);
real_value_type norm = 0.0;
// Calcuate Pade coefficients (1-based instead of 0-based as in the c vector)
vector<real_value_type> c(p+2);
c(1)=1;
- for(size_type i = 1; i <= p; ++i)
+ for(size_type i = 1; i <= (unsigned) p; ++i)
c(i+1) = c(i) * ((p + 1.0 - i)/(i * (2.0 * p + 1 - i)));
// Calcuate the infinty norm of H, which is defined as the largest row sum of a matrix
for(size_type i=0; i<n; ++i)
{
real_value_type temp = 0.0;
for(size_type j=0;j<n;j++)
temp += std::abs<real_value_type>(H(j,i)); // Correct me, if H is complex, can I use that abs?
norm = std::max<real_value_type>(norm, temp);
}
if (norm == 0.0)
{
boost::throw_exception(boost::numeric::ublas::bad_argument());
std::cerr<<"Error! Null input in the routine EXPM_PAD.\n";
exit(0);
}
// Scaling, seek s such that || H*2^(-s) || < 1/2, and set scale = 2^(-s)
int s = 0;
real_value_type scale = 1.0;
if(norm > 0.5) {
s = std::max<int>(0, static_cast<int>((log(norm) / log(2.0) + 2.0)));
scale /= static_cast<real_value_type>(std::pow(2.0, s));
U.assign(scale * H); // Here U is used as temp value due to that H is const
}
else
U.assign(H);
// Horner evaluation of the irreducible fraction, see the following ref above.
// Initialise P (numerator) and Q (denominator)
H2.assign( prod(U, U) );
Q.assign( c(p+1)*I );
P.assign( c(p)*I );
size_type odd = 1;
for( size_type k = p - 1; k > 0; --k)
{
if( odd == 1)
{
Q = ( prod(Q, H2) + c(k) * I );
}
else
{
P = ( prod(P, H2) + c(k) * I );
}
odd = 1 - odd;
}
if( odd == 1)
{
Q = ( prod(Q, U) );
Q -= P ;
//U.assign( -(I + 2*(Q\P)));
}
else
{
P = (prod(P, U));
Q -= P;
//U.assign( I + 2*(Q\P));
}
// In origine expokit package, they use lapack ZGESV to obtain inverse matrix,
// and in that ZGESV routine, it uses LU decomposition for obtaing inverse matrix.
// Since in ublas, there is no matrix inversion template, I simply use the build-in
// LU decompostion package in ublas, and back substitute by myself.
//
//////////////// Implement Matrix Inversion ///////////////////////
permutation_matrix<size_type> pm(n);
int res = lu_factorize(Q, pm);
if( res != 0)
{
std::cerr << "Error in the matrix inversion in template expm_pad.\n";
exit(0);
}
H2 = I; // H2 is not needed anymore, so it is temporary used as identity matrix for substituting.
lu_substitute(Q, pm, H2);
if( odd == 1)
U.assign( -(I + 2.0 * prod(H2, P)));
else
U.assign( I + 2.0 * prod(H2, P));
// Squaring
- for(size_t i = 0; i < s; ++i)
+ for(size_t i = 0; i < (unsigned) s; ++i)
{
U = (prod(U,U));
}
return U;
}
}}}
#endif
diff --git a/Utilities/tests/utilitiesTestsKinematics.h b/Utilities/tests/utilitiesTestsKinematics.h
--- a/Utilities/tests/utilitiesTestsKinematics.h
+++ b/Utilities/tests/utilitiesTestsKinematics.h
@@ -1,124 +1,154 @@
// -*- C++ -*-
//
// utilitiesTestKinematics.h is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2019 The Herwig Collaboration, 2015 Marco A. Harrendorf
//
// 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_Utilities_Test_Kinematics_H
#define HERWIG_Utilities_Test_Kinematics_H
#include <boost/test/unit_test.hpp>
#include "Herwig/Utilities/Kinematics.h"
#include "ThePEG/Config/Unitsystem.h"
using namespace Herwig::Kinematics;
using namespace ThePEG::Units;
struct FixKinematics1 {
FixKinematics1()
{BOOST_TEST_MESSAGE( "setup fixture for utilitiesKinematicsTestTest" ); }
~FixKinematics1() { BOOST_TEST_MESSAGE( "teardown fixture for utilitiesKinematicsTest" ); }
};
/*
* Start of boost unit tests for Kinematics.h
*
* @todo Implement unit test for threeBodyDecay
*/
BOOST_AUTO_TEST_SUITE(utilitiesKinematicsTest)
/*
* Boost unit tests
*
*/
+BOOST_AUTO_TEST_CASE(LorentzTest)
+{
+ Energy mi,mj,mq;
+ Energy Pi,Pj,Pq;
+ double Phi_i,Phi_j,Phi_q;
+ double The_i,The_j,The_q;
+ Energy mRes_q;
+ Energy P_Res_q;
+ double Phi_Res_q;
+ double The_Res_q;
+ using namespace ThePEG;
+ mi = 0.0*GeV;
+ Pi = 0.0*GeV;
+ Phi_i = 0.0;
+ The_i = 0.0;
+ Lorentz5Momentum pi(mi,Momentum3(Pi*cos(Phi_i)*sin(The_i),Pi*sin(Phi_i)*sin(The_i),Pi*cos(The_i)));
+ mj = 0.0*GeV;
+ Pj = 0.0*GeV;
+ Phi_j = 0.0;
+ The_j = 0.0;
+ Lorentz5Momentum pj(mj,Momentum3(Pj*cos(Phi_j)*sin(The_j),Pj*sin(Phi_j)*sin(The_j),Pj*cos(The_j)));
+ // Lorentz5Momentum pq(mq,Momentum3(Pj*cos(Phi_j)*sin(The_j),Pj*sin(Phi_j)*sin(The_j),Pj*cos(The_j)));
+
+ double eps=1e-14;
+ BOOST_CHECK_CLOSE(pi.mass()/GeV, pi.mass()/GeV, eps);
+ BOOST_CHECK_CLOSE(pi.m()/GeV , pi.m()/GeV, eps);
+ BOOST_CHECK_CLOSE(pi.x()/GeV , pi.x()/GeV, eps);
+ BOOST_CHECK_CLOSE(pi.y()/GeV , pi.y()/GeV, eps);
+ BOOST_CHECK_CLOSE(pi.z()/GeV , pi.z()/GeV, eps);
+}
BOOST_AUTO_TEST_CASE(generateAnglesTest)
{
double flatMinusPiToPlusPi, flatNullToTwoPi;
for(int i = 0; i < 100; ++i) {
generateAngles(flatMinusPiToPlusPi, flatNullToTwoPi);
BOOST_CHECK( -M_PI <= flatMinusPiToPlusPi );
BOOST_CHECK( flatMinusPiToPlusPi <= M_PI);
BOOST_CHECK( 0. <= flatNullToTwoPi);
BOOST_CHECK(flatNullToTwoPi <= 2*M_PI);
}
}
BOOST_AUTO_TEST_CASE(unitDirectionTest)
{
using namespace Herwig::Kinematics;
BOOST_CHECK_EQUAL( unitDirection(1.1, -1), Axis() );
BOOST_CHECK_EQUAL( unitDirection(-1.1, -1), Axis() );
BOOST_CHECK_EQUAL( unitDirection(1.1, 0), Axis() );
BOOST_CHECK_EQUAL( unitDirection(-1.1, -1), Axis() );
BOOST_CHECK_EQUAL( unitDirection(1, 0), Axis(0, 0, 1) );
BOOST_CHECK_EQUAL( unitDirection(1, M_PI/2.), Axis(0, 0, 1) );
BOOST_CHECK(unitDirection(0, M_PI/2).almostEqual(Axis(0, 1, 0), 0.001) );
BOOST_CHECK_EQUAL( unitDirection(0, 0), Axis(1, 0, 0) );
}
BOOST_AUTO_TEST_CASE(pstarTwoBodyDecayTest)
{
BOOST_CHECK_EQUAL(pstarTwoBodyDecay(Energy(-100*GeV), Energy(60*GeV), Energy(60*GeV))/GeV, Energy(0*GeV)/GeV);
BOOST_CHECK_EQUAL(pstarTwoBodyDecay(Energy(100*GeV), Energy(-40*GeV), Energy(40*GeV))/GeV, Energy(0*GeV)/GeV);
BOOST_CHECK_EQUAL(pstarTwoBodyDecay(Energy(100*GeV), Energy(-40*GeV), Energy(-40*GeV))/GeV, Energy(0*GeV)/GeV);
BOOST_CHECK_EQUAL(pstarTwoBodyDecay(Energy(100*GeV), Energy(60*GeV), Energy(60*GeV))/GeV, Energy(0*GeV)/GeV);
BOOST_CHECK_EQUAL(pstarTwoBodyDecay(Energy(100*GeV), Energy(50*GeV), Energy(50*GeV))/GeV, Energy(0*GeV)/GeV);
BOOST_CHECK_EQUAL(pstarTwoBodyDecay(Energy(10*GeV), Energy(6*GeV), Energy(3*GeV))/GeV, Energy(std::sqrt(19*91)/20.*GeV)/GeV);
BOOST_CHECK_EQUAL(pstarTwoBodyDecay(Energy(10*GeV), Energy(3*GeV), Energy(6*GeV))/GeV, Energy(std::sqrt(19*91)/20.*GeV)/GeV);
}
BOOST_AUTO_TEST_CASE(twoBodyDecayTest1)
{
Lorentz5Momentum decayProductOne(GeV);
Lorentz5Momentum decayProductTwo(GeV);
BOOST_CHECK(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(40*GeV), Energy(40*GeV), Axis(), decayProductOne, decayProductTwo));
BOOST_CHECK(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(50*GeV), Energy(50*GeV), Axis(), decayProductOne, decayProductTwo));
BOOST_CHECK(!(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(60*GeV), Energy(60*GeV), Axis(), decayProductOne, decayProductTwo)));
BOOST_CHECK(!(twoBodyDecay(Lorentz5Momentum(-100*GeV), Energy(40*GeV), Energy(40*GeV), Axis(), decayProductOne, decayProductTwo)));
BOOST_CHECK(!(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(-40*GeV), Energy(40*GeV), Axis(), decayProductOne, decayProductTwo)));
BOOST_CHECK(!(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(40*GeV), Energy(-40*GeV), Axis(), decayProductOne, decayProductTwo)));
twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(50*GeV), Energy(50*GeV), Axis(1,0,0), decayProductOne, decayProductTwo);
BOOST_CHECK_EQUAL(decayProductOne/GeV, Lorentz5Momentum(50*GeV)/GeV);
BOOST_CHECK_EQUAL(decayProductTwo/GeV, Lorentz5Momentum(50*GeV)/GeV);
twoBodyDecay(Lorentz5Momentum(10*GeV), Energy(6*GeV), Energy(3*GeV), Axis(1,0,0), decayProductOne, decayProductTwo);
BOOST_CHECK_EQUAL(decayProductOne/GeV, Lorentz5Momentum(6*GeV, Momentum3(std::sqrt(19*91)/20.*GeV, ThePEG::ZERO, ThePEG::ZERO))/GeV);
BOOST_CHECK_EQUAL(decayProductTwo/GeV, Lorentz5Momentum(3*GeV, Momentum3(-(std::sqrt(19*91)/20.*GeV), ThePEG::ZERO, ThePEG::ZERO))/GeV);
}
BOOST_AUTO_TEST_CASE(twoBodyDecayTest2)
{
Lorentz5Momentum decayProductOne(GeV);
Lorentz5Momentum decayProductTwo(GeV);
BOOST_CHECK(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(40*GeV), Energy(40*GeV), 1, M_PI/2., decayProductOne, decayProductTwo));
BOOST_CHECK(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(50*GeV), Energy(50*GeV), 1, M_PI/2., decayProductOne, decayProductTwo));
BOOST_CHECK(!(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(60*GeV), Energy(60*GeV), 1, M_PI/2., decayProductOne, decayProductTwo)));
BOOST_CHECK(!(twoBodyDecay(Lorentz5Momentum(-100*GeV), Energy(40*GeV), Energy(40*GeV), 1, M_PI/2., decayProductOne, decayProductTwo)));
BOOST_CHECK(!(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(-40*GeV), Energy(40*GeV), 1, M_PI/2., decayProductOne, decayProductTwo)));
BOOST_CHECK(!(twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(40*GeV), Energy(-40*GeV), 1, M_PI/2., decayProductOne, decayProductTwo)));
twoBodyDecay(Lorentz5Momentum(100*GeV), Energy(50*GeV), Energy(50*GeV), 1, M_PI/2., decayProductOne, decayProductTwo);
BOOST_CHECK_EQUAL(decayProductOne/GeV, Lorentz5Momentum(50*GeV)/GeV);
BOOST_CHECK_EQUAL(decayProductTwo/GeV, Lorentz5Momentum(50*GeV)/GeV);
twoBodyDecay(Lorentz5Momentum(10*GeV), Energy(6*GeV), Energy(3*GeV), 1, M_PI/2., decayProductOne, decayProductTwo);
BOOST_CHECK_EQUAL(decayProductOne/GeV, Lorentz5Momentum(6*GeV, Momentum3(ThePEG::ZERO, ThePEG::ZERO, std::sqrt(19*91)/20.*GeV))/GeV);
BOOST_CHECK_EQUAL(decayProductTwo/GeV, Lorentz5Momentum(3*GeV, Momentum3(ThePEG::ZERO, ThePEG::ZERO, -(std::sqrt(19*91)/20.*GeV)))/GeV);
}
BOOST_AUTO_TEST_SUITE_END()
#endif /* HERWIG_Utilities_Test_Kinematics_H */
diff --git a/Utilities/tests/utilitiesTestsMain.cc b/Utilities/tests/utilitiesTestsMain.cc
--- a/Utilities/tests/utilitiesTestsMain.cc
+++ b/Utilities/tests/utilitiesTestsMain.cc
@@ -1,39 +1,39 @@
// -*- C++ -*-
//
// utilitiesTestMain.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2019 The Herwig Collaboration, 2015 Marco A. Harrendorf
//
// Herwig is licenced under version 3 of the GPL, see COPYING for details.
// Please respect the MCnet academic guidelines, see GUIDELINES for details.
//
/**
* The following part should be included only once.
*/
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#define BOOST_TEST_MODULE utilitiesTest
/**
* Include global fixture
*
* Global fixture initializes the randomNumber generator
*/
#include "Herwig/Utilities/tests/utilitiesTestsGlobalFixture.h"
/**
* Include here the sub tests
*/
#include "Herwig/Utilities/tests/utilitiesTestsKinematics.h"
-#include "Herwig/Utilities/tests/utilitiesTestMaths.h"
+#include "Herwig/Utilities/tests/utilitiesTestsMaths.h"
#include "Herwig/Utilities/tests/utilitiesTestsStatistic.h"
/**
* Debug and development part
*/
BOOST_AUTO_TEST_CASE(fail)
{
//BOOST_FAIL("Ende");
}
\ No newline at end of file
diff --git a/Utilities/tests/utilitiesTestsMaths.h b/Utilities/tests/utilitiesTestsMaths.h
new file mode 100644
--- /dev/null
+++ b/Utilities/tests/utilitiesTestsMaths.h
@@ -0,0 +1,84 @@
+// -*- C++ -*-
+//
+// utilitiesTestMaths.h is a part of Herwig - A multi-purpose Monte Carlo event generator
+// Copyright (C) 2002-2019 The Herwig Collaboration, 2015 Marco A. Harrendorf
+//
+// 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_Utilities_Test_Maths_H
+#define HERWIG_Utilities_Test_Maths_H
+
+#include <boost/test/unit_test.hpp>
+
+#include "Herwig/Utilities/Maths.h"
+
+/*
+ * Start of boost unit tests for Maths.h
+ *
+ */
+BOOST_AUTO_TEST_SUITE(utilitiesMathsTest)
+
+/*
+ * Boost unit tests
+ *
+ */
+BOOST_AUTO_TEST_CASE(dilogFunction)
+{
+ BOOST_CHECK_EQUAL(Herwig::Math::Li2(0.), 0.);
+ BOOST_CHECK_CLOSE(Herwig::Math::Li2(-1).real(), -1./12. * M_PI * M_PI, 1e-5);
+ BOOST_CHECK_CLOSE(Herwig::Math::Li2(-1).imag(), 0., 1e-5);
+ BOOST_CHECK_CLOSE(Herwig::Math::Li2(1).real(), 1./6. * M_PI * M_PI, 1e-5);
+ BOOST_CHECK_CLOSE(Herwig::Math::Li2(1).imag(), 0., 1e-5);
+
+ BOOST_CHECK_CLOSE(Herwig::Math::Li2(Herwig::Complex(0.5, 1)).real(), 0.203354, 2e-4);
+ BOOST_CHECK_CLOSE(Herwig::Math::Li2(Herwig::Complex(0.5, 1)).imag(), 1.13194, 1e-4);
+ BOOST_CHECK_CLOSE(Herwig::Math::Li2(Herwig::Complex(-0.5, -1)).real(), -0.578503, 1e-4);
+ BOOST_CHECK_CLOSE(Herwig::Math::Li2(Herwig::Complex(-0.5, -1)).imag(), -0.772714, 1e-4);
+}
+
+BOOST_AUTO_TEST_CASE(realDilogFunction)
+{
+ BOOST_CHECK_EQUAL(Herwig::Math::ReLi2(0.), 0.);
+ BOOST_CHECK_CLOSE(Herwig::Math::ReLi2(-1), -1./12. * M_PI * M_PI, 1e-5);
+ BOOST_CHECK_CLOSE(Herwig::Math::ReLi2(1), 1./6. * M_PI * M_PI, 1e-5);
+}
+
+
+BOOST_AUTO_TEST_CASE(angleZeroTo2Pi)
+{
+ BOOST_CHECK_EQUAL(Herwig::Math::angleZeroTo2Pi(0.), 0.);
+ BOOST_CHECK_EQUAL(Herwig::Math::angleZeroTo2Pi(-0.5*M_PI), 1.5*M_PI);
+ BOOST_CHECK_EQUAL(Herwig::Math::angleZeroTo2Pi(-2.5*M_PI), 1.5*M_PI);
+ BOOST_CHECK_EQUAL(Herwig::Math::angleZeroTo2Pi( 2.5*M_PI), 0.5*M_PI);
+ BOOST_CHECK(Herwig::Math::angleZeroTo2Pi( 2.5*M_PI) != 1*M_PI);
+}
+
+BOOST_AUTO_TEST_CASE(angleMinusPiToPi)
+{
+ BOOST_CHECK_EQUAL(Herwig::Math::angleMinusPiToPi(0.), 0.);
+ BOOST_CHECK_EQUAL(Herwig::Math::angleMinusPiToPi(-0.5*M_PI), -0.5*M_PI);
+ BOOST_CHECK_EQUAL(Herwig::Math::angleMinusPiToPi(-2.5*M_PI), -0.5*M_PI);
+ BOOST_CHECK_EQUAL(Herwig::Math::angleMinusPiToPi( 2.5*M_PI), 0.5*M_PI);
+ BOOST_CHECK(Herwig::Math::angleMinusPiToPi( 2.5*M_PI) != 1*M_PI);
+}
+
+BOOST_AUTO_TEST_CASE(median)
+{
+ std::vector<double> medianTest1;
+ medianTest1.push_back(10);
+ medianTest1.push_back(-1);
+ medianTest1.push_back(5);
+ BOOST_CHECK_EQUAL(Herwig::Math::median<double>(medianTest1), 5);
+
+ std::vector<double> medianTest2;
+ medianTest2.push_back(-10);
+ medianTest2.push_back(-1);
+ medianTest2.push_back(-5);
+ medianTest2.push_back(-6);
+ BOOST_CHECK_EQUAL(Herwig::Math::median<double>(medianTest2), -6);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+#endif /* HERWIG_Utilities_Test_Maths_H */
\ No newline at end of file
diff --git a/src/defaults/Hadronization.in b/src/defaults/Hadronization.in
--- a/src/defaults/Hadronization.in
+++ b/src/defaults/Hadronization.in
@@ -1,142 +1,146 @@
# -*- ThePEG-repository -*-
############################################################
# Setup of default hadronization
#
# There are no user servicable parts inside.
#
# Anything that follows below should only be touched if you
# know what you're doing.
#############################################################
cd /Herwig/Particles
create ThePEG::ParticleData Cluster
setup Cluster 81 Cluster 0.00990 0.0 0.0 0.0 0 0 0 1
create ThePEG::ParticleData Remnant
setup Remnant 82 Remnant 0.00990 0.0 0.0 0.0 0 0 0 1
mkdir /Herwig/Hadronization
cd /Herwig/Hadronization
create Herwig::ClusterHadronizationHandler ClusterHadHandler
create Herwig::PartonSplitter PartonSplitter
create Herwig::ClusterFinder ClusterFinder
create Herwig::ColourReconnector ColourReconnector
create Herwig::ClusterFissioner ClusterFissioner
create Herwig::LightClusterDecayer LightClusterDecayer
create Herwig::ClusterDecayer ClusterDecayer
create Herwig::HwppSelector SMHadronSpectrum
newdef ClusterHadHandler:PartonSplitter PartonSplitter
newdef ClusterHadHandler:ClusterFinder ClusterFinder
newdef ClusterHadHandler:ColourReconnector ColourReconnector
newdef ClusterHadHandler:ClusterFissioner ClusterFissioner
newdef ClusterHadHandler:LightClusterDecayer LightClusterDecayer
newdef ClusterHadHandler:ClusterDecayer ClusterDecayer
do ClusterHadHandler:UseHandlersForInteraction QCD
newdef ClusterHadHandler:MinVirtuality2 0.1*GeV2
newdef ClusterHadHandler:MaxDisplacement 1.0e-10*millimeter
newdef ClusterHadHandler:UnderlyingEventHandler NULL
newdef PartonSplitter:HadronSpectrum SMHadronSpectrum
newdef ClusterFinder:HadronSpectrum SMHadronSpectrum
newdef ClusterFissioner:HadronSpectrum SMHadronSpectrum
newdef ClusterDecayer:HadronSpectrum SMHadronSpectrum
newdef LightClusterDecayer:HadronSpectrum SMHadronSpectrum
# ColourReconnector Default Parameters
newdef ColourReconnector:ColourReconnection Yes
newdef ColourReconnector:Algorithm Baryonic
# Statistical CR Parameters:
newdef ColourReconnector:AnnealingFactor 0.9
newdef ColourReconnector:AnnealingSteps 50
newdef ColourReconnector:TriesPerStepFactor 5.0
newdef ColourReconnector:InitialTemperature 0.1
# Plain and Baryonic CR Paramters
newdef ColourReconnector:ReconnectionProbability 0.95
newdef ColourReconnector:ReconnectionProbabilityBaryonic 0.7
# BaryonicMesonic and BaryonicMesonic CR Paramters
newdef ColourReconnector:ReconnectionProbability3Mto3M 0.5
newdef ColourReconnector:ReconnectionProbability3MtoBBbar 0.5
newdef ColourReconnector:ReconnectionProbabilityBbarBto3M 0.5
newdef ColourReconnector:ReconnectionProbability2Bto2B 0.05
newdef ColourReconnector:ReconnectionProbabilityMBtoMB 0.5
newdef ColourReconnector:StepFactor 1.0
newdef ColourReconnector:MesonToBaryonFactor 1.333
# General Parameters and switches
newdef ColourReconnector:MaxDistance 1.0e50
newdef ColourReconnector:OctetTreatment All
newdef ColourReconnector:CR2BeamClusters No
newdef ColourReconnector:Junction Yes
newdef ColourReconnector:PrePlainCR No
newdef ColourReconnector:LocalCR No
newdef ColourReconnector:CausalCR No
# Debugging
newdef ColourReconnector:Debug No
# set ClusterFissioner parameters
-set /Herwig/Hadronization/ClusterFissioner:KinematicThreshold Dynamic
-set /Herwig/Hadronization/ClusterFissioner:KineticThresholdShift 0.08844
-set /Herwig/Hadronization/ClusterFissioner:ProbablityPowerFactor 6.486
-set /Herwig/Hadronization/ClusterFissioner:ProbablityShift -0.87875
+newdef ClusterFissioner:KinematicThreshold Dynamic
+newdef ClusterFissioner:KineticThresholdShift 0.08844
+newdef ClusterFissioner:ProbabilityPowerFactor 6.486
+newdef ClusterFissioner:ProbabilityShift -0.87875
# Clustering parameters for light quarks
-newdef ClusterFissioner:ClMaxLight 3.528693
+newdef ClusterFissioner:ClMaxLight 3.528693*GeV
newdef ClusterFissioner:ClPowLight 1.849375
newdef ClusterFissioner:PSplitLight 0.914156
insert ClusterFissioner:FissionPwt 1 1.0
insert ClusterFissioner:FissionPwt 2 1.0
-insert ClusterFissioner:FissionPwt 3 0.5
+insert ClusterFissioner:FissionPwt 3 0.374094
newdef ClusterDecayer:ClDirLight 1
newdef ClusterDecayer:ClSmrLight 0.78
# Clustering parameters for b-quarks
insert ClusterFissioner:ClMaxHeavy 5 3.757*GeV
-insert ClusterFissioner:ClPowHeavy 5 0.547*GeV
-insert ClusterFissioner:PSplitHeavy 5 0.625*GeV
+insert ClusterFissioner:ClPowHeavy 5 0.547
+insert ClusterFissioner:PSplitHeavy 5 0.625
insert ClusterDecayer:ClDirHeavy 5 1
insert ClusterDecayer:ClSmrHeavy 5 0.078
newdef SMHadronSpectrum:SingleHadronLimitBottom 0.000
# Clustering parameters for c-quarks
insert ClusterFissioner:ClMaxHeavy 4 3.950*GeV
-insert ClusterFissioner:ClPowHeavy 4 2.559*GeV
-insert ClusterFissioner:PSplitHeavy 4 0.994*GeV
+insert ClusterFissioner:ClPowHeavy 4 2.559
+insert ClusterFissioner:PSplitHeavy 4 0.994
insert ClusterDecayer:ClDirHeavy 4 1
insert ClusterDecayer:ClSmrHeavy 4 0.163
newdef SMHadronSpectrum:SingleHadronLimitCharm 0.000
+# Cluster Paramters for light Diquark Cluster
+# currently set according to Light quark defaults
+newdef ClusterFissioner:ClMaxDiquark 3.528693*GeV
+newdef ClusterFissioner:ClPowDiquark 1.849375
# Clustering parameters for exotic quarks
# (e.g. hadronizing Susy particles)
newdef ClusterFissioner:ClMaxExotic 2.7*GeV
newdef ClusterFissioner:ClPowExotic 1.46
newdef ClusterFissioner:PSplitExotic 1.00
newdef ClusterDecayer:ClDirExotic 1
newdef ClusterDecayer:ClSmrExotic 0.
newdef SMHadronSpectrum:SingleHadronLimitExotic 0.
#
insert PartonSplitter:SplitPwt 1 1.0
insert PartonSplitter:SplitPwt 2 1.0
insert PartonSplitter:SplitPwt 3 0.824135
newdef PartonSplitter:Split Light
#
newdef SMHadronSpectrum:PwtDquark 1.0
newdef SMHadronSpectrum:PwtUquark 1.0
newdef SMHadronSpectrum:PwtSquark 0.374094
newdef SMHadronSpectrum:PwtCquark 0.0
newdef SMHadronSpectrum:PwtBquark 0.0
newdef SMHadronSpectrum:PwtDIquark 0.33107
newdef SMHadronSpectrum:SngWt 0.89050
newdef SMHadronSpectrum:DecWt 0.41628
newdef SMHadronSpectrum:Mode 1
newdef SMHadronSpectrum:BelowThreshold All
create Herwig::SpinHadronizer SpinHadronizer

File Metadata

Mime Type
text/x-diff
Expires
Tue, Nov 19, 8:54 PM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3806175
Default Alt Text
(340 KB)

Event Timeline