Page MenuHomeHEPForge

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/Decay/General/FFSDecayer.cc b/Decay/General/FFSDecayer.cc
--- a/Decay/General/FFSDecayer.cc
+++ b/Decay/General/FFSDecayer.cc
@@ -1,463 +1,465 @@
// -*- C++ -*-
//
// FFSDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 FFSDecayer class.
//
#include "FFSDecayer.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/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr FFSDecayer::clone() const {
return new_ptr(*this);
}
IBPtr FFSDecayer::fullclone() const {
return new_ptr(*this);
}
void FFSDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> ) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractFFSVertexPtr>(vert));
perturbativeVertex_ .push_back(dynamic_ptr_cast<FFSVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(inV.at(inter));
outgoingVertexF_[inter] = AbstractFFVVertexPtr();
outgoingVertexS_[inter] = AbstractVSSVertexPtr();
if(outV[0].at(inter)) {
if (outV[0].at(inter)->getName()==VertexType::FFV)
outgoingVertexF_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(outV[0].at(inter));
else
outgoingVertexS_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[0].at(inter));
}
if(outV[1].at(inter)) {
if (outV[1].at(inter)->getName()==VertexType::FFV)
outgoingVertexF_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(outV[1].at(inter));
else
outgoingVertexS_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[1].at(inter));
}
}
}
void FFSDecayer::persistentOutput(PersistentOStream & os) const {
os << perturbativeVertex_ << vertex_
<< incomingVertex_ << outgoingVertexF_
<< outgoingVertexS_;
}
void FFSDecayer::persistentInput(PersistentIStream & is, int) {
is >> perturbativeVertex_ >> vertex_
>> incomingVertex_ >> outgoingVertexF_
>> outgoingVertexS_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<FFSDecayer,GeneralTwoBodyDecayer>
describeHerwigFFSDecayer("Herwig::FFSDecayer", "Herwig.so");
void FFSDecayer::Init() {
static ClassDocumentation<FFSDecayer> documentation
("The FFSDecayer class implements the decay of a fermion to "
"a fermion and a scalar.");
}
double FFSDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,PDT::Spin0)));
//Need to use different barred or unbarred spinors depending on
//whether particle is cc or not.
int itype[2];
if(inpart.dataPtr()->CC()) itype[0] = inpart.id() > 0? 0:1;
else itype[0] = 2;
if(decay[0]->dataPtr()->CC()) itype[1] = decay[0]->id() > 0? 0:1;
else itype[1] = 2;
bool ferm(itype[0] == 0 || itype[1] == 0 || (itype[0] == 2 && itype[1] == 2));
if(meopt==Initialize) {
// spinors and rho
if(ferm) {
SpinorWaveFunction ::calculateWaveFunctions(wave_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wave_[0].wave().Type() != SpinorType::u)
for(unsigned int ix = 0; ix < 2; ++ix) wave_ [ix].conjugate();
}
else {
SpinorBarWaveFunction::calculateWaveFunctions(wavebar_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wavebar_[0].wave().Type() != SpinorType::v)
for(unsigned int ix = 0; ix < 2; ++ix) wavebar_[ix].conjugate();
}
+ // fix rho if no correlations
+ fixRho(rho_);
}
// setup spin info when needed
if(meopt==Terminate) {
// for the decaying particle
if(ferm) {
SpinorWaveFunction::
constructSpinInfo(wave_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorBarWaveFunction::constructSpinInfo(wavebar_,decay[0],outgoing,true);
}
else {
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorWaveFunction::constructSpinInfo(wave_,decay[0],outgoing,true);
}
ScalarWaveFunction::constructSpinInfo(decay[1],outgoing,true);
}
if(ferm)
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar_,decay[0],outgoing);
else
SpinorWaveFunction::
calculateWaveFunctions(wave_ ,decay[0],outgoing);
ScalarWaveFunction scal(decay[1]->momentum(),decay[1]->dataPtr(),outgoing);
Energy2 scale(sqr(inpart.mass()));
for(unsigned int if1 = 0; if1 < 2; ++if1) {
for(unsigned int if2 = 0; if2 < 2; ++if2) {
if(ferm) (*ME())(if1, if2, 0) = 0.;
else (*ME())(if2, if1, 0) = 0.;
for(auto vert : vertex_) {
if(ferm) (*ME())(if1, if2, 0) +=
vert->evaluate(scale,wave_[if1],wavebar_[if2],scal);
else (*ME())(if2, if1, 0) +=
vert->evaluate(scale,wave_[if1],wavebar_[if2],scal);
}
}
}
double output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy FFSDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
double mu1(0.),mu2(0.);
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
if(outa.first->iSpin() == PDT::Spin1Half) {
mu1 = outa.second/inpart.second;
mu2 = outb.second/inpart.second;
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in, outa.first, outb.first);
}
else {
mu1 = outb.second/inpart.second;
mu2 = outa.second/inpart.second;
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in, outb.first, outa.first);
}
double c2 = norm(perturbativeVertex_[0]->norm());
Complex cl = perturbativeVertex_[0]->left();
Complex cr = perturbativeVertex_[0]->right();
double me2 = c2*( (norm(cl) + norm(cr))*(1. + sqr(mu1) - sqr(mu2))
+ 2.*mu1*(conj(cl)*cr + conj(cr)*cl).real() );
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second, outa.second,
outb.second);
Energy output = me2*pcm/16./Constants::pi;
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double FFSDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
int iscal (0), iferm (1), iglu (2);
// get location of outgoing fermion/scalar
if(decay[1]->dataPtr()->iSpin()==PDT::Spin0) swap(iscal,iferm);
// work out whether inpart is a fermion or antifermion
int itype[2];
if(inpart.dataPtr()->CC()) itype[0] = inpart.id() > 0 ? 0 : 1;
else itype[0] = 2;
if(decay[iferm]->dataPtr()->CC()) itype[1] = decay[iferm]->id() > 0 ? 0 : 1;
else itype[1] = 2;
bool ferm(false);
if(itype[0] == itype[1] ) {
ferm = itype[0]==0 || (itype[0]==2 && decay[iscal]->id() < 0);
}
else if(itype[0] == 2) {
ferm = itype[1]==0;
}
else if(itype[1] == 2) {
ferm = itype[0]==0;
}
else if((itype[0] == 1 && itype[1] == 0) ||
(itype[0] == 0 && itype[1] == 1)) {
if(abs(inpart.id())<=16) {
ferm = itype[0]==0;
}
else if(abs(decay[iferm]->id())<=16) {
ferm = itype[1]==0;
}
else {
ferm = true;
}
}
else
assert(false);
if(meopt==Initialize) {
// create spinor (bar) for decaying particle
if(ferm) {
SpinorWaveFunction::calculateWaveFunctions(wave3_, rho3_, const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wave3_[0].wave().Type() != SpinorType::u)
for(unsigned int ix = 0; ix < 2; ++ix) wave3_[ix].conjugate();
}
else {
SpinorBarWaveFunction::calculateWaveFunctions(wavebar3_,rho3_, const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wavebar3_[0].wave().Type() != SpinorType::v)
for(unsigned int ix = 0; ix < 2; ++ix) wavebar3_[ix].conjugate();
}
}
// setup spin information when needed
if(meopt==Terminate) {
if(ferm) {
SpinorWaveFunction::
constructSpinInfo(wave3_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorBarWaveFunction::constructSpinInfo(wavebar3_,decay[iferm],outgoing,true);
}
else {
SpinorBarWaveFunction::
constructSpinInfo(wavebar3_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorWaveFunction::constructSpinInfo(wave3_,decay[iferm],outgoing,true);
}
ScalarWaveFunction::constructSpinInfo( decay[iscal],outgoing,true);
VectorWaveFunction::constructSpinInfo(gluon_, decay[iglu ],outgoing,true,false);
return 0.;
}
// calulate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half, PDT::Spin0,
PDT::Spin1Half, PDT::Spin1)));
// create wavefunctions
if (ferm) SpinorBarWaveFunction::calculateWaveFunctions(wavebar3_, decay[iferm],outgoing);
else SpinorWaveFunction:: calculateWaveFunctions(wave3_ , decay[iferm],outgoing);
ScalarWaveFunction swave3_(decay[iscal]->momentum(), decay[iscal]->dataPtr(),outgoing);
VectorWaveFunction::calculateWaveFunctions(gluon_, decay[iglu ],outgoing,true);
// gauge invariance test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[iglu ]->momentum(),decay[iglu ]->dataPtr(),10,
outgoing));
}
}
#endif
if (! ((incomingVertex_[inter] && (outgoingVertexF_[inter] || outgoingVertexS_[inter])) ||
(outgoingVertexF_[inter] && outgoingVertexS_[inter])))
throw Exception()
<< "Invalid vertices for radiation in FFS decay in FFSDecayer::threeBodyME"
<< Exception::runerror;
// sort out colour flows
int F(1), S(2);
if (decay[iscal]->dataPtr()->iColour()==PDT::Colour3 &&
decay[iferm]->dataPtr()->iColour()==PDT::Colour8)
swap(F,S);
else if (decay[iferm]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[iscal]->dataPtr()->iColour()==PDT::Colour8)
swap(F,S);
Complex diag;
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int ifi = 0; ifi < 2; ++ifi) {
for(unsigned int ifo = 0; ifo < 2; ++ifo) {
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming fermion
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
if (ferm) {
SpinorWaveFunction spinorInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),wave3_[ifi],
gluon_[2*ig],inpart.mass());
assert(wave3_[ifi].particle()->id()==spinorInter.particle()->id());
diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,spinorInter,wavebar3_[ifo],swave3_);
}
else {
SpinorBarWaveFunction spinorBarInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),wavebar3_[ifi],
gluon_[2*ig],inpart.mass());
assert(wavebar3_[ifi].particle()->id()==spinorBarInter.particle()->id());
diag = 0.;
for(auto vertex :vertex_)
diag+= vertex->evaluate(scale,wave3_[ifo], spinorBarInter,swave3_);
}
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(ifi, 0, ifo, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing fermion
if((decay[iferm]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[iferm]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexF_[inter]);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[iferm]->dataPtr();
if(off->CC()) off = off->CC();
if (ferm) {
SpinorBarWaveFunction spinorBarInter =
outgoingVertexF_[inter]->evaluate(scale,3,off,wavebar3_[ifo],
gluon_[2*ig],decay[iferm]->mass());
assert(wavebar3_[ifo].particle()->id()==spinorBarInter.particle()->id());
diag = 0.;
for(auto vertex :vertex_)
diag+= vertex->evaluate(scale,wave3_[ifi],spinorBarInter,swave3_);
}
else {
SpinorWaveFunction spinorInter =
outgoingVertexF_[inter]->evaluate(scale,3,off,wave3_[ifo],
gluon_[2*ig],decay[iferm]->mass());
assert(wave3_[ifo].particle()->id()==spinorInter.particle()->id());
diag = 0.;
for(auto vertex :vertex_)
diag+= vertex->evaluate(scale,spinorInter,wavebar3_[ifi],swave3_);
}
if(!couplingSet) {
gs = abs(outgoingVertexF_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[F].size();++ix) {
(*ME[colourFlow[F][ix].first])(ifi, 0, ifo, ig) +=
colourFlow[F][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing scalar
if((decay[iscal]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[iscal]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexS_[inter]);
// ensure you get correct ougoing particle from first vertex
tcPDPtr off = decay[iscal]->dataPtr();
if(off->CC()) off = off->CC();
ScalarWaveFunction scalarInter =
outgoingVertexS_[inter]->evaluate(scale,3,off,gluon_[2*ig],
swave3_,decay[iscal]->mass());
assert(swave3_.particle()->id()==scalarInter.particle()->id());
if (ferm){
diag = 0.;
for(auto vertex :vertex_)
diag += vertex->evaluate(scale,wave3_[ifi],wavebar3_[ifo],scalarInter);
}
else {
diag = 0.;
for(auto vertex :vertex_)
diag += vertex->evaluate(scale,wave3_[ifo],wavebar3_[ifi],scalarInter);
}
if(!couplingSet) {
gs = abs(outgoingVertexS_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[S].size();++ix) {
(*ME[colourFlow[S][ix].first])(ifi, 0, ifo, ig) +=
colourFlow[S][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha(S,EM)
output*=(4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
diff --git a/Decay/General/FFVCurrentDecayer.cc b/Decay/General/FFVCurrentDecayer.cc
--- a/Decay/General/FFVCurrentDecayer.cc
+++ b/Decay/General/FFVCurrentDecayer.cc
@@ -1,200 +1,202 @@
// -*- C++ -*-
//
// FFVCurrentDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 FFVCurrentDecayer class.
//
#include "FFVCurrentDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "ThePEG/StandardModel/StandardModelBase.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using ThePEG::Helicity::VectorWaveFunction;
using ThePEG::Helicity::SpinorWaveFunction;
using ThePEG::Helicity::SpinorBarWaveFunction;
using ThePEG::Helicity::Direction;
using ThePEG::Helicity::incoming;
using ThePEG::Helicity::outgoing;
IBPtr FFVCurrentDecayer::clone() const {
return new_ptr(*this);
}
IBPtr FFVCurrentDecayer::fullclone() const {
return new_ptr(*this);
}
void FFVCurrentDecayer::doinit() {
FFVPtr_ = dynamic_ptr_cast<FFVVertexPtr>(vertex());
GeneralCurrentDecayer::doinit();
}
void FFVCurrentDecayer::rebind(const TranslationMap & trans)
{
FFVPtr_ = trans.translate(FFVPtr_);
GeneralCurrentDecayer::rebind(trans);
}
IVector FFVCurrentDecayer::getReferences() {
IVector ret = GeneralCurrentDecayer::getReferences();
ret.push_back(FFVPtr_);
return ret;
}
void FFVCurrentDecayer::persistentOutput(PersistentOStream & os) const {
os << FFVPtr_;
}
void FFVCurrentDecayer::persistentInput(PersistentIStream & is, int) {
is >> FFVPtr_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<FFVCurrentDecayer,GeneralCurrentDecayer>
describeHerwigFFVCurrentDecayer("Herwig::FFVCurrentDecayer", "Herwig.so");
void FFVCurrentDecayer::Init() {
static ClassDocumentation<FFVCurrentDecayer> documentation
("There is no documentation for the FFVCurrentDecayer class");
}
double FFVCurrentDecayer::me2(const int ichan, const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
// get the particles for the hadronic curret
Energy q;
ParticleVector hadpart(decay.begin()+1,decay.end());
// fermion types
int itype[2];
if(inpart.dataPtr()->CC()) itype[0] = inpart.id() > 0 ? 0 : 1;
else itype[0] = 2;
if(decay[0]->dataPtr()->CC()) itype[1] = decay[0]->id() > 0 ? 0 : 1;
else itype[1] = 2;
//Need to use different barred or unbarred spinors depending on
//whether particle is cc or not.
bool ferm(itype[0] == 0 || itype[1] == 0 || (itype[0] == 2 && itype[1] == 2));
if(meopt==Initialize) {
// spinors and rho
if(ferm) {
SpinorWaveFunction ::calculateWaveFunctions(wave_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wave_[0].wave().Type() != SpinorType::u)
for(unsigned int ix = 0; ix < 2; ++ix) wave_ [ix].conjugate();
}
else {
SpinorBarWaveFunction::calculateWaveFunctions(wavebar_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wavebar_[0].wave().Type() != SpinorType::v)
for(unsigned int ix = 0; ix < 2; ++ix) wavebar_[ix].conjugate();
}
+ // fix rho if no correlations
+ fixRho(rho_);
}
// setup spin info when needed
if(meopt==Terminate) {
// for the decaying particle
if(ferm) {
SpinorWaveFunction::
constructSpinInfo(wave_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorBarWaveFunction::constructSpinInfo(wavebar_,decay[0],outgoing,true);
}
else {
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorWaveFunction::constructSpinInfo(wave_,decay[0],outgoing,true);
}
weakCurrent()->current(mode(),ichan,q,hadpart,meopt);
return 0.;
}
Energy2 scale(sqr(inpart.mass()));
if(ferm)
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar_,decay[0],outgoing);
else
SpinorWaveFunction::
calculateWaveFunctions(wave_ ,decay[0],outgoing);
// calculate the hadron current
vector<LorentzPolarizationVectorE>
hadron(weakCurrent()->current(mode(),ichan,q,hadpart,meopt));
// prefactor
double pre = sqr(pow(inpart.mass()/q,int(hadpart.size()-2)));
// work out the mapping for the hadron vector
vector<unsigned int> constants(decay.size()+1),ihel(decay.size()+1);
vector<PDT::Spin> ispin(decay.size());
int itemp(1);
unsigned int hhel,ix(decay.size());
do {
--ix;
ispin[ix]=decay[ix]->data().iSpin();
itemp*=ispin[ix];
constants[ix]=itemp;
}
while(ix>0);
constants[decay.size()]=1;
constants[0]=constants[1];
// compute the matrix element
GeneralDecayMEPtr newME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,ispin)));
VectorWaveFunction vWave;
tcPDPtr vec= inpart.dataPtr()->iCharge()-decay[0]->dataPtr()->iCharge() > 0
? getParticleData(ParticleID::Wplus) : getParticleData(ParticleID::Wminus);
Lorentz5Momentum vmom=inpart.momentum()-decay[0]->momentum();
vmom.rescaleMass();
for(hhel=0;hhel<hadron.size();++hhel) {
// map the index for the hadrons to a helicity state
for(ix=decay.size();ix>1;--ix) ihel[ix]=(hhel%constants[ix-1])/constants[ix];
vWave=VectorWaveFunction(vmom,vec,hadron[hhel]*UnitRemoval::InvE,outgoing);
for(unsigned int if1 = 0; if1 < 2; ++if1) {
for(unsigned int if2 = 0; if2 < 2; ++if2) {
ihel[0]=if1;
ihel[1]=if2;
if(!ferm) swap(ihel[0],ihel[1]);
(*newME)(ihel) = FFVPtr_->evaluate(scale,wave_[if1],wavebar_[if2],vWave);
}
}
}
// store the matrix element
ME(newME);
// multiply by the CKM element
int iq,ia;
weakCurrent()->decayModeInfo(mode(),iq,ia);
double ckm(1.);
if(iq<=6) {
if(iq%2==0) ckm = SM().CKM(iq/2-1,(abs(ia)-1)/2);
else ckm = SM().CKM(abs(ia)/2-1,(iq-1)/2);
}
pre /= 0.125*sqr(FFVPtr_->weakCoupling(scale));
double output(0.5*pre*ckm*(ME()->contract(rho_)).real()*
sqr(SM().fermiConstant()*UnitRemoval::E2));
return output;
}
Energy FFVCurrentDecayer::partialWidth(tPDPtr inpart, tPDPtr outa,
vector<tPDPtr> currout) {
vector<long> id;
id.push_back(inpart->id());
id.push_back(outa->id());
for(unsigned int ix=0;ix<currout.size();++ix) id.push_back(currout[ix]->id());
bool cc;
int mode=modeNumber(cc,id);
imode(mode);
return initializePhaseSpaceMode(mode,true,true);
}
diff --git a/Decay/General/FFVDecayer.cc b/Decay/General/FFVDecayer.cc
--- a/Decay/General/FFVDecayer.cc
+++ b/Decay/General/FFVDecayer.cc
@@ -1,456 +1,458 @@
// -*- C++ -*-
//
// FFVDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 FFVDecayer class.
//
#include "FFVDecayer.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/Helicity/WaveFunction/VectorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr FFVDecayer::clone() const {
return new_ptr(*this);
}
IBPtr FFVDecayer::fullclone() const {
return new_ptr(*this);
}
void FFVDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> ) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractFFVVertexPtr>(vert));
perturbativeVertex_ .push_back(dynamic_ptr_cast<FFVVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(inV.at(inter));
if(outV[0].at(inter)) {
if (outV[0].at(inter)->getName()==VertexType::FFV)
outgoingVertexF_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(outV[0].at(inter));
else
outgoingVertexV_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[0].at(inter));
}
if(outV[1].at(inter)) {
if (outV[1].at(inter)->getName()==VertexType::FFV)
outgoingVertexF_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(outV[1].at(inter));
else
outgoingVertexV_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[1].at(inter));
}
}
}
void FFVDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< incomingVertex_ << outgoingVertexF_
<< outgoingVertexV_;
}
void FFVDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> incomingVertex_ >> outgoingVertexF_
>> outgoingVertexV_;
}
double FFVDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,PDT::Spin1)));
// type of process
int itype[2];
if(inpart.dataPtr()->CC()) itype[0] = inpart.id() > 0 ? 0 : 1;
else itype[0] = 2;
if(decay[0]->dataPtr()->CC()) itype[1] = decay[0]->id() > 0 ? 0 : 1;
else itype[1] = 2;
//Need to use different barred or unbarred spinors depending on
//whether particle is cc or not.
bool ferm(itype[0] == 0 || itype[1] == 0 || (itype[0] == 2 && itype[1] == 2));
if(meopt==Initialize) {
// spinors and rho
if(ferm) {
SpinorWaveFunction ::calculateWaveFunctions(wave_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wave_[0].wave().Type() != SpinorType::u)
for(unsigned int ix = 0; ix < 2; ++ix) wave_ [ix].conjugate();
}
else {
SpinorBarWaveFunction::calculateWaveFunctions(wavebar_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wavebar_[0].wave().Type() != SpinorType::v)
for(unsigned int ix = 0; ix < 2; ++ix) wavebar_[ix].conjugate();
}
+ // fix rho if no correlations
+ fixRho(rho_);
}
// setup spin info when needed
if(meopt==Terminate) {
// for the decaying particle
if(ferm) {
SpinorWaveFunction::
constructSpinInfo(wave_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorBarWaveFunction::constructSpinInfo(wavebar_,decay[0],outgoing,true);
}
else {
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorWaveFunction::constructSpinInfo(wave_,decay[0],outgoing,true);
}
VectorWaveFunction::
constructSpinInfo(vector_,decay[1],outgoing,true,false);
}
Energy2 scale(sqr(inpart.mass()));
if(ferm)
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar_,decay[0],outgoing);
else
SpinorWaveFunction::
calculateWaveFunctions(wave_ ,decay[0],outgoing);
bool massless = decay[1]->dataPtr()->mass()==ZERO;
VectorWaveFunction::
calculateWaveFunctions(vector_,decay[1],outgoing,massless);
for(unsigned int if1 = 0; if1 < 2; ++if1) {
for(unsigned int if2 = 0; if2 < 2; ++if2) {
for(unsigned int vhel = 0; vhel < 3; ++vhel) {
if(massless && vhel == 1) ++vhel;
if(ferm)
(*ME())(if1, if2,vhel) = 0.;
else
(*ME())(if2, if1, vhel) = 0.;
for(auto vertex : vertex_) {
if(ferm)
(*ME())(if1, if2,vhel) +=
vertex->evaluate(scale,wave_[if1],wavebar_[if2],vector_[vhel]);
else
(*ME())(if2, if1, vhel) +=
vertex->evaluate(scale,wave_[if1],wavebar_[if2],vector_[vhel]);
}
}
}
}
double output=(ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),decay[1]->dataPtr());
// return the answer
return output;
}
Energy FFVDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
double mu1(outa.second/inpart.second),mu2(outb.second/inpart.second);
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
if( outa.first->iSpin() == PDT::Spin1Half)
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in,
outa.first, outb.first);
else {
swap(mu1,mu2);
perturbativeVertex_[0]->setCoupling(sqr(inpart.second),in,
outb.first,outa.first);
}
Complex cl(perturbativeVertex_[0]->left()),cr(perturbativeVertex_[0]->right());
double me2(0.);
if( mu2 > 0. ) {
me2 = (norm(cl) + norm(cr))*(1. + sqr(mu1*mu2) + sqr(mu2)
- 2.*sqr(mu1) - 2.*sqr(mu2*mu2)
+ sqr(mu1*mu1))
- 6.*mu1*sqr(mu2)*(conj(cl)*cr + conj(cr)*cl).real();
me2 /= sqr(mu2);
}
else
me2 = 2.*( (norm(cl) + norm(cr))*(sqr(mu1) + 1.)
- 4.*mu1*(conj(cl)*cr + conj(cr)*cl).real() );
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second, outa.second,
outb.second);
Energy output = norm(perturbativeVertex_[0]->norm())*me2*pcm/16./Constants::pi;
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<FFVDecayer,GeneralTwoBodyDecayer>
describeHerwigFFVDecayer("Herwig::FFVDecayer", "Herwig.so");
void FFVDecayer::Init() {
static ClassDocumentation<FFVDecayer> documentation
("The FFVDecayer class implements the decay of a fermion to a fermion and a vector boson");
}
double FFVDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
int iferm (0), ivect (1), iglu (2);
// get location of outgoing lepton/vector
if(decay[1]->dataPtr()->iSpin()==PDT::Spin1Half) swap(iferm,ivect);
// work out whether inpart is a fermion or antifermion
int itype[2];
if(inpart.dataPtr()->CC()) itype[0] = inpart.id() > 0 ? 0 : 1;
else itype[0] = 2;
if(decay[iferm]->dataPtr()->CC()) itype[1] = decay[iferm]->id() > 0 ? 0 : 1;
else itype[1] = 2;
bool ferm(itype[0] == 0 || itype[1] == 0 ||
(itype[0] == 2 && itype[1] == 2 && decay[ivect]->id() < 0));
// no emissions from massive vectors
bool massless = decay[ivect]->dataPtr()->mass()==ZERO;
if(meopt==Initialize) {
// create spinor (bar) for decaying particle
if(ferm) {
SpinorWaveFunction::calculateWaveFunctions(wave3_, rho3_, const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wave3_[0].wave().Type() != SpinorType::u)
for(unsigned int ix = 0; ix < 2; ++ix) wave3_[ix].conjugate();
}
else {
SpinorBarWaveFunction::calculateWaveFunctions(wavebar3_,rho3_, const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wavebar3_[0].wave().Type() != SpinorType::v)
for(unsigned int ix = 0; ix < 2; ++ix) wavebar3_[ix].conjugate();
}
}
// setup spin information when needed
if(meopt==Terminate) {
if(ferm) {
SpinorWaveFunction::
constructSpinInfo(wave3_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorBarWaveFunction::constructSpinInfo(wavebar3_,decay[iferm],outgoing,true);
}
else {
SpinorBarWaveFunction::
constructSpinInfo(wavebar3_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorWaveFunction::constructSpinInfo(wave3_,decay[iferm],outgoing,true);
}
VectorWaveFunction::constructSpinInfo(vector3_, decay[ivect],outgoing,true,massless);
VectorWaveFunction::constructSpinInfo(gluon_, decay[iglu ],outgoing,true,false);
return 0.;
}
// calulate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half, PDT::Spin1Half,
PDT::Spin1, PDT::Spin1)));
// create wavefunctions
if (ferm) SpinorBarWaveFunction::calculateWaveFunctions(wavebar3_, decay[iferm],outgoing);
else SpinorWaveFunction:: calculateWaveFunctions(wave3_ , decay[iferm],outgoing);
VectorWaveFunction::calculateWaveFunctions(vector3_, decay[ivect],outgoing,massless);
VectorWaveFunction::calculateWaveFunctions(gluon_, decay[iglu ],outgoing,true );
// gauge invariance test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[iglu ]->momentum(),
decay[iglu ]->dataPtr(),10,
outgoing));
}
}
#endif
if (! ((incomingVertex_[inter] && (outgoingVertexF_[inter] || outgoingVertexV_[inter])) ||
(outgoingVertexF_[inter] && outgoingVertexV_[inter])))
throw Exception()
<< "Invalid vertices for QCD radiation in FFV decay in FFVDecayer::threeBodyME"
<< Exception::runerror;
// sort out colour flows
int F(1), V(2);
if (decay[iferm]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[ivect]->dataPtr()->iColour()==PDT::Colour8)
swap(F,V);
else if (decay[ivect]->dataPtr()->iColour()==PDT::Colour3 &&
decay[iferm]->dataPtr()->iColour()==PDT::Colour8)
swap(F,V);
Complex diag;
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow = colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int ifi = 0; ifi < 2; ++ifi) {
for(unsigned int ifo = 0; ifo < 2; ++ifo) {
for(unsigned int iv = 0; iv < 3; ++iv) {
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming fermion
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
if (ferm){
SpinorWaveFunction spinorInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),wave3_[ifi],
gluon_[2*ig],inpart.mass());
assert(wave3_[ifi].particle()->id()==spinorInter.particle()->id());
diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,spinorInter,wavebar3_[ifo],vector3_[iv]);
}
else {
SpinorBarWaveFunction spinorBarInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),wavebar3_[ifi],
gluon_[2*ig],inpart.mass());
assert(wavebar3_[ifi].particle()->id()==spinorBarInter.particle()->id());
diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,wave3_[ifo], spinorBarInter,vector3_[iv]);
}
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(ifi, ifo, iv, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing fermion
if((decay[iferm]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[iferm]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexF_[inter]);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[iferm]->dataPtr();
if(off->CC()) off = off->CC();
if (ferm) {
SpinorBarWaveFunction spinorBarInter =
outgoingVertexF_[inter]->evaluate(scale,3,off,wavebar3_[ifo],
gluon_[2*ig],decay[iferm]->mass());
assert(wavebar3_[ifo].particle()->id()==spinorBarInter.particle()->id());
diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,wave3_[ifi],spinorBarInter,vector3_[iv]);
}
else {
SpinorWaveFunction spinorInter =
outgoingVertexF_[inter]->evaluate(scale,3,off,wave3_[ifo],
gluon_[2*ig],decay[iferm]->mass());
assert(wave3_[ifo].particle()->id()==spinorInter.particle()->id());
diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,spinorInter,wavebar3_[ifi],vector3_[iv]);
}
if(!couplingSet) {
gs = abs(outgoingVertexF_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[F].size();++ix) {
(*ME[colourFlow[F][ix].first])(ifi, ifo, iv, ig) +=
colourFlow[F][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing vector
if((decay[ivect]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[ivect]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexV_[inter]);
// ensure you get correct ougoing particle from first vertex
tcPDPtr off = decay[ivect]->dataPtr();
if(off->CC()) off = off->CC();
VectorWaveFunction vectorInter =
outgoingVertexV_[inter]->evaluate(scale,3,off,gluon_[2*ig],
vector3_[iv],decay[ivect]->mass());
assert(vector3_[iv].particle()->id()==vectorInter.particle()->id());
if (ferm) {
diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,wave3_[ifi],wavebar3_[ifo],vectorInter);
}
else {
diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,wave3_[ifo],wavebar3_[ifi],vectorInter);
}
if(!couplingSet) {
gs = abs(outgoingVertexV_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[V].size();++ix) {
(*ME[colourFlow[V][ix].first])(ifi, ifo, iv, ig) +=
colourFlow[V][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
if(massless) ++iv;
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha(S,eM)
output *= (4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
diff --git a/Decay/General/FRSDecayer.cc b/Decay/General/FRSDecayer.cc
--- a/Decay/General/FRSDecayer.cc
+++ b/Decay/General/FRSDecayer.cc
@@ -1,189 +1,191 @@
// -*- C++ -*-
//
// FRSDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 FRSDecayer class.
//
#include "FRSDecayer.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/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr FRSDecayer::clone() const {
return new_ptr(*this);
}
IBPtr FRSDecayer::fullclone() const {
return new_ptr(*this);
}
void FRSDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> &,
const vector<map<ShowerInteraction,VertexBasePtr> > &,
map<ShowerInteraction,VertexBasePtr>) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractRFSVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<RFSVertexPtr> (vert));
}
}
void FRSDecayer::persistentOutput(PersistentOStream & os) const {
os << perturbativeVertex_ << vertex_;
}
void FRSDecayer::persistentInput(PersistentIStream & is, int) {
is >> perturbativeVertex_ >> vertex_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<FRSDecayer,GeneralTwoBodyDecayer>
describeHerwigFRSDecayer("Herwig::FRSDecayer", "Herwig.so");
void FRSDecayer::Init() {
static ClassDocumentation<FRSDecayer> documentation
("The FRSDecayer class implements the decay of a fermion to "
"a spin-3/2 fermion and a scalar.");
}
double FRSDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
bool ferm = inpart.id() > 0;
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,PDT::Spin3Half,PDT::Spin0)));
if(meopt==Initialize) {
// spinors and rho
if(ferm) {
SpinorWaveFunction ::calculateWaveFunctions(wave_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wave_[0].wave().Type() != SpinorType::u)
for(unsigned int ix = 0; ix < 2; ++ix) wave_ [ix].conjugate();
}
else {
SpinorBarWaveFunction::calculateWaveFunctions(wavebar_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wavebar_[0].wave().Type() != SpinorType::v)
for(unsigned int ix = 0; ix < 2; ++ix) wavebar_[ix].conjugate();
}
+ // fix rho if no correlations
+ fixRho(rho_);
}
// setup spin info when needed
if(meopt==Terminate) {
// for the decaying particle
if(ferm) {
SpinorWaveFunction::
constructSpinInfo(wave_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
RSSpinorBarWaveFunction::constructSpinInfo(RSwavebar_,decay[0],outgoing,true);
}
else {
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
RSSpinorWaveFunction::constructSpinInfo(RSwave_,decay[0],outgoing,true);
}
ScalarWaveFunction::constructSpinInfo(decay[1],outgoing,true);
}
if(ferm)
RSSpinorBarWaveFunction::
calculateWaveFunctions(RSwavebar_,decay[0],outgoing);
else
RSSpinorWaveFunction::
calculateWaveFunctions(RSwave_ ,decay[0],outgoing);
ScalarWaveFunction scal(decay[1]->momentum(),decay[1]->dataPtr(),outgoing);
Energy2 scale(sqr(inpart.mass()));
for(unsigned int if1 = 0; if1 < 2; ++if1) {
for(unsigned int if2 = 0; if2 < 4; ++if2) {
(*ME())(if1, if2, 0) = 0.;
for(auto vert : vertex_) {
if(ferm) (*ME())(if1, if2, 0) +=
vert->evaluate(scale,wave_[if1],RSwavebar_[if2],scal);
else (*ME())(if1, if2, 0) +=
vert->evaluate(scale,RSwave_[if2],wavebar_[if1],scal);
}
}
}
double output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// test code
// Energy q = inpart.mass();
// Energy m1 = decay[0]->mass();
// Energy m2 = decay[1]->mass();
// Energy2 q2(q*q),m12(m1*m1),m22(m2*m2);
// Energy2 pcm2(0.25*(q2*(q2-2.*m12-2.*m22)+(m12-m22)*(m12-m22))/q2);
// Energy pcm(sqrt(pcm2));
// Energy Qp(sqrt((q+m1)*(q+m1)-m22)),Qm(sqrt((q-m1)*(q-m1)-m22));
// double r23(sqrt(2./3.));
// // couplings
// Complex left = perturbativeVertex_-> left()*perturbativeVertex_-> norm();
// Complex right = perturbativeVertex_->right()*perturbativeVertex_-> norm();
// complex<InvEnergy> A1 = 0.5*(left+right)*UnitRemoval::InvE;
// complex<InvEnergy> B1 = 0.5*(right-left)*UnitRemoval::InvE;
// complex<Energy> h1(-2.*r23*pcm*q/m1*Qm*B1);
// complex<Energy> h2( 2.*r23*pcm*q/m1*Qp*A1);
// cout << "testing 1/2->3/2 0 "
// << output*scale/GeV2 << " "
// << real(h1*conj(h1)+h2*conj(h2))/4./GeV2 << " "
// << real(h1*conj(h1)+h2*conj(h2))/4./(output*scale) << endl;
// return the answer
return output;
}
Energy FRSDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
Energy q = inpart.second;
Energy m1 = outa.second;
Energy m2 = outb.second;
Energy2 q2(q*q),m12(m1*m1),m22(m2*m2);
Energy2 pcm2(0.25*(q2*(q2-2.*m12-2.*m22)+(m12-m22)*(m12-m22))/q2);
Energy pcm(sqrt(pcm2));
Energy Qp(sqrt((q+m1)*(q+m1)-m22)),Qm(sqrt((q-m1)*(q-m1)-m22));
double r23(sqrt(2./3.));
// couplings
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), outa.first,
in, outb.first);
Complex left = perturbativeVertex_[0]-> left()*perturbativeVertex_[0]-> norm();
Complex right = perturbativeVertex_[0]->right()*perturbativeVertex_[0]-> norm();
complex<InvEnergy> A1 = 0.5*(left+right)*UnitRemoval::InvE;
complex<InvEnergy> B1 = 0.5*(right-left)*UnitRemoval::InvE;
complex<Energy> h1(-2.*r23*pcm*q/m1*Qm*B1);
complex<Energy> h2( 2.*r23*pcm*q/m1*Qp*A1);
double me2 = real(h1*conj(h1)+h2*conj(h2))/4./sqr(inpart.second);
Energy output = me2*pcm/8./Constants::pi;
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
diff --git a/Decay/General/FRVDecayer.cc b/Decay/General/FRVDecayer.cc
--- a/Decay/General/FRVDecayer.cc
+++ b/Decay/General/FRVDecayer.cc
@@ -1,219 +1,221 @@
// -*- C++ -*-
//
// FRVDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 FRVDecayer class.
//
#include "FRVDecayer.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/Helicity/WaveFunction/VectorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr FRVDecayer::clone() const {
return new_ptr(*this);
}
IBPtr FRVDecayer::fullclone() const {
return new_ptr(*this);
}
void FRVDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> &,
const vector<map<ShowerInteraction,VertexBasePtr> > &,
map<ShowerInteraction,VertexBasePtr>) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractRFVVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<RFVVertexPtr> (vert));
}
}
void FRVDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_;
}
void FRVDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_;
}
double FRVDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,PDT::Spin3Half,PDT::Spin1)));
// decaying fermion or antifermion
bool ferm = inpart.id() > 0;
// initialize
if(meopt==Initialize) {
// spinors and rho
if(ferm) {
SpinorWaveFunction ::calculateWaveFunctions(wave_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wave_[0].wave().Type() != SpinorType::u)
for(unsigned int ix = 0; ix < 2; ++ix) wave_ [ix].conjugate();
}
else {
SpinorBarWaveFunction::calculateWaveFunctions(wavebar_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming);
if(wavebar_[0].wave().Type() != SpinorType::v)
for(unsigned int ix = 0; ix < 2; ++ix) wavebar_[ix].conjugate();
}
+ // fix rho if no correlations
+ fixRho(rho_);
}
// setup spin info when needed
if(meopt==Terminate) {
// for the decaying particle
if(ferm) {
SpinorWaveFunction::
constructSpinInfo(wave_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
RSSpinorBarWaveFunction::constructSpinInfo(RSwavebar_,decay[0],outgoing,true);
}
else {
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,const_ptr_cast<tPPtr>(&inpart),incoming,true);
RSSpinorWaveFunction::constructSpinInfo(RSwave_,decay[0],outgoing,true);
}
VectorWaveFunction::
constructSpinInfo(vector_,decay[1],outgoing,true,false);
}
Energy2 scale(sqr(inpart.mass()));
if(ferm)
RSSpinorBarWaveFunction::
calculateWaveFunctions(RSwavebar_,decay[0],outgoing);
else
RSSpinorWaveFunction::
calculateWaveFunctions(RSwave_ ,decay[0],outgoing);
bool massless = decay[1]->dataPtr()->mass()==ZERO;
VectorWaveFunction::
calculateWaveFunctions(vector_,decay[1],outgoing,massless);
// loop over helicities
for(unsigned int if1 = 0; if1 < 2; ++if1) {
for(unsigned int if2 = 0; if2 < 4; ++if2) {
for(unsigned int vhel = 0; vhel < 3; ++vhel) {
if(massless && vhel == 1) ++vhel;
(*ME())(if1, if2,vhel) = 0.;
for(auto vert : vertex_) {
if(ferm)
(*ME())(if1, if2,vhel) +=
vert->evaluate(scale,wave_[if1],
RSwavebar_[if2],vector_[vhel]);
else
(*ME())(if1, if2, vhel) +=
vert->evaluate(scale,RSwave_[if2],
wavebar_[if1],vector_[vhel]);
}
}
}
}
double output=(ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// test
// Energy m1(inpart.mass()),m2(decay[0]->mass()),m3(decay[1]->mass());
// Energy2 m12(m1*m1),m22(m2*m2),m32(m3*m3);
// Energy Qp(sqrt(sqr(m1+m2)-sqr(m3))),Qm(sqrt(sqr(m1-m2)-sqr(m3)));
// double r2(sqrt(2.)),r3(sqrt(3.));
// Energy pcm(Kinematics::pstarTwoBodyDecay(m1,m2,m3));
// vector<Complex> left = perturbativeVertex_-> left();
// vector<Complex> right = perturbativeVertex_->right();
// Complex A1 = 0.5*(left [0]+right[0])*perturbativeVertex_-> norm();
// Complex B1 = 0.5*(right[0]- left[0])*perturbativeVertex_-> norm();
// complex<InvEnergy> A2 = 0.5*(left [1]+right[1])*perturbativeVertex_-> norm()*UnitRemoval::InvE;
// complex<InvEnergy> B2 = 0.5*(right[1]- left[1])*perturbativeVertex_-> norm()*UnitRemoval::InvE;
// complex<InvEnergy2> A3 = 0.5*(left [2]+right[2])*perturbativeVertex_-> norm()*UnitRemoval::InvE2;
// complex<InvEnergy2> B3 = 0.5*(right[2]- left[2])*perturbativeVertex_-> norm()*UnitRemoval::InvE2;
// complex<Energy> h1(-2.*Qp*A1),h2(2.*Qm*B1);
// complex<Energy> h3(-2./r3*Qp*(A1-Qm*Qm/m2*A2));
// complex<Energy> h4( 2./r3*Qm*(B1-Qp*Qp/m2*B2));
// complex<Energy> h5(ZERO),h6(ZERO);
// if(decay[1]->mass()>ZERO) {
// h5 = -2.*r2/r3/m2/m3*Qp*(0.5*(m12-m22-m32)*A1+0.5*Qm*Qm*(m1+m2)*A2
// +m12*pcm*pcm*A3);
// h6 = 2.*r2/r3/m2/m3*Qm*(0.5*(m12-m22-m32)*B1-0.5*Qp*Qp*(m1-m2)*B2
// +m12*pcm*pcm*B3);
// }
// cout << "testing 1/2->3/2 1 " << inpart.id() << " "
// << output << " "
// << 0.25*(h1*conj(h1)+h2*conj(h2)+h3*conj(h3)+
// h4*conj(h4)+h5*conj(h5)+h6*conj(h6))/sqr(inpart.mass()) << " "
// << 0.25*(h1*conj(h1)+h2*conj(h2)+h3*conj(h3)+
// h4*conj(h4)+h5*conj(h5)+h6*conj(h6))/sqr(inpart.mass())/output << endl;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),decay[1]->dataPtr());
// return the answer
return output;
}
Energy FRVDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
Energy m1(inpart.second),m2(outa.second),m3(outb.second);
Energy2 m12(m1*m1),m22(m2*m2),m32(m3*m3);
Energy Qp(sqrt(sqr(m1+m2)-sqr(m3))),Qm(sqrt(sqr(m1-m2)-sqr(m3)));
double r2(sqrt(2.)),r3(sqrt(3.));
Energy pcm(Kinematics::pstarTwoBodyDecay(m1,m2,m3));
// couplings
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), outa.first,
in, outb.first);
vector<Complex> left = perturbativeVertex_[0]-> left();
vector<Complex> right = perturbativeVertex_[0]->right();
Complex A1 = 0.5*(left [0]+right[0])*perturbativeVertex_[0]-> norm();
Complex B1 = 0.5*(right[0]- left[0])*perturbativeVertex_[0]-> norm();
complex<InvEnergy> A2 = 0.5*(left [1]+right[1])*perturbativeVertex_[0]-> norm()*UnitRemoval::InvE;
complex<InvEnergy> B2 = 0.5*(right[1]- left[1])*perturbativeVertex_[0]-> norm()*UnitRemoval::InvE;
complex<InvEnergy2> A3 = 0.5*(left [2]+right[2])*perturbativeVertex_[0]-> norm()*UnitRemoval::InvE2;
complex<InvEnergy2> B3 = 0.5*(right[2]- left[2])*perturbativeVertex_[0]-> norm()*UnitRemoval::InvE2;
complex<Energy> h1(-2.*Qp*A1),h2(2.*Qm*B1);
complex<Energy> h3(-2./r3*Qp*(A1-Qm*Qm/m2*A2));
complex<Energy> h4( 2./r3*Qm*(B1-Qp*Qp/m2*B2));
complex<Energy> h5(ZERO),h6(ZERO);
if(outb.second>ZERO) {
h5 = -2.*r2/r3/m2/m3*Qp*(0.5*(m12-m22-m32)*A1+0.5*Qm*Qm*(m1+m2)*A2
+m12*pcm*pcm*A3);
h6 = 2.*r2/r3/m2/m3*Qm*(0.5*(m12-m22-m32)*B1-0.5*Qp*Qp*(m1-m2)*B2
+m12*pcm*pcm*B3);
}
double me2 = 0.25*real(h1*conj(h1)+h2*conj(h2)+h3*conj(h3)+
h4*conj(h4)+h5*conj(h5)+h6*conj(h6))/sqr(inpart.second);
Energy output = me2*pcm/8./Constants::pi;
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<FRVDecayer,GeneralTwoBodyDecayer>
describeHerwigFRVDecayer("Herwig::FRVDecayer", "Herwig.so");
void FRVDecayer::Init() {
static ClassDocumentation<FRVDecayer> documentation
("The FRVDecayer class handles the decay of a fermion to "
"a spin-3/2 particle and a vector boson.");
}
diff --git a/Decay/General/FtoFFFDecayer.cc b/Decay/General/FtoFFFDecayer.cc
--- a/Decay/General/FtoFFFDecayer.cc
+++ b/Decay/General/FtoFFFDecayer.cc
@@ -1,309 +1,311 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the FtoFFFDecayer class.
//
#include "FtoFFFDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/Decay/DecayPhaseSpaceMode.h"
#include "Herwig/PDT/ThreeBodyAllOnCalculator.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include <numeric>
using namespace Herwig;
IBPtr FtoFFFDecayer::clone() const {
return new_ptr(*this);
}
IBPtr FtoFFFDecayer::fullclone() const {
return new_ptr(*this);
}
void FtoFFFDecayer::persistentOutput(PersistentOStream & os) const {
os << sca_ << vec_ << ten_;
}
void FtoFFFDecayer::persistentInput(PersistentIStream & is, int) {
is >> sca_ >> vec_ >> ten_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<FtoFFFDecayer,GeneralThreeBodyDecayer>
describeHerwigFtoFFFDecayer("Herwig::FtoFFFDecayer", "Herwig.so");
void FtoFFFDecayer::Init() {
static ClassDocumentation<FtoFFFDecayer> documentation
("The FtoFFFDecayer class implements the general decay of a fermion to "
"three fermions.");
}
void FtoFFFDecayer::doinit() {
GeneralThreeBodyDecayer::doinit();
if(outgoing().empty()) return;
unsigned int ndiags = getProcessInfo().size();
sca_.resize(ndiags);
vec_.resize(ndiags);
ten_.resize(ndiags);
for(unsigned int ix = 0;ix < ndiags; ++ix) {
TBDiagram current = getProcessInfo()[ix];
tcPDPtr offshell = current.intermediate;
if( offshell->CC() ) offshell = offshell->CC();
if(offshell->iSpin() == PDT::Spin0) {
AbstractFFSVertexPtr vert1 = dynamic_ptr_cast<AbstractFFSVertexPtr>
(current.vertices.first);
AbstractFFSVertexPtr vert2 = dynamic_ptr_cast<AbstractFFSVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a scalar diagram in FtoFFFDecayer::doinit()"
<< Exception::runerror;
sca_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin1) {
AbstractFFVVertexPtr vert1 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.first);
AbstractFFVVertexPtr vert2 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a vector diagram in FtoFFFDecayer::doinit()"
<< Exception::runerror;
vec_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin2) {
AbstractFFTVertexPtr vert1 = dynamic_ptr_cast<AbstractFFTVertexPtr>
(current.vertices.first);
AbstractFFTVertexPtr vert2 = dynamic_ptr_cast<AbstractFFTVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a tensor diagram in FtoFFFDecayer::doinit()"
<< Exception::runerror;
ten_[ix] = make_pair(vert1, vert2);
}
}
}
double FtoFFFDecayer::me2(const int ichan, const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
// particle or CC of particle
bool cc = (*getProcessInfo().begin()).incoming != inpart.id();
// special handling or first/last call
const vector<vector<double> > cfactors(getColourFactors());
const vector<vector<double> > nfactors(getLargeNcColourFactors());
const size_t ncf(numberOfFlows());
Energy2 scale(sqr(inpart.mass()));
if(meopt==Initialize) {
SpinorWaveFunction::
calculateWaveFunctions(inwave_.first,rho_,const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming);
inwave_.second.resize(2);
if(inwave_.first[0].wave().Type() == SpinorType::u) {
for(unsigned int ix = 0; ix < 2; ++ix) {
inwave_.second[ix] = inwave_.first[ix].bar();
inwave_.second[ix].conjugate();
}
}
else {
for(unsigned int ix = 0; ix < 2; ++ix) {
inwave_.second[ix] = inwave_.first[ix].bar();
inwave_.first[ix].conjugate();
}
}
+ // fix rho if no correlations
+ fixRho(rho_);
}
// setup spin info when needed
if(meopt==Terminate) {
// for the decaying particle
if(inpart.id()<0)
SpinorWaveFunction::constructSpinInfo(inwave_.first,
const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming,true);
else
SpinorBarWaveFunction::constructSpinInfo(inwave_.second,
const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming,true);
// outgoing particles
for(unsigned int ix = 0; ix < 3; ++ix) {
SpinorWaveFunction::
constructSpinInfo(outwave_[ix].first,decay[ix],Helicity::outgoing,true);
}
}
// outgoing particles
for(unsigned int ix = 0; ix < 3; ++ix) {
SpinorWaveFunction::
calculateWaveFunctions(outwave_[ix].first,decay[ix],Helicity::outgoing);
outwave_[ix].second.resize(2);
if(outwave_[ix].first[0].wave().Type() == SpinorType::u) {
for(unsigned int iy = 0; iy < 2; ++iy) {
outwave_[ix].second[iy] = outwave_[ix].first[iy].bar();
outwave_[ix].first[iy].conjugate();
}
}
else {
for(unsigned int iy = 0; iy < 2; ++iy) {
outwave_[ix].second[iy] = outwave_[ix].first[iy].bar();
outwave_[ix].second[iy].conjugate();
}
}
}
bool ferm = inpart.id()>0;
vector<Complex> flows(ncf, Complex(0.)),largeflows(ncf, Complex(0.));
static const unsigned int out2[3]={1,0,0},out3[3]={2,2,1};
vector<GeneralDecayMEPtr> mes(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,
PDT::Spin1Half,PDT::Spin1Half)));
vector<GeneralDecayMEPtr> mel(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,
PDT::Spin1Half,PDT::Spin1Half)));
unsigned int ihel[4];
for(ihel[0] = 0; ihel[0] < 2; ++ihel[0]) {
for(ihel[1] = 0; ihel[1] < 2; ++ihel[1]) {
for(ihel[2] = 0; ihel[2] < 2; ++ihel[2]) {
for(ihel[3] = 0; ihel[3] < 2; ++ihel[3]) {
flows = vector<Complex>(ncf, Complex(0.));
largeflows = vector<Complex>(ncf, Complex(0.));
unsigned int idiag=0;
for(vector<TBDiagram>::const_iterator dit=getProcessInfo().begin();
dit!=getProcessInfo().end();++dit) {
if(ichan>=0&&diagramMap()[ichan]!=idiag) {
++idiag;
continue;
}
// the sign from normal ordering
double sign = ferm ? 1. : -1;
// outgoing wavefunction and NO sign
if (dit->channelType==TBDiagram::channel23) sign *= -1.;
else if(dit->channelType==TBDiagram::channel13) sign *= 1.;
else if(dit->channelType==TBDiagram::channel12) sign *= -1.;
else throw Exception()
<< "Unknown diagram type in FtoFFFDecayer::me2()" << Exception::runerror;
// wavefunctions
SpinorWaveFunction w0,w3;
SpinorBarWaveFunction w1,w2;
// incoming wavefunction
if(ferm) {
w0 = inwave_.first [ihel[0]];
w1 = outwave_[dit->channelType].second[ihel[dit->channelType+1]];
}
else {
w0 = outwave_[dit->channelType].first [ihel[dit->channelType+1]];
w1 = inwave_.second[ihel[0]];
}
if(decay[out2[dit->channelType]]->id()<0&&
decay[out3[dit->channelType]]->id()>0) {
w2 = outwave_[out3[dit->channelType]].second[ihel[out3[dit->channelType]+1]];
w3 = outwave_[out2[dit->channelType]].first [ihel[out2[dit->channelType]+1]];
sign *= -1.;
}
else {
w2 = outwave_[out2[dit->channelType]].second[ihel[out2[dit->channelType]+1]];
w3 = outwave_[out3[dit->channelType]].first [ihel[out3[dit->channelType]+1]];
}
tcPDPtr offshell = dit->intermediate;
if(cc&&offshell->CC()) offshell=offshell->CC();
Complex diag(0.);
// intermediate scalar
if (offshell->iSpin() == PDT::Spin0) {
ScalarWaveFunction inters = sca_[idiag].first->
evaluate(scale, widthOption(), offshell, w0, w1);
diag = sca_[idiag].second->evaluate(scale,w3,w2,inters);
}
// intermediate vector
else if(offshell->iSpin() == PDT::Spin1) {
VectorWaveFunction interv = vec_[idiag].first->
evaluate(scale, widthOption(), offshell, w0, w1);
diag = vec_[idiag].second->evaluate(scale,w3,w2,interv);
}
// intermediate tensor
else if(offshell->iSpin() == PDT::Spin2) {
TensorWaveFunction intert = ten_[idiag].first->
evaluate(scale, widthOption(), offshell, w0, w1);
diag = ten_[idiag].second->evaluate(scale,w3,w2,intert);
}
// unknown
else throw Exception()
<< "Unknown intermediate in FtoFFFDecayer::me2()"
<< Exception::runerror;
// apply NO sign
diag *= sign;
// matrix element for the different colour flows
if(ichan<0) {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
else {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1!=colourFlow()) continue;
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1!=colourFlow()) continue;
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
++idiag;
}
// now add the flows to the me2 with appropriate colour factors
for(unsigned int ix = 0; ix < ncf; ++ix) {
(*mes[ix])(ihel[0],ihel[1],ihel[2],ihel[3]) = flows[ix];
(*mel[ix])(ihel[0],ihel[1],ihel[2],ihel[3]) = largeflows[ix];
}
}
}
}
}
double me2(0.);
if(ichan<0) {
vector<double> pflows(ncf,0.);
for(unsigned int ix = 0; ix < ncf; ++ix) {
for(unsigned int iy = 0; iy < ncf; ++ iy) {
double con = cfactors[ix][iy]*(mes[ix]->contract(*mes[iy],rho_)).real();
me2 += con;
if(ix==iy) {
con = nfactors[ix][iy]*(mel[ix]->contract(*mel[iy],rho_)).real();
pflows[ix] += con;
}
}
}
double ptotal(std::accumulate(pflows.begin(),pflows.end(),0.));
ptotal *=UseRandom::rnd();
for(unsigned int ix=0;ix<pflows.size();++ix) {
if(ptotal<=pflows[ix]) {
colourFlow(ix);
ME(mes[ix]);
break;
}
ptotal-=pflows[ix];
}
}
else {
unsigned int iflow = colourFlow();
me2 = nfactors[iflow][iflow]*(mel[iflow]->contract(*mel[iflow],rho_)).real();
}
// return the matrix element squared
return me2;
}
WidthCalculatorBasePtr FtoFFFDecayer::
threeBodyMEIntegrator(const DecayMode & ) const {
vector<int> intype;
vector<Energy> inmass,inwidth;
vector<double> inpow,inweights;
constructIntegratorChannels(intype,inmass,inwidth,inpow,inweights);
return new_ptr(ThreeBodyAllOnCalculator<FtoFFFDecayer>
(inweights,intype,inmass,inwidth,inpow,*this,0,
outgoing()[0]->mass(),outgoing()[1]->mass(),outgoing()[2]->mass(),
relativeError()));
}
diff --git a/Decay/General/FtoFVVDecayer.cc b/Decay/General/FtoFVVDecayer.cc
--- a/Decay/General/FtoFVVDecayer.cc
+++ b/Decay/General/FtoFVVDecayer.cc
@@ -1,401 +1,403 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the FtoFVVDecayer class.
//
#include "FtoFVVDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/Decay/DecayPhaseSpaceMode.h"
#include "Herwig/PDT/ThreeBodyAllOnCalculator.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include <numeric>
using namespace Herwig;
IBPtr FtoFVVDecayer::clone() const {
return new_ptr(*this);
}
IBPtr FtoFVVDecayer::fullclone() const {
return new_ptr(*this);
}
void FtoFVVDecayer::persistentOutput(PersistentOStream & os) const {
os << sca_ << fer_ << vec_ << ten_;
}
void FtoFVVDecayer::persistentInput(PersistentIStream & is, int) {
is >> sca_ >> fer_ >> vec_ >> ten_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<FtoFVVDecayer,GeneralThreeBodyDecayer>
describeHerwigFtoFVVDecayer("Herwig::FtoFVVDecayer", "Herwig.so");
void FtoFVVDecayer::Init() {
static ClassDocumentation<FtoFVVDecayer> documentation
("The FtoFVVDecayer class implements the general decay of a fermion to "
"a fermion and a pair of vectors.");
}
void FtoFVVDecayer::doinit() {
GeneralThreeBodyDecayer::doinit();
if(outgoing().empty()) return;
unsigned int ndiags = getProcessInfo().size();
sca_.resize(ndiags);
fer_.resize(ndiags);
vec_.resize(ndiags);
ten_.resize(ndiags);
for(unsigned int ix = 0;ix < ndiags; ++ix) {
TBDiagram current = getProcessInfo()[ix];
tcPDPtr offshell = current.intermediate;
if(offshell->iSpin() == PDT::Spin0) {
AbstractFFSVertexPtr vert1 = dynamic_ptr_cast<AbstractFFSVertexPtr>
(current.vertices.first);
AbstractVVSVertexPtr vert2 = dynamic_ptr_cast<AbstractVVSVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a scalar diagram in FtoFVVDecayer::doinit()"
<< Exception::runerror;
sca_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin1Half) {
AbstractFFVVertexPtr vert1 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.first);
AbstractFFVVertexPtr vert2 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a scalar diagram in FtoFVVDecayer::doinit()"
<< Exception::runerror;
fer_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin1) {
AbstractFFVVertexPtr vert1 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.first);
AbstractVVVVertexPtr vert2 = dynamic_ptr_cast<AbstractVVVVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a vector diagram in FtoFVVDecayer::doinit()"
<< Exception::runerror;
vec_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin2) {
AbstractFFTVertexPtr vert1 = dynamic_ptr_cast<AbstractFFTVertexPtr>
(current.vertices.first);
AbstractVVTVertexPtr vert2 = dynamic_ptr_cast<AbstractVVTVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a tensor diagram in FtoFVVDecayer::doinit()"
<< Exception::runerror;
ten_[ix] = make_pair(vert1, vert2);
}
}
}
double FtoFVVDecayer::me2(const int ichan, const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
// particle or CC of particle
bool cc = (*getProcessInfo().begin()).incoming != inpart.id();
// special handling or first/last call
//Set up wave-functions
bool ferm( inpart.id() > 0 );
if(meopt==Initialize) {
if( ferm ) {
SpinorWaveFunction::
calculateWaveFunctions(fwave_,rho_,const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming);
if( fwave_[0].wave().Type() != SpinorType::u )
fwave_[0].conjugate();
if( fwave_[1].wave().Type() != SpinorType::u )
fwave_[1].conjugate();
}
else {
SpinorBarWaveFunction::
calculateWaveFunctions(fbwave_, rho_, const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming);
if( fbwave_[0].wave().Type() != SpinorType::v )
fbwave_[0].conjugate();
if( fbwave_[1].wave().Type() != SpinorType::v )
fbwave_[1].conjugate();
}
+ // fix rho if no correlations
+ fixRho(rho_);
}
// setup spin info when needed
if(meopt==Terminate) {
// for the decaying particle
if(ferm)
SpinorWaveFunction::constructSpinInfo(fwave_,
const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming,true);
else
SpinorBarWaveFunction::constructSpinInfo(fbwave_,
const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming,true);
int ivec(-1);
// outgoing particles
for(int ix = 0; ix < 3; ++ix) {
tPPtr p = decay[ix];
if( p->dataPtr()->iSpin() == PDT::Spin1Half ) {
if( ferm ) {
SpinorBarWaveFunction::
constructSpinInfo(fbwave_, p, Helicity::outgoing,true);
}
else {
SpinorWaveFunction::
constructSpinInfo(fwave_ , p, Helicity::outgoing,true);
}
}
else if( p->dataPtr()->iSpin() == PDT::Spin1 ) {
if( ivec < 0 ) {
ivec = ix;
VectorWaveFunction::
constructSpinInfo(vwave_.first , p, Helicity::outgoing, true, false);
}
else {
VectorWaveFunction::
constructSpinInfo(vwave_.second, p, Helicity::outgoing, true, false);
}
}
}
return 0.;
}
// outgoing, keep track of fermion and first occurrence of vector positions
int isp(-1), ivec(-1);
// outgoing particles
pair<bool,bool> mass = make_pair(false,false);
for(int ix = 0; ix < 3; ++ix) {
tPPtr p = decay[ix];
if( p->dataPtr()->iSpin() == PDT::Spin1Half ) {
isp = ix;
if( ferm ) {
SpinorBarWaveFunction::
calculateWaveFunctions(fbwave_, p, Helicity::outgoing);
if( fbwave_[0].wave().Type() != SpinorType::u )
fbwave_[0].conjugate();
if( fbwave_[1].wave().Type() != SpinorType::u )
fbwave_[1].conjugate();
}
else {
SpinorWaveFunction::
calculateWaveFunctions(fwave_, p, Helicity::outgoing);
if( fwave_[0].wave().Type() != SpinorType::v )
fwave_[0].conjugate();
if( fwave_[1].wave().Type() != SpinorType::v )
fwave_[1].conjugate();
}
}
else if( p->dataPtr()->iSpin() == PDT::Spin1 ) {
bool massless = p->id() == ParticleID::gamma || p->id() == ParticleID::g;
if( ivec < 0 ) {
ivec = ix;
VectorWaveFunction::
calculateWaveFunctions(vwave_.first , p, Helicity::outgoing, massless);
mass.first = massless;
}
else {
VectorWaveFunction::
calculateWaveFunctions(vwave_.second, p, Helicity::outgoing, massless);
mass.second = massless;
}
}
}
assert(isp >= 0 && ivec >= 0);
Energy2 scale(sqr(inpart.mass()));
const vector<vector<double> > cfactors(getColourFactors());
const vector<vector<double> > nfactors(getLargeNcColourFactors());
const size_t ncf(numberOfFlows());
vector<Complex> flows(ncf, Complex(0.)), largeflows(ncf, Complex(0.));
vector<GeneralDecayMEPtr>
mes(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,
(isp == 0) ? PDT::Spin1Half : PDT::Spin1,
(isp == 1) ? PDT::Spin1Half : PDT::Spin1,
(isp == 2) ? PDT::Spin1Half : PDT::Spin1)));
vector<GeneralDecayMEPtr>
mel(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,
(isp == 0) ? PDT::Spin1Half : PDT::Spin1,
(isp == 1) ? PDT::Spin1Half : PDT::Spin1,
(isp == 2) ? PDT::Spin1Half : PDT::Spin1)));
//Helicity calculation
for( unsigned int if1 = 0; if1 < 2; ++if1 ) {
for( unsigned int if2 = 0; if2 < 2; ++if2 ) {
for( unsigned int iv1 = 0; iv1 < 3; ++iv1 ) {
if ( mass.first && iv1 == 1 ) continue;
for( unsigned int iv2 = 0; iv2 < 3; ++iv2 ) {
if ( mass.second && iv2 == 1 ) continue;
flows = vector<Complex>(ncf, Complex(0.));
largeflows = vector<Complex>(ncf, Complex(0.));
unsigned int idiag(0);
for(vector<TBDiagram>::const_iterator dit = getProcessInfo().begin();
dit != getProcessInfo().end(); ++dit) {
// If we are selecting a particular channel
if( ichan >= 0 && diagramMap()[ichan] != idiag ) {
++idiag;
continue;
}
tcPDPtr offshell = (*dit).intermediate;
if(cc&&offshell->CC()) offshell=offshell->CC();
Complex diag;
if( offshell->iSpin() == PDT::Spin1Half ) {
// Make sure we connect the correct particles
VectorWaveFunction vw1, vw2;
if( (*dit).channelType == TBDiagram::channel23 ) {
vw1 = vwave_.first[iv1];
vw2 = vwave_.second[iv2];
}
else if( (*dit).channelType == TBDiagram::channel12 ) {
vw1 = vwave_.second[iv2];
vw2 = vwave_.first[iv1];
}
else {
if( ivec < isp ) {
vw1 = vwave_.second[iv2];
vw2 = vwave_.first[iv1];
}
else {
vw1 = vwave_.first[iv1];
vw2 = vwave_.second[iv2];
}
}
if( ferm ) {
SpinorWaveFunction inters =
fer_[idiag].first->evaluate(scale, widthOption(), offshell,
fwave_[if1], vw1);
diag = fer_[idiag].second->evaluate(scale, inters, fbwave_[if2],
vw2);
}
else {
SpinorBarWaveFunction inters =
fer_[idiag].first->evaluate(scale, widthOption(), offshell,
fbwave_[if2], vw1);
diag = fer_[idiag].second->evaluate(scale, fwave_[if1], inters,
vw2);
}
}
else if( offshell->iSpin() == PDT::Spin0 ) {
ScalarWaveFunction inters =
sca_[idiag].first->evaluate(scale, widthOption(), offshell,
fwave_[if1], fbwave_[if2]);
diag = sca_[idiag].second->evaluate(scale, vwave_.first[iv1],
vwave_.second[iv2], inters);
}
else if( offshell->iSpin() == PDT::Spin1 ) {
VectorWaveFunction interv =
vec_[idiag].first->evaluate(scale, widthOption(), offshell,
fwave_[if1], fbwave_[if2]);
diag = vec_[idiag].second->evaluate(scale, vwave_.first[iv1],
vwave_.second[iv2], interv);
}
else if( offshell->iSpin() == PDT::Spin2 ) {
TensorWaveFunction intert =
ten_[idiag].first->evaluate(scale, widthOption(), offshell,
fwave_[if1], fbwave_[if2]);
diag = ten_[idiag].second->evaluate(scale, vwave_.first[iv1],
vwave_.second[iv2], intert);
}
else
throw Exception()
<< "Unknown intermediate in FtoFVVDecayer::me2()"
<< Exception::runerror;
//NO sign
if( !ferm ) diag *= -1;
if(ichan<0) {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
else {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1!=colourFlow()) continue;
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1!=colourFlow()) continue;
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
++idiag;
}// end diagram loop
// now add the flows to the me2
unsigned int h1(if1), h2(if2);
if( !ferm ) swap(h1,h2);
for(unsigned int ix = 0; ix < ncf; ++ix) {
if(isp == 0) {
(*mes[ix])(h1, h2, iv1, iv2) = flows[ix];
(*mel[ix])(h1, h2, iv1, iv2) = largeflows[ix];
}
else if(isp == 1) {
(*mes[ix])(h1, iv1, h2, iv2) = flows[ix];
(*mel[ix])(h1, iv1, h2, iv2) = largeflows[ix];
}
else if(isp == 2) {
(*mes[ix])(h1, iv1, iv2, h2) = flows[ix];
(*mel[ix])(h1, iv1, h2, iv2) = largeflows[ix];
}
}
}//v2
}//v1
}//f2
}//f1
//Finally, work out me2. This depends on whether we are selecting channels
//or not
double me2(0.);
if(ichan<0) {
vector<double> pflows(ncf,0.);
for(unsigned int ix = 0; ix < ncf; ++ix) {
for(unsigned int iy = 0; iy < ncf; ++ iy) {
double con = cfactors[ix][iy]*(mes[ix]->contract(*mes[iy],rho_)).real();
me2 += con;
if(ix==iy) {
con = nfactors[ix][iy]*(mel[ix]->contract(*mel[iy],rho_)).real();
pflows[ix] += con;
}
}
}
double ptotal(std::accumulate(pflows.begin(),pflows.end(),0.));
ptotal *= UseRandom::rnd();
for(unsigned int ix=0;ix<pflows.size();++ix) {
if(ptotal<=pflows[ix]) {
colourFlow(ix);
ME(mes[ix]);
break;
}
ptotal-=pflows[ix];
}
}
else {
unsigned int iflow = colourFlow();
me2 = nfactors[iflow][iflow]*(mel[iflow]->contract(*mel[iflow],rho_)).real();
}
// return the matrix element squared
return me2;
}
WidthCalculatorBasePtr FtoFVVDecayer::
threeBodyMEIntegrator(const DecayMode & ) const {
vector<int> intype;
vector<Energy> inmass,inwidth;
vector<double> inpow,inweights;
constructIntegratorChannels(intype,inmass,inwidth,inpow,inweights);
return new_ptr(ThreeBodyAllOnCalculator<FtoFVVDecayer>
(inweights,intype,inmass,inwidth,inpow,*this,0,
outgoing()[0]->mass(),outgoing()[1]->mass(),
outgoing()[2]->mass(),relativeError()));
}
diff --git a/Decay/General/SFFDecayer.cc b/Decay/General/SFFDecayer.cc
--- a/Decay/General/SFFDecayer.cc
+++ b/Decay/General/SFFDecayer.cc
@@ -1,491 +1,493 @@
// -*- C++ -*-
//
// SFFDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SFFDecayer class.
//
#include "SFFDecayer.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/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr SFFDecayer::clone() const {
return new_ptr(*this);
}
IBPtr SFFDecayer::fullclone() const {
return new_ptr(*this);
}
void SFFDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> ) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractFFSVertexPtr>(vert));
perturbativeVertex_ .push_back(dynamic_ptr_cast<FFSVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(inV.at(inter));
outgoingVertex1_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(outV[0].at(inter));
outgoingVertex2_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(outV[1].at(inter));
}
}
void SFFDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< incomingVertex_ << outgoingVertex1_
<< outgoingVertex2_;
}
void SFFDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> incomingVertex_ >> outgoingVertex1_
>> outgoingVertex2_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SFFDecayer,GeneralTwoBodyDecayer>
describeHerwigSFFDecayer("Herwig::SFFDecayer", "Herwig.so");
void SFFDecayer::Init() {
static ClassDocumentation<SFFDecayer> documentation
("This class implements to decay of a scalar to 2 fermions");
}
double SFFDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin1Half,PDT::Spin1Half)));
// work out which is the fermion and antifermion
int iferm(1),ianti(0);
int itype[2];
for(unsigned int ix=0;ix<2;++ix) {
if(decay[ix]->dataPtr()->CC()) itype[ix] = decay[ix]->id()>0 ? 0:1;
else itype[ix] = 2;
}
if(itype[0]==0||itype[1]==1||(itype[0]==2&&itype[1]==2)) swap(iferm,ianti);
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(rho_,const_ptr_cast<tPPtr>(&inpart),incoming);
swave_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,decay[iferm],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(wave_ ,decay[ianti],outgoing,true);
return 0.;
}
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar_,decay[iferm],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(wave_ ,decay[ianti],outgoing);
Energy2 scale(sqr(inpart.mass()));
for(unsigned int ifm = 0; ifm < 2; ++ifm){
for(unsigned int ia = 0; ia < 2; ++ia) {
if(iferm > ianti) (*ME())(0, ia, ifm) = 0.;
else (*ME())(0, ifm, ia) = 0.;
for(auto vert : vertex_) {
if(iferm > ianti){
(*ME())(0, ia, ifm) += vert->evaluate(scale,wave_[ia],
wavebar_[ifm],swave_);
}
else {
(*ME())(0, ifm, ia) += vert->evaluate(scale,wave_[ia],
wavebar_[ifm],swave_);
}
}
}
}
double output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy SFFDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), outb.first, outa.first,
in);
double mu1(outa.second/inpart.second),mu2(outb.second/inpart.second);
double c2 = norm(perturbativeVertex_[0]->norm());
Complex al(perturbativeVertex_[0]->left()), ar(perturbativeVertex_[0]->right());
double me2 = -c2*( (norm(al) + norm(ar))*( sqr(mu1) + sqr(mu2) - 1.)
+ 2.*(ar*conj(al) + al*conj(ar)).real()*mu1*mu2 );
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second, outa.second,
outb.second);
Energy output = me2*pcm/(8*Constants::pi);
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double SFFDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
// work out which is the fermion and antifermion
int ianti(0), iferm(1), iglu(2);
int itype[2];
for(unsigned int ix=0;ix<2;++ix) {
if(decay[ix]->dataPtr()->CC()) itype[ix] = decay[ix]->id()>0 ? 0:1;
else itype[ix] = 2;
}
if(itype[0]==0 && itype[1]!=0) swap(iferm, ianti);
if(itype[0]==2 && itype[1]==1) swap(iferm, ianti);
if(itype[0]==0 && itype[1]==0 && decay[0]->dataPtr()->id()<decay[1]->dataPtr()->id())
swap(iferm, ianti);
if(itype[0]==1 && itype[1]==1 && decay[0]->dataPtr()->id()<decay[1]->dataPtr()->id())
swap(iferm, ianti);
if(meopt==Initialize) {
// create scalar wavefunction for decaying particle
ScalarWaveFunction::
calculateWaveFunctions(rho3_,const_ptr_cast<tPPtr>(&inpart),incoming);
swave3_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
}
// setup spin information when needed
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorBarWaveFunction::
constructSpinInfo(wavebar3_ ,decay[iferm],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(wave3_ ,decay[ianti],outgoing,true);
VectorWaveFunction::
constructSpinInfo(gluon_ ,decay[iglu ],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin0, PDT::Spin1Half,
PDT::Spin1Half, PDT::Spin1)));
// create wavefunctions
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar3_, decay[iferm],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(wave3_ , decay[ianti],outgoing);
VectorWaveFunction::
calculateWaveFunctions(gluon_ , decay[iglu ],outgoing,true);
// gauge invariance test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[iglu ]->momentum(),
decay[iglu ]->dataPtr(),10,
outgoing));
}
}
#endif
// identify fermion and/or anti-fermion vertex
AbstractFFVVertexPtr outgoingVertexF;
AbstractFFVVertexPtr outgoingVertexA;
identifyVertices(iferm, ianti, inpart, decay, outgoingVertexF, outgoingVertexA,
inter);
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
Energy2 scale(sqr(inpart.mass()));
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int ifm = 0; ifm < 2; ++ifm) {
for(unsigned int ia = 0; ia < 2; ++ia) {
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming scalar
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
ScalarWaveFunction scalarInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),
gluon_[2*ig],swave3_,inpart.mass());
assert(swave3_.particle()->id()==scalarInter.particle()->id());
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,wave3_[ia],
wavebar3_[ifm],scalarInter);
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(0, ia, ifm, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing fermion
if((decay[iferm]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[iferm]->dataPtr()->charged() && inter==ShowerInteraction::QED)) {
assert(outgoingVertexF);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[iferm]->dataPtr();
if(off->CC()) off = off->CC();
SpinorBarWaveFunction interS =
outgoingVertexF->evaluate(scale,3,off,wavebar3_[ifm],
gluon_[2*ig],decay[iferm]->mass());
assert(wavebar3_[ifm].particle()->id()==interS.particle()->id());
if(!couplingSet) {
gs = abs(outgoingVertexF->norm());
couplingSet = true;
}
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,wave3_[ia], interS,swave3_);
for(unsigned int ix=0;ix<colourFlow[1].size();++ix) {
(*ME[colourFlow[1][ix].first])(0, ia, ifm, ig) +=
colourFlow[1][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing antifermion
if((decay[ianti]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[ianti]->dataPtr()->charged() && inter==ShowerInteraction::QED)) {
assert(outgoingVertexA);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[ianti]->dataPtr();
if(off->CC()) off = off->CC();
SpinorWaveFunction interS =
outgoingVertexA->evaluate(scale,3,off,wave3_[ia],
gluon_[2*ig],decay[ianti]->mass());
assert(wave3_[ia].particle()->id()==interS.particle()->id());
if(!couplingSet) {
gs = abs(outgoingVertexA->norm());
couplingSet = true;
}
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,interS,wavebar3_[ifm],swave3_);
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[2][ix].first])(0, ia, ifm, ig) +=
colourFlow[2][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha(S,EM)
output *= (4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
void SFFDecayer::identifyVertices(const int iferm, const int ianti,
const Particle & inpart, const ParticleVector & decay,
AbstractFFVVertexPtr & outgoingVertexF,
AbstractFFVVertexPtr & outgoingVertexA,
ShowerInteraction inter) {
// QCD
if(inter==ShowerInteraction::QCD) {
// work out which fermion each outgoing vertex corresponds to
// two outgoing vertices
if( inpart.dataPtr() ->iColour()==PDT::Colour0 &&
((decay[iferm]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar) ||
(decay[iferm]->dataPtr()->iColour()==PDT::Colour8 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour8))) {
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]) {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))) {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))) {
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
else if(inpart.dataPtr() ->iColour()==PDT::Colour8 &&
decay[iferm]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar) {
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]) {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))) {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))) {
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
// one outgoing vertex
else if(inpart.dataPtr()->iColour()==PDT::Colour3){
if(decay[iferm]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertexF = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertexF = outgoingVertex2_[inter];
}
else if (decay[iferm]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour8) {
if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[ianti]->dataPtr()))) {
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
else {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
}
else if(decay[iferm]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar) {
if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))) {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else {
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
}
else if(inpart.dataPtr()->iColour()==PDT::Colour3bar){
if(decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[iferm]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertexA = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertexA = outgoingVertex2_[inter];
}
else if (decay[iferm]->dataPtr()->iColour()==PDT::Colour8 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar){
if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))) {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else {
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
else if(decay[iferm]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3) {
if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))) {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else {
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
}
else if(inpart.dataPtr()->iColour()==PDT::Colour6 ||
inpart.dataPtr()->iColour()==PDT::Colour6bar) {
if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))) {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else {
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
if (! ((incomingVertex_[inter] && (outgoingVertexF || outgoingVertexA)) ||
( outgoingVertexF && outgoingVertexA))) {
throw Exception()
<< "Invalid vertices for QCD radiation in SFF decay in SFFDecayer::identifyVertices"
<< Exception::runerror;
}
}
// QED
else {
if(decay[iferm]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr())))
outgoingVertexF = outgoingVertex1_[inter];
else
outgoingVertexF = outgoingVertex2_[inter];
}
if(decay[ianti]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[ianti]->dataPtr())))
outgoingVertexA = outgoingVertex1_[inter];
else
outgoingVertexA = outgoingVertex2_[inter];
}
}
}
diff --git a/Decay/General/SRFDecayer.cc b/Decay/General/SRFDecayer.cc
--- a/Decay/General/SRFDecayer.cc
+++ b/Decay/General/SRFDecayer.cc
@@ -1,196 +1,198 @@
// -*- C++ -*-
//
// SRFDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SRFDecayer class.
//
#include "SRFDecayer.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/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr SRFDecayer::clone() const {
return new_ptr(*this);
}
IBPtr SRFDecayer::fullclone() const {
return new_ptr(*this);
}
void SRFDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> &,
const vector<map<ShowerInteraction,VertexBasePtr> > &,
map<ShowerInteraction,VertexBasePtr>) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractRFSVertexPtr>(vert));
perturbativeVertex_ .push_back(dynamic_ptr_cast<RFSVertexPtr> (vert));
}
}
void SRFDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_;
}
void SRFDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SRFDecayer,GeneralTwoBodyDecayer>
describeHerwigSRFDecayer("Herwig::SRFDecayer", "Herwig.so");
void SRFDecayer::Init() {
static ClassDocumentation<SRFDecayer> documentation
("This class implements to decay of a scalar to a spin-3/2 and"
" spin-1/2 fermion");
}
double SRFDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,MEOption meopt) const {
unsigned int irs=0,ifm=1;
if(decay[0]->dataPtr()->iSpin()==PDT::Spin1Half) swap(irs,ifm);
if(!ME()) {
if(irs==0)
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin3Half,PDT::Spin1Half)));
else
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin1Half,PDT::Spin3Half)));
}
bool ferm = decay[ifm]->id()<0;
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(rho_,const_ptr_cast<tPPtr>(&inpart),incoming);
swave_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),incoming,true);
if(ferm) {
RSSpinorBarWaveFunction::
constructSpinInfo(RSwavebar_,decay[irs],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(wave_ ,decay[ifm],outgoing,true);
}
else {
RSSpinorWaveFunction::
constructSpinInfo(RSwave_ ,decay[irs],outgoing,true);
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,decay[ifm],outgoing,true);
}
return 0.;
}
if(ferm) {
RSSpinorBarWaveFunction::
calculateWaveFunctions(RSwavebar_,decay[irs],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(wave_ ,decay[ifm],outgoing);
}
else {
RSSpinorWaveFunction::
calculateWaveFunctions(RSwave_ ,decay[irs],outgoing);
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar_,decay[ifm],outgoing);
}
Energy2 scale(sqr(inpart.mass()));
for(unsigned int ifm = 0; ifm < 4; ++ifm){
for(unsigned int ia = 0; ia < 2; ++ia) {
if(irs==0) {
if(ferm) {
(*ME())(0, ifm, ia) = 0.;
for(auto vert : vertex_)
(*ME())(0, ifm, ia) += vert->evaluate(scale,wave_[ia],
RSwavebar_[ifm],swave_);
}
else {
(*ME())(0, ifm, ia) = 0.;
for(auto vert : vertex_)
(*ME())(0, ifm, ia) += vert->evaluate(scale,RSwave_[ifm],
wavebar_[ia],swave_);
}
}
else {
if(ferm) {
(*ME())(0, ia, ifm) = 0.;
for(auto vert : vertex_)
(*ME())(0, ia, ifm) += vert->evaluate(scale,wave_[ia],
RSwavebar_[ifm],swave_);
}
else {
(*ME())(0, ia, ifm) = 0.;
for(auto vert : vertex_)
(*ME())(0, ia, ifm) += vert->evaluate(scale,RSwave_[ifm],
wavebar_[ia],swave_);
}
}
}
}
double output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[irs]->dataPtr(),
decay[ifm]->dataPtr());
// return the answer
return output;
}
Energy SRFDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
Energy q = inpart.second;
Energy m1 = outa.second, m2 = outb.second;
// couplings
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
if(outa.first->iSpin()==PDT::Spin1Half) {
swap(m1,m2);
perturbativeVertex_[0]->setCoupling(sqr(inpart.second),outb.first,
outa.first, in);
}
else {
perturbativeVertex_[0]->setCoupling(sqr(inpart.second),outa.first,
outb.first, in);
}
Complex left = perturbativeVertex_[0]-> left()*perturbativeVertex_[0]-> norm();
Complex right = perturbativeVertex_[0]->right()*perturbativeVertex_[0]-> norm();
complex<InvEnergy> A1 = 0.5*(left+right)*UnitRemoval::InvE;
complex<InvEnergy> B1 = 0.5*(right-left)*UnitRemoval::InvE;
Energy2 q2(q*q),m12(m1*m1),m22(m2*m2);
Energy2 pcm2(0.25*(q2*(q2-2.*m12-2.*m22)+(m12-m22)*(m12-m22))/q2);
Energy pcm(sqrt(pcm2));
Energy Qp(sqrt(-sqr(m2+m1)+q2)),Qm(sqrt(-sqr(m2-m1)+q2));
double r23(sqrt(2./3.));
complex<Energy> h1(-2.*r23*pcm*q/m1*Qm*B1);
complex<Energy> h2( 2.*r23*pcm*q/m1*Qp*A1);
double me2 = real(h1*conj(h1)+h2*conj(h2))/2./sqr(inpart.second);
Energy output = me2*pcm/8./Constants::pi;
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
diff --git a/Decay/General/SSSDecayer.cc b/Decay/General/SSSDecayer.cc
--- a/Decay/General/SSSDecayer.cc
+++ b/Decay/General/SSSDecayer.cc
@@ -1,410 +1,412 @@
// -*- C++ -*-
//
// SSSDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SSSDecayer class.
//
#include "SSSDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr SSSDecayer::clone() const {
return new_ptr(*this);
}
IBPtr SSSDecayer::fullclone() const {
return new_ptr(*this);
}
void SSSDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> ) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractSSSVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<SSSVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(inV.at(inter));
outgoingVertex1_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[0].at(inter));
outgoingVertex2_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[1].at(inter));
}
}
void SSSDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< incomingVertex_ << outgoingVertex1_
<< outgoingVertex2_;
}
void SSSDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> incomingVertex_ >> outgoingVertex1_
>> outgoingVertex2_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SSSDecayer,GeneralTwoBodyDecayer>
describeHerwigSSSDecayer("Herwig::SSSDecayer", "Herwig.so");
void SSSDecayer::Init() {
static ClassDocumentation<SSSDecayer> documentation
("This class implements the decay of a scalar to 2 scalars.");
}
double SSSDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin0,PDT::Spin0)));
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(rho_,const_ptr_cast<tPPtr>(&inpart),incoming);
swave_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),incoming,true);
for(unsigned int ix=0;ix<2;++ix)
ScalarWaveFunction::
constructSpinInfo(decay[ix],outgoing,true);
}
ScalarWaveFunction s1(decay[0]->momentum(),decay[0]->dataPtr(),outgoing);
ScalarWaveFunction s2(decay[1]->momentum(),decay[1]->dataPtr(),outgoing);
Energy2 scale(sqr(inpart.mass()));
(*ME())(0,0,0) = 0.;
for(auto vert : vertex_) {
(*ME())(0,0,0) += vert->evaluate(scale,s1,s2,swave_);
}
double output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy SSSDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0] && !perturbativeVertex_[0]->kinematics()) {
Energy2 scale(sqr(inpart.second));
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(scale, in, outa.first, outb.first);
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second, outa.second,
outb.second);
double c2 = norm(perturbativeVertex_[0]->norm());
Energy pWidth = c2*pcm/8./Constants::pi/scale*UnitRemoval::E2;
// colour factor
pWidth *= colourFactor(inpart.first,outa.first,outb.first);
return pWidth;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double SSSDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
// work out which is the scalar and anti scalar
int ianti(0), iscal(1), iglu(2);
int itype[2];
for(unsigned int ix=0;ix<2;++ix) {
if(decay[ix]->dataPtr()->CC()) itype[ix] = decay[ix]->id()>0 ? 0:1;
else itype[ix] = 2;
}
if(itype[0]==0 && itype[1]!=0) swap(ianti, iscal);
if(itype[0]==2 && itype[1]==1) swap(ianti, iscal);
if(itype[0]==0 && itype[1]==0 && abs(decay[0]->dataPtr()->id())>abs(decay[1]->dataPtr()->id()))
swap(iscal, ianti);
if(itype[0]==1 && itype[1]==1 && abs(decay[0]->dataPtr()->id())<abs(decay[1]->dataPtr()->id()))
swap(iscal, ianti);
if(meopt==Initialize) {
// create scalar wavefunction for decaying particle
ScalarWaveFunction::calculateWaveFunctions(rho3_,const_ptr_cast<tPPtr>(&inpart),incoming);
swave3_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
}
// setup spin information when needed
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),incoming,true);
ScalarWaveFunction::
constructSpinInfo(decay[iscal],outgoing,true);
ScalarWaveFunction::
constructSpinInfo(decay[ianti],outgoing,true);
VectorWaveFunction::
constructSpinInfo(gluon_,decay[iglu ],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin0, PDT::Spin0,
PDT::Spin0, PDT::Spin1)));
// create wavefunctions
ScalarWaveFunction scal(decay[iscal]->momentum(), decay[iscal]->dataPtr(),outgoing);
ScalarWaveFunction anti(decay[ianti]->momentum(), decay[ianti]->dataPtr(),outgoing);
VectorWaveFunction::calculateWaveFunctions(gluon_,decay[iglu ],outgoing,true);
// gauge invariance test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[iglu ]->momentum(),
decay[iglu ]->dataPtr(),10,
outgoing));
}
}
#endif
AbstractVSSVertexPtr outgoingVertexS;
AbstractVSSVertexPtr outgoingVertexA;
identifyVertices(iscal, ianti, inpart, decay, outgoingVertexS, outgoingVertexA,inter);
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming scalar
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
ScalarWaveFunction scalarInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),
gluon_[2*ig],swave3_,inpart.mass());
assert(swave3_.particle()->id()==scalarInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,scal,anti,scalarInter);
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(0, 0, 0, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from the outgoing scalar
if((decay[iscal]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[iscal]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexS);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[iscal]->dataPtr();
if(off->CC()) off = off->CC();
ScalarWaveFunction scalarInter =
outgoingVertexS->evaluate(scale,3,off,gluon_[2*ig],scal,decay[iscal]->mass());
assert(scal.particle()->id()==scalarInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,swave3_,anti,scalarInter);
if(!couplingSet) {
gs = abs(outgoingVertexS->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[1].size();++ix) {
(*ME[colourFlow[1][ix].first])(0, 0, 0, ig) +=
colourFlow[1][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from the outgoing anti scalar
if((decay[ianti]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[ianti]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexA);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[ianti]->dataPtr();
if(off->CC()) off = off->CC();
ScalarWaveFunction scalarInter =
outgoingVertexA->evaluate(scale,3,off, gluon_[2*ig],anti,decay[ianti]->mass());
assert(anti.particle()->id()==scalarInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,swave3_,scal,scalarInter);
if(!couplingSet) {
gs = abs(outgoingVertexA->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[2][ix].first])(0, 0, 0, ig) +=
colourFlow[2][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha_(S,EM)
output*=(4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
void SSSDecayer::identifyVertices(const int iscal, const int ianti,
const Particle & inpart, const ParticleVector & decay,
AbstractVSSVertexPtr & outgoingVertexS,
AbstractVSSVertexPtr & outgoingVertexA,
ShowerInteraction inter){
// QCD
if(inter==ShowerInteraction::QCD) {
// work out which scalar each outgoing vertex corresponds to
// two outgoing vertices
if( inpart.dataPtr() ->iColour()==PDT::Colour0 &&
((decay[iscal]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar) ||
(decay[iscal]->dataPtr()->iColour()==PDT::Colour8 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour8))){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[iscal]->id()))){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(getParticleData(decay[iscal]->id()))){
outgoingVertexS = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
else if(inpart.dataPtr() ->iColour()==PDT::Colour8 &&
decay[iscal]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[iscal]->id()))){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(getParticleData(decay[iscal]->id()))){
outgoingVertexS = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
// one outgoing vertex
else if(inpart.dataPtr() ->iColour()==PDT::Colour3){
if(decay[iscal]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertexS = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertexS = outgoingVertex2_[inter];
}
else if (decay[iscal]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour8){
if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[ianti]->dataPtr()->id()))){
outgoingVertexS = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
else {
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
}
}
else if(inpart.dataPtr() ->iColour()==PDT::Colour3bar){
if(decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[iscal]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertexA = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertexA = outgoingVertex2_[inter];
}
else if (decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[iscal]->dataPtr()->iColour()==PDT::Colour8){
if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[iscal]->dataPtr()->id()))){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else {
outgoingVertexS = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
}
if (! ((incomingVertex_[inter] && (outgoingVertexS || outgoingVertexA)) ||
( outgoingVertexS && outgoingVertexA)))
throw Exception()
<< "Invalid vertices for QCD radiation in SSS decay in SSSDecayer::identifyVertices"
<< Exception::runerror;
}
// QED
else {
if(decay[iscal]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iscal]->dataPtr())))
outgoingVertexS = outgoingVertex1_[inter];
else
outgoingVertexS = outgoingVertex2_[inter];
}
if(decay[ianti]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[ianti]->dataPtr())))
outgoingVertexA = outgoingVertex1_[inter];
else
outgoingVertexA = outgoingVertex2_[inter];
}
}
}
diff --git a/Decay/General/SSVDecayer.cc b/Decay/General/SSVDecayer.cc
--- a/Decay/General/SSVDecayer.cc
+++ b/Decay/General/SSVDecayer.cc
@@ -1,366 +1,368 @@
// -*- C++ -*-
//
// SSVDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SSVDecayer class.
//
#include "SSVDecayer.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 "Herwig/Utilities/Kinematics.h"
#include "ThePEG/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr SSVDecayer::clone() const {
return new_ptr(*this);
}
IBPtr SSVDecayer::fullclone() const {
return new_ptr(*this);
}
void SSVDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> fourV) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractVSSVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<VSSVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(inV.at(inter));
fourPointVertex_[inter] = dynamic_ptr_cast<AbstractVVSSVertexPtr>(fourV.at(inter));
outgoingVertexS_[inter] = AbstractVSSVertexPtr();
outgoingVertexV_[inter] = AbstractVVVVertexPtr();
if(outV[0].at(inter)) {
if (outV[0].at(inter)->getName()==VertexType::VSS)
outgoingVertexS_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[0].at(inter));
else
outgoingVertexV_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[0].at(inter));
}
if(outV[1].at(inter)) {
if (outV[1].at(inter)->getName()==VertexType::VSS)
outgoingVertexS_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[1].at(inter));
else
outgoingVertexV_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[1].at(inter));
}
}
}
void SSVDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< incomingVertex_ << outgoingVertexS_
<< outgoingVertexV_ << fourPointVertex_;
}
void SSVDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> incomingVertex_ >> outgoingVertexS_
>> outgoingVertexV_ >> fourPointVertex_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SSVDecayer,GeneralTwoBodyDecayer>
describeHerwigSSVDecayer("Herwig::SSVDecayer", "Herwig.so");
void SSVDecayer::Init() {
static ClassDocumentation<SSVDecayer> documentation
("This implements the decay of a scalar to a vector and a scalar");
}
double SSVDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
unsigned int isc(0),ivec(1);
if(decay[0]->dataPtr()->iSpin() != PDT::Spin0) swap(isc,ivec);
if(!ME()) {
if(ivec==1)
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin0,PDT::Spin1)));
else
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin1,PDT::Spin0)));
}
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(rho_,const_ptr_cast<tPPtr>(&inpart),incoming);
swave_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),incoming,true);
ScalarWaveFunction::
constructSpinInfo(decay[isc],outgoing,true);
VectorWaveFunction::
constructSpinInfo(vector_,decay[ivec],outgoing,true,false);
}
VectorWaveFunction::
calculateWaveFunctions(vector_,decay[ivec],outgoing,false);
ScalarWaveFunction sca(decay[isc]->momentum(),decay[isc]->dataPtr(),outgoing);
Energy2 scale(sqr(inpart.mass()));
//make sure decay matrix element is in the correct order
double output(0.);
if(ivec == 0) {
for(unsigned int ix = 0; ix < 3; ++ix) {
(*ME())(0, ix, 0) = 0.;
for(auto vert : vertex_)
(*ME())(0, ix, 0) += vert->evaluate(scale,vector_[ix],sca, swave_);
}
}
else {
for(unsigned int ix = 0; ix < 3; ++ix) {
(*ME())(0, 0, ix) = 0.;
for(auto vert : vertex_)
(*ME())(0, 0, ix) += vert->evaluate(scale,vector_[ix],sca,swave_);
}
}
output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy SSVDecayer:: partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
double mu1sq(sqr(outa.second/inpart.second)),
mu2sq(sqr(outb.second/inpart.second));
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
if(outa.first->iSpin() == PDT::Spin0) {
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), outb.first, outa.first,in);
}
else {
swap(mu1sq,mu2sq);
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), outa.first, outb.first,in);
}
double me2(0.);
if(mu2sq == 0.)
me2 = -2.*mu1sq - 2.;
else
me2 = ( sqr(mu2sq - mu1sq) - 2.*(mu2sq + mu1sq) + 1. )/mu2sq;
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second, outa.second,
outb.second);
Energy output = pcm*me2*norm(perturbativeVertex_[0]->norm())/8./Constants::pi;
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double SSVDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
int iscal (0), ivect (1), iglu (2);
// get location of outgoing scalar/vector
if(decay[1]->dataPtr()->iSpin()==PDT::Spin0) swap(iscal,ivect);
if(meopt==Initialize) {
// create scalar wavefunction for decaying particle
ScalarWaveFunction::calculateWaveFunctions(rho3_,const_ptr_cast<tPPtr>(&inpart),incoming);
swave3_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
}
// setup spin information when needed
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),incoming,true);
ScalarWaveFunction::
constructSpinInfo(decay[iscal],outgoing,true);
VectorWaveFunction::
constructSpinInfo(vector3_,decay[ivect],outgoing,true,false);
VectorWaveFunction::
constructSpinInfo(gluon_, decay[iglu ],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin0, PDT::Spin0,
PDT::Spin1, PDT::Spin1)));
// create wavefunctions
ScalarWaveFunction scal(decay[iscal]->momentum(), decay[iscal]->dataPtr(),outgoing);
VectorWaveFunction::calculateWaveFunctions(vector3_,decay[ivect],outgoing,false);
VectorWaveFunction::calculateWaveFunctions(gluon_, decay[iglu ],outgoing,true );
// gauge invariance test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[iglu ]->momentum(),
decay[iglu ]->dataPtr(),10,
outgoing));
}
}
#endif
if (! ((incomingVertex_[inter] && (outgoingVertexS_[inter] || outgoingVertexV_[inter])) ||
(outgoingVertexS_[inter] && outgoingVertexV_[inter])))
throw Exception()
<< "Invalid vertices for radiation in SSV decay in SSVDecayer::threeBodyME"
<< Exception::runerror;
// sort out colour flows
int S(1), V(2);
if (decay[iscal]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[ivect]->dataPtr()->iColour()==PDT::Colour8)
swap(S,V);
else if (decay[ivect]->dataPtr()->iColour()==PDT::Colour3 &&
decay[iscal]->dataPtr()->iColour()==PDT::Colour8)
swap(S,V);
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int iv = 0; iv < 3; ++iv) {
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming scalar
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
ScalarWaveFunction scalarInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),
gluon_[2*ig],swave3_,inpart.mass());
assert(swave3_.particle()->id()==scalarInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vector3_[iv],scal,scalarInter);
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(0, 0, iv, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from the outgoing scalar
if((decay[iscal]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[iscal]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexS_[inter]);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[iscal]->dataPtr();
if(off->CC()) off = off->CC();
ScalarWaveFunction scalarInter =
outgoingVertexS_[inter]->evaluate(scale,3,off,gluon_[2*ig],scal,decay[iscal]->mass());
assert(scal.particle()->id()==scalarInter.particle()->id());
if(!couplingSet) {
gs = abs(outgoingVertexS_[inter]->norm());
couplingSet = true;
}
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vector3_[iv],scalarInter,swave3_);
for(unsigned int ix=0;ix<colourFlow[S].size();++ix) {
(*ME[colourFlow[S][ix].first])(0, 0, iv, ig) +=
colourFlow[S][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing vector
if((decay[ivect]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[ivect]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexV_[inter]);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[ivect]->dataPtr();
if(off->CC()) off = off->CC();
VectorWaveFunction vectorInter =
outgoingVertexV_[inter]->evaluate(scale,3,off,gluon_[2*ig],
vector3_[iv],decay[ivect]->mass());
assert(vector3_[iv].particle()->id()==vectorInter.particle()->id());
if(!couplingSet) {
gs = abs(outgoingVertexV_[inter]->norm());
couplingSet = true;
}
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectorInter,scal,swave3_);
for(unsigned int ix=0;ix<colourFlow[V].size();++ix) {
(*ME[colourFlow[V][ix].first])(0, 0, iv, ig) +=
colourFlow[V][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from 4 point vertex
if (fourPointVertex_[inter]) {
Complex diag = fourPointVertex_[inter]->evaluate(scale, gluon_[2*ig], vector3_[iv],
scal, swave3_);
for(unsigned int ix=0;ix<colourFlow[3].size();++ix) {
(*ME[colourFlow[3][ix].first])(0, 0, iv, ig) +=
colourFlow[3][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha_(S,EM)
output*=(4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
diff --git a/Decay/General/SVVDecayer.cc b/Decay/General/SVVDecayer.cc
--- a/Decay/General/SVVDecayer.cc
+++ b/Decay/General/SVVDecayer.cc
@@ -1,432 +1,434 @@
// -*- C++ -*-
//
// SVVDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SVVDecayer class.
//
#include "SVVDecayer.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/Helicity/Vertex/Scalar/VVSVertex.h"
#include "ThePEG/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr SVVDecayer::clone() const {
return new_ptr(*this);
}
IBPtr SVVDecayer::fullclone() const {
return new_ptr(*this);
}
void SVVDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> ) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractVVSVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<VVSVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(inV.at(inter));
outgoingVertex1_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[0].at(inter));
outgoingVertex2_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[1].at(inter));
}
}
void SVVDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< incomingVertex_ << outgoingVertex1_
<< outgoingVertex2_;
}
void SVVDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> incomingVertex_ >> outgoingVertex1_
>> outgoingVertex2_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SVVDecayer,GeneralTwoBodyDecayer>
describeHerwigSVVDecayer("Herwig::SVVDecayer", "Herwig.so");
void SVVDecayer::Init() {
static ClassDocumentation<SVVDecayer> documentation
("This implements the decay of a scalar to 2 vector bosons.");
}
double SVVDecayer::me2(const int , const Particle & inpart,
const ParticleVector& decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin1,PDT::Spin1)));
bool photon[2];
for(unsigned int ix=0;ix<2;++ix)
photon[ix] = decay[ix]->mass()==ZERO;
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(rho_,const_ptr_cast<tPPtr>(&inpart),incoming);
swave_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),incoming,true);
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::
constructSpinInfo(vectors_[ix],decay[ix],outgoing,true,photon[ix]);
}
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::
calculateWaveFunctions(vectors_[ix],decay[ix],outgoing,photon[ix]);
Energy2 scale(sqr(inpart.mass()));
unsigned int iv1,iv2;
for(iv2 = 0; iv2 < 3; ++iv2) {
if( photon[1] && iv2 == 1 ) ++iv2;
for(iv1=0;iv1<3;++iv1) {
if( photon[0] && iv1 == 1) ++iv1;
(*ME())(0, iv1, iv2) = 0.;
for(auto vert : vertex_)
(*ME())(0, iv1, iv2) += vert->evaluate(scale,vectors_[0][iv1],
vectors_[1][iv2],swave_);
}
}
double output = ME()->contract(rho_).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy SVVDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
Energy2 scale(sqr(inpart.second));
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(scale, outa.first ,
outb.first, in);
double mu1sq = sqr(outa.second/inpart.second);
double mu2sq = sqr(outb.second/inpart.second);
double m1pm2 = mu1sq + mu2sq;
double me2(0.);
if( mu1sq > 0. && mu2sq > 0.)
me2 = ( m1pm2*(m1pm2 - 2.) + 8.*mu1sq*mu2sq + 1.)/4./mu1sq/mu2sq;
else if( mu1sq == 0. || mu2sq == 0. )
me2 = 3.;
else
me2 = 4.;
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second,outa.second,
outb.second);
Energy output = norm(perturbativeVertex_[0]->norm())*
me2*pcm/(8*Constants::pi)/scale*UnitRemoval::E2;
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double SVVDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
if(meopt==Initialize) {
// create scalar wavefunction for decaying particle
ScalarWaveFunction::
calculateWaveFunctions(rho3_,const_ptr_cast<tPPtr>(&inpart),incoming);
swave3_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
}
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),incoming,true);
VectorWaveFunction::
constructSpinInfo(vectors3_[0],decay[0],outgoing,true,false);
VectorWaveFunction::
constructSpinInfo(vectors3_[1],decay[1],outgoing,true,false);
VectorWaveFunction::
constructSpinInfo(gluon_ ,decay[2],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin0, PDT::Spin1,
PDT::Spin1, PDT::Spin1)));
bool massless[2];
for(unsigned int ix=0;ix<2;++ix)
massless[ix] = decay[ix]->mass()!=ZERO;
// create wavefunctions
VectorWaveFunction::calculateWaveFunctions(vectors3_[0],decay[0],outgoing,massless[0]);
VectorWaveFunction::calculateWaveFunctions(vectors3_[1],decay[1],outgoing,massless[1]);
VectorWaveFunction::calculateWaveFunctions(gluon_ ,decay[2],outgoing,true);
// gauge test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[2]->momentum(),
decay[2]->dataPtr(),10,
outgoing));
}
}
#endif
// get the outgoing vertices
AbstractVVVVertexPtr outgoingVertex1;
AbstractVVVVertexPtr outgoingVertex2;
identifyVertices(inpart,decay, outgoingVertex1, outgoingVertex2,inter);
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int iv1 = 0; iv1 < 3; ++iv1) {
if(massless[0] && iv1==1) continue;
for(unsigned int iv2 = 0; iv2 < 3; ++iv2) {
if(massless[1] && iv2==1) continue;
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming vector
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
ScalarWaveFunction scalarInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),gluon_[2*ig],
swave3_,inpart.mass());
assert(swave3_.particle()->id()==scalarInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectors3_[0][iv1],
vectors3_[1][iv2],scalarInter);
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(0, iv1, iv2, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from the 1st outgoing vector
if((decay[0]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[0]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertex1);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[0]->dataPtr();
if(off->CC()) off = off->CC();
VectorWaveFunction vectorInter =
outgoingVertex1->evaluate(scale,3,off,gluon_[2*ig],vectors3_[0][iv1],decay[0]->mass());
assert(vectors3_[0][iv1].particle()->id()==vectorInter.particle()->id());
Complex diag =0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectorInter,vectors3_[1][iv2],swave3_);
if(!couplingSet) {
gs = abs(outgoingVertex1->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[1].size();++ix) {
(*ME[colourFlow[1][ix].first])(0, iv1, iv2, ig) +=
colourFlow[1][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
if((decay[1]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[1]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertex2);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[1]->dataPtr();
if(off->CC()) off = off->CC();
VectorWaveFunction vectorInter =
outgoingVertex2->evaluate(scale,3,off, gluon_[2*ig],vectors3_[1][iv2],decay[1]->mass());
assert(vectors3_[1][iv2].particle()->id()==vectorInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectors3_[0][iv1],vectorInter,swave3_);
if(!couplingSet) {
gs = abs(outgoingVertex2->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[2][ix].first])(0, iv1, iv2, ig) +=
colourFlow[2][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha_(S,EM)
output*=(4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
void SVVDecayer::identifyVertices(const Particle & inpart, const ParticleVector & decay,
AbstractVVVVertexPtr & outgoingVertex1,
AbstractVVVVertexPtr & outgoingVertex2,
ShowerInteraction inter) {
if(inter==ShowerInteraction::QCD) {
// work out which scalar each outgoing vertex corresponds to
// two outgoing vertices
if( inpart.dataPtr() ->iColour()==PDT::Colour0 &&
((decay[0]->dataPtr()->iColour()==PDT::Colour3 &&
decay[1]->dataPtr()->iColour()==PDT::Colour3bar) ||
(decay[0]->dataPtr()->iColour()==PDT::Colour8 &&
decay[1]->dataPtr()->iColour()==PDT::Colour8))){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[0]->id()))){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(getParticleData(decay[0]->id()))){
outgoingVertex1 = outgoingVertex2_[inter];
outgoingVertex2 = outgoingVertex1_[inter];
}
}
else if(inpart.dataPtr() ->iColour()==PDT::Colour8 &&
decay[0]->dataPtr()->iColour()==PDT::Colour3 &&
decay[1]->dataPtr()->iColour()==PDT::Colour3bar){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[0]->id()))){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(getParticleData(decay[0]->id()))){
outgoingVertex1 = outgoingVertex2_[inter];
outgoingVertex2 = outgoingVertex1_[inter];
}
}
// one outgoing vertex
else if(inpart.dataPtr()->iColour()==PDT::Colour3){
if(decay[0]->dataPtr()->iColour()==PDT::Colour3 &&
decay[1]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertex1 = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertex1 = outgoingVertex2_[inter];
}
else if (decay[0]->dataPtr()->iColour()==PDT::Colour3 &&
decay[1]->dataPtr()->iColour()==PDT::Colour8){
if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[1]->dataPtr()->id()))){
outgoingVertex1 = outgoingVertex2_[inter];
outgoingVertex2 = outgoingVertex1_[inter];
}
else {
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
}
}
else if(inpart.dataPtr()->iColour()==PDT::Colour3bar){
if(decay[1]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[0]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertex2 = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertex2 = outgoingVertex2_[inter];
}
else if (decay[0]->dataPtr()->iColour()==PDT::Colour8 &&
decay[1]->dataPtr()->iColour()==PDT::Colour3bar){
if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[0]->dataPtr()->id()))){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else {
outgoingVertex1 = outgoingVertex2_[inter];
outgoingVertex2 = outgoingVertex1_[inter];
}
}
}
if (! ((incomingVertex_[inter] && (outgoingVertex1 || outgoingVertex2)) ||
( outgoingVertex1 && outgoingVertex2)))
throw Exception()
<< "Invalid vertices for QCD radiation in SVV decay in SVVDecayer::identifyVertices"
<< Exception::runerror;
}
else {
if(decay[0]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[0]->dataPtr())))
outgoingVertex1 = outgoingVertex1_[inter];
else
outgoingVertex1 = outgoingVertex2_[inter];
}
if(decay[1]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[1]->dataPtr())))
outgoingVertex2 = outgoingVertex1_[inter];
else
outgoingVertex2 = outgoingVertex2_[inter];
}
}
}
diff --git a/Decay/General/StoFFFFDecayer.cc b/Decay/General/StoFFFFDecayer.cc
--- a/Decay/General/StoFFFFDecayer.cc
+++ b/Decay/General/StoFFFFDecayer.cc
@@ -1,1306 +1,1308 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the StoFFFFDecayer class.
//
#include "StoFFFFDecayer.h"
#include "ThePEG/PDT/DecayMode.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/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/Decay/DecayPhaseSpaceMode.h"
#include "ThePEG/Helicity/Vertex/Vector/FFVVertex.h"
#include "ThePEG/Helicity/Vertex/Scalar/FFSVertex.h"
#include "Herwig/Models/StandardModel/StandardModel.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include <numeric>
using namespace Herwig;
using namespace ThePEG::Helicity;
namespace {
inline bool isParticle(tPPtr part) {
return part->id()>0 && part->dataPtr()->CC();
}
inline bool isAntiParticle(tPPtr part) {
return part->id()<0 && part->dataPtr()->CC();
}
inline bool isMajorana(tPPtr part) {
return !part->dataPtr()->CC();
}
}
IBPtr StoFFFFDecayer::clone() const {
return new_ptr(*this);
}
IBPtr StoFFFFDecayer::fullclone() const {
return new_ptr(*this);
}
void StoFFFFDecayer::persistentOutput(PersistentOStream & os) const {
os << firstVVS_ << firstVSS_ << firstSSS_ << firstFFS_
<< secondFFV_ << secondFFS_
<< thirdFFV_ << thirdFFS_ << sign_;
}
void StoFFFFDecayer::persistentInput(PersistentIStream & is, int) {
is >> firstVVS_ >> firstVSS_ >> firstSSS_ >> firstFFS_
>> secondFFV_ >> secondFFS_
>> thirdFFV_ >> thirdFFS_ >> sign_;
}
DescribeClass<StoFFFFDecayer,GeneralFourBodyDecayer>
describeStoFFFFDecayer("Herwig::StoFFFFDecayer", "Herwig.so");
void StoFFFFDecayer::Init() {
static ClassDocumentation<StoFFFFDecayer> documentation
("The StoFFFFDecayer class performs the 4-fermion "
"decays of scalar particles in BSM models");
}
void StoFFFFDecayer::doinit() {
GeneralFourBodyDecayer::doinit();
unsigned int ndiags = getProcessInfo().size();
firstVVS_ .resize(ndiags);
firstVSS_ .resize(ndiags);
firstSSS_ .resize(ndiags);
firstFFS_ .resize(ndiags);
secondFFV_.resize(ndiags);
secondFFS_.resize(ndiags);
thirdFFV_ .resize(ndiags);
thirdFFS_ .resize(ndiags);
for(unsigned int ix = 0;ix < ndiags; ++ix) {
const NBDiagram & current = getProcessInfo()[ix];
// first vertex
firstVVS_[ix] = dynamic_ptr_cast<AbstractVVSVertexPtr>(current.vertex);
firstVSS_[ix] = dynamic_ptr_cast<AbstractVSSVertexPtr>(current.vertex);
firstSSS_[ix] = dynamic_ptr_cast<AbstractSSSVertexPtr>(current.vertex);
firstFFS_[ix] = dynamic_ptr_cast<AbstractFFSVertexPtr>(current.vertex);
// get the other vertices
const NBVertex & second = current.vertices.begin()->second.incoming ?
current.vertices.begin()->second : (++current.vertices.begin())->second;
// get the other vertices
const NBVertex & third = current.vertices.begin()->second.incoming ?
(++current.vertices.begin())->second : (++second.vertices.begin())->second;
// second vertex
secondFFV_[ix] = dynamic_ptr_cast<AbstractFFVVertexPtr>(second.vertex);
secondFFS_[ix] = dynamic_ptr_cast<AbstractFFSVertexPtr>(second.vertex);
// third vertex
thirdFFV_ [ix] = dynamic_ptr_cast<AbstractFFVVertexPtr>(third .vertex);
thirdFFS_ [ix] = dynamic_ptr_cast<AbstractFFSVertexPtr>(third .vertex);
assert( ( firstVVS_[ix] || firstVSS_[ix] ||
firstSSS_[ix] || firstFFS_[ix] ) &&
(secondFFV_[ix] || secondFFS_[ix] ) &&
( thirdFFV_[ix] || thirdFFS_[ix] ));
// NO sign
int order =
current.channelType[0]*1000+current.channelType[1]*100+
current.channelType[2]*10 +current.channelType[3];
switch(order) {
case 1234: case 1342: case 1423:
case 2143: case 2314: case 2431:
case 3124: case 3241: case 3412:
case 4132: case 4213: case 4321:
sign_.push_back( 1.);
break;
case 1243: case 1324: case 1432:
case 2134: case 2341: case 2413:
case 3142: case 3214: case 3421:
case 4123: case 4231: case 4312:
sign_.push_back(-1.);
break;
default:
assert(false);
}
}
}
double StoFFFFDecayer::me2(const int ichan, const Particle & inpart,
const ParticleVector & decay, MEOption meopt) const {
// particle or CC of particle
bool cc = (*getProcessInfo().begin()).incoming->id() != inpart.id();
// special handling or first/last call
const vector<vector<double> > & cfactors(getColourFactors());
const vector<vector<double> > & nfactors(getLargeNcColourFactors());
const size_t ncf(numberOfFlows());
Energy2 scale(sqr(inpart.mass()));
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(rho_,const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming);
swave_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),
Helicity::incoming);
+ // fix rho if no correlations
+ fixRho(rho_);
}
// setup spin info when needed
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming,true);
// outgoing particles
for(unsigned int ix = 0; ix < 4; ++ix) {
SpinorWaveFunction::
constructSpinInfo(outwave_[ix].first,decay[ix],Helicity::outgoing,true);
}
}
// outgoing particles
for(unsigned int ix = 0; ix < 4; ++ix) {
SpinorWaveFunction::
calculateWaveFunctions(outwave_[ix].first,decay[ix],Helicity::outgoing);
outwave_[ix].second.resize(2);
if(outwave_[ix].first[0].wave().Type() == SpinorType::u) {
for(unsigned int iy = 0; iy < 2; ++iy) {
outwave_[ix].second[iy] = outwave_[ix].first[iy].bar();
outwave_[ix].first[iy].conjugate();
}
}
else {
for(unsigned int iy = 0; iy < 2; ++iy) {
outwave_[ix].second[iy] = outwave_[ix].first[iy].bar();
outwave_[ix].second[iy].conjugate();
}
}
}
// matrix element for the colour flows
vector<Complex> flows(ncf, Complex(0.)),largeflows(ncf, Complex(0.));
vector<GeneralDecayMEPtr> mes(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin0,
PDT::Spin1Half,PDT::Spin1Half,
PDT::Spin1Half,PDT::Spin1Half)));
vector<GeneralDecayMEPtr> mel(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin0,
PDT::Spin1Half,PDT::Spin1Half,
PDT::Spin1Half,PDT::Spin1Half)));
// calculate the matrix element
unsigned int ihel[4];
for(ihel[0] = 0; ihel[0] < 2; ++ihel[0]) {
for(ihel[1] = 0; ihel[1] < 2; ++ihel[1]) {
for(ihel[2] = 0; ihel[2] < 2; ++ihel[2]) {
for(ihel[3] = 0; ihel[3] < 2; ++ihel[3]) {
flows = vector<Complex>(ncf, Complex(0.));
largeflows = vector<Complex>(ncf, Complex(0.));
unsigned int idiag=0;
for(vector<NBDiagram>::const_iterator dit=getProcessInfo().begin();
dit!=getProcessInfo().end();++dit) {
if(ichan>=0&&diagramMap()[ichan]!=idiag) {
++idiag;
continue;
}
// location of the particles
int iloc[4];
for(unsigned int ix=0;ix<4;++ix) iloc[ix] = dit->channelType[ix]-1;
// NO sign
double sign = sign_[idiag];
// work out the type of topology
bool topo = dit->vertices.begin()->second.incoming;
const NBVertex & second = topo ?
dit->vertices.begin() ->second :
(++(dit->vertices.begin()))->second;
const NBVertex & third = topo ?
(++(dit-> vertices.begin()))->second :
(++(second.vertices.begin()))->second;
// extract the intermediates
tPDPair inter = make_pair(second.incoming,
third .incoming);
if( inter.second->CC()) inter.second = inter.second->CC();
if(cc&&inter.first ->CC()) inter.first = inter.first ->CC();
if(cc&&inter.second->CC()) inter.second = inter.second->CC();
// value for the diagram
Complex diag(0.);
// first compute the last part of the diagram
VectorWaveFunction offVector2;
ScalarWaveFunction offScalar2;
// intermediate scalar
if(inter.second->iSpin()==PDT::Spin0) {
if( (isAntiParticle(decay[iloc[2]]) || isMajorana(decay[iloc[2]])) &&
(isParticle (decay[iloc[3]]) || isMajorana(decay[iloc[3]])) ) {
sign *= -1.;
offScalar2 = thirdFFS_[idiag]->
evaluate(scale, widthOption(),inter.second,
outwave_[iloc[2]].first [ihel[iloc[2]]],
outwave_[iloc[3]].second[ihel[iloc[3]]]);
}
else {
offScalar2 = thirdFFS_[idiag]->
evaluate(scale, widthOption(),inter.second,
outwave_[iloc[3]].first [ihel[iloc[3]]],
outwave_[iloc[2]].second[ihel[iloc[2]]]);
}
}
// intermediate vector
else if(inter.second->iSpin()==PDT::Spin1) {
if( (isAntiParticle(decay[iloc[2]]) || isMajorana(decay[iloc[2]])) &&
(isParticle(decay[iloc[3]])||isMajorana(decay[iloc[3]]))) {
sign *= -1.;
offVector2 = thirdFFV_[idiag]->
evaluate(scale, widthOption(),inter.second,
outwave_[iloc[2]].first [ihel[iloc[2]]],
outwave_[iloc[3]].second[ihel[iloc[3]]]);
}
else {
offVector2 = thirdFFV_[idiag]->
evaluate(scale, widthOption(),inter.second,
outwave_[iloc[3]].first [ihel[iloc[3]]],
outwave_[iloc[2]].second[ihel[iloc[2]]]);
}
}
// unknown
else
assert(false);
// first topology
if(topo) {
// first intermediate
if(inter.first->CC()) inter.first = inter.first->CC();
VectorWaveFunction offVector1;
ScalarWaveFunction offScalar1;
// intermeidate scalar
if(inter.first->iSpin()==PDT::Spin0) {
if(decay[iloc[0]]->id()<0&&
decay[iloc[1]]->id()>0) {
sign *= -1.;
offScalar1 = secondFFS_[idiag]->
evaluate(scale, widthOption(),inter.first,
outwave_[iloc[0]].first [ihel[iloc[0]]],
outwave_[iloc[1]].second[ihel[iloc[1]]]);
}
else {
offScalar1 = secondFFS_[idiag]->
evaluate(scale, widthOption(),inter.first,
outwave_[iloc[1]].first [ihel[iloc[1]]],
outwave_[iloc[0]].second[ihel[iloc[0]]]);
}
}
// intermediate vector
else if(inter.first->iSpin()==PDT::Spin1) {
if(decay[iloc[0]]->id()<0&&
decay[iloc[1]]->id()>0) {
sign *= -1.;
offVector1 = secondFFV_[idiag]->
evaluate(scale, widthOption(),inter.first,
outwave_[iloc[0]].first [ihel[iloc[0]]],
outwave_[iloc[1]].second[ihel[iloc[1]]]);
}
else {
offVector1 = secondFFV_[idiag]->
evaluate(scale, widthOption(),inter.first,
outwave_[iloc[1]].first [ihel[iloc[1]]],
outwave_[iloc[0]].second[ihel[iloc[0]]]);
}
}
// unknown
else
assert(false);
// put it all together
if(inter.first->iSpin()==PDT::Spin0) {
if(inter.second->iSpin()==PDT::Spin0) {
diag = firstSSS_[idiag]->
evaluate(scale,swave_,offScalar1,offScalar2);
}
else if(inter.second->iSpin()==PDT::Spin1) {
diag = firstVSS_[idiag]->
evaluate(scale,offVector2,offScalar1,swave_);
}
}
else if(inter.first->iSpin()==PDT::Spin1) {
if(inter.second->iSpin()==PDT::Spin0) {
diag = firstVSS_[idiag]->
evaluate(scale,offVector1,offScalar2,swave_);
}
else if(inter.second->iSpin()==PDT::Spin1) {
diag = firstVVS_[idiag]->
evaluate(scale,offVector1,offVector2,swave_);
}
}
}
// second topology
else {
if(((isAntiParticle(decay[iloc[0]]) || isMajorana(decay[iloc[0]]))&&
(isParticle (decay[iloc[1]]) || isMajorana(decay[iloc[1]])))) {
sign *= -1.;
SpinorWaveFunction inters = firstFFS_[idiag]->
evaluate(scale,widthOption(),inter.first,
outwave_[iloc[0]].first [ihel[iloc[0]]],swave_);
if(inter.second->iSpin()==PDT::Spin0) {
diag = secondFFS_[idiag]->
evaluate(scale,inters,outwave_[iloc[1]].second[ihel[iloc[1]]],
offScalar2);
}
else {
diag = secondFFV_[idiag]->
evaluate(scale,inters,outwave_[iloc[1]].second[ihel[iloc[1]]],
offVector2);
}
}
else {
SpinorBarWaveFunction inters = firstFFS_[idiag]->
evaluate(scale,widthOption(),inter.first,
outwave_[iloc[0]].second[ihel[iloc[0]]],swave_);
if(inter.second->iSpin()==PDT::Spin0) {
diag = secondFFS_[idiag]->
evaluate(scale,outwave_[iloc[1]].first [ihel[iloc[1]]],inters,
offScalar2);
}
else {
diag = secondFFV_[idiag]->
evaluate(scale,outwave_[iloc[1]].first [ihel[iloc[1]]],inters,
offVector2);
}
}
}
// apply NO sign
diag *= sign;
// matrix element for the different colour flows
if(ichan<0) {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
else {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1!=colourFlow()) continue;
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1!=colourFlow()) continue;
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
++idiag;
}
// now add the flows to the me2 with appropriate colour factors
for(unsigned int ix = 0; ix < ncf; ++ix) {
(*mes[ix])(0,ihel[0],ihel[1],ihel[2],ihel[3]) = flows[ix];
(*mel[ix])(0,ihel[0],ihel[1],ihel[2],ihel[3]) = largeflows[ix];
}
}
}
}
}
double me2(0.);
if(ichan<0) {
vector<double> pflows(ncf,0.);
for(unsigned int ix = 0; ix < ncf; ++ix) {
for(unsigned int iy = 0; iy < ncf; ++ iy) {
double con = cfactors[ix][iy]*(mes[ix]->contract(*mes[iy],rho_)).real();
me2 += con;
if(ix==iy) {
con = nfactors[ix][iy]*(mel[ix]->contract(*mel[iy],rho_)).real();
pflows[ix] += con;
}
}
}
double ptotal(std::accumulate(pflows.begin(),pflows.end(),0.));
ptotal *=UseRandom::rnd();
for(unsigned int ix=0;ix<pflows.size();++ix) {
if(ptotal<=pflows[ix]) {
colourFlow(ix);
ME(mes[ix]);
break;
}
ptotal-=pflows[ix];
}
}
else {
unsigned int iflow = colourFlow();
me2 = nfactors[iflow][iflow]*(mel[iflow]->contract(*mel[iflow],rho_)).real();
}
// return the matrix element squared
return me2*scale*UnitRemoval::InvE2;
}
// OLD TESTING CODE
// extracted from StandardModel.h
// public:
// virtual void StopCouplings(Energy2 &,tcPDPtr, tcPDPtr,
// double &, double &, double &,
// Complex &, Complex &,
// vector<Complex> &, vector<Complex> &,
// vector<Complex> &,
// vector<Complex> &, vector<Complex> &,
// vector<Complex> &, vector<Complex> &,
// vector<Complex> &, vector<Complex> &,
// vector<vector<Complex> > &, vector<vector<Complex> > &,
// vector<vector<Complex> > &, vector<vector<Complex> > &,
// vector<Complex> &, vector<Complex> &,
// vector<Complex> &, vector<Complex> &,
// double &, double &) {
// assert(false);
// }
// extracted from RunningMass.cc
// if(id==5) return 4.8787783899999999*GeV;
// if(id==15) return 1.7770999999999999*GeV;
// extracted from MSSM.h
// public:
// virtual void StopCouplings(Energy2 &, tcPDPtr, tcPDPtr,
// double & g, double & sw, double & cw,
// Complex & aL, Complex & aR,
// vector<Complex> & cL, vector<Complex> & cR,
// vector<Complex> & d,
// vector<Complex> & bL, vector<Complex> & bR,
// vector<Complex> & kL, vector<Complex> & kR,
// vector<Complex> & fL, vector<Complex> & fR,
// vector<vector<Complex> > & eL, vector<vector<Complex> > & eR,
// vector<vector<Complex> > & gL, vector<vector<Complex> > & gR,
// vector<Complex> & hL, vector<Complex> & hR,
// vector<Complex> & lL, vector<Complex> & lR,
// double & ytop, double & ytau);
// extracted from MSSM.cc
// void MSSM::StopCouplings(Energy2 & scale, tcPDPtr ferm, tcPDPtr anti, double & g, double & sw, double & cw,
// Complex & aL, Complex & aR,
// vector<Complex> & cL, vector<Complex> & cR,
// vector<Complex> & d,
// vector<Complex> & bL, vector<Complex> & bR,
// vector<Complex> & kL, vector<Complex> & kR,
// vector<Complex> & fL, vector<Complex> & fR,
// vector<vector<Complex> > & eL, vector<vector<Complex> > & eR,
// vector<vector<Complex> > & gL, vector<vector<Complex> > & gR,
// vector<Complex> & hL, vector<Complex> & hR,
// vector<Complex> & lL, vector<Complex> & lR,
// double & ytop, double & ytau) {
// MixingMatrixPtr stop = stopMix();
// MixingMatrixPtr sbot = sbottomMix();
// MixingMatrixPtr stau = stauMix();
// MixingMatrixPtr neut = neutralinoMix();
// MixingMatrixPtr vmix = charginoVMix();
// MixingMatrixPtr umix = charginoUMix();
// sw = sqrt( sin2ThetaW());
// cw = sqrt(1.-sin2ThetaW());
// g = sqrt(4.0*Constants::pi*alphaEMMZ()/sin2ThetaW());
// Energy mb = mass(scale,getParticleData(ParticleID::b));
// Energy mt = mass(scale,getParticleData(ParticleID::t));
// Energy mw = getParticleData(ParticleID::Wplus)->mass();
// double tb = tanBeta();
// double sb = tb/sqrt(1 + sqr(tb));
// double cb = sqrt(1 - sqr(sb));
// Complex n1prime = (*neut)(0,0)*cw + (*neut)(0,1)*sw;
// Complex n2prime = (*neut)(0,1)*cw - (*neut)(0,0)*sw;
// double yt = double(mt/mw)/sb/sqrt(2.);
// Complex bracketl = 2./ 3.*sw*( conj(n1prime) - sw*conj(n2prime)/cw );
// Complex bracketr = 2./3.*sw*n1prime - n2prime*(-0.5 + 2./3.*sqr(sw))/cw;
// ytop = mt/tb/mw;
// aL = -yt*conj((*neut)(0,3))*(*stop)(0,0) + sqrt(2.)*(*stop)(0,1)*bracketl;
// aR = -yt* (*neut)(0,3) *(*stop)(0,1) - sqrt(2.)*(*stop)(0,0)*bracketr;
// cL.resize(2); cR.resize(2); d.resize(2.);
// kL.resize(2); kR.resize(2);
// bL.resize(2); bR.resize(2);
// double yb = double(mb/mw)/cb/sqrt(2.);
// bracketl = -1./3.*sw*( conj(n1prime) - sw*conj(n2prime)/cw );
// bracketr = -1./3.*sw*n1prime - n2prime*(0.5 -1./3.*sqr(sw))/cw;
// for(unsigned int k=0;k<2;++k) {
// cL[k] =-yb*conj((*neut)(0,2))*(*sbot)(k,0) + sqrt(2.)*(*sbot)(k,1)*bracketl;
// cR[k] =-yb* (*neut)(0,2) *(*sbot)(k,1) - sqrt(2.)*(*sbot)(k,0)*bracketr;
// d[k] = (*stop)(0,0)*(*sbot)(k,0);
// bL[k] = mb*conj((*umix)(k,1))/sqrt(2.)/mw/cb*(*stop)(0,0);
// bR[k] = -(*vmix)(k,0)*(*stop)(0,0)+mt*(*vmix)(k,1)/sqrt(2.)/mw/sb*(*stop)(0,1);
// kR[k] = - (*neut)(0,3)*conj((*vmix)(k,1))/sqrt(2.)
// + (*neut)(0,1) *conj((*vmix)(k,0));
// kL[k] = conj((*neut)(0,2))* (*umix)(k,1) /sqrt(2.)
// +conj((*neut)(0,1))* (*umix)(k,0) ;
// }
// fL.resize(2); fR.resize(2);
// double qf = ferm->charge()/eplus;
// Energy mf = (abs(ferm->id())<5||(abs(ferm->id())>=11&&abs(ferm->id())<=14)) ? ZERO : mass(scale, ferm);
// Energy ma = (abs(anti->id())<5||(abs(anti->id())>=11&&abs(anti->id())<=14)) ? ZERO : mass(scale, anti);
// fL[0] = 0.;
// fR[0] = - sqrt(2.)*(qf*sw*n1prime - n2prime*(-0.5 + qf*sqr(sw))/cw);
// fL[1] = + sqrt(2.)*qf*sw*( conj(n1prime) - sw*conj(n2prime)/cw );
// fR[1] = 0.;
// eL.resize(2,vector<Complex>(2,0.));
// eR.resize(2,vector<Complex>(2,0.));
// for(unsigned int i=0;i<2;++i) {
// eR[i][0] = ma*conj((*umix)(i,1))/sqrt(2.)/mw/cb;
// eL[i][0] = -(*vmix)(i,0);
// eL[i][1] = 0.;
// eR[i][1] = 0.;
// }
// hL.resize(2); hR.resize(2);
// double ya = double(ma/mw)/cb/sqrt(2.);
// qf =-anti->charge()/eplus;
// bracketl = qf*sw*( conj(n1prime) - sw*conj(n2prime)/cw );
// bracketr = qf*sw*n1prime - n2prime*(0.5 +qf*sqr(sw))/cw;
// if(abs(anti->id())==ParticleID::tauminus) {
// for(unsigned int k=0;k<2;++k) {
// hR[k] =-ya*conj((*neut)(0,2))*(*stau)(k,0) + sqrt(2.)*(*stau)(k,1)*bracketl;
// hL[k] =-ya* (*neut)(0,2) *(*stau)(k,1) - sqrt(2.)*(*stau)(k,0)*bracketr;
// }
// }
// else {
// hR[0] = 0.;
// hL[0] = - sqrt(2.)*bracketr;
// hR[1] = + sqrt(2.)*bracketl;
// hL[1] = 0.;
// }
// gL.resize(2,vector<Complex>(2,0.));
// gR.resize(2,vector<Complex>(2,0.));
// double y = ma/mw/sqrt(2.)/cb;
// ytau = ma/mw*tb;
// for(unsigned int i=0;i<2;++i) {
// if(abs(anti->id())==ParticleID::tauminus) {
// for(unsigned int j=0;j<2;++j) {
// gL[i][j] = 0.;
// gR[i][j] = -(*umix)(i,0)*(*stau)(j,0) + ya*(*stau)(j,1)*(*umix)(i,1);
// }
// }
// else {
// gL[i][0] = 0.;
// gR[i][0] = -(*umix)(i,0);
// gL[i][1] = 0.;
// gR[i][1] = 0.;
// }
// }
// double tw = sw/cw;
// lL.resize(2);
// lR.resize(2);
// for(unsigned int j = 0; j < 2; ++j) {
// lL[j] = -(conj((*neut)(0, 3)*(*vmix)(j,0) + ((*neut)(0,1) + (*neut)(0,0)*tw)*(*vmix)(j,1)/sqrt(2)))*cb;
// lR[j] = -( (*neut)(0, 2)*(*umix)(j,0) - ((*neut)(0,1) + (*neut)(0,0)*tw)*(*umix)(j,1)/sqrt(2) )*sb;
// }
// }
// extracted from SSFFHVertex.cc
// if(particle1->id()!=ParticleID::b) theMassLast.first = theMSSM->mass(q2,particle1);
// if(particle2->id()!=ParticleID::b) theMassLast.second = theMSSM->mass(q2,particle2);
// extracted from FourBodyDecayConstructor.cc
// from createDecayMode
// unsigned int nferm=0;
// tcPDPtr bottom,ferm,anti,chi;
// for(OrderedParticles::const_iterator it=diagrams[0].outgoing.begin();
// it!=diagrams[0].outgoing.end();++it) {
// if((**it).iSpin()==PDT::Spin1Half) ++nferm;
// if(abs((**it).id())==ParticleID::b)
// bottom = *it;
// else if(abs((**it).id())>1000000)
// chi = *it;
// else if((**it).id()<0)
// anti = *it;
// else
// ferm = *it;
// }
// if(!bottom||!chi||!ferm||!anti) return;
// if(ferm->id()-abs(anti->id())!=1) return;
//if(anti->id()!=ParticleID::tauplus) return;
// from decayList
// set<PDPtr> new_particles;
// for(set<PDPtr>::iterator it=particles.begin();it!=particles.end();++it) {
// if((**it).id()==ParticleID::SUSY_t_1) new_particles.insert(*it);
// }
// NBodyDecayConstructorBase::DecayList(new_particles);
// extracted from StoFFFFDecayer.h
// private :
// InvEnergy2 stopMatrixElement(const Particle & inpart,
// const ParticleVector & decay,
// InvEnergy2 me2) const;
// #include "Herwig/Models/StandardModel/StandardModel.h"
// InvEnergy2 StoFFFFDecayer::stopMatrixElement(const Particle & inpart,
// const ParticleVector & decay,
// InvEnergy2 me2) const {
// // extract the momenta and check the process
// bool found[4]={false,false,false,false};
// Lorentz5Momentum pb,pf,pfp,pChi;
// double col = 1.;
// tcPDPtr ferm,anti;
// for(unsigned int ix=0;ix<decay.size();++ix) {
// long id = decay[ix]->id();
// if(id==ParticleID::b) {
// found[0] = true;
// pb = decay[ix]->momentum();
// }
// else if(id==ParticleID::SUSY_chi_10) {
// found[1] = true;
// pChi = decay[ix]->momentum();
// }
// else if(abs(id)%2==0) {
// if(decay[ix]->dataPtr()->coloured()) col = 3.;
// found[2] = true;
// pf = decay[ix]->momentum();
// ferm = decay[ix]->dataPtr();
// }
// else {
// found[3] = true;
// pfp = decay[ix]->momentum();
// anti = decay[ix]->dataPtr();
// }
// }
// // check the process
// if(!found[0]||!found[1]||!found[2]||!found[3]) return ZERO;
// // extract the couplings we need
// HwSMPtr model = dynamic_ptr_cast<HwSMPtr>(generator()->standardModel());
// double sw(0.),cw(0.),g(0.);
// Energy mb = getParticleData(ParticleID::b)->mass();
// Energy mt = getParticleData(ParticleID::t)->mass();
// Energy mChi = getParticleData(ParticleID::SUSY_chi_10)->mass();
// Energy mw = getParticleData(ParticleID::Wplus)->mass();
// Energy mbt[2] = {getParticleData(ParticleID::SUSY_b_1)->mass(),
// getParticleData(ParticleID::SUSY_b_2)->mass()};
// Energy mP[2] = {getParticleData(ParticleID::SUSY_chi_1plus)->mass(),
// getParticleData(ParticleID::SUSY_chi_2plus)->mass()};
// Energy msf[2]={ZERO,ZERO};
// tcPDPtr sf = getParticleData(1000000+abs(ferm->id()));
// msf[0] = sf->mass();
// sf = getParticleData(2000000+abs(ferm->id()));
// msf[1] = sf ? sf->mass() : 1e30*GeV;
// Energy msfp[2]={getParticleData(1000000+abs(anti->id()))->mass(),
// getParticleData(2000000+abs(anti->id()))->mass()};
// Complex aL(0.),aR(0.);
// vector<Complex> cL,cR,d,bL,bR,kL,kR,fL,fR,hL,hR,lL,lR;
// vector<vector<Complex> > eL,eR,gL,gR;
// double ytop,ytau;
// Energy2 scale = sqr(inpart.mass());
// model->StopCouplings(scale,ferm,anti,g,sw,cw,aL,aR,cL,cR,d,bL,bR,kL,kR,fL,fR,eL,eR,gL,gR,hL,hR,
// lL,lR,ytop,ytau);
// // compute the matrix element
// Lorentz5Momentum pw = pf+pfp; pw.rescaleMass();
// Lorentz5Momentum ptop = pw+pb; ptop.rescaleMass();
// Lorentz5Momentum ptt = inpart.momentum();
// Lorentz5Momentum pbt = inpart.momentum()-pw; pbt.rescaleMass();
// Lorentz5Momentum pChiP= inpart.momentum()-pb; pb.rescaleMass();
// Lorentz5Momentum psf = pChi+pf;psf.rescaleMass();
// Lorentz5Momentum psfp = pChi+pfp;psfp.rescaleMass();
// Energy2 ptpt = ptop*ptop;
// Energy2 pChipfp = pChi*pfp;
// Energy2 ptopptop = ptop.m2();
// Energy2 pbpf = pb*pf;
// Energy2 pbpfp = pb*pfp;
// Energy2 ptoppfp = ptop*pfp;
// Energy2 pChipt = pChi*ptop;
// Energy2 pChiptt = pChi*ptt;
// Energy2 pfpfp = pf*pfp;
// Energy2 pChipb = pChi*pb;
// Energy2 pChipf = pChi*pf;
// Energy2 pttptt = ptt.m2();
// Energy2 pbptt = pb*ptt;
// Energy2 pfpptt = pfp*ptt;
// Energy2 pfppt = pfp*ptop;
// Energy2 pfptt = pf*ptt;
// Energy2 ptptt = ptop*ptt;
// Energy2 pfpt = pf*ptop;
// Energy2 pbpt = pb*ptop;
// Energy2 pChiPpChiP=pChiP*pChiP;
// Energy2 pChipChiP=pChi*pChiP;
// Energy2 pbpChiP = pb*pChiP;
// Energy2 pfppChiP = pfp*pChiP;
// Energy2 ptpChiP = ptop*pChiP;
// Energy2 pttpChiP = ptt*pChiP;
// Energy2 pfpChiP = pf*pChiP;
// Energy mf = pf.mass();
// Energy mfp = pfp.mass();
// assert(model);
// InvEnergy2 pTop = 1./(ptop.m2()-sqr(mt));
// InvEnergy2 pW = 1./(pw .m2()-sqr(mw));
// InvEnergy2 pBT[2] = {1./(pbt.m2()-sqr(mbt[0])),1./(pbt.m2()-sqr(mbt[1]))};
// InvEnergy2 pP[2] = {1./(pChiP.m2()-sqr(mP[0])),1./(pChiP.m2()-sqr(mP[1]))};
// InvEnergy2 pSF [2] = {1./(psf .m2()-sqr(msf [0])),1./(psf .m2()-sqr(msf [1]))};
// if(abs(ferm->id())==ParticleID::nu_e||abs(ferm->id())==ParticleID::nu_mu||abs(ferm->id())==ParticleID::nu_tau)
// pSF[1] = ZERO;
// InvEnergy2 pSFP[2] = {1./(psfp.m2()-sqr(msfp[0])),1./(psfp.m2()-sqr(msfp[1]))};
// // top squared
// InvEnergy2 mett = pow(g,6)*sqr(pTop*pW)*
// ( norm(aR) * ( -4.*pChipfp*pbpf*ptopptop + 8.*pChipt*pbpf*ptoppfp ) +
// norm(aL) * ( 4.*pChipfp*pbpf*sqr(mt))
// + real(aL*conj(aR)) * ( - 8*pbpf*ptoppfp*mChi*mt )
// );
// // colour factors
// mett *=col;
// // sbottom squared
// complex<InvEnergy2> mebb(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// mebb += pow(g,6)*d[i]*d[j]*pBT[i]*pBT[j]*sqr(pW)*
// (pChipb*(cR[i]*cR[j]+cL[i]*cL[j])- mChi*mb*(cL[j]*cR[i]+cL[i]*cR[j]))*
// ( - 4*pfpfp*pttptt + 8*pfptt*pfpptt + pfpfp*sqr(mfp) + pfpfp*sqr(mf)
// - 4*pfptt*sqr(mfp) - 4*pfpptt*sqr(mf) + 2*sqr(mf)*sqr(mfp) );
// }
// }
// // colour factors
// mebb *=col;
// // stop sbottom
// complex<InvEnergy2> metb(ZERO);
// for(unsigned int i=0;i<2;++i) {
// metb += pow(g,6)*d[i]*pTop*pBT[i]*sqr(pW)*cR[i]*
// (
// + aR*( - 2*pChipb*pfpfp*ptptt + 2*pChipb*pfpt*
// pfpptt + 2*pChipb*pfptt*pfppt + 2*pChipf*pbpfp*ptptt -
// 2*pChipf*pbpt*pfpptt - 2*pChipf*pbptt*pfppt - 2*pChipfp
// *pbpf*ptptt - 2*pChipfp*pbpt*pfptt + 2*pChipfp*pbptt*
// pfpt + 2*pChipt*pbpf*pfpptt + 2*pChipt*pbpfp*pfptt - 2*
// pChipt*pbptt*pfpfp + 2*pChiptt*pbpf*pfppt - 2*pChiptt*
// pbpfp*pfpt + 2*pChiptt*pbpt*pfpfp)
// + aL*mChi*mt*( - 2*pbpf*pfpptt - 2*pbpfp*pfptt + 2*pbptt*pfpfp ));
// }
// // colour factors
// metb *=col;
// complex<InvEnergy2> mecc(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// mecc += pow(g,6)*pP[i]*pP[j]*sqr(pW)*
// (+ bR[i]*bR[j]*kR[i]*kR[j] * ( 16*pChipb*pChipf*pChipfp + 16*pChipb*pChipf*pfpfp + 16*pChipb*pChipf*sqr(mfp) + 16*pChipf*
// pChipfp*pbpf + 16*pChipf*pbpf*pfpfp + 16*pChipf*pbpf*sqr(mfp) + 8*pChipf*pbpfp*sqr(mfp) - 8*pChipf*pbpfp*sqr(mf) - 16*sqr(pChipf)*pbpfp )
// + bR[i]*bR[j]*kL[i]*kL[j] * ( 8*pChipfp*pbpf*mP[i]*mP[j] )
// + bL[i]*bL[j]*kR[i]*kR[j] * ( 8*pChipf*pbpfp*mP[i]*mP[j] )
// + bL[i]*bL[j]*kL[i]*kL[j] * ( 16*pChipb*pChipf*pChipfp + 16*pChipb*
// pChipfp*pfpfp + 16*pChipb*pChipfp*sqr(mf) + 16*pChipf*
// pChipfp*pbpfp - 8*pChipfp*pbpf*sqr(mfp) + 8*pChipfp*pbpf*
// sqr(mf) + 16*pChipfp*pbpfp*pfpfp + 16*pChipfp*pbpfp*sqr(mf) -
// 16*sqr(pChipfp)*pbpf )
// + mb*bL[j]*bR[i]*kR[i]*kR[j] * ( - 8*pChipf*pChipfp*mP[j] - 8*pChipf*pfpfp*mP[j] - 8*pChipf*sqr(mfp)*mP[j] )
// + mb*bL[j]*bR[i]*kL[i]*kL[j] * ( - 8*pChipf*pChipfp*mP[i] - 8*pChipfp*pfpfp*mP[i] - 8*pChipfp*sqr(mf)*mP[i] )
// + mb*bL[i]*bR[j]*kR[i]*kR[j] * ( - 8*pChipf*pChipfp*mP[i] - 8*pChipf*pfpfp*mP[i] - 8*pChipf*sqr(mfp)*mP[i] )
// + mb*bL[i]*bR[j]*kL[i]*kL[j] * ( - 8*pChipf*pChipfp*mP[j] - 8*pChipfp*pfpfp*mP[j] - 8*pChipfp*sqr(mf)*mP[j] )
// + mChi*bR[i]*bR[j]*kR[i]*kL[j] * ( - 4*pChipb*pfpfp*mP[j] + 4*pChipf*pbpfp*mP[j] - 4*pChipfp*pbpf*mP[j] - 8*pbpf*pfpfp*mP[j] - 4*pbpf*sqr(mfp)*mP[j] + 4*pbpfp*sqr(mf)*mP[j] )
// + mChi*bR[i]*bR[j]*kL[i]*kR[j] * ( - 4*pChipb*pfpfp*mP[i] + 4*pChipf*pbpfp*mP[i] - 4*pChipfp*pbpf*mP[i] - 8*pbpf*pfpfp*mP[i] - 4*
// pbpf*sqr(mfp)*mP[i] + 4*pbpfp*sqr(mf)*mP[i] )
// + mChi*bL[i]*bL[j]*kR[i]*kL[j] * ( - 4*pChipb*pfpfp*mP[i] - 4*pChipf*pbpfp*mP[i] + 4*pChipfp*pbpf*mP[i] + 4*pbpf*sqr(mfp)*mP[i] - 8*pbpfp*pfpfp*mP[i] - 4*pbpfp*sqr(mf)*mP[i] )
// + mChi*bL[i]*bL[j]*kL[i]*kR[j] * ( - 4*pChipb*pfpfp*mP[j] - 4*pChipf*pbpfp*mP[j] + 4*pChipfp*pbpf*mP[j] + 4*pbpf*sqr(mfp)*mP[j] - 8*
// pbpfp*pfpfp*mP[j] - 4*pbpfp*sqr(mf)*mP[j] )
// + mChi*mb*bL[j]*bR[i]*kR[i]*kL[j] * ( 8*pChipf*pfpfp + 8*pChipfp*
// pfpfp + 4*pfpfp*sqr(mfp) + 4*pfpfp*sqr(mf) + 8*sqr(pfpfp) )
// + mChi*mb*bL[j]*bR[i]*kL[i]*kR[j] * ( 4*pfpfp*mP[i]*mP[j] )
// + mChi*mb*bL[i]*bR[j]*kR[i]*kL[j] * ( 4*pfpfp*mP[i]*mP[j] )
// + mChi*mb*bL[i]*bR[j]*kL[i]*kR[j] * ( 8*pChipf*pfpfp + 8*pChipfp*
// pfpfp + 4*pfpfp*sqr(mfp) + 4*pfpfp*sqr(mf) + 8*sqr(pfpfp) )
// + sqr(mChi)*bR[i]*bR[j]*kR[i]*kR[j] * ( - 8*pChipf*pbpfp )
// + sqr(mChi)*bL[i]*bL[j]*kL[i]*kL[j] * ( - 8*pChipfp*pbpf )
// + mChi*sqr(mChi)*mb*bL[j]*bR[i]*kR[i]*kL[j] * ( 4*pfpfp )
// + mChi*sqr(mChi)*mb*bL[i]*bR[j]*kL[i]*kR[j] * ( 4*pfpfp ));
// }
// }
// mecc *=col;
// complex<InvEnergy2> metc(ZERO);
// for(unsigned int j=0;j<2;++j) {
// metc += pow(g,6)*pP[j]*pTop*sqr(pW)/sqrt(2.)*(
// + aR*bR[j]*kR[j] * ( - 8*pChipb*pChipf*pbpfp + 8*pChipb*pChipf*pfpfp - 8*pChipb*
// pChipfp*pbpf - 8*pChipb*pChipfp*sqr(mf) + 8*pChipb*pbpf*pfpfp - 8*pChipb*pbpfp*
// sqr(mf) - 4*pChipb*pfpfp*sqr(mfp) - 4*
// pChipb*pfpfp*sqr(mf) - 8*pChipb*sqr(mf)*sqr(mfp) + 8*sqr(pChipb)*pfpfp + 8*pChipf*pChipfp*
// pbpf + 8*pChipf*pbpf*pbpfp + 16*
// pChipf*pbpf*pfpfp + 16*pChipf*pbpf*sqr(mfp) + 4*pChipf*pbpfp*sqr(mfp) - 4*pChipf*pbpfp*
// sqr(mf) - 8*sqr(pChipf)*pbpfp + 4*pChipfp*
// pbpf*sqr(mfp) - 4*pChipfp*pbpf*sqr(mf) - 8*
// pChipfp*sqr(pbpf) )
// + aR*mb*bL[j]*kR[j] * ( - 4*pChipb*pfpfp*mP[j] - 4*pChipf*pbpfp*mP[j] - 8*pChipf*pfpfp*mP[j]
// - 4*pChipf*sqr(mfp)*mP[j] + 4*pChipfp*
// pbpf*mP[j] + 4*pChipfp*sqr(mf)*mP[j] )
// + aR*sqr(mb)*bR[j]*kR[j] * ( 8*pChipf*pChipfp + 4*pChipf*sqr(mfp) + 4*pChipfp*sqr(mf) )
// + aR*mChi*bR[j]*kL[j] * ( - 8*pbpf*pbpfp*mP[j] - 8*pbpf*pfpfp*mP[j] - 8*pbpf*sqr(mfp)*mP[j] )
// + aR*mChi*mb*bL[j]*kL[j] * ( 8*sqr(mf)*sqr(mfp) + 8*
// pChipf*pbpfp + 8*pChipf*pfpfp + 8*
// pChipf*sqr(mfp) + 8*pbpfp*pfpfp + 8*
// pbpfp*sqr(mf) + 8*pfpfp*sqr(mfp) + 8*pfpfp*
// sqr(mf) + 8*sqr(pfpfp) )
// + aR*sqr(mChi)*bR[j]*kR[j] * ( 8*pbpf*pbpfp + 4
// *pbpf*sqr(mfp) + 4*pbpfp*sqr(mf) )
// + aR*sqr(mChi)*sqr(mb)*bR[j]*kR[j] * ( - 4*pfpfp )
// + aL*mt*bR[j]*kL[j] * ( 8*pChipfp*pbpf*mP[j] )
// + aL*mb*mt*bL[j]*kL[j] * ( - 8*pChipf*pChipfp - 8*pChipfp*pfpfp - 8*pChipfp*sqr(mf) )
// + aL*mChi*mt*bR[j]*kR[j] * ( - 4*pChipb*pfpfp + 4*pChipf*pbpfp - 4*pChipfp*pbpf - 8*pbpf*pfpfp - 4*pbpf*sqr(mfp) + 4*pbpfp*sqr(mf) )
// + aL*mChi*mb*mt*bL[j]*kR[j] * ( 4*pfpfp*mP[j] ));
// }
// metc *= col;
// complex<InvEnergy2> mebc(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// mebc += pow(g,6)*pP[j]*sqr(pW)*d[i]*pBT[i]/sqrt(2.)*
// (+ cR[i]*bR[j]*kR[j] * ( - 8*pChipb*pChipf*pfpptt + 4
// *pChipb*pChipf*sqr(mfp) - 8*pChipb*
// pChipfp*pfptt + 4*pChipb*pChipfp*sqr(mf) + 8*pChipb*pChiptt*pfpfp + 2*pChipb*
// pfpfp*sqr(mfp) + 2*pChipb*pfpfp*sqr(mf) - 4
// *pChipb*pfptt*sqr(mfp) - 4*pChipb*pfpptt*sqr(mf) + 4
// *pChipb*sqr(mf)*sqr(mfp) + 8*pChipf*pbpfp*
// pfptt + 2*pChipf*pbpfp*sqr(mfp) - 2*
// pChipf*pbpfp*sqr(mf) - 8*pChipf*pbptt*pfpfp - 4*pChipf*pbptt*sqr(mfp) - 8*pChipfp*pbpf*
// pfptt - 2*pChipfp*pbpf*sqr(mfp) + 2*
// pChipfp*pbpf*sqr(mf) + 4*pChipfp*pbptt*sqr(mf) + 8*pChiptt*pbpf*pfpfp + 4*pChiptt*pbpf*
// sqr(mfp) - 4*pChiptt*pbpfp*sqr(mf))
// + cL[i]*bL[j]*kL[j] * ( - 8*pChipb*pChipf*pfpptt + 4
// *pChipb*pChipf*sqr(mfp) - 8*pChipb*
// pChipfp*pfptt + 4*pChipb*pChipfp*sqr(mf) + 8*pChipb*pChiptt*pfpfp + 2*pChipb*
// pfpfp*sqr(mfp) + 2*pChipb*pfpfp*sqr(mf) - 4
// *pChipb*pfptt*sqr(mfp) - 4*pChipb*pfpptt*sqr(mf) + 4
// *pChipb*sqr(mf)*sqr(mfp) - 8*pChipf*pbpfp*
// pfpptt + 2*pChipf*pbpfp*sqr(mfp) - 2*
// pChipf*pbpfp*sqr(mf) + 4*pChipf*pbptt*sqr(mfp) + 8*pChipfp*pbpf*pfpptt - 2*pChipfp*pbpf
// *sqr(mfp) + 2*pChipfp*pbpf*sqr(mf) - 8*
// pChipfp*pbptt*pfpfp - 4*pChipfp*pbptt*sqr(mf) - 4*pChiptt*pbpf*sqr(mfp) + 8*pChiptt*
// pbpfp*pfpfp + 4*pChiptt*pbpfp*sqr(mf))
// + mb*cR[i]*bL[j]*kR[j] * ( 4*pChipf*pfpptt*mP[j] - 2*pChipf*sqr(mfp)*mP[j] + 4*pChipfp*pfptt*mP[j]
// - 2*pChipfp*sqr(mf)*mP[j] - 4*pChiptt*pfpfp*mP[j] )
// + mb*cL[i]*bR[j]*kL[j] * ( 4*pChipf*pfpptt*mP[j] - 2*pChipf*sqr(mfp)*mP[j] + 4*pChipfp*pfptt*mP[j]
// - 2*pChipfp*sqr(mf)*mP[j] - 4*pChiptt*pfpfp*mP[j] )
// + mChi*cR[i]*bR[j]*kL[j] * ( - 4*pbpf*pfpptt*mP[j] + 2*pbpf*sqr(mfp)*mP[j] - 4*pbpfp*pfptt*mP[j]
// + 2*pbpfp*sqr(mf)*mP[j] + 4*pbptt*pfpfp*mP[j] )
// + mChi*cL[i]*bL[j]*kR[j] * ( - 4*pbpf*pfpptt*mP[j] + 2*pbpf*sqr(mfp)*mP[j] - 4*pbpfp*pfptt*mP[j] + 2*pbpfp*sqr(mf)*mP[j] + 4*pbptt*pfpfp*mP[j] )
// + mChi*mb*cR[i]*bL[j]*kL[j] * ( - 4*sqr(mf)*sqr(mfp) + 4*pChipf*pfpptt - 2*pChipf*sqr(mfp) + 4*pChipfp*pfptt - 2*pChipfp*sqr(mf) - 4*pChiptt*pfpfp - 2*pfpfp*sqr(mfp) - 2*pfpfp*sqr(mf) + 4*pfptt*sqr(mfp) + 4*pfpptt*sqr(mf) )
// + mChi*mb*cL[i]*bR[j]*kR[j] * ( - 4*sqr(mf)*sqr(mfp) + 4*pChipf*pfpptt - 2*pChipf*sqr(mfp) + 4*pChipfp*pfptt - 2*pChipfp*sqr(mf) - 4*pChiptt*pfpfp - 2*pfpfp*sqr(mfp) - 2*pfpfp*sqr(mf) + 4*pfptt*sqr(mfp) + 4*pfpptt*sqr(mf) )
// + sqr(mChi)*cR[i]*bR[j]*kR[j] * ( 4*pbpf*pfpptt - 2*pbpf*sqr(mfp) + 4*pbpfp*pfptt - 2*pbpfp*sqr(mf) - 4*pbptt*pfpfp )
// + sqr(mChi)*cL[i]*bL[j]*kL[j] * ( 4*pbpf*pfpptt - 2*pbpf*sqr(mfp) + 4*pbpfp*pfptt - 2*pbpfp*sqr(mf) - 4*pbptt*pfpfp ));
// }
// }
// mebc *= col;
// complex<InvEnergy2> meff(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// for(unsigned int k=0;k<2;++k) {
// for(unsigned int l=0;l<2;++l) {
// meff += pow(g,6)*pP[i]*pP[j]*pSF[k]*pSF[l]*pChipf*(fR[k]*fR[l]+fL[k]*fL[l])*
// (
// + ( eR[i][k]*eR[j][l]*bR[i]*bR[j] + eL[i][k]*eL[j][l]*bL[i]*bL[j] )* ( 4.*pbpfp*mP[i]*mP[j] )
// + ( eR[i][k]*eR[j][l]*bL[i]*bL[j] + eL[i][k]*eL[j][l]*bR[i]*bR[j] )* ( - 4*pbpfp*pChiPpChiP +
// 8*pbpChiP*pfppChiP )
// +mb*mP[i]*( eR[i][k]*eR[j][l]*bL[j]*bR[i] + eL[i][k]*eL[j][l]*bL[i]*bR[j] ) * ( - 4*pfppChiP )
// +mb*mP[j]*( eR[i][k]*eR[j][l]*bL[i]*bR[j] + eL[i][k]*eL[j][l]*bL[j]*bR[i] ) * ( - 4*pfppChiP ));
// }
// }
// }
// }
// meff *= col;
// complex<InvEnergy2> mepp(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// for(unsigned int k=0;k<2;++k) {
// for(unsigned int l=0;l<2;++l) {
// mepp += pow(g,6)*pP[i]*pP[j]*pSFP[k]*pSFP[l]*pChipfp*(hR[k]*hR[l]+hL[k]*hL[l])*
// ((gR[i][k]*gR[j][l]*bR[i]*bR[j] + gL[i][k]*gL[j][l]*bL[i]*bL[j]) * ( 4*pbpf*mP[i]*mP[j] ) +
// (gR[i][k]*gR[j][l]*bL[i]*bL[j] + gL[i][k]*gL[j][l]*bR[i]*bR[j]) *
// ( - 4*pbpf*pChiPpChiP + 8*pbpChiP*pfpChiP ));
// }
// }
// }
// }
// mepp *= col;
// complex<InvEnergy2> metf(ZERO);
// for(unsigned int j=0;j<2;++j) {
// for(unsigned int l=0;l<2;++l) {
// metf += pow(g,6)*pTop*pW*pP[j]*pSF[l]*
// (+ aR*eL[j][l]*fR[l]*bR[j] * ( 2*pChipb*pfpfp*ptpChiP - 2*pChipb*pfpt*pfppChiP
// - 2*pChipb*pfpChiP*pfppt - 2*pChipf*pbpfp*ptpChiP
// + 2*pChipf*pbpt*pfppChiP + 2*pChipf*pbpChiP*pfppt
// - 2*pChipfp*pbpf*ptpChiP + 2*pChipfp*pbpt*pfpChiP
// - 2*pChipfp*pbpChiP*pfpt + 2*pChipt*pbpf*pfppChiP
// - 2*pChipt*pbpfp*pfpChiP + 2*pChipt*pbpChiP*pfpfp
// + 2*pChipChiP*pbpf*pfppt + 2*pChipChiP*pbpfp*pfpt
// - 2*pChipChiP*pbpt*pfpfp )
// + aL*mChi*mt*eL[j][l]*fR[l]*bR[j] * ( - 2*pbpf*pfppChiP + 2*pbpfp*pfpChiP - 2*pbpChiP*pfpfp )
// );
// }
// }
// metf *= col;
// // sbottom sfermion interference
// complex<InvEnergy2> mebf(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// for(unsigned int l=0;l<2;++l) {
// mebf += 2.*pow(g,6)*d[i]*pBT[i]*pW*pP[j]*pSF[l]*
// (cR[i]*eL[j][l]*fR[l]*bR[j] * ( pChipb*pfpfp*pttpChiP - pChipb*pfptt*pfppChiP - pChipb*pfpChiP*pfpptt +
// pChipf*pbpfp*pttpChiP - pChipf*pbptt*pfppChiP - pChipf*pbpChiP*pfpptt -
// pChipfp*pbpf*pttpChiP + pChipfp*pbptt*pfpChiP - pChipfp*pbpChiP*pfptt +
// pChiptt*pbpf*pfppChiP - pChiptt*pbpfp*pfpChiP + pChiptt*pbpChiP*pfpfp +
// pChipChiP*pbpf*pfpptt + pChipChiP*pbpfp*pfptt - pChipChiP*pbptt*pfpfp)
// + mChi*mP[j]*cL[i]*eL[j][l]*fR[l]*bL[j] * ( - pbpf*pfpptt - pbpfp*pfptt + pbptt*pfpfp ));
// }
// }
// }
// mebf *= col;
// // chi W sfermion interference
// complex<InvEnergy2> mecf(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// for(unsigned int l=0;l<2;++l) {
// mecf -= pow(g,6)*pP[i]*pW*pP[j]*pSF[l]*eL[j][l]*fR[l]/sqrt(2.)*
// (+ bR[i]*bR[j]*kR[i] * ( 8*pChipf*pbpfp*pChiPpChiP - 16*pChipf*pbpChiP*pfppChiP )
// + bR[i]*bR[j]*kL[i]*mChi*mP[i] * ( 4*pbpf*pfppChiP - 4*pbpfp*pfpChiP + 4*pbpChiP*pfpfp)
// + bL[i]*bL[j]*kR[i]*mP[i]*mP[j] * ( - 8*pChipf*pbpfp )
// + bL[i]*bL[j]*kL[i]*mChi*mP[j] * (-4*pbpf*pfppChiP + 4*pbpfp*pfpChiP + 4*pbpChiP*pfpfp)
// );
// }
// }
// }
// mecf *= col;
// complex<InvEnergy2> mebp(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// for(unsigned int l=0;l<2;++l) {
// mebp += pow(g,6)*d[i]*pBT[i]*pW*pP[j]*pSFP[l]*
// (
// + cL[i]*gR[j][l]*hL[l]*bL[j] *
// ( - 2*pChipb*pfpfp*pttpChiP + 2*pChipb*pfptt*pfppChiP
// + 2*pChipb*pfpChiP*pfpptt + 2*pChipf*pbpfp*pttpChiP
// - 2*pChipf*pbptt*pfppChiP + 2*pChipf*pbpChiP*pfpptt
// - 2*pChipfp*pbpf*pttpChiP + 2*pChipfp*pbptt*pfpChiP
// + 2*pChipfp*pbpChiP*pfptt + 2*pChiptt*pbpf*pfppChiP
// - 2*pChiptt*pbpfp*pfpChiP - 2*pChiptt*pbpChiP*pfpfp
// - 2*pChipChiP*pbpf*pfpptt - 2*pChipChiP*pbpfp*pfptt
// + 2*pChipChiP*pbptt*pfpfp)
// + mChi*cR[i]*gR[j][l]*hL[l]*bR[j]*mP[j] *
// ( 2*pbpf*pfpptt + 2*pbpfp*pfptt - 2*pbptt*pfpfp ));
// }
// }
// }
// mebp *= col;
// // chi W anti sfermion interference
// complex<InvEnergy2> mecp(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// for(unsigned int l=0;l<1;++l) {
// mecp +=pow(g,6)*pP[i]*pW*pP[j]*pSFP[l]/sqrt(2.)*gR[j][l]*hL[l]*
// (
// + bR[i]*bR[j]*kL[i] * ( - 8*pChipfp*pbpf*mP[i]*mP[j] )
// + bL[i]*bL[j]*kL[i] * ( 8*pChipfp*pbpf*pfppChiP + 8*pChipfp*pbpf*pChiPpChiP - 8*pChipfp*pbpfp*pfpChiP - 8*pChipfp*pbpChiP*pfpfp - 16*pChipfp*pbpChiP*pfpChiP)
// + mChi*bR[i]*bR[j]*kR[i]*mP[j]*( 4*pbpf*pfppChiP - 4*pbpfp*pfpChiP + 4*pbpChiP*pfpfp)
// + mChi*bL[i]*bL[j]*kR[i]*mP[i]*( - 4*pbpf*pfppChiP + 8*pbpfp*pfpfp + 4*pbpfp*pfpChiP + 4*pbpChiP*pfpfp));
// }
// }
// }
// mecp *= col;
// // sfermion antisfermion interferences
// complex<InvEnergy2> mefp(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// for(unsigned int k=0;k<1;++k) {
// for(unsigned int l=0;l<2;++l) {
// mefp +=pow(g,6)*pP[i]*pP[j]*pSF[k]*pSFP[l]*fR[k]*gR[j][l]*
// (
// + eR[i][k]*hR[l]*bR[i]*bR[j]*mP[i]*mP[j] *
// ( 2*pChipb*pfpfp - 2*pChipf*pbpfp - 2*pChipfp*pbpf )
// + eR[i][k]*hR[l]*bL[i]*bL[j]*
// ( - 2*pChipb*pfpfp*pChiPpChiP + 2*pChipf*pbpfp*pChiPpChiP
// - 4*pChipf*pbpChiP*pfppChiP + 2*pChipfp*pbpf*pChiPpChiP
// - 4*pChipfp*pbpChiP*pfpChiP + 4*pChipChiP*pbpChiP*pfpfp)
// + eL[i][k]*hL[l]*bL[i]*bL[j]*mChi*mP[i] *
// (-2*pbpf*pfppChiP + 2*pbpfp*pfpChiP + 2*pbpChiP*pfpfp )
// + eL[i][k]*hL[l]*bR[i]*bR[j]*mChi*mP[j] *
// ( 2*pbpf*pfppChiP - 2*pbpfp*pfpChiP + 2*pbpChiP*pfpfp ));
// }
// }
// }
// }
// mefp *= col;
// // top higgs
// Energy mHiggs = getParticleData(ParticleID::Hplus)->mass();
// InvEnergy2 pH = 1./(pw.m2()-sqr(mHiggs));
// InvEnergy2 meht = 0.25*sqr(ytop*ytau)*pow(g,6)*sqr(pTop*pH)*pfpfp*
// ( norm(aR) *sqr(mt)*4*pChipb +
// norm(aL) * ( - 4*pChipb*ptpt + 8*pChipt*pbpt )
// + real(aL*conj(aR)) * mChi*mt * ( - 8*pbpt ));
// // colour factors
// meht *=col;
// // chargino higgs
// complex<InvEnergy2> mehc(ZERO);
// for(unsigned int i=0;i<2;++i) {
// for(unsigned int j=0;j<2;++j) {
// mehc += pow(g,6)*pP[i]*pP[j]*sqr(pH)*sqr(ytau)*pfpfp*
// ( + (bR[i]*bR[j]*lR[i]*lR[j]+bL[i]*bL[j]*lL[i]*lL[j])*mP[i]*mP[j]*2*pChipb
// + (bR[i]*bR[j]*lL[i]*lL[j]+bL[i]*bL[j]*lR[i]*lR[j])*(-2*pChipb*pChiPpChiP+4*pChipChiP*pbpChiP)
// + (bR[i]*bR[j]*lR[i]*lL[j]+bL[i]*bL[j]*lL[i]*lR[j])*mChi*mP[i]*2*pbpChiP
// + (bR[i]*bR[j]*lL[i]*lR[j]+bL[i]*bL[j]*lR[i]*lL[j])*mChi*mP[j]*2*pbpChiP);
// }
// }
// // hehbc =
// // + cR1*d1*bR2*kR2 * ( 2*pChi.pb*pf.pfp*mP2 )
// // + cL1*d1*bL2*kL2 * ( 2*pChi.pb*pf.pfp*mP2 )
// // + mChi*cR1*d1*bR2*kL2 * ( 2*pb.pChiP*pf.pfp )
// // + mChi*cL1*d1*bL2*kR2 * ( 2*pb.pChiP*pf.pfp );
// // InvEnergy2 meTotal = meff.real()+mecc.real()+2.*mecf.real();
// InvEnergy2 meTotal = abs(mehc.real());
// // if(abs(anti->id())==ParticleID::tauminus) {
// // cerr << "testing inter " << (me2-mepp.real()-meff.real())*GeV2 << " " << 2.*mefp.real()*GeV2
// // << " " << 0.5*(me2-mepp.real()-meff.real())/mefp.real() << "\n";
// // cerr << "testing the matrix element " << meTotal*GeV2 << " "
// // << me2*GeV2 << " " << meTotal/me2 << "\n";
// // }
// return meTotal;
// }
// extracted from main diagram loop
// //\todo remove testing
// top
// if(!(abs(inter.first ->id())==ParticleID::t&&
// abs(inter.second->id())==ParticleID::Wplus)) {
// ++idiag;
// continue;
// }
// sbottom
// if(!((abs(inter.first ->id())==ParticleID::SUSY_b_1||
// abs(inter.first ->id())==ParticleID::SUSY_b_2)&&
// abs(inter.second->id())==ParticleID::Wplus)) {
// ++idiag;
// continue;
// }
// chargino W
// if(!((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())==ParticleID::Wplus)) {
// ++idiag;
// continue;
// }
// sneutrino
// if(!((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// inter.second->id()<0)) {
// ++idiag;
// continue;
// }
// sneutrino
// if(!((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// inter.second->id()<0)) {
// ++idiag;
// continue;
// }
// charged slepton
// if(!((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// inter.second->id()>0)) {
// ++idiag;
// continue;
// }
// slepton sneutrino
// if(!((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// abs(inter.second->id())!=ParticleID::Hplus&&
// inter.second->id()>0) &&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// abs(inter.second->id())!=ParticleID::Hplus&&
// inter.second->id()<0)) {
// ++idiag;
// continue;
// }
// chi W and charged slepton
// if(!((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())==ParticleID::Wplus) &&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// inter.second->id()>0) ) {
// ++idiag;
// continue;
// }
// chi W and sneutrino
// if(!((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())==ParticleID::Wplus) &&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// inter.second->id()<0) ) {
// ++idiag;
// continue;
// }
// top/sbottom interference
// if(!(abs(inter.first ->id())==ParticleID::t&&
// abs(inter.second->id())==ParticleID::Wplus)&&
// !((abs(inter.first ->id())==ParticleID::SUSY_b_1||
// abs(inter.first ->id())==ParticleID::SUSY_b_2)&&
// abs(inter.second->id())==ParticleID::Wplus)) {
// ++idiag;
// continue;
// }
// top chiW interference
// if(!(abs(inter.first ->id())==ParticleID::t&&
// abs(inter.second->id())==ParticleID::Wplus)&&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())==ParticleID::Wplus)) {
// ++idiag;
// continue;
// }
// bottom chiW interference
// if(!((abs(inter.first ->id())==ParticleID::SUSY_b_1||
// abs(inter.first ->id())==ParticleID::SUSY_b_2)&&
// abs(inter.second->id())==ParticleID::Wplus)&&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())==ParticleID::Wplus)) {
// ++idiag;
// continue;
// }
// top charged slepton
// if(!(abs(inter.first ->id())==ParticleID::t&&
// abs(inter.second->id())==ParticleID::Wplus) &&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// inter.second->id()>0)) {
// ++idiag;
// continue;
// }
// top sneutrino
// if(!(abs(inter.first ->id())==ParticleID::t&&
// abs(inter.second->id())==ParticleID::Wplus) &&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// inter.second->id()<0)) {
// ++idiag;
// continue;
// }
// ~b sneutrino
// if(!((abs(inter.first ->id())==ParticleID::SUSY_b_1||
// abs(inter.first ->id())==ParticleID::SUSY_b_2)&&
// abs(inter.second->id())==ParticleID::Wplus)&&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// inter.second->id()<0)) {
// ++idiag;
// continue;
// }
// ~b slepton
// if(!((abs(inter.first ->id())==ParticleID::SUSY_b_1||
// abs(inter.first ->id())==ParticleID::SUSY_b_2)&&
// abs(inter.second->id())==ParticleID::Wplus)&&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())!=ParticleID::Wplus&&
// inter.second->id()>0)) {
// ++idiag;
// continue;
// }
// top H
// if(!(abs(inter.first ->id())==ParticleID::t&&
// abs(inter.second->id())==ParticleID::Hplus)) {
// ++idiag;
// continue;
// }
// sbottom H
// if(!((abs(inter.first ->id())==ParticleID::SUSY_b_1||
// abs(inter.first ->id())==ParticleID::SUSY_b_2)&&
// abs(inter.second->id())==ParticleID::Hplus)) {
// ++idiag;
// continue;
// }
// chargino H
// if(!((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())==ParticleID::Hplus)) {
// ++idiag;
// continue;
// }
// top/sbottom interference
// if(!(abs(inter.first ->id())==ParticleID::t&&
// abs(inter.second->id())==ParticleID::Hplus)&&
// !((abs(inter.first ->id())==ParticleID::SUSY_b_1||
// abs(inter.first ->id())==ParticleID::SUSY_b_2)&&
// abs(inter.second->id())==ParticleID::Hplus)) {
// ++idiag;
// continue;
// }
// top chiH interference
// if(!(abs(inter.first ->id())==ParticleID::t&&
// abs(inter.second->id())==ParticleID::Hplus)&&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())==ParticleID::Hplus)) {
// ++idiag;
// continue;
// }
// bottom chiH interference
// if(!((abs(inter.first ->id())==ParticleID::SUSY_b_1||
// abs(inter.first ->id())==ParticleID::SUSY_b_2)&&
// abs(inter.second->id())==ParticleID::Hplus)&&
// !((abs(inter.first ->id())==ParticleID::SUSY_chi_1plus||
// abs(inter.first ->id())==ParticleID::SUSY_chi_2plus)&&
// abs(inter.second->id())==ParticleID::Hplus)) {
// ++idiag;
// continue;
// }
// all Higgs
// if(abs(inter.second->id())==ParticleID::Hplus) {
// ++idiag;
// continue;
// }
//reomved from end of me2()
//InvEnergy2 output = stopMatrixElement(inpart,decay,me2*UnitRemoval::InvE2);
// return output*scale;
diff --git a/Decay/General/StoFFVDecayer.cc b/Decay/General/StoFFVDecayer.cc
--- a/Decay/General/StoFFVDecayer.cc
+++ b/Decay/General/StoFFVDecayer.cc
@@ -1,387 +1,389 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the StoFFVDecayer class.
//
#include "StoFFVDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/PDT/ThreeBodyAllOnCalculator.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include <numeric>
using namespace Herwig;
using namespace ThePEG;
using namespace ThePEG::Helicity;
IBPtr StoFFVDecayer::clone() const {
return new_ptr(*this);
}
IBPtr StoFFVDecayer::fullclone() const {
return new_ptr(*this);
}
void StoFFVDecayer::persistentOutput(PersistentOStream & os) const {
os << sca_ << fer_ << vec_ << RSfer_ << four_;
}
void StoFFVDecayer::persistentInput(PersistentIStream & is, int) {
is >> sca_ >> fer_ >> vec_ >> RSfer_ >> four_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<StoFFVDecayer,GeneralThreeBodyDecayer>
describeHerwigStoFFVDecayer("Herwig::StoFFVDecayer", "Herwig.so");
void StoFFVDecayer::Init() {
static ClassDocumentation<StoFFVDecayer> documentation
("The StoFFVDecayer class implements the general decay of a scalar to "
"a two fermions and a vector.");
}
WidthCalculatorBasePtr StoFFVDecayer::
threeBodyMEIntegrator(const DecayMode & ) const {
vector<int> intype;
vector<Energy> inmass,inwidth;
vector<double> inpow,inweights;
constructIntegratorChannels(intype,inmass,inwidth,inpow,inweights);
return new_ptr(ThreeBodyAllOnCalculator<StoFFVDecayer>
(inweights,intype,inmass,inwidth,inpow,*this,0,
outgoing()[0]->mass(),outgoing()[1]->mass(),
outgoing()[2]->mass(),relativeError()));
}
void StoFFVDecayer::doinit() {
GeneralThreeBodyDecayer::doinit();
if(outgoing().empty()) return;
unsigned int ndiags = getProcessInfo().size();
sca_.resize(ndiags);
fer_.resize(ndiags);
RSfer_.resize(ndiags);
vec_.resize(ndiags);
four_.resize(ndiags);
for(unsigned int ix = 0;ix < ndiags; ++ix) {
TBDiagram current = getProcessInfo()[ix];
tcPDPtr offshell = current.intermediate;
// four point vertex
if(!offshell) {
four_[ix] = dynamic_ptr_cast<AbstractFFVSVertexPtr>(current.vertices.first);
continue;
}
if( offshell->CC() ) offshell = offshell->CC();
if(offshell->iSpin() == PDT::Spin0) {
AbstractVSSVertexPtr vert1 = dynamic_ptr_cast<AbstractVSSVertexPtr>
(current.vertices.first);
AbstractFFSVertexPtr vert2 = dynamic_ptr_cast<AbstractFFSVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a scalar diagram in StoFFVDecayer::doinit()"
<< Exception::runerror;
sca_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin1Half) {
AbstractFFSVertexPtr vert1 = dynamic_ptr_cast<AbstractFFSVertexPtr>
(current.vertices.first);
AbstractFFVVertexPtr vert2 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a fermion diagram in StoFFVDecayer::doinit()"
<< Exception::runerror;
fer_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin1) {
AbstractVVSVertexPtr vert1 = dynamic_ptr_cast<AbstractVVSVertexPtr>
(current.vertices.first);
AbstractFFVVertexPtr vert2 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a vector diagram in StoFFVDecayer::doinit()"
<< Exception::runerror;
vec_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin3Half) {
AbstractRFSVertexPtr vert1 = dynamic_ptr_cast<AbstractRFSVertexPtr>
(current.vertices.first);
AbstractRFVVertexPtr vert2 = dynamic_ptr_cast<AbstractRFVVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a RS fermion diagram in StoFFVDecayer::doinit()"
<< Exception::runerror;
RSfer_[ix] = make_pair(vert1, vert2);
}
}
}
double StoFFVDecayer::me2(const int ichan, const Particle & inpart,
const ParticleVector & decay, MEOption meopt) const {
// particle or CC of particle
bool cc = (*getProcessInfo().begin()).incoming != inpart.id();
// special handling or first/last call
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(rho_,const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming);
swave_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),
Helicity::incoming);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming,true);
for(unsigned int ix=0;ix<decay.size();++ix) {
if(decay[ix]->dataPtr()->iSpin()==PDT::Spin1) {
VectorWaveFunction::constructSpinInfo(outVector_,decay[ix],
Helicity::outgoing,true,false);
}
else {
SpinorWaveFunction::
constructSpinInfo(outspin_[ix].first,decay[ix],Helicity::outgoing,true);
}
}
}
unsigned int ivec(0);
bool massless(false);
for(unsigned int ix = 0; ix < decay.size();++ix) {
if(decay[ix]->dataPtr()->iSpin() == PDT::Spin1) {
ivec = ix;
massless = decay[ivec]->mass()==ZERO;
VectorWaveFunction::
calculateWaveFunctions(outVector_, decay[ix], Helicity::outgoing,massless);
}
else {
SpinorWaveFunction::
calculateWaveFunctions(outspin_[ix].first,decay[ix],Helicity::outgoing);
outspin_[ix].second.resize(2);
// Need a ubar and a v spinor
if(outspin_[ix].first[0].wave().Type() == SpinorType::u) {
for(unsigned int iy = 0; iy < 2; ++iy) {
outspin_[ix].second[iy] = outspin_[ix].first[iy].bar();
outspin_[ix].first[iy].conjugate();
}
}
else {
for(unsigned int iy = 0; iy < 2; ++iy) {
outspin_[ix].second[iy] = outspin_[ix].first[iy].bar();
outspin_[ix].second[iy].conjugate();
}
}
}
}
const vector<vector<double> > cfactors(getColourFactors());
const vector<vector<double> > nfactors(getLargeNcColourFactors());
Energy2 scale(sqr(inpart.mass()));
const size_t ncf(numberOfFlows());
vector<Complex> flows(ncf, Complex(0.)), largeflows(ncf, Complex(0.));
// setup the DecayMatrixElement
vector<GeneralDecayMEPtr>
mes(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin0,
ivec == 0 ? PDT::Spin1 : PDT::Spin1Half,
ivec == 1 ? PDT::Spin1 : PDT::Spin1Half,
ivec == 2 ? PDT::Spin1 : PDT::Spin1Half)));
vector<GeneralDecayMEPtr>
mel(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin0,
ivec == 0 ? PDT::Spin1 : PDT::Spin1Half,
ivec == 1 ? PDT::Spin1 : PDT::Spin1Half,
ivec == 2 ? PDT::Spin1 : PDT::Spin1Half)));
//the channel possiblities
static const unsigned int out2[3] = {1,0,0}, out3[3] = {2,2,1};
for(unsigned int s1 = 0; s1 < 2; ++s1) {
for(unsigned int s2 = 0; s2 < 2; ++s2) {
for(unsigned int v1 = 0; v1 < 3; ++v1) {
if(massless&&v1==1) ++v1;
flows = vector<Complex>(ncf, Complex(0.));
largeflows = vector<Complex>(ncf, Complex(0.));
unsigned int idiag(0);
Complex diag;
for(vector<TBDiagram>::const_iterator dit=getProcessInfo().begin();
dit!=getProcessInfo().end();++dit) {
// channels if selecting
if( ichan >= 0 && diagramMap()[ichan] != idiag ) {
++idiag;
continue;
}
tcPDPtr offshell = dit->intermediate;
if(offshell) {
if(cc&&offshell->CC()) offshell=offshell->CC();
unsigned int o2(out2[dit->channelType]), o3(out3[dit->channelType]);
double sign = (o3 < o2) ? 1. : -1.;
// intermediate scalar
if(offshell->iSpin() == PDT::Spin0) {
ScalarWaveFunction inters = sca_[idiag].first->
evaluate(scale, widthOption(), offshell, outVector_[v1], swave_);
unsigned int h1(s1),h2(s2);
if(o2 > o3) swap(h1, h2);
if(decay[o2]->id() < 0 && decay[o3]->id() > 0) {
diag = -sign*sca_[idiag].second->
evaluate(scale,outspin_[o2].first[h1],
outspin_[o3].second[h2],inters);
}
else {
diag = sign*sca_[idiag].second->
evaluate(scale, outspin_[o3].first [h2],
outspin_[o2].second[h1],inters);
}
}
// intermediate fermion
else if(offshell->iSpin() == PDT::Spin1Half) {
int iferm = (decay[o2]->dataPtr()->iSpin() == PDT::Spin1Half)
? o2 : o3;
unsigned int h1(s1),h2(s2);
if(dit->channelType > iferm) swap(h1, h2);
sign = iferm < dit->channelType ? 1. : -1.;
if((decay[dit->channelType]->id() < 0 && decay[iferm]->id() > 0 ) ||
(decay[dit->channelType]->id()*offshell->id()>0)) {
SpinorWaveFunction inters = fer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].first[h1], swave_);
diag = -sign*fer_[idiag].second->
evaluate(scale,inters,outspin_[iferm].second[h2], outVector_[v1]);
}
else {
SpinorBarWaveFunction inters = fer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].second[h1],swave_);
diag = sign*fer_[idiag].second->
evaluate(scale,outspin_[iferm].first [h2],inters, outVector_[v1]);
}
}
// intermediate vector
else if(offshell->iSpin() == PDT::Spin1) {
VectorWaveFunction interv = vec_[idiag].first->
evaluate(scale, widthOption(), offshell, outVector_[v1], swave_);
unsigned int h1(s1),h2(s2);
if(o2 > o3) swap(h1,h2);
if(decay[o2]->id() < 0 && decay[o3]->id() > 0) {
diag =-sign*vec_[idiag].second->
evaluate(scale, outspin_[o2].first[h1],
outspin_[o3].second[h2], interv);
}
else {
diag = sign*vec_[idiag].second->
evaluate(scale, outspin_[o3].first[h2],
outspin_[o2].second[h1], interv);
}
}
// intermediate RS fermion
else if(offshell->iSpin() == PDT::Spin3Half) {
int iferm = (decay[o2]->dataPtr()->iSpin() == PDT::Spin1Half)
? o2 : o3;
unsigned int h1(s1),h2(s2);
if(dit->channelType > iferm) swap(h1, h2);
sign = iferm < dit->channelType ? 1. : -1.;
if((decay[dit->channelType]->id() < 0 && decay[iferm]->id() > 0 ) ||
(decay[dit->channelType]->id()*offshell->id()>0)) {
RSSpinorWaveFunction inters = RSfer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].first[h1], swave_);
diag = -sign*RSfer_[idiag].second->
evaluate(scale,inters,outspin_[iferm].second[h2], outVector_[v1]);
}
else {
RSSpinorBarWaveFunction inters = RSfer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].second[h1],swave_);
diag = sign*RSfer_[idiag].second->
evaluate(scale,outspin_[iferm].first [h2],inters, outVector_[v1]);
}
}
// unknown
else throw Exception()
<< "Unknown intermediate in StoFFVDecayer::me2()"
<< Exception::runerror;
}
else {
unsigned int o2 = ivec > 0 ? 0 : 1;
unsigned int o3 = ivec < 2 ? 2 : 1;
if(decay[o2]->id() < 0 && decay[o3]->id() > 0) {
diag =-four_[idiag]->
evaluate(scale, outspin_[o2].first[s1],
outspin_[o3].second[s2], outVector_[v1], swave_);
}
else {
diag = four_[idiag]->
evaluate(scale, outspin_[o3].first[s2],
outspin_[o2].second[s1], outVector_[v1], swave_);
}
}
// matrix element for the different colour flows
if(ichan < 0) {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
else {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1 != colourFlow()) continue;
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1!=colourFlow()) continue;
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
++idiag;
} //end of diagrams
// now add the flows to the me2 with appropriate colour factors
for(unsigned int ix = 0; ix < ncf; ++ix) {
if ( ivec == 0 ) {
(*mes[ix])(0, v1, s1, s2) = flows[ix];
(*mel[ix])(0, v1, s1, s2) = largeflows[ix];
}
else if( ivec == 1 ) {
(*mes[ix])(0, s1, v1, s2) = flows[ix];
(*mel[ix])(0, s1, v1, s2) = largeflows[ix];
}
else if( ivec == 2 ) {
(*mes[ix])(0, s1, s2, v1) = flows[ix];
(*mel[ix])(0, s1, s2, v1) = largeflows[ix];
}
}
}
}
}
double me2(0.);
if(ichan < 0) {
vector<double> pflows(ncf,0.);
for(unsigned int ix = 0; ix < ncf; ++ix) {
for(unsigned int iy = 0; iy < ncf; ++ iy) {
double con = cfactors[ix][iy]*(mes[ix]->contract(*mes[iy],rho_)).real();
me2 += con;
if(ix == iy) {
con = nfactors[ix][iy]*(mel[ix]->contract(*mel[iy],rho_)).real();
pflows[ix] += con;
}
}
}
double ptotal(std::accumulate(pflows.begin(),pflows.end(),0.));
ptotal *= UseRandom::rnd();
for(unsigned int ix = 0;ix < pflows.size(); ++ix) {
if(ptotal <= pflows[ix]) {
colourFlow(ix);
ME(mes[ix]);
break;
}
ptotal -= pflows[ix];
}
}
else {
unsigned int iflow = colourFlow();
me2 = nfactors[iflow][iflow]*(mel[iflow]->contract(*mel[iflow],rho_)).real();
}
// return the matrix element squared
return me2;
}
diff --git a/Decay/General/StoSFFDecayer.cc b/Decay/General/StoSFFDecayer.cc
--- a/Decay/General/StoSFFDecayer.cc
+++ b/Decay/General/StoSFFDecayer.cc
@@ -1,422 +1,424 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the StoSFFDecayer class.
//
#include "StoSFFDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/PDT/ThreeBodyAllOnCalculator.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include <numeric>
using namespace Herwig;
using namespace ThePEG;
using namespace ThePEG::Helicity;
IBPtr StoSFFDecayer::clone() const {
return new_ptr(*this);
}
IBPtr StoSFFDecayer::fullclone() const {
return new_ptr(*this);
}
void StoSFFDecayer::persistentOutput(PersistentOStream & os) const {
os << sca_ << fer_ << vec_ << ten_ << RSfer_ << four_;
}
void StoSFFDecayer::persistentInput(PersistentIStream & is, int) {
is >> sca_ >> fer_ >> vec_ >> ten_ >> RSfer_ >> four_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<StoSFFDecayer,GeneralThreeBodyDecayer>
describeHerwigStoSFFDecayer("Herwig::StoSFFDecayer", "Herwig.so");
void StoSFFDecayer::Init() {
static ClassDocumentation<StoSFFDecayer> documentation
("The StoSFFDecayer class implements the general decay of a scalar to "
"a scalar and two fermions.");
}
WidthCalculatorBasePtr StoSFFDecayer::
threeBodyMEIntegrator(const DecayMode & ) const {
vector<int> intype;
vector<Energy> inmass,inwidth;
vector<double> inpow,inweights;
constructIntegratorChannels(intype,inmass,inwidth,inpow,inweights);
return new_ptr(ThreeBodyAllOnCalculator<StoSFFDecayer>
(inweights,intype,inmass,inwidth,inpow,*this,0,
outgoing()[0]->mass(),outgoing()[1]->mass(),outgoing()[2]->mass(),
relativeError()));
}
void StoSFFDecayer::doinit() {
GeneralThreeBodyDecayer::doinit();
if(outgoing().empty()) return;
unsigned int ndiags = getProcessInfo().size();
sca_.resize(ndiags);
fer_.resize(ndiags);
RSfer_.resize(ndiags);
vec_.resize(ndiags);
ten_.resize(ndiags);
four_.resize(ndiags);
for(unsigned int ix = 0;ix < ndiags; ++ix) {
TBDiagram current = getProcessInfo()[ix];
tcPDPtr offshell = current.intermediate;
// four point vertex
if(!offshell) {
four_[ix] = dynamic_ptr_cast<AbstractFFSSVertexPtr>(current.vertices.first);
continue;
}
if( offshell->CC() ) offshell = offshell->CC();
if(offshell->iSpin() == PDT::Spin0) {
AbstractSSSVertexPtr vert1 = dynamic_ptr_cast<AbstractSSSVertexPtr>
(current.vertices.first);
AbstractFFSVertexPtr vert2 = dynamic_ptr_cast<AbstractFFSVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a scalar diagram in StoSFFDecayer::doinit()"
<< Exception::runerror;
sca_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin1Half) {
AbstractFFSVertexPtr vert1 = dynamic_ptr_cast<AbstractFFSVertexPtr>
(current.vertices.first);
AbstractFFSVertexPtr vert2 = dynamic_ptr_cast<AbstractFFSVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a fermion diagram in StoSFFDecayer::doinit()"
<< Exception::runerror;
fer_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin1) {
AbstractVSSVertexPtr vert1 = dynamic_ptr_cast<AbstractVSSVertexPtr>
(current.vertices.first);
AbstractFFVVertexPtr vert2 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a vector diagram in StoSFFDecayer::doinit()"
<< Exception::runerror;
vec_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin2) {
AbstractSSTVertexPtr vert1 = dynamic_ptr_cast<AbstractSSTVertexPtr>
(current.vertices.first);
AbstractFFTVertexPtr vert2 = dynamic_ptr_cast<AbstractFFTVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a tensor diagram in StoSFFDecayer::doinit()"
<< Exception::runerror;
ten_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin3Half) {
AbstractRFSVertexPtr vert1 = dynamic_ptr_cast<AbstractRFSVertexPtr>
(current.vertices.first);
AbstractRFSVertexPtr vert2 = dynamic_ptr_cast<AbstractRFSVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a RS fermion diagram in StoSFFDecayer::doinit()"
<< Exception::runerror;
RSfer_[ix] = make_pair(vert1, vert2);
}
}
}
double StoSFFDecayer::me2(const int ichan, const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
// particle or CC of particle
bool cc = (*getProcessInfo().begin()).incoming != inpart.id();
// special handling or first/last call
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(rho_,const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming);
swave_ = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),
Helicity::incoming);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
ScalarWaveFunction::
constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming,true);
for(unsigned int ix=0;ix<decay.size();++ix) {
if(decay[ix]->dataPtr()->iSpin()==PDT::Spin0) {
ScalarWaveFunction::constructSpinInfo(decay[ix],Helicity::outgoing,true);
}
else {
SpinorWaveFunction::
constructSpinInfo(outspin_[ix].first,decay[ix],Helicity::outgoing,true);
}
}
return 0.;
}
// get the wavefunctions for all the particles
ScalarWaveFunction outScalar;
unsigned int isca(0);
for(unsigned int ix=0;ix<decay.size();++ix) {
if(decay[ix]->dataPtr()->iSpin()==PDT::Spin0) {
isca = ix;
outScalar = ScalarWaveFunction(decay[ix]->momentum(),
decay[ix]->dataPtr(),Helicity::outgoing);
}
else {
SpinorWaveFunction::
calculateWaveFunctions(outspin_[ix].first,decay[ix],Helicity::outgoing);
outspin_[ix].second.resize(2);
if(outspin_[ix].first[0].wave().Type() == SpinorType::u) {
for(unsigned int iy = 0; iy < 2; ++iy) {
outspin_[ix].second[iy] = outspin_[ix].first[iy].bar();
outspin_[ix].first[iy].conjugate();
}
}
else {
for(unsigned int iy = 0; iy < 2; ++iy) {
outspin_[ix].second[iy] = outspin_[ix].first[iy].bar();
outspin_[ix].second[iy].conjugate();
}
}
}
}
const vector<vector<double> > cfactors(getColourFactors());
const vector<vector<double> > nfactors(getLargeNcColourFactors());
Energy2 scale(sqr(inpart.mass()));
const size_t ncf(numberOfFlows());
vector<Complex> flows(ncf, Complex(0.)), largeflows(ncf, Complex(0.));
vector<GeneralDecayMEPtr>
mes(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin0,
isca==0 ? PDT::Spin0 : PDT::Spin1Half,
isca==1 ? PDT::Spin0 : PDT::Spin1Half,
isca==2 ? PDT::Spin0 : PDT::Spin1Half)));
vector<GeneralDecayMEPtr>
mel(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin0,
isca == 0 ? PDT::Spin0 : PDT::Spin1Half,
isca == 1 ? PDT::Spin0 : PDT::Spin1Half,
isca == 2 ? PDT::Spin0 : PDT::Spin1Half)));
static const unsigned int out2[3]={1,0,0},out3[3]={2,2,1};
for(unsigned int s1 = 0;s1 < 2; ++s1) {
for(unsigned int s2 = 0;s2 < 2; ++s2) {
flows = vector<Complex>(ncf, Complex(0.));
largeflows = vector<Complex>(ncf, Complex(0.));
unsigned int idiag(0);
for(vector<TBDiagram>::const_iterator dit = getProcessInfo().begin();
dit != getProcessInfo().end(); ++dit) {
// channels if selecting
if( ichan >= 0 && diagramMap()[ichan] != idiag ) {
++idiag;
continue;
}
tcPDPtr offshell = dit->intermediate;
Complex diag;
if(offshell) {
if(cc&&offshell->CC()) offshell=offshell->CC();
double sign = out3[dit->channelType] < out2[dit->channelType] ? 1. : -1.;
// intermediate scalar
if (offshell->iSpin() == PDT::Spin0) {
ScalarWaveFunction inters = sca_[idiag].first->
evaluate(scale, widthOption(), offshell, swave_, outScalar);
unsigned int h1(s1),h2(s2);
if(out2[dit->channelType]>out3[dit->channelType]) swap(h1,h2);
if(decay[out2[dit->channelType]]->id()<0&&
decay[out3[dit->channelType]]->id()>0) {
diag =-sign*sca_[idiag].second->
evaluate(scale,
outspin_[out2[dit->channelType]].first [h1],
outspin_[out3[dit->channelType]].second[h2],inters);
}
else {
diag = sign*sca_[idiag].second->
evaluate(scale,
outspin_[out3[dit->channelType]].first [h2],
outspin_[out2[dit->channelType]].second[h1],inters);
}
}
// intermediate fermion
else if(offshell->iSpin() == PDT::Spin1Half) {
int iferm =
decay[out2[dit->channelType]]->dataPtr()->iSpin()==PDT::Spin1Half
? out2[dit->channelType] : out3[dit->channelType];
unsigned int h1(s1),h2(s2);
if(dit->channelType>iferm) swap(h1,h2);
sign = iferm<dit->channelType ? 1. : -1.;
if((decay[dit->channelType]->id() < 0 &&decay[iferm]->id() > 0 ) ||
(decay[dit->channelType]->id()*offshell->id()>0)) {
SpinorWaveFunction inters = fer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].first [h1],swave_);
diag = -sign*fer_[idiag].second->
evaluate(scale,inters,outspin_[iferm].second[h2],outScalar);
}
else {
SpinorBarWaveFunction inters = fer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].second[h1],swave_);
diag = sign*fer_[idiag].second->
evaluate(scale,outspin_[iferm].first [h2],inters,outScalar);
}
}
// intermediate vector
else if(offshell->iSpin() == PDT::Spin1) {
VectorWaveFunction interv = vec_[idiag].first->
evaluate(scale, widthOption(), offshell, swave_, outScalar);
unsigned int h1(s1),h2(s2);
if(out2[dit->channelType]>out3[dit->channelType]) swap(h1,h2);
if(decay[out2[dit->channelType]]->id()<0&&
decay[out3[dit->channelType]]->id()>0) {
diag =-sign*vec_[idiag].second->
evaluate(scale,
outspin_[out2[dit->channelType]].first [h1],
outspin_[out3[dit->channelType]].second[h2],interv);
}
else {
diag = sign*vec_[idiag].second->
evaluate(scale,
outspin_[out3[dit->channelType]].first [h2],
outspin_[out2[dit->channelType]].second[h1],interv);
}
}
// intermediate tensor
else if(offshell->iSpin() == PDT::Spin2) {
TensorWaveFunction intert = ten_[idiag].first->
evaluate(scale, widthOption(), offshell, swave_, outScalar);
unsigned int h1(s1),h2(s2);
if(out2[dit->channelType]>out3[dit->channelType]) swap(h1,h2);
if(decay[out2[dit->channelType]]->id()<0&&
decay[out3[dit->channelType]]->id()>0) {
diag =-sign*ten_[idiag].second->
evaluate(scale,
outspin_[out2[dit->channelType]].first [h1],
outspin_[out3[dit->channelType]].second[h2],intert);
}
else {
diag = sign*ten_[idiag].second->
evaluate(scale,
outspin_[out3[dit->channelType]].first [h2],
outspin_[out2[dit->channelType]].second[h1],intert);
}
}
// intermediate RS fermion
else if(offshell->iSpin() == PDT::Spin3Half) {
int iferm =
decay[out2[dit->channelType]]->dataPtr()->iSpin()==PDT::Spin1Half
? out2[dit->channelType] : out3[dit->channelType];
unsigned int h1(s1),h2(s2);
if(dit->channelType>iferm) swap(h1,h2);
sign = iferm<dit->channelType ? 1. : -1.;
if((decay[dit->channelType]->id() < 0 &&decay[iferm]->id() > 0 ) ||
(decay[dit->channelType]->id()*offshell->id()>0)) {
RSSpinorWaveFunction inters = RSfer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].first [h1],swave_);
diag = -sign*RSfer_[idiag].second->
evaluate(scale,inters,outspin_[iferm].second[h2],outScalar);
}
else {
RSSpinorBarWaveFunction inters = RSfer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].second[h1],swave_);
diag = sign*RSfer_[idiag].second->
evaluate(scale,outspin_[iferm].first [h2],inters,outScalar);
}
}
// unknown
else throw Exception()
<< "Unknown intermediate in StoSFFDecayer::me2()"
<< Exception::runerror;
}
// four point diagram
else {
unsigned int o2 = isca > 0 ? 0 : 1;
unsigned int o3 = isca < 2 ? 2 : 1;
if(decay[o2]->id() < 0 && decay[o3]->id() > 0) {
diag =-four_[idiag]->
evaluate(scale, outspin_[o2].first[s1],
outspin_[o3].second[s2], outScalar, swave_);
}
else {
diag = four_[idiag]->
evaluate(scale, outspin_[o3].first[s2],
outspin_[o2].second[s1], outScalar, swave_);
}
}
// matrix element for the different colour flows
if(ichan < 0) {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
else {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1 != colourFlow()) continue;
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1!=colourFlow()) continue;
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
++idiag;
}
for(unsigned int ix = 0; ix < ncf; ++ix) {
if(isca == 0) {
(*mes[ix])(0, 0, s1, s2) = flows[ix];
(*mel[ix])(0, 0, s1, s2) = largeflows[ix];
}
else if(isca == 1 ) {
(*mes[ix])(0, s1, 0, s2) = flows[ix];
(*mel[ix])(0, s1, 0, s2) = largeflows[ix];
}
else if(isca == 2) {
(*mes[ix])(0, s1,s2, 0) = flows[ix];
(*mel[ix])(0, s1,s2, 0) = largeflows[ix] ;
}
}
}
}
double me2(0.);
if(ichan < 0) {
vector<double> pflows(ncf,0.);
for(unsigned int ix = 0; ix < ncf; ++ix) {
for(unsigned int iy = 0; iy < ncf; ++ iy) {
double con = cfactors[ix][iy]*(mes[ix]->contract(*mes[iy],rho_)).real();
me2 += con;
if(ix == iy) {
con = nfactors[ix][iy]*(mel[ix]->contract(*mel[iy],rho_)).real();
pflows[ix] += con;
}
}
}
double ptotal(std::accumulate(pflows.begin(),pflows.end(),0.));
ptotal *= UseRandom::rnd();
for(unsigned int ix = 0;ix < pflows.size(); ++ix) {
if(ptotal <= pflows[ix]) {
colourFlow(ix);
ME(mes[ix]);
break;
}
ptotal -= pflows[ix];
}
}
else {
unsigned int iflow = colourFlow();
me2 = nfactors[iflow][iflow]*(mel[iflow]->contract(*mel[iflow],rho_)).real();
}
// return the matrix element squared
return me2;
}
diff --git a/Decay/General/TFFDecayer.cc b/Decay/General/TFFDecayer.cc
--- a/Decay/General/TFFDecayer.cc
+++ b/Decay/General/TFFDecayer.cc
@@ -1,351 +1,353 @@
// -*- C++ -*-
//
// TFFDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 TFFDecayer class.
//
#include "TFFDecayer.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/Helicity/WaveFunction/TensorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr TFFDecayer::clone() const {
return new_ptr(*this);
}
IBPtr TFFDecayer::fullclone() const {
return new_ptr(*this);
}
void TFFDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> &,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> fourV) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractFFTVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<FFTVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
fourPointVertex_[inter] = dynamic_ptr_cast<AbstractFFVTVertexPtr>(fourV.at(inter));
outgoingVertex1_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr> (outV[0].at(inter));
outgoingVertex2_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr> (outV[1].at(inter));
}
}
void TFFDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< outgoingVertex1_ << outgoingVertex2_
<< fourPointVertex_;
}
void TFFDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> outgoingVertex1_ >> outgoingVertex2_
>> fourPointVertex_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<TFFDecayer,GeneralTwoBodyDecayer>
describeHerwigTFFDecayer("Herwig::TFFDecayer", "Herwig.so");
void TFFDecayer::Init() {
static ClassDocumentation<TFFDecayer> documentation
("The TFFDecayer class implements the decay of a tensor particle "
"to 2 fermions ");
}
double TFFDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
unsigned int iferm(0),ianti(1);
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin2,PDT::Spin1Half,PDT::Spin1Half)));
if(decay[0]->id()>=0) swap(iferm,ianti);
if(meopt==Initialize) {
TensorWaveFunction::
calculateWaveFunctions(tensors_,rho_,const_ptr_cast<tPPtr>(&inpart),
incoming,false);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
TensorWaveFunction::
constructSpinInfo(tensors_,const_ptr_cast<tPPtr>(&inpart),
incoming,true,false);
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,decay[iferm],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(wave_ ,decay[ianti],outgoing,true);
return 0.;
}
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar_,decay[iferm],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(wave_ ,decay[ianti],outgoing);
Energy2 scale(sqr(inpart.mass()));
unsigned int thel,fhel,ahel;
for(thel=0;thel<5;++thel) {
for(fhel=0;fhel<2;++fhel) {
for(ahel=0;ahel<2;++ahel) {
if(iferm > ianti) {
(*ME())(thel,fhel,ahel) = 0.;
for(auto vert : vertex_)
(*ME())(thel,fhel,ahel) +=
vert->evaluate(scale,wave_[ahel],
wavebar_[fhel],tensors_[thel]);
}
else {
(*ME())(thel,ahel,fhel) = 0.;
for(auto vert : vertex_)
(*ME())(thel,ahel,fhel) +=
vert->evaluate(scale,wave_[ahel],
wavebar_[fhel],tensors_[thel]);
}
}
}
}
double output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy TFFDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
Energy2 scale = sqr(inpart.second);
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(scale, in, outa.first, outb.first);
double musq = sqr(outa.second/inpart.second);
double b = sqrt(1- 4.*musq);
double me2 = b*b*(5-2*b*b)*scale/120.*UnitRemoval::InvE2;
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second,outa.second,
outb.second);
Energy output = norm(perturbativeVertex_[0]->norm())*me2*pcm/(8.*Constants::pi);
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double TFFDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
// work out which is the fermion and antifermion
int ianti(0), iferm(1), iglu(2);
int itype[2];
for(unsigned int ix=0;ix<2;++ix) {
if(decay[ix]->dataPtr()->CC()) itype[ix] = decay[ix]->id()>0 ? 0:1;
else itype[ix] = 2;
}
if(itype[0]==0 && itype[1]!=0) swap(iferm, ianti);
if(itype[0]==2 && itype[1]==1) swap(iferm, ianti);
if(itype[0]==0 && itype[1]==0 && decay[0]->dataPtr()->id()<decay[1]->dataPtr()->id())
swap(iferm, ianti);
if(itype[0]==1 && itype[1]==1 && decay[0]->dataPtr()->id()<decay[1]->dataPtr()->id())
swap(iferm, ianti);
if(meopt==Initialize) {
// create tensor wavefunction for decaying particle
TensorWaveFunction::
calculateWaveFunctions(tensors3_, rho3_, const_ptr_cast<tPPtr>(&inpart), incoming, false);
}
// setup spin information when needed
if(meopt==Terminate) {
TensorWaveFunction::
constructSpinInfo(tensors3_, const_ptr_cast<tPPtr>(&inpart),incoming,true, false);
SpinorBarWaveFunction::
constructSpinInfo(wavebar3_ ,decay[iferm],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(wave3_ ,decay[ianti],outgoing,true);
VectorWaveFunction::
constructSpinInfo(gluon_ ,decay[iglu ],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin2, PDT::Spin1Half,
PDT::Spin1Half, PDT::Spin1)));
// create wavefunctions
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar3_, decay[iferm],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(wave3_ , decay[ianti],outgoing);
VectorWaveFunction::
calculateWaveFunctions(gluon_ , decay[iglu ],outgoing,true);
// gauge invariance test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[iglu ]->momentum(),
decay[iglu ]->dataPtr(),10,
outgoing));
}
}
#endif
if (! (outgoingVertex1_[inter] && outgoingVertex2_[inter]))
throw Exception()
<< "Invalid vertices for QCD radiation in TFF decay in TFFDecayer::threeBodyME"
<< Exception::runerror;
// identify fermion and/or anti-fermion vertex
AbstractFFVVertexPtr outgoingVertexF = outgoingVertex1_[inter];
AbstractFFVVertexPtr outgoingVertexA = outgoingVertex2_[inter];
if(outgoingVertex1_[inter]!=outgoingVertex2_[inter] &&
outgoingVertex1_[inter]->isIncoming(getParticleData(decay[ianti]->id())))
swap (outgoingVertexF, outgoingVertexA);
if(! (inpart.dataPtr()->iColour()==PDT::Colour0)){
throw Exception()
<< "Invalid vertices for QCD radiation in TFF decay in TFFDecayer::threeBodyME"
<< Exception::runerror;
}
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int it = 0; it < 5; ++it) {
for(unsigned int ifm = 0; ifm < 2; ++ifm) {
for(unsigned int ia = 0; ia < 2; ++ia) {
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from outgoing fermion
if((decay[iferm]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[iferm]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexF);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[iferm]->dataPtr();
if(off->CC()) off = off->CC();
SpinorBarWaveFunction interS =
outgoingVertexF->evaluate(scale,3,off,wavebar3_[ifm],
gluon_[2*ig],decay[iferm]->mass());
assert(wavebar3_[ifm].particle()->id()==interS.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,wave3_[ia], interS,tensors3_[it]);
if(!couplingSet) {
gs = abs(outgoingVertexF->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[1].size();++ix) {
(*ME[colourFlow[1][ix].first])(it, ifm, ia, ig) +=
colourFlow[1][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing antifermion
if((decay[ianti]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[ianti]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexA);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[ianti]->dataPtr();
if(off->CC()) off = off->CC();
SpinorWaveFunction interS =
outgoingVertexA->evaluate(scale,3,off,wave3_[ia],
gluon_[2*ig],decay[ianti]->mass());
assert(wave3_[ia].particle()->id()==interS.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,interS,wavebar3_[ifm],tensors3_[it]);
if(!couplingSet) {
gs = abs(outgoingVertexA->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[2][ix].first])(it, ifm, ia, ig) +=
colourFlow[2][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from 4 point vertex
if (fourPointVertex_[inter]) {
Complex diag = fourPointVertex_[inter]->evaluate(scale, wave3_[ia], wavebar3_[ifm],
gluon_[2*ig], tensors3_[it]);
for(unsigned int ix=0;ix<colourFlow[3].size();++ix) {
(*ME[colourFlow[3][ix].first])(it, ifm, ia, ig) +=
colourFlow[3][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha_(s,em)
output *= (4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
diff --git a/Decay/General/TSSDecayer.cc b/Decay/General/TSSDecayer.cc
--- a/Decay/General/TSSDecayer.cc
+++ b/Decay/General/TSSDecayer.cc
@@ -1,130 +1,132 @@
// -*- C++ -*-
//
// TSSDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 TSSDecayer class.
//
#include "TSSDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/Utilities/Kinematics.h"
#include "ThePEG/PDT/DecayMode.h"
#include "ThePEG/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/TensorWaveFunction.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr TSSDecayer::clone() const {
return new_ptr(*this);
}
IBPtr TSSDecayer::fullclone() const {
return new_ptr(*this);
}
void TSSDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & ,
const vector<map<ShowerInteraction,VertexBasePtr> > & ,
map<ShowerInteraction,VertexBasePtr> ) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractSSTVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<SSTVertexPtr> (vert));
}
}
void TSSDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_;
}
void TSSDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<TSSDecayer,GeneralTwoBodyDecayer>
describeHerwigTSSDecayer("Herwig::TSSDecayer", "Herwig.so");
void TSSDecayer::Init() {
static ClassDocumentation<TSSDecayer> documentation
("This class implements the decay of a tensor particle into "
"2 scalars.");
}
double TSSDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin2,PDT::Spin0,PDT::Spin0)));
if(meopt==Initialize) {
TensorWaveFunction::
calculateWaveFunctions(tensors_,rho_,const_ptr_cast<tPPtr>(&inpart),
incoming,false);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
TensorWaveFunction::
constructSpinInfo(tensors_,const_ptr_cast<tPPtr>(&inpart),
incoming,true,false);
for(unsigned int ix=0;ix<2;++ix)
ScalarWaveFunction::
constructSpinInfo(decay[ix],outgoing,true);
return 0.;
}
ScalarWaveFunction sca1(decay[0]->momentum(),decay[0]->dataPtr(),outgoing);
ScalarWaveFunction sca2(decay[1]->momentum(),decay[1]->dataPtr(),outgoing);
Energy2 scale(sqr(inpart.mass()));
for(unsigned int thel=0;thel<5;++thel) {
(*ME())(thel,0,0) =0.;
for(auto vert : vertex_)
(*ME())(thel,0,0) += vert->evaluate(scale,sca1,sca2,tensors_[thel]);
}
double output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy TSSDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
Energy2 scale(sqr(inpart.second));
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(scale, outa.first, outb.first, in);
double musq = sqr(outa.second/inpart.second);
double b = sqrt(1. - 4.*musq);
double me2 = scale*pow(b,4)/120*UnitRemoval::InvE2;
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second,outa.second,
outb.second);
Energy output = norm(perturbativeVertex_[0]->norm())*me2*pcm/(8.*Constants::pi);
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
diff --git a/Decay/General/TVVDecayer.cc b/Decay/General/TVVDecayer.cc
--- a/Decay/General/TVVDecayer.cc
+++ b/Decay/General/TVVDecayer.cc
@@ -1,338 +1,340 @@
// -*- C++ -*-
//
// TVVDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 TVVDecayer class.
//
#include "TVVDecayer.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/Helicity/WaveFunction/VectorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/TensorWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "ThePEG/Helicity/LorentzTensor.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr TVVDecayer::clone() const {
return new_ptr(*this);
}
IBPtr TVVDecayer::fullclone() const {
return new_ptr(*this);
}
void TVVDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> &,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> fourV) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractVVTVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<VVTVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
fourPointVertex_[inter] = dynamic_ptr_cast<AbstractVVVTVertexPtr>(fourV.at(inter));
outgoingVertex1_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr> (outV[0].at(inter));
outgoingVertex2_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr> (outV[1].at(inter));
}
}
void TVVDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< outgoingVertex1_ << outgoingVertex2_
<< fourPointVertex_;
}
void TVVDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> outgoingVertex1_ >> outgoingVertex2_
>> fourPointVertex_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<TVVDecayer,GeneralTwoBodyDecayer>
describeHerwigTVVDecayer("Herwig::TVVDecayer", "Herwig.so");
void TVVDecayer::Init() {
static ClassDocumentation<TVVDecayer> documentation
("This class implements the decay of a tensor to 2 vector bosons");
}
double TVVDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin2,PDT::Spin1,PDT::Spin1)));
bool photon[2];
for(unsigned int ix=0;ix<2;++ix)
photon[ix] = decay[ix]->mass()==ZERO;
if(meopt==Initialize) {
TensorWaveFunction::
calculateWaveFunctions(tensors_,rho_,const_ptr_cast<tPPtr>(&inpart),
incoming,false);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
TensorWaveFunction::
constructSpinInfo(tensors_,const_ptr_cast<tPPtr>(&inpart),
incoming,true,false);
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::
constructSpinInfo(vectors_[ix],decay[ix],outgoing,true,photon[ix]);
return 0.;
}
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::
calculateWaveFunctions(vectors_[ix],decay[ix],outgoing,photon[ix]);
Energy2 scale(sqr(inpart.mass()));
unsigned int thel,v1hel,v2hel;
for(thel=0;thel<5;++thel) {
for(v1hel=0;v1hel<3;++v1hel) {
for(v2hel=0;v2hel<3;++v2hel) {
(*ME())(thel,v1hel,v2hel) = 0.;
for(auto vert : vertex_)
(*ME())(thel,v1hel,v2hel) += vert->evaluate(scale,
vectors_[0][v1hel],
vectors_[1][v2hel],
tensors_[thel]);
if(photon[1]) ++v2hel;
}
if(photon[0]) ++v1hel;
}
}
double output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy TVVDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
Energy2 scale(sqr(inpart.second));
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(scale, outa.first, outb.first, in);
double mu2 = sqr(outa.second/inpart.second);
double b = sqrt(1 - 4.*mu2);
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second,outa.second,
outb.second);
Energy2 me2;
if(outa.second > ZERO && outb.second > ZERO)
me2 = scale*(30 - 20.*b*b + 3.*pow(b,4))/120.;
else
me2 = scale/10.;
Energy output = norm(perturbativeVertex_[0]->norm())*me2*pcm
/(8.*Constants::pi)*UnitRemoval::InvE2;
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double TVVDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
bool massless[2];
for(unsigned int ix=0;ix<2;++ix)
massless[ix] = decay[ix]->mass()==ZERO;
int iglu(2);
if(meopt==Initialize) {
// create tensor wavefunction for decaying particle
TensorWaveFunction::
calculateWaveFunctions(tensors3_, rho3_, const_ptr_cast<tPPtr>(&inpart), incoming, false);
}
// setup spin information when needed
if(meopt==Terminate) {
TensorWaveFunction::
constructSpinInfo(tensors3_, const_ptr_cast<tPPtr>(&inpart),incoming,true, false);
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::
constructSpinInfo(vectors3_[ix],decay[ix ],outgoing,true, massless[ix]);
VectorWaveFunction::
constructSpinInfo(gluon_ ,decay[iglu ],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin2, PDT::Spin1,
PDT::Spin1, PDT::Spin1)));
// create wavefunctions
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::
calculateWaveFunctions(vectors3_[ix],decay[ix ],outgoing,massless[ix]);
VectorWaveFunction::
calculateWaveFunctions(gluon_ ,decay[iglu ],outgoing,true);
// gauge test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[iglu ]->momentum(),
decay[iglu ]->dataPtr(),10,
outgoing));
}
}
#endif
// work out which vector each outgoing vertex corresponds to
if(outgoingVertex1_[inter]!=outgoingVertex2_[inter] &&
outgoingVertex1_[inter]->isIncoming(getParticleData(decay[1]->id())))
swap(outgoingVertex1_[inter], outgoingVertex2_[inter]);
if (! (outgoingVertex1_[inter] && outgoingVertex2_[inter]))
throw Exception()
<< "Invalid vertices for radiation in TVV decay in TVVDecayer::threeBodyME"
<< Exception::runerror;
if( !(!inpart.dataPtr()->coloured() && inter ==ShowerInteraction::QCD) &&
!(!inpart.dataPtr()->charged() && inter ==ShowerInteraction::QED))
throw Exception()
<< "Invalid vertices for radiation in TVV decay in TVVDecayer::threeBodyME"
<< Exception::runerror;
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int it = 0; it < 5; ++it) {
for(unsigned int iv0 = 0; iv0 < 3; ++iv0) {
for(unsigned int iv1 = 0; iv1 < 3; ++iv1) {
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from first outgoing vector
if((decay[0]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[0]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertex1_[inter]);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[0]->dataPtr();
if(off->CC()) off = off->CC();
VectorWaveFunction vectInter =
outgoingVertex1_[inter]->evaluate(scale,3,off,gluon_[2*ig],
vectors3_[0][iv0],decay[0]->mass());
assert(vectors3_[0][iv0].particle()->PDGName()==vectInter.particle()->PDGName());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectors3_[1][iv1],
vectInter,tensors3_[it]);
if(!couplingSet) {
gs = abs(outgoingVertex1_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[1].size();++ix) {
(*ME[colourFlow[1][ix].first])(it, iv0, iv1, ig) +=
colourFlow[1][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from second outgoing vector
if((decay[1]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[1]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertex2_[inter]);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[1]->dataPtr();
if(off->CC()) off = off->CC();
VectorWaveFunction vectInter =
outgoingVertex2_[inter]->evaluate(scale,3,off,vectors3_[1][iv1],
gluon_[2*ig],decay[1]->mass());
assert(vectors3_[1][iv1].particle()->PDGName()==vectInter.particle()->PDGName());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectInter,vectors3_[0][iv0],
tensors3_[it]);
if(!couplingSet) {
gs = abs(outgoingVertex2_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[2][ix].first])(it, iv0, iv1, ig) +=
colourFlow[2][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from 4 point vertex
if (fourPointVertex_[inter]) {
Complex diag = fourPointVertex_[inter]->evaluate(scale, vectors3_[0][iv0],
vectors3_[1][iv1],gluon_[2*ig],
tensors3_[it]);
for(unsigned int ix=0;ix<colourFlow[3].size();++ix) {
(*ME[colourFlow[3][ix].first])(it, iv0, iv1, ig) +=
colourFlow[3][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
if(massless[1]) ++iv1;
}
if(massless[0]) ++iv0;
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha_(s,em)
output *= (4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
diff --git a/Decay/General/VFFDecayer.cc b/Decay/General/VFFDecayer.cc
--- a/Decay/General/VFFDecayer.cc
+++ b/Decay/General/VFFDecayer.cc
@@ -1,470 +1,472 @@
// -*- C++ -*-
//
// VFFDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 VFFDecayer class.
//
#include "VFFDecayer.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/Helicity/WaveFunction/VectorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "ThePEG/Helicity/Vertex/Vector/FFVVertex.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr VFFDecayer::clone() const {
return new_ptr(*this);
}
IBPtr VFFDecayer::fullclone() const {
return new_ptr(*this);
}
void VFFDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> ) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractFFVVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<FFVVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(inV.at(inter));
outgoingVertex1_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(outV[0].at(inter));
outgoingVertex2_[inter] = dynamic_ptr_cast<AbstractFFVVertexPtr>(outV[1].at(inter));
}
}
void VFFDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< incomingVertex_ << outgoingVertex1_
<< outgoingVertex2_;
}
void VFFDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> incomingVertex_ >> outgoingVertex1_
>> outgoingVertex2_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<VFFDecayer,GeneralTwoBodyDecayer>
describeHerwigVFFDecayer("Herwig::VFFDecayer", "Herwig.so");
void VFFDecayer::Init() {
static ClassDocumentation<VFFDecayer> documentation
("The VFFDecayer implements the matrix element for the"
" decay of a vector to fermion-antifermion pair");
}
double VFFDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
int iferm(1),ianti(0);
if(decay[0]->id()>0) swap(iferm,ianti);
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half)));
if(meopt==Initialize) {
VectorWaveFunction::calculateWaveFunctions(vectors_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming,false);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
VectorWaveFunction::constructSpinInfo(vectors_,const_ptr_cast<tPPtr>(&inpart),
incoming,true,false);
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,decay[iferm],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(wave_ ,decay[ianti],outgoing,true);
return 0.;
}
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar_,decay[iferm],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(wave_ ,decay[ianti],outgoing);
// compute the matrix element
Energy2 scale(inpart.mass()*inpart.mass());
for(unsigned int ifm = 0; ifm < 2; ++ifm) { //loop over fermion helicities
for(unsigned int ia = 0; ia < 2; ++ia) {// loop over antifermion helicities
for(unsigned int vhel = 0; vhel < 3; ++vhel) {//loop over vector helicities
if(iferm > ianti) {
(*ME())(vhel, ia, ifm) = 0.;
for(auto vert : vertex_)
(*ME())(vhel, ia, ifm) +=
vert->evaluate(scale,wave_[ia],
wavebar_[ifm],vectors_[vhel]);
}
else {
(*ME())(vhel,ifm,ia)= 0.;
for(auto vert : vertex_)
(*ME())(vhel,ifm,ia) +=
vert->evaluate(scale,wave_[ia],
wavebar_[ifm],vectors_[vhel]);
}
}
}
}
double output=(ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy VFFDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
double mu1(outa.second/inpart.second), mu2(outb.second/inpart.second);
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), outa.first, outb.first,in);
Complex cl(perturbativeVertex_[0]->left()), cr(perturbativeVertex_[0]->right());
double me2 = (norm(cl) + norm(cr))*( sqr(sqr(mu1) - sqr(mu2))
+ sqr(mu1) + sqr(mu2) - 2.)
- 6.*(cl*conj(cr) + cr*conj(cl)).real()*mu1*mu2;
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second,outa.second,
outb.second);
Energy output = -norm(perturbativeVertex_[0]->norm())*me2*pcm /
(24.*Constants::pi);
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double VFFDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
// work out which is the fermion and antifermion
int ianti(0), iferm(1), iglu(2);
int itype[2];
for(unsigned int ix=0;ix<2;++ix) {
if(decay[ix]->dataPtr()->CC()) itype[ix] = decay[ix]->id()>0 ? 0:1;
else itype[ix] = 2;
}
if(itype[0]==0 && itype[1]!=0) swap(iferm, ianti);
if(itype[0]==2 && itype[1]==1) swap(iferm, ianti);
if(itype[0]==0 && itype[1]==0 && decay[0]->dataPtr()->id()<decay[1]->dataPtr()->id())
swap(iferm, ianti);
if(itype[0]==1 && itype[1]==1 && decay[0]->dataPtr()->id()<decay[1]->dataPtr()->id())
swap(iferm, ianti);
if(meopt==Initialize) {
// create vector wavefunction for decaying particle
VectorWaveFunction::calculateWaveFunctions(vector3_, rho3_, const_ptr_cast<tPPtr>(&inpart),
incoming, false);
}
// setup spin information when needed
if(meopt==Terminate) {
VectorWaveFunction::
constructSpinInfo(vector3_ ,const_ptr_cast<tPPtr>(&inpart),outgoing,true,false);
SpinorBarWaveFunction::
constructSpinInfo(wavebar3_,decay[iferm],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(wave3_ ,decay[ianti],outgoing,true);
VectorWaveFunction::
constructSpinInfo(gluon_ ,decay[iglu ],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin1, PDT::Spin1Half,
PDT::Spin1Half, PDT::Spin1)));
// create wavefunctions
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar3_, decay[iferm],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(wave3_ , decay[ianti],outgoing);
VectorWaveFunction::
calculateWaveFunctions(gluon_ , decay[iglu ],outgoing,true);
// gauge invariance test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[iglu ]->momentum(),
decay[iglu ]->dataPtr(),10,
outgoing));
}
}
#endif
// identify fermion and/or anti-fermion vertex
AbstractFFVVertexPtr outgoingVertexF;
AbstractFFVVertexPtr outgoingVertexA;
identifyVertices(iferm, ianti, inpart, decay, outgoingVertexF, outgoingVertexA,inter);
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int iv = 0; iv < 3; ++iv) {
for(unsigned int ifm = 0; ifm < 2; ++ifm) {
for(unsigned int ia = 0; ia < 2; ++ia) {
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming vector
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
VectorWaveFunction vectorInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),vector3_[iv],
gluon_[2*ig],inpart.mass());
assert(vector3_[iv].particle()->id()==vectorInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,wave3_[ia],wavebar3_[ifm],vectorInter);
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(iv, ia, ifm, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing fermion
if((decay[iferm]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[iferm]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexF);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[iferm]->dataPtr();
if(off->CC()) off = off->CC();
SpinorBarWaveFunction interS =
outgoingVertexF->evaluate(scale,3,off,wavebar3_[ifm],
gluon_[2*ig],decay[iferm]->mass());
assert(wavebar3_[ifm].particle()->id()==interS.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,wave3_[ia], interS,vector3_[iv]);
if(!couplingSet) {
gs = abs(outgoingVertexF->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[1].size();++ix) {
(*ME[colourFlow[1][ix].first])(iv, ia, ifm, ig) +=
colourFlow[1][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from outgoing antifermion
if((decay[ianti]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[ianti]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexA);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[ianti]->dataPtr();
if(off->CC()) off = off->CC();
SpinorWaveFunction interS =
outgoingVertexA->evaluate(scale,3,off,wave3_[ia],
gluon_[2*ig],decay[ianti]->mass());
assert(wave3_[ia].particle()->id()==interS.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,interS,wavebar3_[ifm],vector3_[iv]);
if(!couplingSet) {
gs = abs(outgoingVertexA->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[2][ix].first])(iv, ia, ifm, ig) +=
colourFlow[2][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha_(S,EM)
output*=(4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
//return output
return output;
}
void VFFDecayer::identifyVertices(const int iferm, const int ianti,
const Particle & inpart, const ParticleVector & decay,
AbstractFFVVertexPtr & outgoingVertexF,
AbstractFFVVertexPtr & outgoingVertexA,
ShowerInteraction inter){
// QCD vertices
if(inter==ShowerInteraction::QCD) {
// work out which fermion each outgoing vertex corresponds to
// two outgoing vertices
if( inpart.dataPtr() ->iColour()==PDT::Colour0 &&
((decay[iferm]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar) ||
(decay[iferm]->dataPtr()->iColour()==PDT::Colour8 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour8))){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))){
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))){
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
else if(inpart.dataPtr() ->iColour()==PDT::Colour8 &&
decay[iferm]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))){
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))){
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
// one outgoing vertex
else if(inpart.dataPtr()->iColour()==PDT::Colour3){
if(decay[iferm]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertexF = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertexF = outgoingVertex2_[inter];
}
else if (decay[iferm]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour8){
if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[ianti]->dataPtr()))){
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
else {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
}
}
else if(inpart.dataPtr()->iColour()==PDT::Colour3bar){
if(decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[iferm]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertexA = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertexA = outgoingVertex2_[inter];
}
else if (decay[iferm]->dataPtr()->iColour()==PDT::Colour8 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar){
if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))){
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else {
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
}
else if(inpart.dataPtr()->iColour()==PDT::Colour6 ||
inpart.dataPtr()->iColour()==PDT::Colour6bar) {
if (outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr()))) {
outgoingVertexF = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else {
outgoingVertexF = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
if (! ((incomingVertex_[inter] && (outgoingVertexF || outgoingVertexA)) ||
( outgoingVertexF && outgoingVertexA)))
throw Exception()
<< "Invalid vertices for QCD radiation in VFF decay in VFFDecayer::identifyVertices"
<< Exception::runerror;
}
// QED
else {
if(decay[iferm]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iferm]->dataPtr())))
outgoingVertexF = outgoingVertex1_[inter];
else
outgoingVertexF = outgoingVertex2_[inter];
}
if(decay[ianti]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[ianti]->dataPtr())))
outgoingVertexA = outgoingVertex1_[inter];
else
outgoingVertexA = outgoingVertex2_[inter];
}
}
}
diff --git a/Decay/General/VSSDecayer.cc b/Decay/General/VSSDecayer.cc
--- a/Decay/General/VSSDecayer.cc
+++ b/Decay/General/VSSDecayer.cc
@@ -1,416 +1,418 @@
// -*- C++ -*-
//
// VSSDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 VSSDecayer class.
//
#include "VSSDecayer.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 "Herwig/Utilities/Kinematics.h"
#include "ThePEG/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr VSSDecayer::clone() const {
return new_ptr(*this);
}
IBPtr VSSDecayer::fullclone() const {
return new_ptr(*this);
}
void VSSDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> ) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractVSSVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<VSSVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(inV.at(inter));
outgoingVertex1_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[0].at(inter));
outgoingVertex2_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[1].at(inter));
}
}
void VSSDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< incomingVertex_ << outgoingVertex1_
<< outgoingVertex2_;
}
void VSSDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> incomingVertex_ >> outgoingVertex1_
>> outgoingVertex2_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<VSSDecayer,GeneralTwoBodyDecayer>
describeHerwigVSSDecayer("Herwig::VSSDecayer", "Herwig.so");
void VSSDecayer::Init() {
static ClassDocumentation<VSSDecayer> documentation
("This implements the decay of a vector to 2 scalars");
}
double VSSDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1,PDT::Spin0,PDT::Spin0)));
if(meopt==Initialize) {
VectorWaveFunction::calculateWaveFunctions(vectors_,rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming,false);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
VectorWaveFunction::constructSpinInfo(vectors_,const_ptr_cast<tPPtr>(&inpart),
incoming,true,false);
for(unsigned int ix=0;ix<2;++ix)
ScalarWaveFunction::
constructSpinInfo(decay[ix],outgoing,true);
return 0.;
}
ScalarWaveFunction sca1(decay[0]->momentum(),decay[0]->dataPtr(),outgoing);
ScalarWaveFunction sca2(decay[1]->momentum(),decay[1]->dataPtr(),outgoing);
Energy2 scale(sqr(inpart.mass()));
for(unsigned int ix=0;ix<3;++ix) {
(*ME())(ix,0,0) = 0.;
for(auto vert : vertex_)
(*ME())(ix,0,0) += vert->evaluate(scale,vectors_[ix],sca1,sca2);
}
double output=(ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy VSSDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in, outa.first,
outb.first);
double mu1sq = sqr(outa.second/inpart.second);
double mu2sq = sqr(outb.second/inpart.second);
double me2 = sqr(mu1sq - mu2sq) - 2.*(mu1sq + mu2sq);
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second,outa.second,
outb.second);
Energy output = -norm(perturbativeVertex_[0]->norm())*me2*pcm /
(24.*Constants::pi);
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double VSSDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
// work out which is the scalar and anti-scalar
int ianti(0), iscal(1), iglu(2);
int itype[2];
for(unsigned int ix=0;ix<2;++ix) {
if(decay[ix]->dataPtr()->CC()) itype[ix] = decay[ix]->id()>0 ? 0:1;
else itype[ix] = 2;
}
if(itype[0]==0 && itype[1]!=0) swap(ianti, iscal);
if(itype[0]==2 && itype[1]==1) swap(ianti, iscal);
if(itype[0]==0 && itype[1]==0 && abs(decay[0]->dataPtr()->id())>abs(decay[1]->dataPtr()->id()))
swap(iscal, ianti);
if(itype[0]==1 && itype[1]==1 && abs(decay[0]->dataPtr()->id())<abs(decay[1]->dataPtr()->id()))
swap(iscal, ianti);
if(meopt==Initialize) {
// create vector wavefunction for decaying particle
VectorWaveFunction::calculateWaveFunctions(vector3_, rho3_, const_ptr_cast<tPPtr>(&inpart),
incoming, false);
}
// setup spin information when needed
if(meopt==Terminate) {
VectorWaveFunction::
constructSpinInfo(vector3_ ,const_ptr_cast<tPPtr>(&inpart),outgoing,true,false);
ScalarWaveFunction::constructSpinInfo( decay[iscal],outgoing,true);
ScalarWaveFunction::constructSpinInfo( decay[ianti],outgoing,true);
VectorWaveFunction::constructSpinInfo(gluon_,decay[iglu ],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin1, PDT::Spin0,
PDT::Spin0, PDT::Spin1)));
// create wavefunctions
ScalarWaveFunction scal(decay[iscal]->momentum(), decay[iscal]->dataPtr(),outgoing);
ScalarWaveFunction anti(decay[ianti]->momentum(), decay[ianti]->dataPtr(),outgoing);
VectorWaveFunction::calculateWaveFunctions(gluon_,decay[iglu ],outgoing,true);
// gauge test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[iglu ]->momentum(),
decay[iglu ]->dataPtr(),10,
outgoing));
}
}
#endif
// identify scalar and/or anti-scalar vertex
AbstractVSSVertexPtr outgoingVertexS;
AbstractVSSVertexPtr outgoingVertexA;
identifyVertices(iscal, ianti, inpart, decay, outgoingVertexS, outgoingVertexA,inter);
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int iv = 0; iv < 3; ++iv) {
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming vector
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
VectorWaveFunction vectorInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),vector3_[iv],
gluon_[2*ig],inpart.mass());
assert(vector3_[iv].particle()->id()==vectorInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectorInter,scal,anti);
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(iv, 0, 0, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from the outgoing scalar
if((decay[iscal]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[iscal]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexS);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[iscal]->dataPtr();
if(off->CC()) off = off->CC();
ScalarWaveFunction scalarInter =
outgoingVertexS->evaluate(scale,3,off,gluon_[2*ig],scal,decay[iscal]->mass());
assert(scal.particle()->id()==scalarInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vector3_[iv],anti,scalarInter);
if(!couplingSet) {
gs = abs(outgoingVertexS->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[1].size();++ix) {
(*ME[colourFlow[1][ix].first])(iv, 0, 0, ig) +=
colourFlow[1][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
if((decay[ianti]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[ianti]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexA);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[ianti]->dataPtr();
if(off->CC()) off = off->CC();
ScalarWaveFunction scalarInter =
outgoingVertexA->evaluate(scale,3,off, gluon_[2*ig],anti,decay[ianti]->mass());
assert(anti.particle()->id()==scalarInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vector3_[iv],scal,scalarInter);
if(!couplingSet) {
gs = abs(outgoingVertexA->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[2][ix].first])(iv, 0, 0, ig) +=
colourFlow[2][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha_(S,EM)
output*=(4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
void VSSDecayer::identifyVertices(const int iscal, const int ianti,
const Particle & inpart, const ParticleVector & decay,
AbstractVSSVertexPtr & outgoingVertexS,
AbstractVSSVertexPtr & outgoingVertexA,
ShowerInteraction inter){
if(inter==ShowerInteraction::QCD) {
// work out which scalar each outgoing vertex corresponds to
// two outgoing vertices
if( inpart.dataPtr() ->iColour()==PDT::Colour0 &&
((decay[iscal]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar) ||
(decay[iscal]->dataPtr()->iColour()==PDT::Colour8 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour8))){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[iscal]->id()))){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(getParticleData(decay[iscal]->id()))){
outgoingVertexS = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
else if(inpart.dataPtr() ->iColour()==PDT::Colour8 &&
decay[iscal]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[iscal]->id()))){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(getParticleData(decay[iscal]->id()))){
outgoingVertexS = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
// one outgoing vertex
else if(inpart.dataPtr()->iColour()==PDT::Colour3){
if(decay[iscal]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertexS = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertexS = outgoingVertex2_[inter];
}
else if (decay[iscal]->dataPtr()->iColour()==PDT::Colour3 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour8){
if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[ianti]->dataPtr()->id()))){
outgoingVertexS = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
else {
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
}
}
else if(inpart.dataPtr()->iColour()==PDT::Colour3bar){
if(decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[iscal]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertexA = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertexA = outgoingVertex2_[inter];
}
else if (decay[iscal]->dataPtr()->iColour()==PDT::Colour8 &&
decay[ianti]->dataPtr()->iColour()==PDT::Colour3bar){
if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[iscal]->dataPtr()->id()))){
outgoingVertexS = outgoingVertex1_[inter];
outgoingVertexA = outgoingVertex2_[inter];
}
else {
outgoingVertexS = outgoingVertex2_[inter];
outgoingVertexA = outgoingVertex1_[inter];
}
}
}
if (! ((incomingVertex_[inter] && (outgoingVertexS || outgoingVertexA)) ||
( outgoingVertexS && outgoingVertexA)))
throw Exception()
<< "Invalid vertices for QCD radiation in VSS decay in VSSDecayer::identifyVertices"
<< Exception::runerror;
}
else {
if(decay[iscal]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[iscal]->dataPtr())))
outgoingVertexS = outgoingVertex1_[inter];
else
outgoingVertexS = outgoingVertex2_[inter];
}
if(decay[ianti]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[ianti]->dataPtr())))
outgoingVertexA = outgoingVertex1_[inter];
else
outgoingVertexA = outgoingVertex2_[inter];
}
}
}
diff --git a/Decay/General/VVSDecayer.cc b/Decay/General/VVSDecayer.cc
--- a/Decay/General/VVSDecayer.cc
+++ b/Decay/General/VVSDecayer.cc
@@ -1,313 +1,315 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the VVSDecayer class.
//
#include "VVSDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr VVSDecayer::clone() const {
return new_ptr(*this);
}
IBPtr VVSDecayer::fullclone() const {
return new_ptr(*this);
}
void VVSDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr>) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractVVSVertexPtr>(vert));
perturbativeVertex_.push_back(dynamic_ptr_cast<VVSVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(inV.at(inter));
outgoingVertexS_[inter] = AbstractVSSVertexPtr();
outgoingVertexV_[inter] = AbstractVVVVertexPtr();
if(outV[0].at(inter)) {
if (outV[0].at(inter)->getName()==VertexType::VSS)
outgoingVertexS_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[0].at(inter));
else
outgoingVertexV_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[0].at(inter));
}
if(outV[1].at(inter)) {
if (outV[1].at(inter)->getName()==VertexType::VSS)
outgoingVertexS_[inter] = dynamic_ptr_cast<AbstractVSSVertexPtr>(outV[1].at(inter));
else
outgoingVertexV_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[1].at(inter));
}
}
}
void VVSDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< incomingVertex_ << outgoingVertexS_ << outgoingVertexV_;
}
void VVSDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> incomingVertex_ >> outgoingVertexS_ >> outgoingVertexV_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<VVSDecayer,GeneralTwoBodyDecayer>
describeHerwigVVSDecayer("Herwig::VVSDecayer", "Herwig.so");
void VVSDecayer::Init() {
static ClassDocumentation<VVSDecayer> documentation
("The VVSDecayer class implements the decay of a vector"
" to a vector and a scalar");
}
double VVSDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
bool massless = ( decay[0]->id()==ParticleID::gamma ||
decay[0]->id()==ParticleID::g );
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1,PDT::Spin1,PDT::Spin0)));
if(meopt==Initialize) {
VectorWaveFunction::calculateWaveFunctions(vectors_[0],rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming,false);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
VectorWaveFunction::constructSpinInfo(vectors_[0],const_ptr_cast<tPPtr>(&inpart),
incoming,true,false);
VectorWaveFunction::
constructSpinInfo(vectors_[1],decay[0],outgoing,true,massless);
ScalarWaveFunction::
constructSpinInfo(decay[1],outgoing,true);
return 0.;
}
VectorWaveFunction::
calculateWaveFunctions(vectors_[1],decay[0],outgoing,massless);
ScalarWaveFunction sca(decay[1]->momentum(),decay[1]->dataPtr(),outgoing);
Energy2 scale(sqr(inpart.mass()));
for(unsigned int in=0;in<3;++in) {
for(unsigned int out=0;out<3;++out) {
if(massless&&out==1) ++out;
(*ME())(in,out,0) = 0.;
for(auto vert : vertex_)
(*ME())(in,out,0) +=
vert->evaluate(scale,vectors_[0][in],vectors_[1][out],sca);
}
}
double output=(ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy VVSDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
Energy2 scale(sqr(inpart.second));
double mu1sq = sqr(outa.second/inpart.second);
double mu2sq = sqr(outb.second/inpart.second);
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
if( outb.first->iSpin() == PDT::Spin0 )
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in,
outa.first, outb.first);
else {
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in,
outb.first, outa.first);
swap(mu1sq, mu2sq);
}
double vn = norm(perturbativeVertex_[0]->norm());
if(vn == ZERO || mu1sq == ZERO) return ZERO;
double me2 = 2. + 0.25*sqr(1. + mu1sq - mu2sq)/mu1sq;
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second,outa.second,
outb.second);
Energy output = vn*me2*pcm/(24.*Constants::pi)/scale*UnitRemoval::E2;
// colour factor
output *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return output;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double VVSDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
unsigned int ivec(0),isca(1);
if(decay[ivec]->dataPtr()->iSpin()!=PDT::Spin1) swap(ivec,isca);
if(meopt==Initialize) {
// create vector wavefunction for decaying particle
VectorWaveFunction::calculateWaveFunctions(vectors3_[0], rho3_,
const_ptr_cast<tPPtr>(&inpart),
incoming, false);
}
if(meopt==Terminate) {
VectorWaveFunction::
constructSpinInfo(vectors3_[0] ,const_ptr_cast<tPPtr>(&inpart),outgoing,true,false);
VectorWaveFunction::
constructSpinInfo(vectors3_[1],decay[ivec],outgoing,true,false);
ScalarWaveFunction::
constructSpinInfo(decay[isca],outgoing,true);
VectorWaveFunction::
constructSpinInfo(gluon_ ,decay[2],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin1, PDT::Spin1,
PDT::Spin0, PDT::Spin1)));
bool massless= decay[ivec]->mass()!=ZERO;
// create wavefunctions
VectorWaveFunction::calculateWaveFunctions(vectors3_[1],decay[0],outgoing,massless);
ScalarWaveFunction scal(decay[isca]->momentum(), decay[isca]->dataPtr(),outgoing);
VectorWaveFunction::calculateWaveFunctions(gluon_ ,decay[2],outgoing,true);
// gauge test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[2]->momentum(),
decay[2]->dataPtr(),10,
outgoing));
}
}
#endif
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int iv0 = 0; iv0 < 3; ++iv0) {
for(unsigned int iv1 = 0; iv1 < 3; ++iv1) {
if(massless && iv1==1) continue;
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming vector
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
VectorWaveFunction vectorInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),vectors3_[0][iv0],
gluon_[2*ig],inpart.mass());
assert(vectors3_[0][iv0].particle()->id()==vectorInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectorInter,vectors3_[1][iv1],scal);
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(iv0, iv1, 0, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from the outgoing vector
if((decay[ivec]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[ivec]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexV_[inter]);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[ivec]->dataPtr();
if(off->CC()) off = off->CC();
VectorWaveFunction vectorInter =
outgoingVertexV_[inter]->evaluate(scale,3,off,gluon_[2*ig],vectors3_[1][iv1],decay[ivec]->mass());
assert(vectors3_[1][iv1].particle()->id()==vectorInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectors3_[0][iv0],vectorInter,scal);
if(!couplingSet) {
gs = abs(outgoingVertexV_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[1].size();++ix) {
(*ME[colourFlow[1][ix].first])(iv0, iv1, 0, ig) +=
colourFlow[1][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from the outgoing scalar
if((decay[isca]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[isca]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertexS_[inter]);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[isca]->dataPtr();
if(off->CC()) off = off->CC();
ScalarWaveFunction scalarInter =
outgoingVertexS_[inter]->evaluate(scale,3,off,gluon_[2*ig],scal,decay[isca]->mass());
assert(scal.particle()->id()==scalarInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectors3_[0][iv0],vectors3_[1][iv1],scalarInter);
if(!couplingSet) {
gs = abs(outgoingVertexS_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[2][ix].first])(iv0, iv1, 0, ig) +=
colourFlow[2][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha_(S,EM)
output*=(4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
diff --git a/Decay/General/VVVDecayer.cc b/Decay/General/VVVDecayer.cc
--- a/Decay/General/VVVDecayer.cc
+++ b/Decay/General/VVVDecayer.cc
@@ -1,441 +1,443 @@
// -*- C++ -*-
//
// VVVDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 VVVDecayer class.
//
#include "VVVDecayer.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/Helicity/WaveFunction/VectorWaveFunction.h"
#include "Herwig/Utilities/Kinematics.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
IBPtr VVVDecayer::clone() const {
return new_ptr(*this);
}
IBPtr VVVDecayer::fullclone() const {
return new_ptr(*this);
}
void VVVDecayer::setDecayInfo(PDPtr incoming, PDPair outgoing,
vector<VertexBasePtr> vertex,
map<ShowerInteraction,VertexBasePtr> & inV,
const vector<map<ShowerInteraction,VertexBasePtr> > & outV,
map<ShowerInteraction,VertexBasePtr> fourV) {
decayInfo(incoming,outgoing);
for(auto vert : vertex) {
vertex_ .push_back(dynamic_ptr_cast<AbstractVVVVertexPtr>(vert));
perturbativeVertex_ .push_back(dynamic_ptr_cast<VVVVertexPtr> (vert));
}
vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED};
for(auto & inter : itemp) {
incomingVertex_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(inV.at(inter));
outgoingVertex1_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[0].at(inter));
outgoingVertex2_[inter] = dynamic_ptr_cast<AbstractVVVVertexPtr>(outV[1].at(inter));
fourPointVertex_[inter] = dynamic_ptr_cast<AbstractVVVVVertexPtr>(fourV.at(inter));
}
}
void VVVDecayer::persistentOutput(PersistentOStream & os) const {
os << vertex_ << perturbativeVertex_
<< incomingVertex_ << outgoingVertex1_
<< outgoingVertex2_ << fourPointVertex_;
}
void VVVDecayer::persistentInput(PersistentIStream & is, int) {
is >> vertex_ >> perturbativeVertex_
>> incomingVertex_ >> outgoingVertex1_
>> outgoingVertex2_ >> fourPointVertex_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<VVVDecayer,GeneralTwoBodyDecayer>
describeHerwigVVVDecayer("Herwig::VVVDecayer", "Herwig.so");
void VVVDecayer::Init() {
static ClassDocumentation<VVVDecayer> documentation
("The VVVDecayer class implements the decay of a vector boson "
"into 2 vector bosons");
}
double VVVDecayer::me2(const int , const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1,PDT::Spin1,PDT::Spin1)));
bool massless[2];
for(unsigned int ix=0;ix<2;++ix)
massless[ix] = (decay[ix]->id()==ParticleID::gamma ||
decay[ix]->id()==ParticleID::g);
if(meopt==Initialize) {
VectorWaveFunction::calculateWaveFunctions(vectors_[0],rho_,
const_ptr_cast<tPPtr>(&inpart),
incoming,false);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
VectorWaveFunction::constructSpinInfo(vectors_[0],const_ptr_cast<tPPtr>(&inpart),
incoming,true,false);
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::
constructSpinInfo(vectors_[ix+1],decay[ix],outgoing,true,massless[ix]);
return 0.;
}
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::
calculateWaveFunctions(vectors_[ix+1],decay[ix],outgoing,massless[ix]);
Energy2 scale(sqr(inpart.mass()));
for(unsigned int iv3=0;iv3<3;++iv3) {
for(unsigned int iv2=0;iv2<3;++iv2) {
for(unsigned int iv1=0;iv1<3;++iv1) {
(*ME())(iv1,iv2,iv3) = 0.;
for(auto vert : vertex_) {
(*ME())(iv1,iv2,iv3) += vert->
evaluate(scale,vectors_[1][iv2],vectors_[2][iv3],vectors_[0][iv1]);
}
}
}
}
double output = (ME()->contract(rho_)).real()/scale*UnitRemoval::E2;
// colour and identical particle factors
output *= colourFactor(inpart.dataPtr(),decay[0]->dataPtr(),
decay[1]->dataPtr());
// return the answer
return output;
}
Energy VVVDecayer::partialWidth(PMPair inpart, PMPair outa,
PMPair outb) const {
if( inpart.second < outa.second + outb.second ) return ZERO;
if(perturbativeVertex_.size()==1 &&
perturbativeVertex_[0]) {
tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first;
perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in,
outa.first, outb.first);
double mu1(outa.second/inpart.second), mu1sq(sqr(mu1)),
mu2(outb.second/inpart.second), mu2sq(sqr(mu2));
double vn = norm(perturbativeVertex_[0]->norm());
if(vn == ZERO || mu1sq == ZERO || mu2sq == ZERO) return ZERO;
double me2 =
(mu1 - mu2 - 1.)*(mu1 - mu2 + 1.)*(mu1 + mu2 - 1.)*(mu1 + mu2 + 1.)
* (sqr(mu1sq) + sqr(mu2sq) + 10.*(mu1sq*mu2sq + mu1sq + mu2sq) + 1.)
/4./mu1sq/mu2sq;
Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second,outa.second,
outb.second);
Energy pWidth = vn*me2*pcm/24./Constants::pi;
// colour factor
pWidth *= colourFactor(inpart.first,outa.first,outb.first);
// return the answer
return pWidth;
}
else {
return GeneralTwoBodyDecayer::partialWidth(inpart,outa,outb);
}
}
double VVVDecayer::threeBodyME(const int , const Particle & inpart,
const ParticleVector & decay,
ShowerInteraction inter, MEOption meopt) {
if(meopt==Initialize) {
// create vector wavefunction for decaying particle
VectorWaveFunction::calculateWaveFunctions(vector3_, rho3_, const_ptr_cast<tPPtr>(&inpart),
incoming, false);
}
if(meopt==Terminate) {
VectorWaveFunction::
constructSpinInfo(vector3_ ,const_ptr_cast<tPPtr>(&inpart),outgoing,true,false);
VectorWaveFunction::
constructSpinInfo(vectors3_[0],decay[0],outgoing,true,false);
VectorWaveFunction::
constructSpinInfo(vectors3_[1],decay[1],outgoing,true,false);
VectorWaveFunction::
constructSpinInfo(gluon_ ,decay[2],outgoing,true,false);
return 0.;
}
// calculate colour factors and number of colour flows
unsigned int nflow;
vector<DVector> cfactors = getColourFactors(inpart, decay, nflow);
vector<GeneralDecayMEPtr> ME(nflow,new_ptr(GeneralDecayMatrixElement(PDT::Spin1, PDT::Spin1,
PDT::Spin1, PDT::Spin1)));
bool massless[2];
for(unsigned int ix=0;ix<2;++ix)
massless[ix] = decay[ix]->mass()!=ZERO;
// create wavefunctions
VectorWaveFunction::calculateWaveFunctions(vectors3_[0],decay[0],outgoing,massless[0]);
VectorWaveFunction::calculateWaveFunctions(vectors3_[1],decay[1],outgoing,massless[1]);
VectorWaveFunction::calculateWaveFunctions(gluon_ ,decay[2],outgoing,true);
// gauge test
#ifdef GAUGE_CHECK
gluon_.clear();
for(unsigned int ix=0;ix<3;++ix) {
if(ix==1) gluon_.push_back(VectorWaveFunction());
else {
gluon_.push_back(VectorWaveFunction(decay[2]->momentum(),
decay[2]->dataPtr(),10,
outgoing));
}
}
#endif
// get the outgoing vertices
AbstractVVVVertexPtr outgoingVertex1;
AbstractVVVVertexPtr outgoingVertex2;
identifyVertices(inpart,decay, outgoingVertex1, outgoingVertex2,inter);
Energy2 scale(sqr(inpart.mass()));
const GeneralTwoBodyDecayer::CFlow & colourFlow
= colourFlows(inpart, decay);
double gs(0.);
bool couplingSet(false);
#ifdef GAUGE_CHECK
double total=0.;
#endif
for(unsigned int iv0 = 0; iv0 < 3; ++iv0) {
for(unsigned int iv1 = 0; iv1 < 3; ++iv1) {
if(massless[0] && iv1==1) continue;
for(unsigned int iv2 = 0; iv2 < 3; ++iv2) {
if(massless[1] && iv2==1) continue;
for(unsigned int ig = 0; ig < 2; ++ig) {
// radiation from the incoming vector
if((inpart.dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(inpart.dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(incomingVertex_[inter]);
VectorWaveFunction vectorInter =
incomingVertex_[inter]->evaluate(scale,3,inpart.dataPtr(),vector3_[iv0],
gluon_[2*ig],inpart.mass());
assert(vector3_[iv0].particle()->id()==vectorInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vectorInter,vectors3_[0][iv1],
vectors3_[1][iv2]);
if(!couplingSet) {
gs = abs(incomingVertex_[inter]->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[0].size();++ix) {
(*ME[colourFlow[0][ix].first])(iv0, iv1, iv2, ig) +=
colourFlow[0][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from the 1st outgoing vector
if((decay[0]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[0]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertex1);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[0]->dataPtr();
if(off->CC()) off = off->CC();
VectorWaveFunction vectorInter =
outgoingVertex1->evaluate(scale,3,off,gluon_[2*ig],vectors3_[0][iv1],decay[0]->mass());
assert(vectors3_[0][iv1].particle()->id()==vectorInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vector3_[iv0],vectorInter,vectors3_[1][iv2]);
if(!couplingSet) {
gs = abs(outgoingVertex1->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[1].size();++ix) {
(*ME[colourFlow[1][ix].first])(iv0, iv1, iv2, ig) +=
colourFlow[1][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// radiation from the second outgoing vector
if((decay[1]->dataPtr()->coloured() && inter==ShowerInteraction::QCD) ||
(decay[1]->dataPtr()->charged() && inter==ShowerInteraction::QED) ) {
assert(outgoingVertex2);
// ensure you get correct outgoing particle from first vertex
tcPDPtr off = decay[1]->dataPtr();
if(off->CC()) off = off->CC();
VectorWaveFunction vectorInter =
outgoingVertex2->evaluate(scale,3,off, gluon_[2*ig],vectors3_[1][iv2],decay[1]->mass());
assert(vectors3_[1][iv2].particle()->id()==vectorInter.particle()->id());
Complex diag = 0.;
for(auto vertex : vertex_)
diag += vertex->evaluate(scale,vector3_[iv0],vectors3_[0][iv1],vectorInter);
if(!couplingSet) {
gs = abs(outgoingVertex2->norm());
couplingSet = true;
}
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[2][ix].first])(iv0, iv1, iv2, ig) +=
colourFlow[2][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
// 4-point vertex
if (fourPointVertex_[inter]) {
Complex diag = fourPointVertex_[inter]->evaluate(scale,0, vector3_[iv0],
vectors3_[0][iv1],
vectors3_[1][iv2],gluon_[2*ig]);
for(unsigned int ix=0;ix<colourFlow[2].size();++ix) {
(*ME[colourFlow[3][ix].first])(iv0, iv1, iv2, ig) +=
colourFlow[3][ix].second*diag;
}
#ifdef GAUGE_CHECK
total+=norm(diag);
#endif
}
}
}
}
}
// contract matrices
double output=0.;
for(unsigned int ix=0; ix<nflow; ++ix){
for(unsigned int iy=0; iy<nflow; ++iy){
output+=cfactors[ix][iy]*(ME[ix]->contract(*ME[iy],rho3_)).real();
}
}
// divide by alpha_(S,EM)
output*=(4.*Constants::pi)/sqr(gs);
#ifdef GAUGE_CHECK
double ratio = output/total;
if(abs(ratio)>1e-20) {
generator()->log() << "Test of gauge invariance in decay\n" << inpart << "\n";
for(unsigned int ix=0;ix<decay.size();++ix)
generator()->log() << *decay[ix] << "\n";
generator()->log() << "Test of gauge invariance " << ratio << "\n";
}
#endif
// return the answer
return output;
}
void VVVDecayer::identifyVertices(const Particle & inpart, const ParticleVector & decay,
AbstractVVVVertexPtr & outgoingVertex1,
AbstractVVVVertexPtr & outgoingVertex2,
ShowerInteraction inter) {
if(inter==ShowerInteraction::QCD) {
// work out which scalar each outgoing vertex corresponds to
// two outgoing vertices
if( inpart.dataPtr() ->iColour()==PDT::Colour0 &&
((decay[0]->dataPtr()->iColour()==PDT::Colour3 &&
decay[1]->dataPtr()->iColour()==PDT::Colour3bar) ||
(decay[0]->dataPtr()->iColour()==PDT::Colour8 &&
decay[1]->dataPtr()->iColour()==PDT::Colour8))){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[0]->id()))){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(getParticleData(decay[0]->id()))){
outgoingVertex1 = outgoingVertex2_[inter];
outgoingVertex2 = outgoingVertex1_[inter];
}
}
else if(inpart.dataPtr() ->iColour()==PDT::Colour8 &&
decay[0]->dataPtr()->iColour()==PDT::Colour3 &&
decay[1]->dataPtr()->iColour()==PDT::Colour3bar){
if(outgoingVertex1_[inter]==outgoingVertex2_[inter]){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[0]->id()))){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else if (outgoingVertex2_[inter]->isIncoming(getParticleData(decay[0]->id()))){
outgoingVertex1 = outgoingVertex2_[inter];
outgoingVertex2 = outgoingVertex1_[inter];
}
}
// one outgoing vertex
else if(inpart.dataPtr()->iColour()==PDT::Colour3){
if(decay[0]->dataPtr()->iColour()==PDT::Colour3 &&
decay[1]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertex1 = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertex1 = outgoingVertex2_[inter];
}
else if (decay[0]->dataPtr()->iColour()==PDT::Colour3 &&
decay[1]->dataPtr()->iColour()==PDT::Colour8){
if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[1]->dataPtr()->id()))){
outgoingVertex1 = outgoingVertex2_[inter];
outgoingVertex2 = outgoingVertex1_[inter];
}
else {
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
}
}
else if(inpart.dataPtr()->iColour()==PDT::Colour3bar){
if(decay[1]->dataPtr()->iColour()==PDT::Colour3bar &&
decay[0]->dataPtr()->iColour()==PDT::Colour0){
if (outgoingVertex1_[inter]) outgoingVertex2 = outgoingVertex1_[inter];
else if(outgoingVertex2_[inter]) outgoingVertex2 = outgoingVertex2_[inter];
}
else if (decay[0]->dataPtr()->iColour()==PDT::Colour8 &&
decay[1]->dataPtr()->iColour()==PDT::Colour3bar){
if (outgoingVertex1_[inter]->isIncoming(getParticleData(decay[0]->dataPtr()->id()))){
outgoingVertex1 = outgoingVertex1_[inter];
outgoingVertex2 = outgoingVertex2_[inter];
}
else {
outgoingVertex1 = outgoingVertex2_[inter];
outgoingVertex2 = outgoingVertex1_[inter];
}
}
}
if (! ((incomingVertex_[inter] && (outgoingVertex1 || outgoingVertex2)) ||
( outgoingVertex1 && outgoingVertex2)))
throw Exception()
<< "Invalid vertices for QCD radiation in VVV decay in VVVDecayer::identifyVertices"
<< Exception::runerror;
}
else {
if(decay[0]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[0]->dataPtr())))
outgoingVertex1 = outgoingVertex1_[inter];
else
outgoingVertex1 = outgoingVertex2_[inter];
}
if(decay[1]->dataPtr()->charged()) {
if (outgoingVertex1_[inter] &&
outgoingVertex1_[inter]->isIncoming(const_ptr_cast<tPDPtr>(decay[1]->dataPtr())))
outgoingVertex2 = outgoingVertex1_[inter];
else
outgoingVertex2 = outgoingVertex2_[inter];
}
}
}
diff --git a/Decay/General/VtoFFVDecayer.cc b/Decay/General/VtoFFVDecayer.cc
--- a/Decay/General/VtoFFVDecayer.cc
+++ b/Decay/General/VtoFFVDecayer.cc
@@ -1,369 +1,371 @@
// -*- C++ -*-
//
// This is the implementation of the non-inlined, non-templated member
// functions of the VtoFFVDecayer class.
//
#include "VtoFFVDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/PDT/ThreeBodyAllOnCalculator.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include <numeric>
using namespace Herwig;
using namespace ThePEG;
using namespace ThePEG::Helicity;
IBPtr VtoFFVDecayer::clone() const {
return new_ptr(*this);
}
IBPtr VtoFFVDecayer::fullclone() const {
return new_ptr(*this);
}
void VtoFFVDecayer::persistentOutput(PersistentOStream & os) const {
os << sca_ << fer_ << vec_ << ten_;
}
void VtoFFVDecayer::persistentInput(PersistentIStream & is, int) {
is >> sca_ >> fer_ >> vec_ >> ten_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<VtoFFVDecayer,GeneralThreeBodyDecayer>
describeHerwigVtoFFVDecayer("Herwig::VtoFFVDecayer", "Herwig.so");
void VtoFFVDecayer::Init() {
static ClassDocumentation<VtoFFVDecayer> documentation
("The VtoFFVDecayer class implements the general three-body "
"decay of a vector to a two fermions and a vector.");
}
WidthCalculatorBasePtr VtoFFVDecayer::
threeBodyMEIntegrator(const DecayMode & ) const {
vector<int> intype;
vector<Energy> inmass,inwidth;
vector<double> inpow,inweights;
constructIntegratorChannels(intype,inmass,inwidth,inpow,inweights);
return new_ptr(ThreeBodyAllOnCalculator<VtoFFVDecayer>
(inweights,intype,inmass,inwidth,inpow,*this,0,
outgoing()[0]->mass(),outgoing()[1]->mass(),
outgoing()[2]->mass(),relativeError()));
}
void VtoFFVDecayer::doinit() {
GeneralThreeBodyDecayer::doinit();
if(outgoing().empty()) return;
unsigned int ndiags = getProcessInfo().size();
sca_.resize(ndiags);
fer_.resize(ndiags);
vec_.resize(ndiags);
ten_.resize(ndiags);
for(unsigned int ix = 0;ix < ndiags; ++ix) {
TBDiagram current = getProcessInfo()[ix];
tcPDPtr offshell = current.intermediate;
if( offshell->CC() ) offshell = offshell->CC();
if(offshell->iSpin() == PDT::Spin0) {
AbstractVVSVertexPtr vert1 = dynamic_ptr_cast<AbstractVVSVertexPtr>
(current.vertices.first);
AbstractFFSVertexPtr vert2 = dynamic_ptr_cast<AbstractFFSVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a scalar diagram in VtoFFVDecayer::doinit()"
<< Exception::runerror;
sca_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin1Half) {
AbstractFFVVertexPtr vert1 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.first);
AbstractFFVVertexPtr vert2 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a fermion diagram in VtoFFVDecayer::doinit()"
<< Exception::runerror;
fer_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin1) {
AbstractVVVVertexPtr vert1 = dynamic_ptr_cast<AbstractVVVVertexPtr>
(current.vertices.first);
AbstractFFVVertexPtr vert2 = dynamic_ptr_cast<AbstractFFVVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a vector diagram in VtoFFVDecayer::doinit()"
<< Exception::runerror;
vec_[ix] = make_pair(vert1, vert2);
}
else if(offshell->iSpin() == PDT::Spin2) {
AbstractVVTVertexPtr vert1 = dynamic_ptr_cast<AbstractVVTVertexPtr>
(current.vertices.first);
AbstractFFTVertexPtr vert2 = dynamic_ptr_cast<AbstractFFTVertexPtr>
(current.vertices.second);
if(!vert1||!vert2) throw Exception()
<< "Invalid vertices for a tensor diagram in VtoFFVDecayer::doinit()"
<< Exception::runerror;
ten_[ix] = make_pair(vert1, vert2);
}
}
}
double VtoFFVDecayer::me2(const int ichan, const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
// particle or CC of particle
bool cc = (*getProcessInfo().begin()).incoming != inpart.id();
// special handling or first/last call
if(meopt==Initialize) {
VectorWaveFunction::
calculateWaveFunctions(inVector_,rho_,const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming,false);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
VectorWaveFunction::
constructSpinInfo(inVector_,const_ptr_cast<tPPtr>(&inpart),
Helicity::incoming,true,false);
for(unsigned int ix=0;ix<decay.size();++ix) {
if(decay[ix]->dataPtr()->iSpin()==PDT::Spin1) {
VectorWaveFunction::constructSpinInfo(outVector_,decay[ix],
Helicity::outgoing,true,false);
}
else {
SpinorWaveFunction::
constructSpinInfo(outspin_[ix].first,decay[ix],Helicity::outgoing,true);
}
}
}
unsigned int ivec(0);
bool massless = false;
for(unsigned int ix = 0; ix < decay.size();++ix) {
if(decay[ix]->dataPtr()->iSpin() == PDT::Spin1) {
massless = decay[ix]->id()==ParticleID::g || decay[ix]->id()==ParticleID::gamma;
ivec = ix;
VectorWaveFunction::
calculateWaveFunctions(outVector_, decay[ix], Helicity::outgoing, massless );
}
else {
SpinorWaveFunction::
calculateWaveFunctions(outspin_[ix].first,decay[ix],Helicity::outgoing);
outspin_[ix].second.resize(2);
// Need a ubar and a v spinor
if(outspin_[ix].first[0].wave().Type() == SpinorType::u) {
for(unsigned int iy = 0; iy < 2; ++iy) {
outspin_[ix].second[iy] = outspin_[ix].first[iy].bar();
outspin_[ix].first[iy].conjugate();
}
}
else {
for(unsigned int iy = 0; iy < 2; ++iy) {
outspin_[ix].second[iy] = outspin_[ix].first[iy].bar();
outspin_[ix].second[iy].conjugate();
}
}
}
}
const vector<vector<double> > cfactors(getColourFactors());
const vector<vector<double> > nfactors(getLargeNcColourFactors());
Energy2 scale(sqr(inpart.mass()));
const size_t ncf(numberOfFlows());
vector<Complex> flows(ncf, Complex(0.)), largeflows(ncf, Complex(0.));
// setup the DecayMatrixElement
vector<GeneralDecayMEPtr>
mes(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin1,
ivec == 0 ? PDT::Spin1 : PDT::Spin1Half,
ivec == 1 ? PDT::Spin1 : PDT::Spin1Half,
ivec == 2 ? PDT::Spin1 : PDT::Spin1Half)));
vector<GeneralDecayMEPtr>
mel(ncf,new_ptr(GeneralDecayMatrixElement(PDT::Spin1,
ivec == 0 ? PDT::Spin1 : PDT::Spin1Half,
ivec == 1 ? PDT::Spin1 : PDT::Spin1Half,
ivec == 2 ? PDT::Spin1 : PDT::Spin1Half)));
//the channel possiblities
static const unsigned int out2[3] = {1,0,0}, out3[3] = {2,2,1};
for(unsigned int vi = 0; vi < 3; ++vi) {
for(unsigned int s1 = 0; s1 < 2; ++s1) {
for(unsigned int s2 = 0; s2 < 2; ++s2) {
for(unsigned int v1 = 0; v1 < 3; ++v1) {
if ( massless && v1 == 1 ) continue;
flows = vector<Complex>(ncf, Complex(0.));
largeflows = vector<Complex>(ncf, Complex(0.));
unsigned int idiag(0);
for(vector<TBDiagram>::const_iterator dit=getProcessInfo().begin();
dit!=getProcessInfo().end();++dit) {
// channels if selecting
if( ichan >=0 && diagramMap()[ichan] != idiag ) {
++idiag;
continue;
}
tcPDPtr offshell = dit->intermediate;
if(cc&&offshell->CC()) offshell=offshell->CC();
Complex diag;
unsigned int o2(out2[dit->channelType]), o3(out3[dit->channelType]);
double sign = (o3 < o2) ? 1. : -1.;
// intermediate scalar
if(offshell->iSpin() == PDT::Spin0) {
ScalarWaveFunction inters = sca_[idiag].first->
evaluate(scale, widthOption(), offshell, outVector_[v1],
inVector_[vi]);
unsigned int h1(s1),h2(s2);
if(o2 > o3) swap(h1, h2);
if(decay[o2]->id() < 0 && decay[o3]->id() > 0) {
diag = -sign*sca_[idiag].second->
evaluate(scale,outspin_[o2].first[h1],
outspin_[o3].second[h2],inters);
}
else {
diag = sign*sca_[idiag].second->
evaluate(scale, outspin_[o3].first [h2],
outspin_[o2].second[h1],inters);
}
}
// intermediate fermion
else if(offshell->iSpin() == PDT::Spin1Half) {
int iferm = (decay[o2]->dataPtr()->iSpin() == PDT::Spin1Half)
? o2 : o3;
unsigned int h1(s1),h2(s2);
if(dit->channelType > iferm) swap(h1, h2);
sign = iferm < dit->channelType ? 1. : -1.;
if(decay[dit->channelType]->id() < 0 && decay[iferm]->id() > 0 ) {
SpinorWaveFunction inters = fer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].first[h1], inVector_[vi]);
diag = -sign*fer_[idiag].second->
evaluate(scale,inters,outspin_[iferm].second[h2], outVector_[v1]);
}
else {
SpinorBarWaveFunction inters = fer_[idiag].first->
evaluate(scale,widthOption(),offshell,
outspin_[dit->channelType].second[h1],inVector_[vi]);
diag = sign*fer_[idiag].second->
evaluate(scale,outspin_[iferm].first [h2],inters, outVector_[v1]);
}
}
// intermediate vector
else if(offshell->iSpin() == PDT::Spin1) {
VectorWaveFunction interv = vec_[idiag].first->
evaluate(scale, widthOption(), offshell, outVector_[v1],
inVector_[vi]);
unsigned int h1(s1),h2(s2);
if(o2 > o3) swap(h1,h2);
if(decay[o2]->id() < 0 && decay[o3]->id() > 0) {
diag =-sign*vec_[idiag].second->
evaluate(scale, outspin_[o2].first[h1],
outspin_[o3].second[h2], interv);
}
else {
diag = sign*vec_[idiag].second->
evaluate(scale, outspin_[o3].first[h2],
outspin_[o2].second[h1], interv);
}
}
else if(offshell->iSpin() == PDT::Spin2) {
TensorWaveFunction intert = ten_[idiag].first->
evaluate(scale, widthOption(), offshell, inVector_[vi],
outVector_[v1]);
unsigned int h1(s1),h2(s2);
if(out2[dit->channelType]>out3[dit->channelType]) swap(h1,h2);
if(decay[out2[dit->channelType]]->id()<0&&
decay[out3[dit->channelType]]->id()>0) {
diag =-sign*ten_[idiag].second->
evaluate(scale,
outspin_[out2[dit->channelType]].first [h1],
outspin_[out3[dit->channelType]].second[h2],intert);
}
else {
diag = sign*ten_[idiag].second->
evaluate(scale,
outspin_[out3[dit->channelType]].first [h2],
outspin_[out2[dit->channelType]].second[h1],intert);
}
}
// unknown
else throw Exception()
<< "Unknown intermediate in VtoFFVDecayer::me2()"
<< Exception::runerror;
// matrix element for the different colour flows
if(ichan < 0) {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
else {
for(unsigned iy = 0; iy < dit->colourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1 != colourFlow()) continue;
flows[dit->colourFlow[iy].first - 1] +=
dit->colourFlow[iy].second * diag;
}
for(unsigned iy = 0; iy < dit->largeNcColourFlow.size(); ++iy) {
if(dit->colourFlow[iy].first - 1!=colourFlow()) continue;
largeflows[dit->largeNcColourFlow[iy].first - 1] +=
dit->largeNcColourFlow[iy].second * diag;
}
}
++idiag;
} //end of diagrams
// now add the flows to the me2 with appropriate colour factors
for(unsigned int ix = 0; ix < ncf; ++ix) {
if (ivec == 0) {
(*mes[ix])(vi, v1, s1, s2) = flows[ix];
(*mel[ix])(vi, v1, s1, s2) = largeflows[ix];
}
else if(ivec == 1) {
(*mes[ix])(vi, s1, v1, s2) = flows[ix];
(*mel[ix])(vi, s1, v1, s2) = largeflows[ix];
}
else if(ivec == 2) {
(*mes[ix])(vi, s1, s2, v1) = flows[ix];
(*mel[ix])(vi, s1, s2, v1) = largeflows[ix];
}
}
}
}
}
}
double me2(0.);
if(ichan < 0) {
vector<double> pflows(ncf,0.);
for(unsigned int ix = 0; ix < ncf; ++ix) {
for(unsigned int iy = 0; iy < ncf; ++ iy) {
double con = cfactors[ix][iy]*(mes[ix]->contract(*mes[iy],rho_)).real();
me2 += con;
if(ix == iy) {
con = nfactors[ix][iy]*(mel[ix]->contract(*mel[iy],rho_)).real();
pflows[ix] += con;
}
}
}
double ptotal(std::accumulate(pflows.begin(),pflows.end(),0.));
ptotal *= UseRandom::rnd();
for(unsigned int ix = 0;ix < pflows.size(); ++ix) {
if(ptotal <= pflows[ix]) {
colourFlow(ix);
ME(mes[ix]);
break;
}
ptotal -= pflows[ix];
}
}
else {
unsigned int iflow = colourFlow();
me2 = nfactors[iflow][iflow]*(mel[iflow]->contract(*mel[iflow],rho_)).real();
}
// return the matrix element squared
return me2;
}
diff --git a/Decay/HwDecayerBase.cc b/Decay/HwDecayerBase.cc
--- a/Decay/HwDecayerBase.cc
+++ b/Decay/HwDecayerBase.cc
@@ -1,156 +1,163 @@
// -*- C++ -*-
//
// HwDecayerBase.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 HwDecayerBase class.
//
#include "HwDecayerBase.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/PDT/DecayMode.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/Repository/CurrentGenerator.h"
#include "Herwig/Shower/RealEmissionProcess.h"
+#include "Herwig/Shower/ShowerHandler.h"
using namespace Herwig;
bool HwDecayerBase::accept(const DecayMode & dm) const {
// get the primary products
tPDVector products=dm.orderedProducts();
// add products for which the decay mode is all ready specified
if(!dm.cascadeProducts().empty()) {
for(ModeMSet::const_iterator mit=dm.cascadeProducts().begin();
mit!=dm.cascadeProducts().end();++mit) {
products.push_back(const_ptr_cast<PDPtr>((**mit).parent()));
}
}
// can this mode be handled ?
return accept(dm.parent(),products);
}
ParticleVector HwDecayerBase::decay(const DecayMode & dm,
const Particle & p) const {
// handling of the decay including the special features of the
// DecayMode
// get the primary products
tPDVector products=dm.orderedProducts();
// add products for which the decay mode is all ready specified
if(!dm.cascadeProducts().empty()) {
for(ModeMSet::const_iterator mit=dm.cascadeProducts().begin();
mit!=dm.cascadeProducts().end();++mit) {
products.push_back(const_ptr_cast<PDPtr>((**mit).parent()));
}
}
// perform the primary decay
ParticleVector output=decay(p,products);
// perform the secondary decays
if(!dm.cascadeProducts().empty()) {
unsigned int iloc=dm.orderedProducts().size();
for(ModeMSet::const_iterator mit=dm.cascadeProducts().begin();
mit!=dm.cascadeProducts().end();++mit) {
if(!(*mit)->decayer())
throw Exception() << "Decay mode " << (**mit).tag()
<< "does not have a decayer, can't perform"
<< "decay in HwDecayerBase::decay()"
<< Exception::eventerror;
ParticleVector children=(*mit)->decayer()->decay(**mit,*output[iloc]);
for(unsigned int ix=0;ix<children.size();++ix) {
output[iloc]->addChild(children[ix]);
}
++iloc;
}
}
return output;
}
void HwDecayerBase::persistentOutput(PersistentOStream & os) const {
os << _initialize << _dbOutput;
}
void HwDecayerBase::persistentInput(PersistentIStream & is, int) {
is >> _initialize >> _dbOutput;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeAbstractClass<HwDecayerBase,Decayer>
describeHerwigHwDecayerBase("Herwig::HwDecayerBase", "Herwig.so");
void HwDecayerBase::Init() {
static ClassDocumentation<HwDecayerBase> documentation
("The HwDecayerBase class is the base class for Decayers in Hw++.");
static Switch<HwDecayerBase,bool> interfaceInitialize
("Initialize",
"Initialization of the phase space calculation",
&HwDecayerBase::_initialize, false, false, false);
static SwitchOption interfaceInitializeon
(interfaceInitialize,
"Yes",
"At initialisation find max weight and optimise the integration",
true);
static SwitchOption interfaceInitializeoff
(interfaceInitialize,
"No",
"Use the maximum weight and channel weights supplied for the integration",
false);
static Switch<HwDecayerBase,bool> interfaceDatabaseOutput
("DatabaseOutput",
"Whether to print the database information",
&HwDecayerBase::_dbOutput, false, false, false);
static SwitchOption interfaceDatabaseOutputYes
(interfaceDatabaseOutput,
"Yes",
"Output information on the decayer initialization",
true);
static SwitchOption interfaceDatabaseOutputNo
(interfaceDatabaseOutput,
"No",
"Do not output information about the decayer initialization",
false);
}
void HwDecayerBase::dofinish() {
Decayer::dofinish();
if(initialize() && databaseOutput()) {
string fname = CurrentGenerator::current().filename() +
string("-") + name() + string(".output");
ofstream output(fname.c_str());
dataBaseOutput(output,true);
}
}
bool HwDecayerBase::softMatrixElementVeto(PPtr , PPtr,
const bool &,
const Energy & ,
const vector<tcPDPtr> & ,
const double & ,
const Energy & ,
const Energy & ) {
return false;
}
RealEmissionProcessPtr HwDecayerBase::generateHardest(RealEmissionProcessPtr) {
return RealEmissionProcessPtr();
}
void HwDecayerBase::initializeMECorrection(RealEmissionProcessPtr , double & ,
double & ) {
assert(false);
}
RealEmissionProcessPtr HwDecayerBase::applyHardMatrixElementCorrection(RealEmissionProcessPtr) {
assert(false);
return RealEmissionProcessPtr();
}
+
+void HwDecayerBase::fixRho(RhoDMatrix & rho) const {
+ if(ShowerHandler::currentHandlerIsSet() &&
+ !ShowerHandler::currentHandler()->spinCorrelations())
+ rho.reset();
+}
diff --git a/Decay/HwDecayerBase.h b/Decay/HwDecayerBase.h
--- a/Decay/HwDecayerBase.h
+++ b/Decay/HwDecayerBase.h
@@ -1,237 +1,244 @@
// -*- C++ -*-
//
// HwDecayerBase.h is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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_HwDecayerBase_H
#define HERWIG_HwDecayerBase_H
//
// This is the declaration of the HwDecayerBase class.
//
#include "ThePEG/PDT/Decayer.h"
#include "Herwig/Shower/RealEmissionProcess.fh"
#include "HwDecayerBase.fh"
namespace Herwig {
struct Branching;
using namespace ThePEG;
/**
* The HwDecayerBase class is the base class for Decayers in Herwig. It inherits
* from the Decayer class of ThePEG and implements additional functionality for the
* output of the results to the particle database and initialization of the datbase.
*
* It also provide the option of specifying a class based on the DecayRadiationGenerator
* which should be used to generate QED radiation in the decay
*
* @see \ref HwDecayerBaseInterfaces "The interfaces"
* defined for HwDecayerBase.
*/
class HwDecayerBase: public Decayer {
public:
/**
* The default constructor.
*/
HwDecayerBase() : _initialize(false), _dbOutput(false) {}
/** @name Virtual functions required by the Decayer class. */
//@{
/**
* Check if this decayer can perfom the decay specified by the
* given decay mode.
* @param dm the DecayMode describing the decay.
* @return true if this decayer can handle the given mode, otherwise false.
*/
virtual bool accept(const DecayMode & dm) const;
/**
* Perform a decay for a given DecayMode and a given Particle instance.
* @param dm the DecayMode describing the decay.
* @param p the Particle instance to be decayed.
* @return a ParticleVector containing the decay products.
*/
virtual ParticleVector decay(const DecayMode & dm, const Particle & p) const;
//@}
public:
/**
* Virtual members to be overridden by inheriting classes
* which implement hard corrections
*/
//@{
/**
* Type of POWHEG correction
*/
enum POWHEGType {No, ISR, FSR, Both};
/**
* Has a POWHEG style correction
*/
virtual POWHEGType hasPOWHEGCorrection() {return No;}
/**
* Has an old fashioned ME correction
*/
virtual bool hasMECorrection() {return false;}
/**
* Initialize the ME correction
*/
virtual void initializeMECorrection(RealEmissionProcessPtr , double & ,
double & );
/**
* Apply the hard matrix element correction to a given hard process or decay
*/
virtual RealEmissionProcessPtr applyHardMatrixElementCorrection(RealEmissionProcessPtr);
/**
* Apply the soft matrix element correction
* @param parent The initial particle in the current branching
* @param progenitor The progenitor particle of the jet
* @param fs Whether the emission is initial or final-state
* @param highestpT The highest pT so far in the shower
* @param ids ids of the particles produced in the branching
* @param z The momentum fraction of the branching
* @param scale the evolution scale of the branching
* @param pT The transverse momentum of the branching
* @return If true the emission should be vetoed
*/
virtual bool softMatrixElementVeto(PPtr parent,
PPtr progenitor,
const bool & fs,
const Energy & highestpT,
const vector<tcPDPtr> & ids,
const double & z,
const Energy & scale,
const Energy & pT);
/**
* Apply the POWHEG style correction
*/
virtual RealEmissionProcessPtr generateHardest(RealEmissionProcessPtr);
//@}
protected:
/** @name Virtual functions to replaced those from the Decayer class.
* This is so that the decay and accept members of this class can handle all
* the more complicated features of the DecayMode class
*/
//@{
/**
* Check if this decayer can perfom the decay for a particular mode
* @param parent The decaying particle
* @param children The decay products
* @return true If this decayer can handle the given mode, otherwise false.
*/
virtual bool accept(tcPDPtr parent, const tPDVector & children) const = 0;
/**
* Perform the decay of the particle to the specified decay products
* @param parent The decaying particle
* @param children The decay products
* @return a ParticleVector containing the decay products.
*/
virtual ParticleVector decay(const Particle & parent,
const tPDVector & children) const = 0;
//@}
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();
public:
/**
* Functions for the Herwig decayer
*/
//@{
/**
* Output the setup information for the particle database
* @param os The stream to output the information to
* @param header Whether or not to output the information for MySQL
*/
virtual void dataBaseOutput(ofstream & os,bool header) const = 0;
/**
* Access to the initialize variable
*/
bool initialize() const {return _initialize;}
/**
* Access the database output variable
*/
bool databaseOutput() const {return _dbOutput;}
//@}
protected:
+ /**
+ * Set rho to be diagonal if no correlations
+ */
+ void fixRho(RhoDMatrix &) const;
+
+protected:
+
/** @name Standard Interfaced functions. */
//@{
/**
* Finalize this object. Called in the run phase just after a
* run has ended. Used eg. to write out statistics.
*/
virtual void dofinish();
//@}
private:
/**
* The assignment operator is private and must never be called.
* In fact, it should not even be implemented.
*/
HwDecayerBase & operator=(const HwDecayerBase &);
private:
/**
* perform initialisation
*/
bool _initialize;
/**
* Print out database
*/
bool _dbOutput;
};
}
#endif /* HERWIG_HwDecayerBase_H */
diff --git a/Decay/Perturbative/SMHiggsFermionsDecayer.cc b/Decay/Perturbative/SMHiggsFermionsDecayer.cc
--- a/Decay/Perturbative/SMHiggsFermionsDecayer.cc
+++ b/Decay/Perturbative/SMHiggsFermionsDecayer.cc
@@ -1,393 +1,395 @@
// -*- C++ -*-
//
// SMHiggsFermionsDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SMHiggsFermionsDecayer class.
//
#include "SMHiggsFermionsDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/ParVector.h"
#include "ThePEG/Interface/Reference.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/PDT/DecayMode.h"
#include "Herwig/Decay/DecayVertex.h"
#include "ThePEG/Helicity/ScalarSpinInfo.h"
#include "ThePEG/Helicity/FermionSpinInfo.h"
#include "ThePEG/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "Herwig/Models/StandardModel/StandardModel.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include "Herwig/Utilities/Maths.h"
#include "Herwig/Shower/RealEmissionProcess.h"
#include "Herwig/Shower/ShowerAlpha.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
SMHiggsFermionsDecayer::SMHiggsFermionsDecayer() :
CF_(4./3.), NLO_(false) {
_maxwgt.resize(9);
_maxwgt[0]=0.;
_maxwgt[1]=0;
_maxwgt[2]=0;
_maxwgt[3]=0.0194397;
_maxwgt[4]=0.463542;
_maxwgt[5]=0.;
_maxwgt[6]=6.7048e-09;
_maxwgt[7]=0.00028665;
_maxwgt[8]=0.0809643;
}
void SMHiggsFermionsDecayer::doinit() {
PerturbativeDecayer::doinit();
// get the vertices from the Standard Model object
tcHwSMPtr hwsm=dynamic_ptr_cast<tcHwSMPtr>(standardModel());
if(!hwsm)
throw InitException() << "SMHiggsFermionsDecayer needs the StandardModel class"
<< " to be either the Herwig one or a class inheriting"
<< " from it";
_hvertex = hwsm->vertexFFH();
// make sure they are initialized
_hvertex->init();
// get the width generator for the higgs
tPDPtr higgs = getParticleData(ParticleID::h0);
// set up the decay modes
vector<double> wgt(0);
unsigned int imode=0;
tPDVector extpart(3);
DecayPhaseSpaceModePtr mode;
int iy;
extpart[0]=higgs;
for(unsigned int istep=0;istep<11;istep+=10) {
for(unsigned ix=1;ix<7;++ix) {
if(istep<10||ix%2!=0) {
iy = ix+istep;
extpart[1]=getParticleData( iy);
extpart[2]=getParticleData(-iy);
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
addMode(mode,_maxwgt[imode],wgt);
++imode;
}
}
}
// Energy quarkMass = getParticleData(ParticleID::b )->mass();
// Energy higgsMass = getParticleData(ParticleID::h0)->mass();
// double mu = quarkMass/higgsMass;
// double beta = sqrt(1.-4.*sqr(mu));
// double beta2 = sqr(beta);
// double aS = SM().alphaS(sqr(higgsMass));
// double L = log((1.+beta)/(1.-beta));
// cerr << "testing " << beta << " " << mu << "\n";
// cerr << "testing " << aS << " " << L << "\n";
// double fact =
// 6.-0.75*(1.+beta2)/beta2+12.*log(mu)-8.*log(beta)
// +(5./beta-2.*beta+0.375*sqr(1.-beta2)/beta2/beta)*L
// +(1.+beta2)/beta*(4.*L*log(0.5*(1.+beta)/beta)
// -2.*log(0.5*(1.+beta))*log(0.5*(1.-beta))
// +8.*Herwig::Math::ReLi2((1.-beta)/(1.+beta))
// -4.*Herwig::Math::ReLi2(0.5*(1.-beta)));
// cerr << "testing correction "
// << 1.+4./3.*aS/Constants::twopi*fact
// << "\n";
// double real = 4./3.*aS/Constants::twopi*
// (8.-0.75*(1.+beta2)/beta2+8.*log(mu)-8.*log(beta)
// +(3./beta+0.375*sqr(1.-beta2)/pow(beta,3))*L
// +(1.+beta2)/beta*(-0.5*sqr(L)+4.*L*log(0.5*(1.+beta))
// -2.*L*log(beta)-2.*log(0.5*(1.+beta))*log(0.5*(1.-beta))
// +6.*Herwig::Math::ReLi2((1.-beta)/(1.+beta))
// -4.*Herwig::Math::ReLi2(0.5*(1.-beta))
// -2./3.*sqr(Constants::pi)));
// double virt = 4./3.*aS/Constants::twopi*
// (-2.+4.*log(mu)+(2./beta-2.*beta)*L
// +(1.+beta2)/beta*(0.5*sqr(L)-2.*L*log(beta)+2.*sqr(Constants::pi)/3.
// +2.*Herwig::Math::ReLi2((1.-beta)/(1.+beta))));
// cerr << "testing real " << real << "\n";
// cerr << "testing virtual " << virt << "\n";
// cerr << "testing total no mb corr " << 1.+real+virt << "\n";
// cerr << "testing total mb corr " << 1.+real+virt +(8./3. - 2.*log(sqr(mu)))*aS/Constants::pi << "\n";
// InvEnergy2 Gf = 1.166371e-5/GeV2;
// Gf = sqrt(2.)*4*Constants::pi*SM().alphaEM(sqr(higgsMass))/8./SM().sin2ThetaW()/
// sqr(getParticleData(ParticleID::Wplus)->mass());
// cerr << "testing GF " << Gf*GeV2 << "\n";
// Energy LO = (3./8./Constants::pi)*sqrt(2)*sqr(quarkMass)*Gf*higgsMass*beta*beta*beta;
// cerr << "testing LO " << LO/GeV << "\n";
// cerr << "testing quark mass " << quarkMass/GeV << "\n";
// cerr << "testing gamma " << (1.+real+virt)*LO/MeV << "\n";
}
bool SMHiggsFermionsDecayer::accept(tcPDPtr parent, const tPDVector & children) const {
if(parent->id()!=ParticleID::h0||children.size()!=2) return false;
tPDVector::const_iterator pit = children.begin();
int id1=(**pit).id();
++pit;
int id2=(**pit).id();
if(id1==-id2&&(abs(id1)<=6||(abs(id1)>=11&&abs(id1)<=16)))
return true;
else
return false;
}
ParticleVector SMHiggsFermionsDecayer::decay(const Particle & parent,
const tPDVector & children) const {
// id's of the decaying particles
tPDVector::const_iterator pit(children.begin());
int id1((**pit).id());
int imode=-1;
if(abs(id1)<=6) imode = abs(id1)-1;
else if(abs(id1)>=11&&abs(id1)<=16) imode = (abs(id1)-11)/2+6;
ParticleVector output(generate(false,false,imode,parent));
// set up the colour flow
if(output[0]->hasColour()) output[0]->antiColourNeighbour(output[1]);
else if(output[1]->hasColour()) output[1]->antiColourNeighbour(output[0]);
return output;
}
void SMHiggsFermionsDecayer::persistentOutput(PersistentOStream & os) const {
os << _maxwgt << _hvertex << NLO_;
}
void SMHiggsFermionsDecayer::persistentInput(PersistentIStream & is, int) {
is >> _maxwgt >> _hvertex >> NLO_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SMHiggsFermionsDecayer,PerturbativeDecayer>
describeHerwigSMHiggsFermionsDecayer("Herwig::SMHiggsFermionsDecayer", "HwPerturbativeHiggsDecay.so");
void SMHiggsFermionsDecayer::Init() {
static ClassDocumentation<SMHiggsFermionsDecayer> documentation
("The SMHiggsFermionsDecayer class implements the decat of the Standard Model"
" Higgs boson to the Standard Model fermions");
static ParVector<SMHiggsFermionsDecayer,double> interfaceMaxWeights
("MaxWeights",
"Maximum weights for the various decays",
&SMHiggsFermionsDecayer::_maxwgt, 9, 1.0, 0.0, 10.0,
false, false, Interface::limited);
static Switch<SMHiggsFermionsDecayer,bool> interfaceNLO
("NLO",
"Whether to return the LO or NLO result",
&SMHiggsFermionsDecayer::NLO_, false, false, false);
static SwitchOption interfaceNLOLO
(interfaceNLO,
"No",
"Leading-order result",
false);
static SwitchOption interfaceNLONLO
(interfaceNLO,
"Yes",
"NLO result",
true);
}
// return the matrix element squared
double SMHiggsFermionsDecayer::me2(const int, const Particle & part,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin1Half,PDT::Spin1Half)));
int iferm(1),ianti(0);
if(decay[0]->id()>0) swap(iferm,ianti);
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(_rho,const_ptr_cast<tPPtr>(&part),incoming);
_swave = ScalarWaveFunction(part.momentum(),part.dataPtr(),incoming);
+ // fix rho if no correlations
+ fixRho(_rho);
}
if(meopt==Terminate) {
ScalarWaveFunction::constructSpinInfo(const_ptr_cast<tPPtr>(&part),
incoming,true);
SpinorBarWaveFunction::
constructSpinInfo(_wavebar,decay[iferm],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(_wave ,decay[ianti],outgoing,true);
return 0.;
}
SpinorBarWaveFunction::
calculateWaveFunctions(_wavebar,decay[iferm],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(_wave ,decay[ianti],outgoing);
Energy2 scale(sqr(part.mass()));
unsigned int ifm,ia;
for(ifm=0;ifm<2;++ifm) {
for(ia=0;ia<2;++ia) {
if(iferm>ianti)
(*ME())(0,ia,ifm)=_hvertex->evaluate(scale,_wave[ia],
_wavebar[ifm],_swave);
else
(*ME())(0,ifm,ia)=_hvertex->evaluate(scale,_wave[ia],
_wavebar[ifm],_swave);
}
}
int id = abs(decay[0]->id());
double output=(ME()->contract(_rho)).real()*UnitRemoval::E2/scale;
if(id <=6) output*=3.;
// test of the partial width
// Ptr<Herwig::StandardModel>::transient_const_pointer
// hwsm=dynamic_ptr_cast<Ptr<Herwig::StandardModel>::transient_const_pointer>(standardModel());
// double g2(hwsm->alphaEM(scale)*4.*Constants::pi/hwsm->sin2ThetaW());
// Energy mass(hwsm->mass(scale,decay[0]->dataPtr())),
// mw(getParticleData(ParticleID::Wplus)->mass());
// double beta(sqrt(1.-4.*decay[0]->mass()*decay[0]->mass()/scale));
// cerr << "testing alpha " << hwsm->alphaEM(scale) << "\n";
// Energy test(g2*mass*mass*beta*beta*beta*part.mass()/32./Constants::pi/mw/mw);
// if(abs(decay[0]->id())<=6){test *=3.;}
// cout << "testing the answer " << output << " "
// << test/GeV
// << endl;
// leading-order result
if(!NLO_) return output;
// fermion mass
Energy particleMass = decay[0]->dataPtr()->mass();
// check decay products coloured, otherwise return
if(!decay[0]->dataPtr()->coloured()||
particleMass==ZERO) return output;
// inital masses, couplings etc
// higgs mass
mHiggs_ = part.mass();
// strong coupling
aS_ = SM().alphaS(sqr(mHiggs_));
// reduced mass
mu_ = particleMass/mHiggs_;
mu2_ = sqr(mu_);
// generate y
double yminus = 0.;
double yplus = 1.-2.*mu_*(1.-mu_)/(1.-2*mu2_);
double y = yminus + UseRandom::rnd()*(yplus-yminus);
//generate z for D31,2
double v = sqrt(sqr(2.*mu2_+(1.-2.*mu2_)*(1.-y))-4.*mu2_)/(1.-2.*mu2_)/(1.-y);
double zplus = (1.+v)*(1.-2.*mu2_)*y/2./(mu2_ +(1.-2.*mu2_)*y);
double zminus = (1.-v)*(1.-2.*mu2_)*y/2./(mu2_ +(1.-2.*mu2_)*y);
double z = zminus + UseRandom::rnd()*(zplus-zminus);
// map y,z to x1,x2 for both possible emissions
double x2 = 1. - y*(1.-2.*mu2_);
double x1 = 1. - z*(x2-2.*mu2_);
//get the dipoles
InvEnergy2 D1 = dipoleSubtractionTerm( x1, x2);
InvEnergy2 D2 = dipoleSubtractionTerm( x2, x1);
InvEnergy2 dipoleSum = abs(D1) + abs(D2);
//jacobian
double jac = (1.-y)*(yplus-yminus)*(zplus-zminus);
//calculate real
Energy2 realPrefactor = 0.25*sqr(mHiggs_)*sqr(1.-2.*mu2_)
/sqrt(calculateLambda(1,mu2_,mu2_))/sqr(Constants::twopi);
InvEnergy2 realEmission = 4.*Constants::pi*aS_*CF_*calculateRealEmission( x1, x2);
// calculate the virtual
double virtualTerm = calculateVirtualTerm();
// running mass correction
virtualTerm += (8./3. - 2.*log(mu2_))*aS_/Constants::pi;
//answer = (born + virtual + real)/born * LO
output *= 1. + virtualTerm + 2.*jac*realPrefactor*(realEmission*abs(D1)/dipoleSum - D1);
// return the answer
return output;
}
void SMHiggsFermionsDecayer::dataBaseOutput(ofstream & os,bool header) const {
if(header) os << "update decayers set parameters=\"";
// parameters for the PerturbativeDecayer base class
for(unsigned int ix=0;ix<_maxwgt.size();++ix) {
os << "newdef " << name() << ":MaxWeights " << ix << " "
<< _maxwgt[ix] << "\n";
}
PerturbativeDecayer::dataBaseOutput(os,false);
if(header) os << "\n\" where BINARY ThePEGName=\""
<< fullName() << "\";" << endl;
}
void SMHiggsFermionsDecayer::doinitrun() {
PerturbativeDecayer::doinitrun();
if(initialize()) {
for(unsigned int ix=0;ix<numberModes();++ix) {
_maxwgt[ix] = mode(ix)->maxWeight();
}
}
}
//calculate lambda
double SMHiggsFermionsDecayer::calculateLambda(double x, double y, double z) const{
return sqr(x)+sqr(y)+sqr(z)-2.*x*y-2.*x*z-2.*y*z;
}
//calculates the dipole subtraction term for x1, D31,2 (Dij,k),
// 2 is the spectator anti-fermion and 3 is the gluon
InvEnergy2 SMHiggsFermionsDecayer::
dipoleSubtractionTerm(double x1, double x2) const{
InvEnergy2 commonPrefactor = CF_*8.*Constants::pi*aS_/sqr(mHiggs_);
return commonPrefactor/(1.-x2)*
(2.*(1.-2.*mu2_)/(2.-x1-x2)-
sqrt((1.-4.*mu2_)/(sqr(x2)-4.*mu2_))*
(x2-2.*mu2_)*(2.+(x1-1.)/(x2-2.*mu2_)+2.*mu2_/(1.-x2))/(1.-2.*mu2_));
}
//return ME for real emission
InvEnergy2 SMHiggsFermionsDecayer::
calculateRealEmission(double x1, double x2) const {
InvEnergy2 prefactor = 2./sqr(mHiggs_)/(1.-4.*mu2_);
return prefactor*(2. + (1.-x1)/(1.-x2) + (1.-x2)/(1.-x1)
+ 2.*(1.-2.*mu2_)*(1.-4.*mu2_)/(1.-x1)/(1.-x2)
- 2.*(1.-4.*mu2_)*(1./(1.-x2)+1./(1.-x1))
- 2.*mu2_*(1.-4.*mu2_)*(1./sqr(1.-x2)+1./sqr(1.-x1)));
}
double SMHiggsFermionsDecayer::
calculateVirtualTerm() const {
// logs and prefactors
double beta = sqrt(1.-4.*mu2_);
double L = log((1.+beta)/(1.-beta));
double prefactor = CF_*aS_/Constants::twopi;
// non-singlet piece
double nonSingletTerm = calculateNonSingletTerm(beta, L);
double virtualTerm =
-2.+4.*log(mu_)+(2./beta - 2.*beta)*L
+ (2.-4.*mu2_)/beta*(0.5*sqr(L) - 2.*L*log(beta)
+ 2.*Herwig::Math::ReLi2((1.-beta)/(1.+beta))
+ 2.*sqr(Constants::pi)/3.);
double iEpsilonTerm =
2.*(3.-sqr(Constants::pi)/2. + 0.5*log(mu2_) - 1.5*log(1.-2.*mu2_)
-(1.-2.*mu2_)/beta*(0.5*sqr(L)+sqr(Constants::pi)/6.
-2.*L*log(1.-2.*mu2_))
+ nonSingletTerm);
return prefactor*(virtualTerm+iEpsilonTerm);
}
//non-singlet piece of I(epsilon) insertion operator
double SMHiggsFermionsDecayer::
calculateNonSingletTerm(double beta, double L) const {
return 1.5*log(1.-2.*mu2_)
+ (1.-2.*mu2_)/beta*(- 2.*L*log(4.*(1.-2.*mu2_)/sqr(1.+beta))+
+ 2.*Herwig::Math::ReLi2(sqr((1.-beta)/(1.+beta)))
- 2.*Herwig::Math::ReLi2(2.*beta/(1.+beta))
- sqr(Constants::pi)/6.)
+ log(1.-mu_)
- 2.*log(1.-2.*mu_)
- 2.*mu2_/(1.-2.*mu2_)*log(mu_/(1.-mu_))
- mu_/(1.-mu_)
+ 2.*mu_*(2*mu_-1.)/(1.-2.*mu2_)
+ 0.5*sqr(Constants::pi);
}
double SMHiggsFermionsDecayer::matrixElementRatio(const Particle & inpart, const ParticleVector & decay2,
const ParticleVector & decay3, MEOption,
ShowerInteraction inter) {
mHiggs_ = inpart.mass();
mu_ = decay2[0]->mass()/mHiggs_;
mu2_ = sqr(mu_);
double x1 = 2.*decay3[0]->momentum().t()/mHiggs_;
double x2 = 2.*decay3[1]->momentum().t()/mHiggs_;
double pre = inter==ShowerInteraction::QCD ? CF_ : sqr(double(decay2[0]->dataPtr()->iCharge())/3.);
return pre*calculateRealEmission(x1,x2)*4.*Constants::pi*sqr(mHiggs_);
}
diff --git a/Decay/Perturbative/SMHiggsGGHiggsPPDecayer.cc b/Decay/Perturbative/SMHiggsGGHiggsPPDecayer.cc
--- a/Decay/Perturbative/SMHiggsGGHiggsPPDecayer.cc
+++ b/Decay/Perturbative/SMHiggsGGHiggsPPDecayer.cc
@@ -1,424 +1,426 @@
// -*- C++ -*-
//
// SMHiggsGGHiggsPPDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SMHiggsGGHiggsPPDecayer class.
//
#include "SMHiggsGGHiggsPPDecayer.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/Reference.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/Helicity/WaveFunction/ScalarWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h"
#include "ThePEG/PDT/DecayMode.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include "Herwig/Utilities/HiggsLoopFunctions.h"
using namespace Herwig;
using namespace Herwig::HiggsLoopFunctions;
using namespace ThePEG::Helicity;
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SMHiggsGGHiggsPPDecayer,PerturbativeDecayer>
describeHerwigSMHiggsGGHiggsPPDecayer("Herwig::SMHiggsGGHiggsPPDecayer",
"HwPerturbativeHiggsDecay.so");
bool SMHiggsGGHiggsPPDecayer::accept(tcPDPtr parent,
const tPDVector & children) const {
int idp = parent->id();
int id0 = children[0]->id();
int id1 = children[1]->id();
if((idp == ParticleID::h0 &&
id0 == ParticleID::g && id1 == ParticleID::g) ||
(idp == ParticleID::h0 &&
id0 == ParticleID::gamma && id1 == ParticleID::gamma)||
(idp == ParticleID::h0 &&
id0 == ParticleID::Z0 && id1 == ParticleID::gamma)||
(idp == ParticleID::h0 &&
id0 == ParticleID::gamma && id1 == ParticleID::Z0))
return true;
else
return false;
}
ParticleVector SMHiggsGGHiggsPPDecayer::decay(const Particle & parent,
const tPDVector & children) const {
int imode(2);
if(children[0]->id() == ParticleID::gamma &&
children[1]->id() == ParticleID::gamma)
imode = 1;
else if(children[0]->id() ==ParticleID::g)
imode = 0;
ParticleVector out(generate(true,false,imode,parent));
//colour flow
if(children[0]->id() == ParticleID::g &&
children[1]->id() == ParticleID::g) {
out[0]->colourNeighbour(out[1]);
out[0]->antiColourNeighbour(out[1]);
}
return out;
}
void SMHiggsGGHiggsPPDecayer::persistentOutput(PersistentOStream & os) const {
os << _hggvertex << _hppvertex << _hzpvertex << _h0wgt
<< _minloop << _maxloop << _massopt;
}
void SMHiggsGGHiggsPPDecayer::persistentInput(PersistentIStream & is, int) {
is >> _hggvertex >> _hppvertex >> _hzpvertex >> _h0wgt
>> _minloop >> _maxloop >> _massopt;
}
void SMHiggsGGHiggsPPDecayer::Init() {
static ClassDocumentation<SMHiggsGGHiggsPPDecayer> documentation
("This is an implentation of h0->gg or h0->gamma,gamma "
"decayer using the SMHGGVertex.");
static Reference<SMHiggsGGHiggsPPDecayer,AbstractVVSVertex>
interfaceSMHGGVertex
("SMHGGVertex",
"Pointer to SMHGGVertex",
&SMHiggsGGHiggsPPDecayer::_hggvertex, false, false, true,
false, false);
static Reference<SMHiggsGGHiggsPPDecayer,AbstractVVSVertex>
interfaceSMHPPVertex
("SMHPPVertex",
"Pointer to SMHPPVertex",
&SMHiggsGGHiggsPPDecayer::_hppvertex, false, false, true,
false, false);
static Reference<SMHiggsGGHiggsPPDecayer,AbstractVVSVertex>
interfaceSMHZPVertex
("SMHZPVertex",
"Pointer to SMHZPVertex",
&SMHiggsGGHiggsPPDecayer::_hzpvertex, false, false, true,
false, false);
static ParVector<SMHiggsGGHiggsPPDecayer,double> interfaceMaxWeights
("MaxWeights",
"Maximum weights for the various decays",
&SMHiggsGGHiggsPPDecayer::_h0wgt, 3, 1.0, 0.0, 10.0,
false, false, Interface::limited);
static Parameter<SMHiggsGGHiggsPPDecayer,int> interfaceMinimumInLoop
("MinimumInLoop",
"The minimum flavour of the quarks to include in the loops",
&SMHiggsGGHiggsPPDecayer::_minloop, 6, 4, 6,
false, false, Interface::limited);
static Parameter<SMHiggsGGHiggsPPDecayer,int> interfaceMaximumInLoop
("MaximumInLoop",
"The maximum flavour of the quarks to include in the loops",
&SMHiggsGGHiggsPPDecayer::_maxloop, 6, 4, 6,
false, false, Interface::limited);
static Switch<SMHiggsGGHiggsPPDecayer,unsigned int> interfaceMassOption
("MassOption",
"Option for the treatment of the masses in the loop diagrams",
&SMHiggsGGHiggsPPDecayer::_massopt, 0, false, false);
static SwitchOption interfaceMassOptionFull
(interfaceMassOption,
"Full",
"Include the full mass dependence",
0);
static SwitchOption interfaceMassOptionLarge
(interfaceMassOption,
"Large",
"Use the heavy mass limit",
1);
}
double SMHiggsGGHiggsPPDecayer::me2(const int,
const Particle & part,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin1,PDT::Spin1)));
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(_rho,const_ptr_cast<tPPtr>(&part),incoming);
_swave = ScalarWaveFunction(part.momentum(),part.dataPtr(),incoming);
+ // fix rho if no correlations
+ fixRho(_rho);
}
if(meopt==Terminate) {
ScalarWaveFunction::constructSpinInfo(const_ptr_cast<tPPtr>(&part),
incoming,true);
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::constructSpinInfo(_vwave[ix],decay[ix],
outgoing,true,
decay[ix]->id()!=ParticleID::Z0);
return 0.;
}
for(unsigned int ix=0;ix<2;++ix)
VectorWaveFunction::
calculateWaveFunctions(_vwave[ix],decay[ix],outgoing,decay[ix]->id()!=ParticleID::Z0);
//Set up decay matrix
Energy2 scale(sqr(part.mass()));
unsigned int v1hel,v2hel;
AbstractVVSVertexPtr vertex;
unsigned int vstep1(2),vstep2(2);
double sym(1.);
if(decay[0]->id() == ParticleID::g &&
decay[1]->id() == ParticleID::g) {
vertex = _hggvertex;
sym = 2.;
}
else if(decay[0]->id() == ParticleID::gamma &&
decay[1]->id() == ParticleID::gamma) {
vertex = _hppvertex;
sym = 2.;
}
else if(decay[0]->id() == ParticleID::Z0 &&
decay[1]->id() == ParticleID::gamma) {
vertex = _hzpvertex;
vstep1 = 1;
}
else if(decay[1]->id() == ParticleID::Z0 &&
decay[0]->id() == ParticleID::gamma) {
vertex = _hzpvertex;
vstep2 = 1;
}
else
assert(false);
// loop over the helicities of the outgoing bosons
for(v1hel = 0;v1hel < 3;v1hel+=vstep1) {
for(v2hel = 0;v2hel < 3;v2hel+=vstep2) {
(*ME())(0,v1hel,v2hel) = vertex->evaluate(scale,_vwave[0][v1hel],
_vwave[1][v2hel],_swave);
}
}
//store matrix element
double output = ME()->contract(_rho).real()*UnitRemoval::E2/scale;
//colour factor (N^2 - 1)/4
if(decay[0]->id() == ParticleID::g) output *= 8.;
//symmetric final states
output /= sym;
// return the answer
return output;
}
void SMHiggsGGHiggsPPDecayer::doinit() {
PerturbativeDecayer::doinit();
// get the width generator for the higgs
tPDPtr higgs = getParticleData(ParticleID::h0);
if(_hggvertex) {
_hggvertex->init();
}
else {
throw InitException() << "SMHiggsGGHiggsPPDecayer::doinit() - "
<< "_hggvertex is null";
}
if(_hppvertex) {
_hppvertex->init();
}
else {
throw InitException() << "SMHiggsGGHiggsPPDecayer::doinit() - "
<< "_hppvertex is null";
}
if(_hzpvertex) {
_hzpvertex->init();
}
else {
throw InitException() << "SMHiggsGGHiggsZPDecayer::doinit() - "
<< "_hzpvertex is null";
}
//set up decay modes
DecayPhaseSpaceModePtr mode;
tPDVector extpart(3);
vector<double> wgt(0);
// glu,glu mode
extpart[0] = getParticleData(ParticleID::h0);
extpart[1] = getParticleData(ParticleID::g);
extpart[2] = getParticleData(ParticleID::g);
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
addMode(mode,_h0wgt[0],wgt);
// gamma,gamma mode
extpart[1] = getParticleData(ParticleID::gamma);
extpart[2] = getParticleData(ParticleID::gamma);
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
addMode(mode,_h0wgt[1],wgt);
// Z0,gamma mode
extpart[1] = getParticleData(ParticleID::Z0);
extpart[2] = getParticleData(ParticleID::gamma);
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
addMode(mode,_h0wgt[2],wgt);
}
void SMHiggsGGHiggsPPDecayer::doinitrun() {
_hggvertex->initrun();
_hppvertex->initrun();
_hzpvertex->initrun();
PerturbativeDecayer::doinitrun();
if(initialize()) {
for(unsigned int ix=0;ix<numberModes();++ix) {
_h0wgt[ix] = mode(ix)->maxWeight();
}
}
}
void SMHiggsGGHiggsPPDecayer::dataBaseOutput(ofstream & os,bool header) const {
if(header) os << "update decayers set parameters=\"";
// parameters for the PerturbativeDecayer base class
for(unsigned int ix=0;ix<_h0wgt.size();++ix) {
os << "newdef " << name() << ":MaxWeights " << ix << " "
<< _h0wgt[ix] << "\n";
}
os << "newdef " << name() << ":SMHGGVertex " << _hggvertex->fullName() << "\n";
os << "newdef " << name() << ":SMHPPVertex " << _hppvertex->fullName() << "\n";
os << "newdef " << name() << ":SMHZPVertex " << _hzpvertex->fullName() << "\n";
PerturbativeDecayer::dataBaseOutput(os,false);
if(header) os << "\n\" where BINARY ThePEGName=\""
<< fullName() << "\";" << endl;
}
double SMHiggsGGHiggsPPDecayer::matrixElementRatio(const Particle & inpart, const ParticleVector & decay2,
const ParticleVector & decay3, MEOption,
ShowerInteraction inter) {
assert(inter==ShowerInteraction::QCD);
// extract partons and LO momentas
vector<cPDPtr> partons(1,inpart.dataPtr());
vector<Lorentz5Momentum> lomom(1,inpart.momentum());
for(unsigned int ix=0;ix<2;++ix) {
partons.push_back(decay2[ix]->dataPtr());
lomom.push_back(decay2[ix]->momentum());
}
vector<Lorentz5Momentum> realmom(1,inpart.momentum());
for(unsigned int ix=0;ix<3;++ix) {
if(ix==2) partons.push_back(decay3[ix]->dataPtr());
realmom.push_back(decay3[ix]->momentum());
}
Energy2 scale = sqr(inpart.mass());
Energy2 lome = loME(inpart.mass());
double reme = realME(partons,realmom);
double ratio = reme/lome*scale;
// // analytic value for mt -> infinity
// double x1 = 2.*decay3[0]->momentum().t()/inpart.mass();
// double x2 = 2.*decay3[1]->momentum().t()/inpart.mass();
// double x3 = 2.*decay3[2]->momentum().t()/inpart.mass();
// double test = 8.*Constants::pi*3.*(1.+pow(1-x1,4)+pow(1-x2,4)+pow(1-x3,4))
// /(1.-x1)/(1.-x2)/(1.-x3);
// generator()->log() << "TESTING RATIO " << test << " " << ratio << " " << ratio/test << "\n";
// remember the symmetry factor
return ratio/3.;
}
double SMHiggsGGHiggsPPDecayer::realME(//const vector<cPDPtr> & partons,
const vector<cPDPtr> &,
const vector<Lorentz5Momentum> & momenta) const {
// using std::norm;
// ScalarWaveFunction hout(momenta[0],partons[0],outgoing);
// LorentzPolarizationVector g[3][2];
// // calculate the polarization vectors for the gluons
// for(unsigned int iw=0;iw<3;++iw) {
// VectorWaveFunction gwave(momenta[iw+1],partons[iw+1],outgoing);
// for(unsigned int ix=0;ix<2;++ix) {
// //if(iw==2) gwave.reset(10);
// //else
// gwave.reset(2*ix);
// g[iw][ix] = gwave.wave();
// }
// }
Energy2 mh2 = momenta[0].mass2();
Energy2 s = (momenta[1]+momenta[2]).m2();
Energy2 t = (momenta[1]+momenta[3]).m2();
Energy2 u = (momenta[2]+momenta[3]).m2();
// calculate the loop functions
Complex A4stu(0.),A2stu(0.),A2tsu(0.),A2ust(0.);
for(int ix=_minloop;ix<=_maxloop;++ix) {
// loop functions
if(_massopt==0) {
Energy2 mf2=sqr(getParticleData(ix)->mass());
A4stu+=A4(s,t,u,mf2);
A2stu+=A2(s,t,u,mf2);
A2tsu+=A2(t,s,u,mf2);
A2ust+=A2(u,s,t,mf2);
}
else {
A4stu=-1./3.;
A2stu=-sqr(s/mh2)/3.;
A2tsu=-sqr(t/mh2)/3.;
A2ust=-sqr(u/mh2)/3.;
}
}
// Complex A3stu=0.5*(A2stu+A2ust+A2tsu-A4stu);
// // compute the dot products for the matrix element
// // and polarization vector * momenta
// Energy2 pdot[3][3];
// complex<InvEnergy> eps[3][3][2];
// for(unsigned int ig=0;ig<3;++ig) {
// for(unsigned int ip=0;ip<3;++ip) {
// pdot[ig][ip]=momenta[ig+1]*momenta[ip+1];
// for(unsigned int ih=0;ih<2;++ih) {
// if(ig!=ip)
// eps[ig][ip][ih]=g[ig][ih].dot(momenta[ip+1])/pdot[ig][ip];
// else
// eps[ig][ip][ih]=ZERO;
// }
// }
// }
// prefactors
Energy mw(getParticleData(ParticleID::Wplus)->mass());
// Energy3 pre=sqr(mh2)/mw;
// // compute the matrix element
// double output(0.);
// complex<InvEnergy2> wdot[3][3];
// for(unsigned int ghel1=0;ghel1<2;++ghel1) {
// for(unsigned int ghel2=0;ghel2<2;++ghel2) {
// for(unsigned int ghel3=0;ghel3<2;++ghel3) {
// wdot[0][1]=g[0][ghel1].dot(g[1][ghel2])/pdot[0][1];
// wdot[0][2]=g[0][ghel1].dot(g[2][ghel3])/pdot[0][2];
// wdot[1][0]=wdot[0][1];
// wdot[1][2]=g[1][ghel2].dot(g[2][ghel3])/pdot[1][2];
// wdot[2][0]=wdot[0][2];
// wdot[2][1]=wdot[1][2];
// // last piece
// Complex diag=pre*A3stu*(eps[0][2][ghel1]*eps[1][0][ghel2]*eps[2][1][ghel3]-
// eps[0][1][ghel1]*eps[1][2][ghel2]*eps[2][0][ghel3]+
// (eps[2][0][ghel3]-eps[2][1][ghel3])*wdot[0][1]+
// (eps[1][2][ghel2]-eps[1][0][ghel2])*wdot[0][2]+
// (eps[0][1][ghel1]-eps[0][2][ghel1])*wdot[1][2]);
// // first piece
// diag+=pre*(+A2stu*(eps[0][1][ghel1]*eps[1][0][ghel2]-wdot[0][1])*
// (eps[2][0][ghel3]-eps[2][1][ghel3])
// +A2tsu*(eps[0][2][ghel1]*eps[2][0][ghel3]-wdot[0][2])*
// (eps[1][2][ghel2]-eps[1][0][ghel2])
// +A2ust*(eps[1][2][ghel2]*eps[2][1][ghel3]-wdot[1][2])*
// (eps[0][1][ghel1]-eps[0][2][ghel1]));
// output+=norm(diag);
// }
// }
// }
// // colour factor and what's left of the prefactor
// output *= 6.;
double me=4.*24./s/t/u*pow<4,1>(mh2)/sqr(mw)*
(norm(A2stu)+norm(A2ust)+norm(A2tsu)+norm(A4stu));
return me;
}
Energy2 SMHiggsGGHiggsPPDecayer::loME(Energy mh) const {
Complex loop(0.);
Energy2 mh2(sqr(mh));
Energy mw(getParticleData(ParticleID::Wplus)->mass());
for(int ix=_minloop;ix<=_maxloop;++ix) {
// loop functions
if(_massopt==0) {
Energy2 mf2=sqr(getParticleData(ix)->mass());
loop += A1(mh2,mf2);
}
else {
loop += 2./3.;
}
}
return 1./Constants::pi*sqr(mh2)/sqr(mw)*norm(loop);
}
diff --git a/Decay/Perturbative/SMHiggsWWDecayer.cc b/Decay/Perturbative/SMHiggsWWDecayer.cc
--- a/Decay/Perturbative/SMHiggsWWDecayer.cc
+++ b/Decay/Perturbative/SMHiggsWWDecayer.cc
@@ -1,325 +1,327 @@
// -*- C++ -*-
//
// SMHiggsWWDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SMHiggsWWDecayer class.
//
#include "SMHiggsWWDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/Parameter.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "Herwig/Models/StandardModel/StandardModel.h"
#include "ThePEG/PDT/EnumParticles.h"
#include "ThePEG/PDT/DecayMode.h"
#include "ThePEG/PDT/ParticleData.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
typedef Selector<tDMPtr> DecaySelector;
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SMHiggsWWDecayer,PerturbativeDecayer>
describeHerwigSMHiggsWWDecayer("Herwig::SMHiggsWWDecayer", "HwPerturbativeHiggsDecay.so");
void SMHiggsWWDecayer::Init() {
static ClassDocumentation<SMHiggsWWDecayer> documentation
("The SMHiggsWWDecayer class performs the decay of the Standard Model Higgs"
" boson to W+W- and Z0Z0");
static ParVector<SMHiggsWWDecayer,double> interfaceWMaximum
("WMaximum",
"The maximum weight for H-> W+W- decays",
&SMHiggsWWDecayer::_wmax, 2, 1.0, 0.0, 10000.0,
false, false, Interface::limited);
static ParVector<SMHiggsWWDecayer,double> interfaceZMaximum
("ZMaximum",
"The maximum weight for H-> Z0Z0 decays",
&SMHiggsWWDecayer::_zmax, 2, 1.0, 0.0, 10000.0,
false, false, Interface::limited);
}
SMHiggsWWDecayer::SMHiggsWWDecayer() : _wmax(2,1.00), _zmax(2,1.00)
{}
void SMHiggsWWDecayer::doinit() {
PerturbativeDecayer::doinit();
// get the vertices from the Standard Model object
tcHwSMPtr hwsm=dynamic_ptr_cast<tcHwSMPtr>(standardModel());
if(!hwsm)
throw InitException() << "SMHiggsWWDecayer needs the StandardModel class"
<< " to be either the Herwig one or a class inheriting"
<< " from it";
_theFFWVertex = hwsm->vertexFFW();
_theFFZVertex = hwsm->vertexFFZ();
_theHVVVertex = hwsm->vertexWWH();
// get the width generator for the higgs
tPDPtr higgs = getParticleData(ParticleID::h0);
// the W+W- decays
for(unsigned int ix=0;ix<2;++ix) {
tPDPtr h0 = getParticleData(ParticleID::h0);
tPDPtr wplus = getParticleData(ParticleID::Wplus);
tPDPtr wminus = getParticleData(ParticleID::Wminus);
DecaySelector wpDecay = wplus->decaySelector();
DecaySelector wmDecay = wminus->decaySelector();
tPDVector extpart(5);
extpart[0]=h0;
DecayPhaseSpaceModePtr mode;
DecayPhaseSpaceChannelPtr newchannel;
vector<double> wgt(1,1.);
unsigned int imode=0;
for(DecaySelector::const_iterator wp=wpDecay.begin();wp!=wpDecay.end();++wp) {
// extract the decay products of W+
tPDVector prod=(*wp).second->orderedProducts();
if(prod[0]->id()<prod[1]->id()) swap(prod[0],prod[1]);
extpart[1]=prod[0];
extpart[2]=prod[1];
for(DecaySelector::const_iterator wm=wmDecay.begin();wm!=wmDecay.end();++wm) {
// extract the decay products of W-
tPDVector prod=(*wm).second->orderedProducts();
if(prod[0]->id()<prod[1]->id()) swap(prod[0],prod[1]);
extpart[3]=prod[0];
extpart[4]=prod[1];
// create the new mode
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
// create the phase space channel
newchannel=new_ptr(DecayPhaseSpaceChannel(mode));
newchannel->addIntermediate(extpart[0],0,0.0,-1,-2);
if(ix==0) {
newchannel->addIntermediate(wplus ,1,0., 1, 2);
newchannel->addIntermediate(wminus ,1,0., 3, 4);
}
else {
newchannel->addIntermediate(wplus ,0,0., 1, 2);
newchannel->addIntermediate(wminus ,0,0., 3, 4);
}
mode->addChannel(newchannel);
addMode(mode,_wmax[ix],wgt);
// insert mode into selector
_ratio.push_back(wp->second->brat()*wm->second->brat());
if(ix==0) _wdecays.insert (_ratio.back(),imode);
++imode;
}
}
// the Z0Z0 decays
tPDPtr Z0=getParticleData(ParticleID::Z0);
DecaySelector Z0Decay = Z0->decaySelector();
for(DecaySelector::const_iterator z1=Z0Decay.begin();z1!=Z0Decay.end();++z1) {
// extract the decay products of Z0
tPDVector prod=(*z1).second->orderedProducts();
if(prod[0]->id()<prod[1]->id()) swap(prod[0],prod[1]);
extpart[1]=prod[0];
extpart[2]=prod[1];
for(DecaySelector::const_iterator z2=Z0Decay.begin();z2!=Z0Decay.end();++z2) {
// extract the decay products of Z0
tPDVector prod=(*z2).second->orderedProducts();
if(prod[0]->id()<prod[1]->id()) swap(prod[0],prod[1]);
extpart[3]=prod[0];
extpart[4]=prod[1];
// create the new mode
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
// create the phase space channel
newchannel=new_ptr(DecayPhaseSpaceChannel(mode));
newchannel->addIntermediate(extpart[0],0,0.0,-1,-2);
if(ix==0) {
newchannel->addIntermediate(Z0 ,1,0., 1, 2);
newchannel->addIntermediate(Z0 ,1,0., 3, 4);
}
else {
newchannel->addIntermediate(Z0 ,0,0., 1, 2);
newchannel->addIntermediate(Z0 ,0,0., 3, 4);
}
mode->addChannel(newchannel);
addMode(mode,_zmax[ix],wgt);
// insert mode into selector
_ratio.push_back(z1->second->brat()*z2->second->brat());
if(ix==0) _zdecays.insert (_ratio.back(),imode);
++imode;
}
}
}
}
bool SMHiggsWWDecayer::accept(tcPDPtr parent, const tPDVector & children) const {
// if not two decay products return false
if(children.size()!=2) return false;
// if not decaying higgs return false
if(parent->id()!=ParticleID::h0) return false;
tPDVector::const_iterator pit = children.begin();
int id1=(**pit).id();
++pit;
int id2=(**pit).id();
if((id1==-id2&&abs(id1)==ParticleID::Wplus)||
(id1== id2&& id1 ==ParticleID::Z0))
return true;
else
return false;
}
void SMHiggsWWDecayer::persistentOutput(PersistentOStream & os) const {
os << _theFFWVertex << _theFFZVertex << _theHVVVertex
<< _wdecays << _zdecays << _ratio << _wmax << _zmax;
}
void SMHiggsWWDecayer::persistentInput(PersistentIStream & is, int) {
is >> _theFFWVertex >> _theFFZVertex >> _theHVVVertex
>> _wdecays >> _zdecays >> _ratio >> _wmax >> _zmax;
}
ParticleVector SMHiggsWWDecayer::decay(const Particle & parent,
const tPDVector & children) const {
// select the decay modes of the gauge bosons
unsigned int imode;
if(abs(children[0]->id())==ParticleID::Wplus)
imode=_wdecays.select(UseRandom::rnd());
else
imode=_zdecays.select(UseRandom::rnd());
// use different phase space for low/high mass higgs
if(parent.mass()>1.8*children[0]->mass())
imode+=_wdecays.size()+_zdecays.size();
// generate the kinematics
return generate(true,false,imode,parent);
}
double SMHiggsWWDecayer::me2(const int, const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin1Half,PDT::Spin1Half,
PDT::Spin1Half,PDT::Spin1Half)));
// check if Z or W decay
bool Z0=decay[0]->id()==-decay[1]->id();
if(meopt==Initialize) {
ScalarWaveFunction::
calculateWaveFunctions(_rho,const_ptr_cast<tPPtr>(&inpart),incoming);
_swave = ScalarWaveFunction(inpart.momentum(),inpart.dataPtr(),incoming);
+ // fix rho if no correlations
+ fixRho(_rho);
}
if(meopt==Terminate) {
ScalarWaveFunction::constructSpinInfo(const_ptr_cast<tPPtr>(&inpart),
incoming,true);
SpinorBarWaveFunction::
constructSpinInfo(_fwave1,decay[0],outgoing,true);
SpinorWaveFunction ::
constructSpinInfo(_awave1,decay[1],outgoing,true);
SpinorBarWaveFunction::
constructSpinInfo(_fwave2,decay[2],outgoing,true);
SpinorWaveFunction ::
constructSpinInfo(_awave2,decay[3],outgoing,true);
return 0.;
}
SpinorBarWaveFunction::
calculateWaveFunctions(_fwave1,decay[0],outgoing);
SpinorWaveFunction ::
calculateWaveFunctions(_awave1,decay[1],outgoing);
SpinorBarWaveFunction::
calculateWaveFunctions(_fwave2,decay[2],outgoing);
SpinorWaveFunction ::
calculateWaveFunctions(_awave2,decay[3],outgoing);
// get the intermediates and vertex
tcPDPtr inter[2];
AbstractFFVVertexPtr vert;
if(Z0) {
inter[0]=getParticleData(ParticleID::Z0);
inter[1]=inter[0];
vert=_theFFZVertex;
}
else {
inter[0]=getParticleData(ParticleID::Wplus);
inter[1]=getParticleData(ParticleID::Wminus);
vert=_theFFWVertex;
}
// construct the spinors for the outgoing particles
Energy2 scale0(sqr(inpart.mass()));
Energy2 scale1((decay[0]->momentum()+decay[1]->momentum()).m2());
Energy2 scale2((decay[2]->momentum()+decay[3]->momentum()).m2());
// for decays to quarks ensure boson is massive enough to
// put quarks on constituent mass-shell
if(scale1<sqr(decay[0]->dataPtr()->constituentMass()+
decay[1]->dataPtr()->constituentMass())) return 0.;
if(scale2<sqr(decay[2]->dataPtr()->constituentMass()+
decay[3]->dataPtr()->constituentMass())) return 0.;
// compute the boson currents
VectorWaveFunction curr1[2][2],curr2[2][2];
unsigned int ohel1,ohel2,ohel3,ohel4;
for(ohel1=0;ohel1<2;++ohel1) {
for(ohel2=0;ohel2<2;++ohel2) {
curr1[ohel1][ohel2]=vert->evaluate(scale1,1,inter[0],
_awave1[ohel2],_fwave1[ohel1]);
curr2[ohel1][ohel2]=vert->evaluate(scale2,1,inter[1],
_awave2[ohel2],_fwave2[ohel1]);
}
}
// compute the matrix element
for(ohel1=0;ohel1<2;++ohel1) {
for(ohel2=0;ohel2<2;++ohel2) {
for(ohel3=0;ohel3<2;++ohel3) {
for(ohel4=0;ohel4<2;++ohel4) {
(*ME())(0,ohel1,ohel2,ohel3,ohel4)=
_theHVVVertex->evaluate(scale0,curr1[ohel1][ohel2],
curr2[ohel3][ohel4],_swave);
}
}
}
}
double output=(ME()->contract(_rho)).real()*scale0*UnitRemoval::InvE2;
// set up the colour flows
if(decay[0]->coloured()) {
output*=3.;
decay[0]->antiColourNeighbour(decay[1]);
}
if(decay[2]->coloured()) {
output*=3.;
decay[2]->antiColourNeighbour(decay[3]);
}
// divide out the gauge boson branching ratios
output/=_ratio[imode()];
// if Z0 decays identical particle factor
if(Z0) output*=0.5;
// return the answer
return output;
}
void SMHiggsWWDecayer::dataBaseOutput(ofstream & os,bool header) const {
if(header) os << "update decayers set parameters=\"";
for(unsigned int ix=0;ix<2;++ix) {
os << "newdef " << name() << ":WMaximum " << ix << " " << _wmax[ix] << "\n";
os << "newdef " << name() << ":ZMaximum " << ix << " " << _zmax[ix] << "\n";
}
PerturbativeDecayer::dataBaseOutput(os,false);
if(header) os << "\n\" where BINARY ThePEGName=\"" << fullName() << "\";" << endl;
}
void SMHiggsWWDecayer::doinitrun() {
PerturbativeDecayer::doinitrun();
for(unsigned int ix=0;ix<2;++ix) {
_zmax[ix]=0.;
_wmax[ix]=0.;
}
unsigned int ntest=_wdecays.size()+_zdecays.size();
if(initialize()) {
for(unsigned int ix=0;ix<numberModes();++ix) {
unsigned int iloc = ix<ntest ? 0 : 1;
if(mode(ix)->externalParticles(1)->iCharge()==
-mode(ix)->externalParticles(2)->iCharge()) {
_zmax[iloc]=max(mode(ix)->maxWeight(),_zmax[iloc]);
}
else {
_wmax[iloc]=max(mode(ix)->maxWeight(),_wmax[iloc]);
}
}
}
}
diff --git a/Decay/Perturbative/SMTopDecayer.cc b/Decay/Perturbative/SMTopDecayer.cc
--- a/Decay/Perturbative/SMTopDecayer.cc
+++ b/Decay/Perturbative/SMTopDecayer.cc
@@ -1,764 +1,766 @@
// -*- C++ -*-
//
// SMTopDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SMTopDecayer class.
//
#include "SMTopDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/ParVector.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Interface/Reference.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/PDT/DecayMode.h"
#include "Herwig/Decay/DecayVertex.h"
#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h"
#include "Herwig/PDT/ThreeBodyAllOn1IntegralCalculator.h"
#include "Herwig/Shower/RealEmissionProcess.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
SMTopDecayer::SMTopDecayer()
: _wquarkwgt(6,0.),_wleptonwgt(3,0.), _xg_sampling(1.5),
_initialenhance(1.), _finalenhance(2.3) {
_wleptonwgt[0] = 0.302583;
_wleptonwgt[1] = 0.301024;
_wleptonwgt[2] = 0.299548;
_wquarkwgt[0] = 0.851719;
_wquarkwgt[1] = 0.0450162;
_wquarkwgt[2] = 0.0456962;
_wquarkwgt[3] = 0.859839;
_wquarkwgt[4] = 3.9704e-06;
_wquarkwgt[5] = 0.000489657;
generateIntermediates(true);
}
bool SMTopDecayer::accept(tcPDPtr parent, const tPDVector & children) const {
if(abs(parent->id()) != ParticleID::t) return false;
int id0(0),id1(0),id2(0);
for(tPDVector::const_iterator it = children.begin();
it != children.end();++it) {
int id=(**it).id(),absid(abs(id));
if(absid==ParticleID::b&&double(id)/double(parent->id())>0) {
id0=id;
}
else {
switch (absid) {
case ParticleID::nu_e:
case ParticleID::nu_mu:
case ParticleID::nu_tau:
id1 = id;
break;
case ParticleID::eminus:
case ParticleID::muminus:
case ParticleID::tauminus:
id2 = id;
break;
case ParticleID::b:
case ParticleID::d:
case ParticleID::s:
id1 = id;
break;
case ParticleID::u:
case ParticleID::c:
id2=id;
break;
default :
break;
}
}
}
if(id0==0||id1==0||id2==0) return false;
if(double(id1)/double(id2)>0) return false;
return true;
}
ParticleVector SMTopDecayer::decay(const Particle & parent,
const tPDVector & children) const {
int id1(0),id2(0);
for(tPDVector::const_iterator it = children.begin();
it != children.end();++it) {
int id=(**it).id(),absid=abs(id);
if(absid == ParticleID::b && double(id)/double(parent.id())>0) continue;
//leptons
if(absid > 10 && absid%2==0) id1=absid;
if(absid > 10 && absid%2==1) id2=absid;
//quarks
if(absid < 10 && absid%2==0) id2=absid;
if(absid < 10 && absid%2==1) id1=absid;
}
unsigned int imode(0);
if(id2 >=11 && id2<=16) imode = (id1-12)/2;
else imode = id1+1+id2/2;
bool cc = parent.id() == ParticleID::tbar;
ParticleVector out(generate(true,cc,imode,parent));
//arrange colour flow
PPtr pparent=const_ptr_cast<PPtr>(&parent);
out[1]->incomingColour(pparent,out[1]->id()<0);
ParticleVector products = out[0]->children();
if(products[0]->hasColour())
products[0]->colourNeighbour(products[1],true);
else if(products[0]->hasAntiColour())
products[0]->colourNeighbour(products[1],false);
return out;
}
void SMTopDecayer::persistentOutput(PersistentOStream & os) const {
os << FFWVertex_ << FFGVertex_ << FFPVertex_ << WWWVertex_
<< _wquarkwgt << _wleptonwgt << _wplus
<< _initialenhance << _finalenhance << _xg_sampling;
}
void SMTopDecayer::persistentInput(PersistentIStream & is, int) {
is >> FFWVertex_ >> FFGVertex_ >> FFPVertex_ >> WWWVertex_
>> _wquarkwgt >> _wleptonwgt >> _wplus
>> _initialenhance >> _finalenhance >> _xg_sampling;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SMTopDecayer,PerturbativeDecayer>
describeHerwigSMTopDecayer("Herwig::SMTopDecayer", "HwPerturbativeDecay.so");
void SMTopDecayer::Init() {
static ClassDocumentation<SMTopDecayer> documentation
("This is the implementation of the SMTopDecayer which "
"decays top quarks into bottom quarks and either leptons "
"or quark-antiquark pairs including the matrix element for top decay",
"The matrix element correction for top decay \\cite{Hamilton:2006ms}.",
"%\\cite{Hamilton:2006ms}\n"
"\\bibitem{Hamilton:2006ms}\n"
" K.~Hamilton and P.~Richardson,\n"
" ``A simulation of QCD radiation in top quark decays,''\n"
" JHEP {\\bf 0702}, 069 (2007)\n"
" [arXiv:hep-ph/0612236].\n"
" %%CITATION = JHEPA,0702,069;%%\n");
static ParVector<SMTopDecayer,double> interfaceQuarkWeights
("QuarkWeights",
"Maximum weights for the hadronic decays",
&SMTopDecayer::_wquarkwgt, 6, 1.0, 0.0, 10.0,
false, false, Interface::limited);
static ParVector<SMTopDecayer,double> interfaceLeptonWeights
("LeptonWeights",
"Maximum weights for the semi-leptonic decays",
&SMTopDecayer::_wleptonwgt, 3, 1.0, 0.0, 10.0,
false, false, Interface::limited);
static Parameter<SMTopDecayer,double> interfaceEnhancementFactor
("InitialEnhancementFactor",
"The enhancement factor for initial-state radiation in the shower to ensure"
" the weight for the matrix element correction is less than one.",
&SMTopDecayer::_initialenhance, 1.0, 1.0, 10000.0,
false, false, Interface::limited);
static Parameter<SMTopDecayer,double> interfaceFinalEnhancementFactor
("FinalEnhancementFactor",
"The enhancement factor for final-state radiation in the shower to ensure"
" the weight for the matrix element correction is less than one",
&SMTopDecayer::_finalenhance, 1.6, 1.0, 1000.0,
false, false, Interface::limited);
static Parameter<SMTopDecayer,double> interfaceSamplingTopHardMEC
("SamplingTopHardMEC",
"The importance sampling power for choosing an initial xg, "
"to sample xg according to xg^-_xg_sampling",
&SMTopDecayer::_xg_sampling, 1.5, 1.2, 2.0,
false, false, Interface::limited);
}
double SMTopDecayer::me2(const int, const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,
PDT::Spin1Half,PDT::Spin1Half)));
// spinors etc for the decaying particle
if(meopt==Initialize) {
// spinors and rho
if(inpart.id()>0)
SpinorWaveFunction ::calculateWaveFunctions(_inHalf,_rho,
const_ptr_cast<tPPtr>(&inpart),
incoming);
else
SpinorBarWaveFunction::calculateWaveFunctions(_inHalfBar,_rho,
const_ptr_cast<tPPtr>(&inpart),
incoming);
+ // fix rho if no correlations
+ fixRho(_rho);
}
// setup spin info when needed
if(meopt==Terminate) {
// for the decaying particle
if(inpart.id()>0) {
SpinorWaveFunction::
constructSpinInfo(_inHalf,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorBarWaveFunction::constructSpinInfo(_inHalfBar,decay[0],outgoing,true);
SpinorWaveFunction ::constructSpinInfo(_outHalf ,decay[1],outgoing,true);
SpinorBarWaveFunction::constructSpinInfo(_outHalfBar,decay[2],outgoing,true);
}
else {
SpinorBarWaveFunction::
constructSpinInfo(_inHalfBar,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorWaveFunction::constructSpinInfo(_inHalf,decay[0],outgoing,true);
SpinorBarWaveFunction::constructSpinInfo(_outHalfBar,decay[1],outgoing,true);
SpinorWaveFunction ::constructSpinInfo(_outHalf ,decay[2],outgoing,true);
}
}
if ( ( decay[1]->momentum() + decay[2]->momentum() ).m()
< decay[1]->data().constituentMass() + decay[2]->data().constituentMass() )
return 0.0;
// spinors for the decay product
if(inpart.id()>0) {
SpinorBarWaveFunction::calculateWaveFunctions(_inHalfBar ,decay[0],outgoing);
SpinorWaveFunction ::calculateWaveFunctions(_outHalf ,decay[1],outgoing);
SpinorBarWaveFunction::calculateWaveFunctions(_outHalfBar,decay[2],outgoing);
}
else {
SpinorWaveFunction ::calculateWaveFunctions(_inHalf ,decay[0],outgoing);
SpinorBarWaveFunction::calculateWaveFunctions(_outHalfBar,decay[1],outgoing);
SpinorWaveFunction ::calculateWaveFunctions(_outHalf ,decay[2],outgoing);
}
Energy2 scale(sqr(inpart.mass()));
if(inpart.id() == ParticleID::t) {
//Define intermediate vector wave-function for Wplus
tcPDPtr Wplus(getParticleData(ParticleID::Wplus));
VectorWaveFunction inter;
unsigned int thel,bhel,fhel,afhel;
for(thel = 0;thel<2;++thel){
for(bhel = 0;bhel<2;++bhel){
inter = FFWVertex_->evaluate(scale,1,Wplus,_inHalf[thel],
_inHalfBar[bhel]);
for(afhel=0;afhel<2;++afhel){
for(fhel=0;fhel<2;++fhel){
(*ME())(thel,bhel,afhel,fhel) =
FFWVertex_->evaluate(scale,_outHalf[afhel],
_outHalfBar[fhel],inter);
}
}
}
}
}
else if(inpart.id() == ParticleID::tbar) {
VectorWaveFunction inter;
tcPDPtr Wminus(getParticleData(ParticleID::Wminus));
unsigned int tbhel,bbhel,afhel,fhel;
for(tbhel = 0;tbhel<2;++tbhel){
for(bbhel = 0;bbhel<2;++bbhel){
inter = FFWVertex_->
evaluate(scale,1,Wminus,_inHalf[bbhel],_inHalfBar[tbhel]);
for(afhel=0;afhel<2;++afhel){
for(fhel=0;fhel<2;++fhel){
(*ME())(tbhel,bbhel,fhel,afhel) =
FFWVertex_->evaluate(scale,_outHalf[afhel],
_outHalfBar[fhel],inter);
}
}
}
}
}
double output = (ME()->contract(_rho)).real();
if(abs(decay[1]->id())<=6) output *=3.;
return output;
}
void SMTopDecayer::doinit() {
PerturbativeDecayer::doinit();
//get vertices from SM object
tcHwSMPtr hwsm = dynamic_ptr_cast<tcHwSMPtr>(standardModel());
if(!hwsm) throw InitException() << "Must have Herwig::StandardModel in "
<< "SMTopDecayer::doinit()";
FFWVertex_ = hwsm->vertexFFW();
FFGVertex_ = hwsm->vertexFFG();
FFPVertex_ = hwsm->vertexFFP();
WWWVertex_ = hwsm->vertexWWW();
//initialise
FFWVertex_->init();
FFGVertex_->init();
FFPVertex_->init();
WWWVertex_->init();
//set up decay modes
_wplus = getParticleData(ParticleID::Wplus);
DecayPhaseSpaceModePtr mode;
DecayPhaseSpaceChannelPtr Wchannel;
tPDVector extpart(4);
vector<double> wgt(1,1.0);
extpart[0] = getParticleData(ParticleID::t);
extpart[1] = getParticleData(ParticleID::b);
//lepton modes
for(int i=11; i<17;i+=2) {
extpart[2] = getParticleData(-i);
extpart[3] = getParticleData(i+1);
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
Wchannel = new_ptr(DecayPhaseSpaceChannel(mode));
Wchannel->addIntermediate(extpart[0],0,0.0,-1,1);
Wchannel->addIntermediate(_wplus,0,0.0,2,3);
Wchannel->init();
mode->addChannel(Wchannel);
addMode(mode,_wleptonwgt[(i-11)/2],wgt);
}
//quark modes
unsigned int iz=0;
for(int ix=1;ix<6;ix+=2) {
for(int iy=2;iy<6;iy+=2) {
// check that the combination of particles is allowed
if(FFWVertex_->allowed(-ix,iy,ParticleID::Wminus)) {
extpart[2] = getParticleData(-ix);
extpart[3] = getParticleData( iy);
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
Wchannel = new_ptr(DecayPhaseSpaceChannel(mode));
Wchannel->addIntermediate(extpart[0],0,0.0,-1,1);
Wchannel->addIntermediate(_wplus,0,0.0,2,3);
Wchannel->init();
mode->addChannel(Wchannel);
addMode(mode,_wquarkwgt[iz],wgt);
++iz;
}
else {
throw InitException() << "SMTopDecayer::doinit() the W vertex"
<< "cannot handle all the quark modes"
<< Exception::abortnow;
}
}
}
}
void SMTopDecayer::dataBaseOutput(ofstream & os,bool header) const {
if(header) os << "update decayers set parameters=\"";
// parameters for the PerturbativeDecayer base class
for(unsigned int ix=0;ix<_wquarkwgt.size();++ix) {
os << "newdef " << name() << ":QuarkWeights " << ix << " "
<< _wquarkwgt[ix] << "\n";
}
for(unsigned int ix=0;ix<_wleptonwgt.size();++ix) {
os << "newdef " << name() << ":LeptonWeights " << ix << " "
<< _wleptonwgt[ix] << "\n";
}
PerturbativeDecayer::dataBaseOutput(os,false);
if(header) os << "\n\" where BINARY ThePEGName=\"" << fullName() << "\";" << endl;
}
void SMTopDecayer::doinitrun() {
PerturbativeDecayer::doinitrun();
if(initialize()) {
for(unsigned int ix=0;ix<numberModes();++ix) {
if(ix<3) _wleptonwgt[ix ] = mode(ix)->maxWeight();
else _wquarkwgt [ix-3] = mode(ix)->maxWeight();
}
}
}
WidthCalculatorBasePtr SMTopDecayer::threeBodyMEIntegrator(const DecayMode & dm) const {
// identify W decay products
int sign = dm.parent()->id() > 0 ? 1 : -1;
int iferm(0),ianti(0);
for(ParticleMSet::const_iterator pit=dm.products().begin();
pit!=dm.products().end();++pit) {
int id = (**pit).id();
if(id*sign != ParticleID::b) {
if (id*sign > 0 ) iferm = id*sign;
else ianti = id*sign;
}
}
assert(iferm!=0&&ianti!=0);
// work out which mode we are doing
int imode(-1);
for(unsigned int ix=0;ix<numberModes();++ix) {
if(mode(ix)->externalParticles(2)->id() == ianti &&
mode(ix)->externalParticles(3)->id() == iferm ) {
imode = ix;
break;
}
}
assert(imode>=0);
// get the masses we need
Energy m[3] = {mode(imode)->externalParticles(1)->mass(),
mode(imode)->externalParticles(3)->mass(),
mode(imode)->externalParticles(2)->mass()};
return
new_ptr(ThreeBodyAllOn1IntegralCalculator<SMTopDecayer>
(3,_wplus->mass(),_wplus->width(),0.0,*this,imode,m[0],m[1],m[2]));
}
InvEnergy SMTopDecayer::threeBodydGammads(const int imode, const Energy2 mt2,
const Energy2 mffb2, const Energy mb,
const Energy mf, const Energy mfb) const {
Energy mffb(sqrt(mffb2));
Energy mw(_wplus->mass());
Energy2 mw2(sqr(mw)),gw2(sqr(_wplus->width()));
Energy mt(sqrt(mt2));
Energy Eb = 0.5*(mt2-mffb2-sqr(mb))/mffb;
Energy Ef = 0.5*(mffb2-sqr(mfb)+sqr(mf))/mffb;
Energy Ebm = sqrt(sqr(Eb)-sqr(mb));
Energy Efm = sqrt(sqr(Ef)-sqr(mf));
Energy2 upp = sqr(Eb+Ef)-sqr(Ebm-Efm);
Energy2 low = sqr(Eb+Ef)-sqr(Ebm+Efm);
InvEnergy width=(dGammaIntegrand(mffb2,upp,mt,mb,mf,mfb,mw)-
dGammaIntegrand(mffb2,low,mt,mb,mf,mfb,mw))
/32./mt2/mt/8/pow(Constants::pi,3)/(sqr(mffb2-mw2)+mw2*gw2);
// couplings
width *= 0.25*sqr(4.*Constants::pi*generator()->standardModel()->alphaEM(mt2)/
generator()->standardModel()->sin2ThetaW());
width *= generator()->standardModel()->CKM(*mode(imode)->externalParticles(0),
*mode(imode)->externalParticles(1));
if(abs(mode(imode)->externalParticles(2)->id())<=6) {
width *=3.;
if(abs(mode(imode)->externalParticles(2)->id())%2==0)
width *=generator()->standardModel()->CKM(*mode(imode)->externalParticles(2),
*mode(imode)->externalParticles(3));
else
width *=generator()->standardModel()->CKM(*mode(imode)->externalParticles(3),
*mode(imode)->externalParticles(2));
}
// final spin average
assert(!std::isnan(double(width*MeV)));
return 0.5*width;
}
Energy6 SMTopDecayer::dGammaIntegrand(Energy2 mffb2, Energy2 mbf2, Energy mt,
Energy mb, Energy mf, Energy mfb, Energy mw) const {
Energy2 mt2(sqr(mt)) ,mb2(sqr(mb)) ,mf2(sqr(mf )),mfb2(sqr(mfb )),mw2(sqr(mw ));
Energy4 mt4(sqr(mt2)),mb4(sqr(mb2)),mf4(sqr(mf2)),mfb4(sqr(mfb2)),mw4(sqr(mw2));
return -mbf2 * ( + 6 * mb2 * mf2 * mfb2 * mffb2 + 6 * mb2 * mt2 * mfb2 * mffb2
+ 6 * mb2 * mt2 * mf2 * mffb2 + 12 * mb2 * mt2 * mf2 * mfb2
- 3 * mb2 * mfb4 * mffb2 + 3 * mb2 * mf2 * mffb2 * mffb2
- 3 * mb2 * mf4 * mffb2 - 6 * mb2 * mt2 * mfb4
- 6 * mb2 * mt2 * mf4 - 3 * mb4 * mfb2 * mffb2
- 3 * mb4 * mf2 * mffb2 - 6 * mb4 * mf2 * mfb2
+ 3 * mt4 * mf4 + 3 * mb4 * mfb4
+ 3 * mb4 * mf4 + 3 * mt4 * mfb4
+ 3 * mb2 * mfb2 * mffb2 * mffb2 + 3 * mt2 * mfb2 * mffb2 * mffb2
- 3 * mt2 * mfb4 * mffb2 + 3 * mt2 * mf2 * mffb2 * mffb2
- 3 * mt2 * mf4 * mffb2 - 3 * mt4 * mfb2 * mffb2
- 3 * mt4 * mf2 * mffb2 - 6 * mt4 * mf2 * mfb2
+ 6 * mt2 * mf2 * mfb2 * mffb2 + 12 * mt2 * mf2 * mw4
+ 12 * mb2 * mfb2 * mw4 + 12 * mb2 * mt2 * mw4
+ 6 * mw2 * mt2 * mfb2 * mbf2 - 12 * mw2 * mt2 * mf2 * mffb2
- 6 * mw2 * mt2 * mf2 * mbf2 - 12 * mw2 * mt2 * mf2 * mfb2
- 12 * mw2 * mb2 * mfb2 * mffb2 - 6 * mw2 * mb2 * mfb2 * mbf2
+ 6 * mw2 * mb2 * mf2 * mbf2 - 12 * mw2 * mb2 * mf2 * mfb2
- 12 * mw2 * mb2 * mt2 * mfb2 - 12 * mw2 * mb2 * mt2 * mf2
+ 12 * mf2 * mfb2 * mw4 + 4 * mbf2 * mbf2 * mw4
- 6 * mfb2 * mbf2 * mw4 - 6 * mf2 * mbf2 * mw4
- 6 * mt2 * mbf2 * mw4 - 6 * mb2 * mbf2 * mw4
+ 12 * mw2 * mt2 * mf4 + 12 * mw2 * mt4 * mf2
+ 12 * mw2 * mb2 * mfb4 + 12 * mw2 * mb4 * mfb2) /mw4 / 3.;
}
void SMTopDecayer::initializeMECorrection(RealEmissionProcessPtr born, double & initial,
double & final) {
// check the outgoing particles
PPtr part[2];
for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) {
part[ix]= born->bornOutgoing()[ix];
}
// check the final-state particles and get the masses
if(abs(part[0]->id())==ParticleID::Wplus&&abs(part[1]->id())==ParticleID::b) {
_ma=part[0]->mass();
_mc=part[1]->mass();
}
else if(abs(part[1]->id())==ParticleID::Wplus&&abs(part[0]->id())==ParticleID::b) {
_ma=part[1]->mass();
_mc=part[0]->mass();
}
else {
return;
}
// set the top mass
_mt=born->bornIncoming()[0]->mass();
// set the gluon mass
_mg=getParticleData(ParticleID::g)->constituentMass();
// set the radiation enhancement factors
initial = _initialenhance;
final = _finalenhance;
// reduced mass parameters
_a=sqr(_ma/_mt);
_g=sqr(_mg/_mt);
_c=sqr(_mc/_mt);
double lambda = sqrt(1.+sqr(_a)+sqr(_c)-2.*_a-2.*_c-2.*_a*_c);
_ktb = 0.5*(3.-_a+_c+lambda);
_ktc = 0.5*(1.-_a+3.*_c+lambda);
useMe();
}
bool SMTopDecayer::softMatrixElementVeto(PPtr parent,
PPtr progenitor,
const bool & ,
const Energy & highestpT,
const vector<tcPDPtr> &,
const double & z,
const Energy & scale,
const Energy & pt) {
// check if we need to apply the full correction
// the initial-state correction
if(abs(progenitor->id())==ParticleID::t&&abs(parent->id())==ParticleID::t) {
// check if hardest so far
// if not just need to remove effect of enhancement
bool veto(false);
// if not hardest so far
if(pt<highestpT)
veto=!UseRandom::rndbool(1./_initialenhance);
// if hardest so far do calculation
else {
// values of kappa and z
double kappa(sqr(scale/_mt));
// parameters for the translation
double w(1.-(1.-z)*(kappa-1.)),u(1.+_a-_c-(1.-z)*kappa),v(sqr(u)-4.*_a*w*z);
// veto if outside phase space
if(v<0.)
veto=true;
// otherwise calculate the weight
else {
v = sqrt(v);
double xa((0.5*(u+v)/w+0.5*(u-v)/z)),xg((1.-z)*kappa);
double f(me(xa,xg)),
J(0.5*(u+v)/sqr(w)-0.5*(u-v)/sqr(z)+_a*sqr(w-z)/(v*w*z));
double wgt(f*J*2./kappa/(1.+sqr(z)-2.*z/kappa)/_initialenhance);
// This next `if' prevents the hardest emission from the
// top shower ever entering the so-called T2 region of the
// phase space if that region is to be populated by the hard MEC.
if(useMEforT2()&&xg>xgbcut(_ktb)) wgt = 0.;
if(wgt>1.) {
generator()->log() << "Violation of maximum for initial-state "
<< " soft veto in "
<< "SMTopDecayer::softMatrixElementVeto"
<< "xg = " << xg << " xa = " << xa
<< "weight = " << wgt << "\n";
wgt=1.;
}
// compute veto from weight
veto = !UseRandom::rndbool(wgt);
}
}
// return the veto
return veto;
}
// final-state correction
else if(abs(progenitor->id())==ParticleID::b&&abs(parent->id())==ParticleID::b) {
// check if hardest so far
// if not just need to remove effect of enhancement
// if not hardest so far
if(pt<highestpT) return !UseRandom::rndbool(1./_finalenhance);
// if hardest so far do calculation
// values of kappa and z
double kappa(sqr(scale/_mt));
// momentum fractions
double xa(1.+_a-_c-z*(1.-z)*kappa),r(0.5*(1.+_c/(1.+_a-xa))),root(sqr(xa)-4.*_a);
if(root<0.) {
generator()->log() << "Imaginary root for final-state veto in "
<< "SMTopDecayer::softMatrixElementVeto"
<< "\nz = " << z << "\nkappa = " << kappa
<< "\nxa = " << xa
<< "\nroot^2= " << root;
return true;
}
root=sqrt(root);
double xg((2.-xa)*(1.-r)-(z-r)*root);
// xfact (below) is supposed to equal xg/(1-z).
double xfact(z*kappa/2./(z*(1.-z)*kappa+_c)*(2.-xa-root)+root);
// calculate the full result
double f(me(xa,xg));
// jacobian
double J(z*root);
double wgt(f*J*2.*kappa/(1.+sqr(z)-2.*_c/kappa/z)/sqr(xfact)/_finalenhance);
if(wgt>1.) {
generator()->log() << "Violation of maximum for final-state soft veto in "
<< "SMTopDecayer::softMatrixElementVeto"
<< "xg = " << xg << " xa = " << xa
<< "weight = " << wgt << "\n";
wgt=1.;
}
// compute veto from weight and return
return !UseRandom::rndbool(wgt);
}
// otherwise don't veto
else return !UseRandom::rndbool(1./_finalenhance);
}
double SMTopDecayer::me(double xw,double xg) {
double prop(1.+_a-_c-xw),xg2(sqr(xg));
double lambda=sqrt(1.+_a*_a+_c*_c-2.*_a-2.*_c-2.*_a*_c);
double denom=(1.-2*_a*_a+_a+_c*_a+_c*_c-2.*_c);
double wgt=-_c*xg2/prop+(1.-_a+_c)*xg-(prop*(1 - xg)+xg2)
+(0.5*(1.+2.*_a+_c)*sqr(prop-xg)*xg+2.*_a*prop*xg2)/denom;
return wgt/(lambda*prop);
}
// xgbcut is the point along the xg axis where the upper bound on the
// top quark (i.e. b) emission phase space goes back on itself in the
// xa vs xg plane i.e. roughly mid-way along the xg axis in
// the xa vs xg Dalitz plot.
double SMTopDecayer::xgbcut(double kt) {
double lambda2 = 1.+_a*_a+_c*_c-2.*_a-2.*_c-2.*_a*_c;
double num1 = kt*kt*(1.-_a-_c);
double num2 = 2.*kt*sqrt(_a*(kt*kt*_c+lambda2*(kt-1.)));
return (num1-num2)/(kt*kt-4.*_a*(kt-1.));
}
double SMTopDecayer::loME(const Particle & inpart, const ParticleVector & decay) {
// spinors
vector<SpinorWaveFunction > swave;
vector<SpinorBarWaveFunction> awave;
vector<VectorWaveFunction> vwave;
tPPtr Wboson = abs(decay[0]->id())==ParticleID::Wplus ? decay[0] : decay[1];
tPPtr bquark = abs(decay[0]->id())==ParticleID::Wplus ? decay[1] : decay[0];
// spinors
if(inpart.id()>0) {
SpinorWaveFunction ::calculateWaveFunctions(swave,const_ptr_cast<tPPtr>(&inpart),
incoming);
SpinorBarWaveFunction::calculateWaveFunctions(awave,bquark,outgoing);
}
else {
SpinorBarWaveFunction::calculateWaveFunctions(awave,const_ptr_cast<tPPtr>(&inpart),
incoming);
SpinorWaveFunction ::calculateWaveFunctions(swave,bquark,outgoing);
}
// polarization vectors
VectorWaveFunction::calculateWaveFunctions(vwave,Wboson,outgoing,false);
Energy2 scale(sqr(inpart.mass()));
double me=0.;
if(inpart.id() == ParticleID::t) {
for(unsigned int thel = 0; thel < 2; ++thel) {
for(unsigned int bhel = 0; bhel < 2; ++bhel) {
for(unsigned int whel = 0; whel < 3; ++whel) {
Complex diag = FFWVertex_->evaluate(scale,swave[thel],awave[bhel],vwave[whel]);
me += norm(diag);
}
}
}
}
else if(inpart.id() == ParticleID::tbar) {
for(unsigned int thel = 0; thel < 2; ++thel) {
for(unsigned int bhel = 0; bhel < 2; ++bhel){
for(unsigned int whel = 0; whel < 3; ++whel) {
Complex diag = FFWVertex_->evaluate(scale,swave[bhel],awave[thel],vwave[whel]);
me += norm(diag);
}
}
}
}
return me;
}
double SMTopDecayer::realME(const Particle & inpart, const ParticleVector & decay,
ShowerInteraction inter) {
// vertex for emission from fermions
AbstractFFVVertexPtr vertex = inter==ShowerInteraction::QCD ? FFGVertex_ : FFPVertex_;
// spinors
vector<SpinorWaveFunction > swave;
vector<SpinorBarWaveFunction> awave;
vector<VectorWaveFunction> vwave,gwave;
tPPtr Wboson = abs(decay[0]->id())==ParticleID::Wplus ? decay[0] : decay[1];
tPPtr bquark = abs(decay[0]->id())==ParticleID::Wplus ? decay[1] : decay[0];
// spinors
if(inpart.id()>0) {
SpinorWaveFunction ::calculateWaveFunctions(swave,const_ptr_cast<tPPtr>(&inpart),
incoming);
SpinorBarWaveFunction::calculateWaveFunctions(awave,bquark,outgoing);
}
else {
SpinorBarWaveFunction::calculateWaveFunctions(awave,const_ptr_cast<tPPtr>(&inpart),
incoming);
SpinorWaveFunction ::calculateWaveFunctions(swave,bquark,outgoing);
}
// polarization vectors
VectorWaveFunction::calculateWaveFunctions(vwave,Wboson,outgoing,false);
VectorWaveFunction::calculateWaveFunctions(gwave,decay[2],outgoing,true );
Energy2 scale(sqr(inpart.mass()));
double me=0.;
vector<Complex> diag(3,0.);
if(inpart.id() == ParticleID::t) {
for(unsigned int thel = 0; thel < 2; ++thel) {
for(unsigned int bhel = 0; bhel < 2; ++bhel) {
for(unsigned int whel = 0; whel < 3; ++whel) {
for(unsigned int ghel =0; ghel <3; ghel+=2) {
// emission from top
SpinorWaveFunction interF = vertex->evaluate(scale,3,inpart.dataPtr(),swave[thel],gwave[ghel]);
diag[0] = FFWVertex_->evaluate(scale,interF,awave[bhel],vwave[whel]);
// emission from bottom
SpinorBarWaveFunction interB = vertex->evaluate(scale,3,bquark->dataPtr()->CC(),awave[bhel],gwave[ghel]);
diag[1] = FFWVertex_->evaluate(scale,swave[thel],interB,vwave[whel]);
// emission from W
if(inter==ShowerInteraction::QED) {
VectorWaveFunction interV = WWWVertex_->evaluate(scale,3,Wboson->dataPtr()->CC(),vwave[whel],gwave[ghel]);
diag[1] = FFWVertex_->evaluate(scale,swave[thel],awave[bhel],interV);
}
Complex sum = std::accumulate(diag.begin(),diag.end(),Complex(0.));
me += norm(sum);
}
}
}
}
}
else if(inpart.id() == ParticleID::tbar) {
for(unsigned int thel = 0; thel < 2; ++thel) {
for(unsigned int bhel = 0; bhel < 2; ++bhel){
for(unsigned int whel = 0; whel < 3; ++whel) {
for(unsigned int ghel =0; ghel <3; ghel+=2) {
// emission from top
SpinorBarWaveFunction interB = vertex->evaluate(scale,3,inpart.dataPtr(),awave[thel],gwave[ghel]);
diag[1] = FFWVertex_->evaluate(scale,swave[bhel],interB,vwave[whel]);
// emission from bottom
SpinorWaveFunction interF = vertex->evaluate(scale,3,bquark->dataPtr()->CC(),swave[bhel],gwave[ghel]);
diag[0] = FFWVertex_->evaluate(scale,interF,awave[thel],vwave[whel]);
// emission from W
if(inter==ShowerInteraction::QED) {
VectorWaveFunction interV = WWWVertex_->evaluate(scale,3,Wboson->dataPtr()->CC(),vwave[whel],gwave[ghel]);
diag[1] = FFWVertex_->evaluate(scale,swave[bhel],awave[thel],interV);
}
Complex sum = std::accumulate(diag.begin(),diag.end(),Complex(0.));
me += norm(sum);
}
}
}
}
}
// divide out the coupling
me /= norm(vertex->norm());
// return the total
return me;
}
double SMTopDecayer::matrixElementRatio(const Particle & inpart,
const ParticleVector & decay2,
const ParticleVector & decay3,
MEOption ,
ShowerInteraction inter) {
double Nc = standardModel()->Nc();
double Cf = (sqr(Nc) - 1.) / (2.*Nc);
// if(inter==ShowerInteraction::QED) return 0.;
// double f = (1. + sqr(e2()) - 2.*sqr(s2()) + s2() + s2()*e2() - 2.*e2());
//
//
// double B = f/s2();
// Energy2 PbPg = decay3[0]->momentum()*decay3[2]->momentum();
// Energy2 PtPg = inpart.momentum()*decay3[2]->momentum();
// Energy2 PtPb = inpart.momentum()*decay3[0]->momentum();
// double R = Cf *((-4.*sqr(mb())*f/s2()) * ((sqr(mb())*e2()/sqr(PbPg)) +
// (sqr(mb())/sqr(PtPg)) - 2.*(PtPb/(PtPg*PbPg))) +
// (16. + 8./s2() + 8.*e2()/s2()) * ((PtPg/PbPg) + (PbPg/PtPg)) -
// (16./s2()) * (1. + e2()));
// return R/B*Constants::pi;
double Bnew = loME(inpart,decay2);
double Rnew = realME(inpart,decay3,inter);
double output = Rnew/Bnew*4.*Constants::pi*sqr(inpart.mass())*UnitRemoval::InvE2;
if(inter==ShowerInteraction::QCD) output *= Cf;
return output;
}
diff --git a/Decay/Perturbative/SMWDecayer.cc b/Decay/Perturbative/SMWDecayer.cc
--- a/Decay/Perturbative/SMWDecayer.cc
+++ b/Decay/Perturbative/SMWDecayer.cc
@@ -1,740 +1,742 @@
// -*- C++ -*-
//
// SMWDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SMWDecayer class.
//
#include "SMWDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/ParVector.h"
#include "ThePEG/Interface/Reference.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/PDT/DecayMode.h"
#include "Herwig/Decay/DecayVertex.h"
#include "ThePEG/Helicity/VectorSpinInfo.h"
#include "ThePEG/Helicity/FermionSpinInfo.h"
#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h"
#include "Herwig/Models/StandardModel/StandardModel.h"
#include "Herwig/Shower/RealEmissionProcess.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include <numeric>
using namespace Herwig;
using namespace ThePEG::Helicity;
const double SMWDecayer::EPS_=0.00000001;
SMWDecayer::SMWDecayer()
: quarkWeight_(6,0.), leptonWeight_(3,0.), CF_(4./3.),
NLO_(false) {
quarkWeight_[0] = 1.01596;
quarkWeight_[1] = 0.0537308;
quarkWeight_[2] = 0.0538085;
quarkWeight_[3] = 1.01377;
quarkWeight_[4] = 1.45763e-05;
quarkWeight_[5] = 0.0018143;
leptonWeight_[0] = 0.356594;
leptonWeight_[1] = 0.356593;
leptonWeight_[2] = 0.356333;
// intermediates
generateIntermediates(false);
}
void SMWDecayer::doinit() {
PerturbativeDecayer::doinit();
// get the vertices from the Standard Model object
tcHwSMPtr hwsm=dynamic_ptr_cast<tcHwSMPtr>(standardModel());
if(!hwsm) throw InitException() << "Must have Herwig StandardModel object in"
<< "SMWDecayer::doinit()"
<< Exception::runerror;
FFWVertex_ = hwsm->vertexFFW();
FFGVertex_ = hwsm->vertexFFG();
WWWVertex_ = hwsm->vertexWWW();
FFPVertex_ = hwsm->vertexFFP();
// make sure they are initialized
FFGVertex_->init();
FFWVertex_->init();
WWWVertex_->init();
FFPVertex_->init();
// now set up the decay modes
DecayPhaseSpaceModePtr mode;
tPDVector extpart(3);
vector<double> wgt(0);
// W modes
extpart[0]=getParticleData(ParticleID::Wplus);
// loop for the quarks
unsigned int iz=0;
for(int ix=1;ix<6;ix+=2) {
for(int iy=2;iy<6;iy+=2) {
// check that the combination of particles is allowed
if(!FFWVertex_->allowed(-ix,iy,ParticleID::Wminus))
throw InitException() << "SMWDecayer::doinit() the W vertex"
<< "cannot handle all the quark modes"
<< Exception::abortnow;
extpart[1] = getParticleData(-ix);
extpart[2] = getParticleData( iy);
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
addMode(mode,quarkWeight_[iz],wgt);
++iz;
}
}
// loop for the leptons
for(int ix=11;ix<17;ix+=2) {
// check that the combination of particles is allowed
// if(!FFWVertex_->allowed(-ix,ix+1,ParticleID::Wminus))
// throw InitException() << "SMWDecayer::doinit() the W vertex"
// << "cannot handle all the lepton modes"
// << Exception::abortnow;
extpart[1] = getParticleData(-ix);
extpart[2] = getParticleData(ix+1);
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
addMode(mode,leptonWeight_[(ix-11)/2],wgt);
}
gluon_ = getParticleData(ParticleID::g);
}
int SMWDecayer::modeNumber(bool & cc,tcPDPtr parent,
const tPDVector & children) const {
int imode(-1);
if(children.size()!=2) return imode;
int id0=parent->id();
tPDVector::const_iterator pit = children.begin();
int id1=(**pit).id();
++pit;
int id2=(**pit).id();
if(abs(id0)!=ParticleID::Wplus) return imode;
int idd(0),idu(0);
if(abs(id1)%2==1&&abs(id2)%2==0) {
idd=abs(id1);
idu=abs(id2);
}
else if(abs(id1)%2==0&&abs(id2)%2==1) {
idd=abs(id2);
idu=abs(id1);
}
if(idd==0&&idu==0) {
return imode;
}
else if(idd<=5) {
imode=idd+idu/2-2;
}
else {
imode=(idd-1)/2+1;
}
cc= (id0==ParticleID::Wminus);
return imode;
}
void SMWDecayer::persistentOutput(PersistentOStream & os) const {
os << FFWVertex_ << quarkWeight_ << leptonWeight_
<< FFGVertex_ << gluon_ << NLO_
<< WWWVertex_ << FFPVertex_;
}
void SMWDecayer::persistentInput(PersistentIStream & is, int) {
is >> FFWVertex_ >> quarkWeight_ >> leptonWeight_
>> FFGVertex_ >> gluon_ >> NLO_
>> WWWVertex_ >> FFPVertex_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SMWDecayer,PerturbativeDecayer>
describeHerwigSMWDecayer("Herwig::SMWDecayer", "HwPerturbativeDecay.so");
void SMWDecayer::Init() {
static ClassDocumentation<SMWDecayer> documentation
("The SMWDecayer class is the implementation of the decay"
" of the W boson to the Standard Model fermions.");
static ParVector<SMWDecayer,double> interfaceWquarkMax
("QuarkMax",
"The maximum weight for the decay of the W to quarks",
&SMWDecayer::quarkWeight_,
0, 0, 0, -10000, 10000, false, false, true);
static ParVector<SMWDecayer,double> interfaceWleptonMax
("LeptonMax",
"The maximum weight for the decay of the W to leptons",
&SMWDecayer::leptonWeight_,
0, 0, 0, -10000, 10000, false, false, true);
static Switch<SMWDecayer,bool> interfaceNLO
("NLO",
"Whether to return the LO or NLO result",
&SMWDecayer::NLO_, false, false, false);
static SwitchOption interfaceNLOLO
(interfaceNLO,
"No",
"Leading-order result",
false);
static SwitchOption interfaceNLONLO
(interfaceNLO,
"Yes",
"NLO result",
true);
}
// return the matrix element squared
double SMWDecayer::me2(const int, const Particle & part,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half)));
int iferm(1),ianti(0);
if(decay[0]->id()>0) swap(iferm,ianti);
if(meopt==Initialize) {
VectorWaveFunction::calculateWaveFunctions(vectors_,rho_,
const_ptr_cast<tPPtr>(&part),
incoming,false);
+ // fix rho if no correlations
+ fixRho(rho_);
}
if(meopt==Terminate) {
VectorWaveFunction::constructSpinInfo(vectors_,const_ptr_cast<tPPtr>(&part),
incoming,true,false);
SpinorBarWaveFunction::
constructSpinInfo(wavebar_,decay[iferm],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(wave_ ,decay[ianti],outgoing,true);
return 0.;
}
SpinorBarWaveFunction::
calculateWaveFunctions(wavebar_,decay[iferm],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(wave_ ,decay[ianti],outgoing);
// compute the matrix element
Energy2 scale(sqr(part.mass()));
for(unsigned int ifm=0;ifm<2;++ifm) {
for(unsigned int ia=0;ia<2;++ia) {
for(unsigned int vhel=0;vhel<3;++vhel) {
if(iferm>ianti) (*ME())(vhel,ia,ifm)=
FFWVertex_->evaluate(scale,wave_[ia],wavebar_[ifm],vectors_[vhel]);
else (*ME())(vhel,ifm,ia)=
FFWVertex_->evaluate(scale,wave_[ia],wavebar_[ifm],vectors_[vhel]);
}
}
}
double output=(ME()->contract(rho_)).real()*UnitRemoval::E2/scale;
if(abs(decay[0]->id())<=6) output*=3.;
if(decay[0]->hasColour()) decay[0]->antiColourNeighbour(decay[1]);
else if(decay[1]->hasColour()) decay[1]->antiColourNeighbour(decay[0]);
// leading-order result
if(!NLO_) return output;
// check decay products coloured, otherwise return
if(!decay[0]->dataPtr()->coloured()) return output;
// inital masses, couplings etc
// W mass
mW_ = part.mass();
// strong coupling
aS_ = SM().alphaS(sqr(mW_));
// reduced mass
double mu1 = (decay[0]->dataPtr()->mass())/mW_;
double mu2 = (decay[1]->dataPtr()->mass())/mW_;
// scale
scale_ = sqr(mW_);
// now for the nlo loop correction
double virt = CF_*aS_/Constants::pi;
// now for the real correction
double realFact=0.;
for(int iemit=0;iemit<2;++iemit) {
double phi = UseRandom::rnd()*Constants::twopi;
// set the emitter and the spectator
double muj = iemit==0 ? mu1 : mu2;
double muk = iemit==0 ? mu2 : mu1;
double muj2 = sqr(muj);
double muk2 = sqr(muk);
// calculate y
double yminus = 0.;
double yplus = 1.-2.*muk*(1.-muk)/(1.-muj2-muk2);
double y = yminus + UseRandom::rnd()*(yplus-yminus);
double v = sqrt(sqr(2.*muk2 + (1.-muj2-muk2)*(1.-y))-4.*muk2)
/(1.-muj2-muk2)/(1.-y);
double zplus = (1.+v)*(1.-muj2-muk2)*y/2./(muj2+(1.-muj2-muk2)*y);
double zminus = (1.-v)*(1.-muj2-muk2)*y/2./(muj2+(1.-muj2-muk2)*y);
double z = zminus + UseRandom::rnd()*(zplus-zminus);
double jac = (1.-y)*(yplus-yminus)*(zplus-zminus);
// calculate x1,x2,x3,xT
double x2 = 1.-y*(1.-muj2-muk2)-muj2+muk2;
double x1 = 1.+muj2-muk2-z*(x2-2.*muk2);
// copy the particle objects over for calculateRealEmission
vector<PPtr> hardProcess(3);
hardProcess[0] = const_ptr_cast<PPtr>(&part);
hardProcess[1] = decay[0];
hardProcess[2] = decay[1];
realFact += 0.25*jac*sqr(1.-muj2-muk2)/
sqrt((1.-sqr(muj-muk))*(1.-sqr(muj+muk)))/Constants::twopi
*2.*CF_*aS_*calculateRealEmission(x1, x2, hardProcess, phi,
muj, muk, iemit, true);
}
// the born + virtual + real
output *= (1. + virt + realFact);
return output;
}
void SMWDecayer::doinitrun() {
PerturbativeDecayer::doinitrun();
if(initialize()) {
for(unsigned int ix=0;ix<numberModes();++ix) {
if(ix<6) quarkWeight_ [ix]=mode(ix)->maxWeight();
else leptonWeight_[ix-6]=mode(ix)->maxWeight();
}
}
}
void SMWDecayer::dataBaseOutput(ofstream & output,
bool header) const {
if(header) output << "update decayers set parameters=\"";
for(unsigned int ix=0;ix<quarkWeight_.size();++ix) {
output << "newdef " << name() << ":QuarkMax " << ix << " "
<< quarkWeight_[ix] << "\n";
}
for(unsigned int ix=0;ix<leptonWeight_.size();++ix) {
output << "newdef " << name() << ":LeptonMax " << ix << " "
<< leptonWeight_[ix] << "\n";
}
// parameters for the PerturbativeDecayer base class
PerturbativeDecayer::dataBaseOutput(output,false);
if(header) output << "\n\" where BINARY ThePEGName=\""
<< fullName() << "\";" << endl;
}
void SMWDecayer::
initializeMECorrection(RealEmissionProcessPtr born, double & initial,
double & final) {
// get the quark and antiquark
ParticleVector qq;
for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix)
qq.push_back(born->bornOutgoing()[ix]);
// ensure quark first
if(qq[0]->id()<0) swap(qq[0],qq[1]);
// centre of mass energy
d_Q_ = (qq[0]->momentum() + qq[1]->momentum()).m();
// quark mass
d_m_ = 0.5*(qq[0]->momentum().m()+qq[1]->momentum().m());
// set the other parameters
setRho(sqr(d_m_/d_Q_));
setKtildeSymm();
// otherwise can do it
initial=1.;
final =1.;
}
bool SMWDecayer::softMatrixElementVeto(PPtr parent,
PPtr progenitor,
const bool & ,
const Energy & highestpT,
const vector<tcPDPtr> & ids,
const double & d_z,
const Energy & d_qt,
const Energy & ) {
// check we should be applying the veto
if(parent->id()!=progenitor->id()||
ids[0]!=ids[1]||
ids[2]->id()!=ParticleID::g) return false;
// calculate pt
Energy2 d_m2 = parent->momentum().m2();
Energy2 pPerp2 = sqr(d_z*d_qt) - d_m2;
if(pPerp2<ZERO) return true;
Energy pPerp = (1.-d_z)*sqrt(pPerp2);
// if not hardest so far don't apply veto
if(pPerp<highestpT) return false;
// calculate the weight
double weight = 0.;
if(parent->id()>0) weight = qWeightX(d_qt, d_z);
else weight = qbarWeightX(d_qt, d_z);
// compute veto from weight and return
return !UseRandom::rndbool(weight);
}
void SMWDecayer::setRho(double r)
{
d_rho_ = r;
d_v_ = sqrt(1.-4.*d_rho_);
}
void SMWDecayer::setKtildeSymm() {
d_kt1_ = (1. + sqrt(1. - 4.*d_rho_))/2.;
setKtilde2();
}
void SMWDecayer::setKtilde2() {
double num = d_rho_ * d_kt1_ + 0.25 * d_v_ *(1.+d_v_)*(1.+d_v_);
double den = d_kt1_ - d_rho_;
d_kt2_ = num/den;
}
double SMWDecayer::getZfromX(double x1, double x2) {
double uval = u(x2);
double num = x1 - (2. - x2)*uval;
double den = sqrt(x2*x2 - 4.*d_rho_);
return uval + num/den;
}
double SMWDecayer::getKfromX(double x1, double x2) {
double zval = getZfromX(x1, x2);
return (1.-x2)/(zval*(1.-zval));
}
double SMWDecayer::MEV(double x1, double x2) {
// Vector part
double num = (x1+2.*d_rho_)*(x1+2.*d_rho_) + (x2+2.*d_rho_)*(x2+2.*d_rho_)
- 8.*d_rho_*(1.+2.*d_rho_);
double den = (1.+2.*d_rho_)*(1.-x1)*(1.-x2);
return (num/den - 2.*d_rho_/((1.-x1)*(1.-x1))
- 2*d_rho_/((1.-x2)*(1.-x2)))/d_v_;
}
double SMWDecayer::MEA(double x1, double x2) {
// Axial part
double num = (x1+2.*d_rho_)*(x1+2.*d_rho_) + (x2+2.*d_rho_)*(x2+2.*d_rho_)
+ 2.*d_rho_*((5.-x1-x2)*(5.-x1-x2) - 19.0 + 4*d_rho_);
double den = d_v_*d_v_*(1.-x1)*(1.-x2);
return (num/den - 2.*d_rho_/((1.-x1)*(1.-x1))
- 2*d_rho_/((1.-x2)*(1.-x2)))/d_v_;
}
double SMWDecayer::u(double x2) {
return 0.5*(1. + d_rho_/(1.-x2+d_rho_));
}
void SMWDecayer::
getXXbar(double kti, double z, double &x, double &xbar) {
double w = sqr(d_v_) + kti*(-1. + z)*z*(2. + kti*(-1. + z)*z);
if (w < 0) {
x = -1.;
xbar = -1;
} else {
x = (1. + sqr(d_v_)*(-1. + z) + sqr(kti*(-1. + z))*z*z*z
+ z*sqrt(w)
- kti*(-1. + z)*z*(2. + z*(-2 + sqrt(w))))/
(1. - kti*(-1. + z)*z + sqrt(w));
xbar = 1. + kti*(-1. + z)*z;
}
}
double SMWDecayer::qWeight(double x, double xbar) {
double rval;
double xg = 2. - xbar - x;
// always return one in the soft gluon region
if(xg < EPS_) return 1.0;
// check it is in the phase space
if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return 0.0;
double k1 = getKfromX(x, xbar);
double k2 = getKfromX(xbar, x);
// Is it in the quark emission zone?
if(k1 < d_kt1_) {
rval = MEV(x, xbar)/PS(x, xbar);
// is it also in the anti-quark emission zone?
if(k2 < d_kt2_) rval *= 0.5;
return rval;
}
return 1.0;
}
double SMWDecayer::qbarWeight(double x, double xbar) {
double rval;
double xg = 2. - xbar - x;
// always return one in the soft gluon region
if(xg < EPS_) return 1.0;
// check it is in the phase space
if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return 0.0;
double k1 = getKfromX(x, xbar);
double k2 = getKfromX(xbar, x);
// Is it in the antiquark emission zone?
if(k2 < d_kt2_) {
rval = MEV(x, xbar)/PS(xbar, x);
// is it also in the quark emission zone?
if(k1 < d_kt1_) rval *= 0.5;
return rval;
}
return 1.0;
}
double SMWDecayer::qWeightX(Energy qtilde, double z) {
double x, xb;
getXXbar(sqr(qtilde/d_Q_), z, x, xb);
// if exceptionally out of phase space, leave this emission, as there
// is no good interpretation for the soft ME correction.
if (x < 0 || xb < 0) return 1.0;
return qWeight(x, xb);
}
double SMWDecayer::qbarWeightX(Energy qtilde, double z) {
double x, xb;
getXXbar(sqr(qtilde/d_Q_), z, xb, x);
// see above in qWeightX.
if (x < 0 || xb < 0) return 1.0;
return qbarWeight(x, xb);
}
double SMWDecayer::PS(double x, double xbar) {
double u = 0.5*(1. + d_rho_ / (1.-xbar+d_rho_));
double z = u + (x - (2.-xbar)*u)/sqrt(xbar*xbar - 4.*d_rho_);
double brack = (1.+z*z)/(1.-z)- 2.*d_rho_/(1-xbar);
// interesting: the splitting function without the subtraction
// term. Actually gives a much worse approximation in the collinear
// limit. double brack = (1.+z*z)/(1.-z);
double den = (1.-xbar)*sqrt(xbar*xbar - 4.*d_rho_);
return brack/den;
}
double SMWDecayer::matrixElementRatio(const Particle & inpart, const ParticleVector & decay2,
const ParticleVector & decay3, MEOption,
ShowerInteraction inter) {
// extract partons and LO momentas
vector<cPDPtr> partons(1,inpart.dataPtr());
vector<Lorentz5Momentum> lomom(1,inpart.momentum());
for(unsigned int ix=0;ix<2;++ix) {
partons.push_back(decay2[ix]->dataPtr());
lomom.push_back(decay2[ix]->momentum());
}
vector<Lorentz5Momentum> realmom(1,inpart.momentum());
for(unsigned int ix=0;ix<3;++ix) {
if(ix==2) partons.push_back(decay3[ix]->dataPtr());
realmom.push_back(decay3[ix]->momentum());
}
if(partons[0]->id()<0) {
swap(partons[1],partons[2]);
swap(lomom[1],lomom[2]);
swap(realmom[1],realmom[2]);
}
scale_ = sqr(inpart.mass());
double lome = loME(partons,lomom);
InvEnergy2 reme = realME(partons,realmom,inter);
double ratio = reme/lome*sqr(inpart.mass())*4.*Constants::pi;
if(inter==ShowerInteraction::QCD) ratio *= CF_;
return ratio;
}
double SMWDecayer::meRatio(vector<cPDPtr> partons,
vector<Lorentz5Momentum> momenta,
unsigned int iemitter, bool subtract) const {
Lorentz5Momentum q = momenta[1]+momenta[2]+momenta[3];
Energy2 Q2=q.m2();
Energy2 lambda = sqrt((Q2-sqr(momenta[1].mass()+momenta[2].mass()))*
(Q2-sqr(momenta[1].mass()-momenta[2].mass())));
InvEnergy2 D[2];
double lome(0.);
for(unsigned int iemit=0;iemit<2;++iemit) {
unsigned int ispect = iemit==0 ? 1 : 0;
Energy2 pipj = momenta[3 ] * momenta[1+iemit ];
Energy2 pipk = momenta[3 ] * momenta[1+ispect];
Energy2 pjpk = momenta[1+iemit] * momenta[1+ispect];
double y = pipj/(pipj+pipk+pjpk);
double z = pipk/( pipk+pjpk);
Energy mij = sqrt(2.*pipj+sqr(momenta[1+iemit].mass()));
Energy2 lamB = sqrt((Q2-sqr(mij+momenta[1+ispect].mass()))*
(Q2-sqr(mij-momenta[1+ispect].mass())));
Energy2 Qpk = q*momenta[1+ispect];
Lorentz5Momentum pkt =
lambda/lamB*(momenta[1+ispect]-Qpk/Q2*q)
+0.5/Q2*(Q2+sqr(momenta[1+ispect].mass())-sqr(momenta[1+ispect].mass()))*q;
Lorentz5Momentum pijt =
q-pkt;
double muj = momenta[1+iemit ].mass()/sqrt(Q2);
double muk = momenta[1+ispect].mass()/sqrt(Q2);
double vt = sqrt((1.-sqr(muj+muk))*(1.-sqr(muj-muk)))/(1.-sqr(muj)-sqr(muk));
double v = sqrt(sqr(2.*sqr(muk)+(1.-sqr(muj)-sqr(muk))*(1.-y))-4.*sqr(muk))
/(1.-y)/(1.-sqr(muj)-sqr(muk));
// dipole term
D[iemit] = 0.5/pipj*(2./(1.-(1.-z)*(1.-y))
-vt/v*(2.-z+sqr(momenta[1+iemit].mass())/pipj));
// matrix element
vector<Lorentz5Momentum> lomom(3);
lomom[0] = momenta[0];
if(iemit==0) {
lomom[1] = pijt;
lomom[2] = pkt ;
}
else {
lomom[2] = pijt;
lomom[1] = pkt ;
}
if(iemit==0) lome = loME(partons,lomom);
}
InvEnergy2 ratio = realME(partons,momenta,ShowerInteraction::QCD)/lome*abs(D[iemitter])
/(abs(D[0])+abs(D[1]));
if(subtract)
return Q2*(ratio-2.*D[iemitter]);
else
return Q2*ratio;
}
double SMWDecayer::loME(const vector<cPDPtr> & partons,
const vector<Lorentz5Momentum> & momenta) const {
// compute the spinors
vector<VectorWaveFunction> vin;
vector<SpinorWaveFunction> aout;
vector<SpinorBarWaveFunction> fout;
VectorWaveFunction win (momenta[0],partons[0],incoming);
SpinorBarWaveFunction qkout(momenta[1],partons[1],outgoing);
SpinorWaveFunction qbout(momenta[2],partons[2],outgoing);
for(unsigned int ix=0;ix<2;++ix){
qkout.reset(ix);
fout.push_back(qkout);
qbout.reset(ix);
aout.push_back(qbout);
}
for(unsigned int ix=0;ix<3;++ix){
win.reset(ix);
vin.push_back(win);
}
// temporary storage of the different diagrams
// sum over helicities to get the matrix element
double total(0.);
for(unsigned int inhel=0;inhel<3;++inhel) {
for(unsigned int outhel1=0;outhel1<2;++outhel1) {
for(unsigned int outhel2=0;outhel2<2;++outhel2) {
Complex diag1 = FFWVertex_->evaluate(scale_,aout[outhel2],fout[outhel1],vin[inhel]);
total += norm(diag1);
}
}
}
// return the answer
return total;
}
InvEnergy2 SMWDecayer::realME(const vector<cPDPtr> & partons,
const vector<Lorentz5Momentum> & momenta,
ShowerInteraction inter) const {
// compute the spinors
vector<VectorWaveFunction> vin;
vector<SpinorWaveFunction> aout;
vector<SpinorBarWaveFunction> fout;
vector<VectorWaveFunction> gout;
VectorWaveFunction win (momenta[0],partons[0],incoming);
SpinorBarWaveFunction qkout(momenta[1],partons[1],outgoing);
SpinorWaveFunction qbout(momenta[2],partons[2],outgoing);
VectorWaveFunction gluon(momenta[3],partons[3],outgoing);
for(unsigned int ix=0;ix<2;++ix){
qkout.reset(ix);
fout.push_back(qkout);
qbout.reset(ix);
aout.push_back(qbout);
gluon.reset(2*ix);
gout.push_back(gluon);
}
for(unsigned int ix=0;ix<3;++ix){
win.reset(ix);
vin.push_back(win);
}
vector<Complex> diag(3,0.);
double total(0.);
AbstractFFVVertexPtr vertex = inter==ShowerInteraction::QCD ? FFGVertex_ : FFPVertex_;
for(unsigned int inhel1=0;inhel1<3;++inhel1) {
for(unsigned int outhel1=0;outhel1<2;++outhel1) {
for(unsigned int outhel2=0;outhel2<2;++outhel2) {
for(unsigned int outhel3=0;outhel3<2;++outhel3) {
SpinorBarWaveFunction off1 =
vertex->evaluate(scale_,3,partons[1]->CC(),fout[outhel1],gout[outhel3]);
diag[0] = FFWVertex_->evaluate(scale_,aout[outhel2],off1,vin[inhel1]);
SpinorWaveFunction off2 =
vertex->evaluate(scale_,3,partons[2]->CC(),aout[outhel2],gout[outhel3]);
diag[1] = FFWVertex_->evaluate(scale_,off2,fout[outhel1],vin[inhel1]);
if(inter==ShowerInteraction::QED) {
VectorWaveFunction off3 =
WWWVertex_->evaluate(scale_,3,partons[0],vin[inhel1],gout[outhel3]);
diag[2] = FFWVertex_->evaluate(scale_,aout[outhel2],fout[outhel1],off3);
}
// sum of diagrams
Complex sum = std::accumulate(diag.begin(),diag.end(),Complex(0.));
// me2
total += norm(sum);
}
}
}
}
// divide out the coupling
total /= norm(vertex->norm());
// double g = sqrt(2.)*abs(FFWVertex_->norm());
// double xg = 2.*momenta[3].t()/momenta[0].mass();
// double xe,mue2;
// if(abs(partons[1]->id())==ParticleID::eminus) {
// xe = 2.*momenta[1].t()/momenta[0].mass();
// mue2 = sqr(momenta[1].mass()/momenta[0].mass());
// }
// else {
// xe = 2.*momenta[2].t()/momenta[0].mass();
// mue2 = sqr(momenta[2].mass()/momenta[0].mass());
// }
// double cg = -4. * g * g * (-pow(mue2, 3.) / 2. + (xg * xg / 4. + (xe / 2. + 1.) * xg + 5. / 2. * xe - 2.) * mue2 * mue2
// + (pow(xg, 3.) / 4. + (xe / 4. - 5. / 4.) * xg * xg + (-7. / 2. * xe + 3.) * xg - 3. * xe * xe
// + 11. / 2. * xe - 7. / 2.) * mue2 + (xg * xg / 2. + (xe - 2.) * xg + xe * xe - 2. * xe + 2.) * (-1. + xg + xe)) * (xe - mue2 - 1.) *
// pow(xg, -2.) * pow(-1. + xg + xe - mue2, -2.);
// cerr << "real " << cg/total << "\n";
// return the total
return total*UnitRemoval::InvE2;
}
double SMWDecayer::calculateRealEmission(double x1, double x2,
vector<PPtr> hardProcess,
double phi, double muj,
double muk, int iemit,
bool subtract) const {
// make partons data object for meRatio
vector<cPDPtr> partons (3);
for(int ix=0; ix<3; ++ix)
partons[ix] = hardProcess[ix]->dataPtr();
partons.push_back(gluon_);
// calculate x3
double x3 = 2.-x1-x2;
double xT = sqrt(max(0.,sqr(x3)-0.25*sqr(sqr(x2)+sqr(x3)-sqr(x1)-4.*sqr(muk)+4.*sqr(muj))
/(sqr(x2)-4.*sqr(muk))));
// calculate the momenta
Energy M = mW_;
Lorentz5Momentum pspect(ZERO,ZERO,-0.5*M*sqrt(max(sqr(x2)-4.*sqr(muk),0.)),
0.5*M*x2,M*muk);
Lorentz5Momentum pemit (-0.5*M*xT*cos(phi),-0.5*M*xT*sin(phi),
0.5*M*sqrt(max(sqr(x1)-sqr(xT)-4.*sqr(muj),0.)),
0.5*M*x1,M*muj);
Lorentz5Momentum pgluon(0.5*M*xT*cos(phi), 0.5*M*xT*sin(phi),
0.5*M*sqrt(max(sqr(x3)-sqr(xT),0.)),0.5*M*x3,ZERO);
if(abs(pspect.z()+pemit.z()-pgluon.z())/M<1e-6)
pgluon.setZ(-pgluon.z());
else if(abs(pspect.z()-pemit.z()+pgluon.z())/M<1e-6)
pemit .setZ(- pemit.z());
// boost and rotate momenta
LorentzRotation eventFrame( ( hardProcess[1]->momentum() +
hardProcess[2]->momentum() ).findBoostToCM() );
Lorentz5Momentum spectator = eventFrame*hardProcess[iemit+1]->momentum();
eventFrame.rotateZ( -spectator.phi() );
eventFrame.rotateY( -spectator.theta() );
eventFrame.invert();
vector<Lorentz5Momentum> momenta(3);
momenta[0] = hardProcess[0]->momentum();
if(iemit==0) {
momenta[2] = eventFrame*pspect;
momenta[1] = eventFrame*pemit ;
}
else {
momenta[1] = eventFrame*pspect;
momenta[2] = eventFrame*pemit ;
}
momenta.push_back(eventFrame*pgluon);
// calculate the weight
double realwgt(0.);
if(1.-x1>1e-5 && 1.-x2>1e-5)
realwgt = meRatio(partons,momenta,iemit,subtract);
return realwgt;
}
diff --git a/Decay/Perturbative/SMZDecayer.cc b/Decay/Perturbative/SMZDecayer.cc
--- a/Decay/Perturbative/SMZDecayer.cc
+++ b/Decay/Perturbative/SMZDecayer.cc
@@ -1,1119 +1,1121 @@
// -*- C++ -*-
//
// SMZDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 SMZDecayer class.
//
#include "SMZDecayer.h"
#include "Herwig/Utilities/Maths.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/ParVector.h"
#include "ThePEG/Interface/Reference.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/PDT/DecayMode.h"
#include "Herwig/Decay/DecayVertex.h"
#include "ThePEG/Helicity/VectorSpinInfo.h"
#include "ThePEG/Helicity/FermionSpinInfo.h"
#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h"
#include "Herwig/Models/StandardModel/StandardModel.h"
#include "Herwig/Shower/RealEmissionProcess.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include <numeric>
using namespace Herwig;
using namespace ThePEG::Helicity;
const double SMZDecayer::EPS_=0.00000001;
SMZDecayer::SMZDecayer()
: quarkWeight_(5,0.), leptonWeight_(6,0.), CF_(4./3.),
NLO_(false) {
quarkWeight_[0] = 0.488029;
quarkWeight_[1] = 0.378461;
quarkWeight_[2] = 0.488019;
quarkWeight_[3] = 0.378027;
quarkWeight_[4] = 0.483207;
leptonWeight_[0] = 0.110709;
leptonWeight_[1] = 0.220276;
leptonWeight_[2] = 0.110708;
leptonWeight_[3] = 0.220276;
leptonWeight_[4] = 0.110458;
leptonWeight_[5] = 0.220276;
// intermediates
generateIntermediates(false);
// QED corrections
hasRealEmissionME(true);
hasOneLoopME(true);
}
void SMZDecayer::doinit() {
PerturbativeDecayer::doinit();
// get the vertices from the Standard Model object
tcHwSMPtr hwsm=dynamic_ptr_cast<tcHwSMPtr>(standardModel());
if(!hwsm) throw InitException() << "Must have Herwig StandardModel object in"
<< "SMZDecayer::doinit()"
<< Exception::runerror;
// cast the vertices
FFZVertex_ = dynamic_ptr_cast<FFVVertexPtr>(hwsm->vertexFFZ());
FFZVertex_->init();
FFGVertex_ = hwsm->vertexFFG();
FFGVertex_->init();
FFPVertex_ = hwsm->vertexFFP();
FFPVertex_->init();
gluon_ = getParticleData(ParticleID::g);
// now set up the decay modes
DecayPhaseSpaceModePtr mode;
tPDVector extpart(3);
vector<double> wgt(0);
// the Z decay modes
extpart[0]=getParticleData(ParticleID::Z0);
// loop over the quarks and the leptons
for(int istep=0;istep<11;istep+=10) {
for(int ix=1;ix<7;++ix) {
int iy=istep+ix;
if(iy==6) continue;
extpart[1] = getParticleData(-iy);
extpart[2] = getParticleData( iy);
mode = new_ptr(DecayPhaseSpaceMode(extpart,this));
if(iy<=6) addMode(mode, quarkWeight_.at(ix-1),wgt);
else addMode(mode,leptonWeight_.at(iy-11),wgt);
}
}
}
int SMZDecayer::modeNumber(bool & cc,tcPDPtr parent,
const tPDVector & children) const {
int imode(-1);
if(children.size()!=2) return imode;
int id0=parent->id();
tPDVector::const_iterator pit = children.begin();
int id1=(**pit).id();
++pit;
int id2=(**pit).id();
// Z to quarks or leptons
cc =false;
if(id0!=ParticleID::Z0) return imode;
if(abs(id1)<6&&id1==-id2) {
imode=abs(id1)-1;
}
else if(abs(id1)>=11&&abs(id1)<=16&&id1==-id2) {
imode=abs(id1)-6;
}
cc = false;
return imode;
}
void SMZDecayer::persistentOutput(PersistentOStream & os) const {
os << FFZVertex_ << FFPVertex_ << FFGVertex_
<< quarkWeight_ << leptonWeight_ << NLO_
<< gluon_;
}
void SMZDecayer::persistentInput(PersistentIStream & is, int) {
is >> FFZVertex_ >> FFPVertex_ >> FFGVertex_
>> quarkWeight_ >> leptonWeight_ >> NLO_
>> gluon_;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<SMZDecayer,PerturbativeDecayer>
describeHerwigSMZDecayer("Herwig::SMZDecayer", "HwPerturbativeDecay.so");
void SMZDecayer::Init() {
static ClassDocumentation<SMZDecayer> documentation
("The SMZDecayer class is the implementation of the decay"
" Z boson to the Standard Model fermions.");
static ParVector<SMZDecayer,double> interfaceZquarkMax
("QuarkMax",
"The maximum weight for the decay of the Z to quarks",
&SMZDecayer::quarkWeight_,
0, 0, 0, -10000, 10000, false, false, true);
static ParVector<SMZDecayer,double> interfaceZleptonMax
("LeptonMax",
"The maximum weight for the decay of the Z to leptons",
&SMZDecayer::leptonWeight_,
0, 0, 0, -10000, 10000, false, false, true);
static Switch<SMZDecayer,bool> interfaceNLO
("NLO",
"Whether to return the LO or NLO result",
&SMZDecayer::NLO_, false, false, false);
static SwitchOption interfaceNLOLO
(interfaceNLO,
"No",
"Leading-order result",
false);
static SwitchOption interfaceNLONLO
(interfaceNLO,
"Yes",
"NLO result",
true);
}
// return the matrix element squared
double SMZDecayer::me2(const int, const Particle & part,
const ParticleVector & decay,
MEOption meopt) const {
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half)));
int iferm(1),ianti(0);
if(decay[0]->id()>0) swap(iferm,ianti);
if(meopt==Initialize) {
VectorWaveFunction::calculateWaveFunctions(_vectors,_rho,
const_ptr_cast<tPPtr>(&part),
incoming,false);
+ // fix rho if no correlations
+ fixRho(_rho);
}
if(meopt==Terminate) {
VectorWaveFunction::constructSpinInfo(_vectors,const_ptr_cast<tPPtr>(&part),
incoming,true,false);
SpinorBarWaveFunction::
constructSpinInfo(_wavebar,decay[iferm],outgoing,true);
SpinorWaveFunction::
constructSpinInfo(_wave ,decay[ianti],outgoing,true);
return 0.;
}
SpinorBarWaveFunction::
calculateWaveFunctions(_wavebar,decay[iferm],outgoing);
SpinorWaveFunction::
calculateWaveFunctions(_wave ,decay[ianti],outgoing);
// compute the matrix element
Energy2 scale(sqr(part.mass()));
unsigned int ifm,ia,vhel;
for(ifm=0;ifm<2;++ifm) {
for(ia=0;ia<2;++ia) {
for(vhel=0;vhel<3;++vhel) {
if(iferm>ianti) (*ME())(vhel,ia,ifm)=
FFZVertex_->evaluate(scale,_wave[ia],_wavebar[ifm],_vectors[vhel]);
else (*ME())(vhel,ifm,ia)=
FFZVertex_->evaluate(scale,_wave[ia],_wavebar[ifm],_vectors[vhel]);
}
}
}
double output=(ME()->contract(_rho)).real()*UnitRemoval::E2/scale;
if(abs(decay[0]->id())<=6) output*=3.;
if(decay[0]->hasColour()) decay[0]->antiColourNeighbour(decay[1]);
else if(decay[1]->hasColour()) decay[1]->antiColourNeighbour(decay[0]);
// if LO return
if(!NLO_) return output; // check decay products coloured, otherwise return
if(!decay[0]->dataPtr()->coloured()) return output;
// inital masses, couplings etc
// fermion mass
Energy particleMass = decay[0]->dataPtr()->mass();
// Z mass
mZ_ = part.mass();
// strong coupling
aS_ = SM().alphaS(sqr(mZ_));
// reduced mass
mu_ = particleMass/mZ_;
mu2_ = sqr(mu_);
// scale
scale_ = sqr(mZ_);
// compute the spinors
vector<SpinorWaveFunction> aout;
vector<SpinorBarWaveFunction> fout;
vector<VectorWaveFunction> vin;
SpinorBarWaveFunction qkout(decay[0]->momentum(),decay[0]->dataPtr(),outgoing);
SpinorWaveFunction qbout(decay[1]->momentum(),decay[1]->dataPtr(),outgoing);
VectorWaveFunction zin (part.momentum() ,part.dataPtr() ,incoming);
for(unsigned int ix=0;ix<2;++ix){
qkout.reset(ix);
fout.push_back(qkout);
qbout.reset(ix);
aout.push_back(qbout);
}
for(unsigned int ix=0;ix<3;++ix){
zin.reset(ix);
vin.push_back(zin);
}
// temporary storage of the different diagrams
// sum over helicities to get the matrix element
double total=0.;
if(mu_!=0.) {
LorentzPolarizationVector momDiff =
(decay[0]->momentum()-decay[1]->momentum())/2./
(decay[0]->momentum().mass()+decay[1]->momentum().mass());
// scalars
Complex scalar1 = zin.wave().dot(momDiff);
for(unsigned int outhel1=0;outhel1<2;++outhel1) {
for(unsigned int outhel2=0;outhel2<2;++outhel2) {
for(unsigned int inhel=0;inhel<3;++inhel) {
// first the LO bit
Complex diag1 = FFZVertex_->evaluate(scale_,aout[outhel2],
fout[outhel1],vin[inhel]);
// extra stuff for NLO
LorentzPolarizationVector left =
aout[outhel2].wave().leftCurrent(fout[outhel1].wave());
LorentzPolarizationVector right =
aout[outhel2].wave().rightCurrent(fout[outhel1].wave());
Complex scalar =
aout[outhel2].wave().scalar(fout[outhel1].wave());
// nlo specific pieces
Complex diag3 =
Complex(0.,1.)*FFZVertex_->norm()*
(FFZVertex_->right()*( left.dot(zin.wave())) +
FFZVertex_-> left()*(right.dot(zin.wave())) -
( FFZVertex_-> left()+FFZVertex_->right())*scalar1*scalar);
// nlo piece
total += real(diag1*conj(diag3) + diag3*conj(diag1));
}
}
}
// rescale
total *= UnitRemoval::E2/scale_;
}
else {
total = ZERO;
}
// now for the NLO bit
double mu4 = sqr(mu2_);
double lmu = mu_!=0. ? log(mu_) : 0.;
double v = sqrt(1.-4.*mu2_),v2(sqr(v));
double omv = 4.*mu2_/(1.+v);
double f1,f2,fNS,VNS;
double r = omv/(1.+v);
double lr = mu_!=0. ? log(r) : 0.;
// normal form
if(mu_>1e-4) {
f1 = CF_*aS_/Constants::pi*
( +1. + 3.*log(0.5*(1.+v)) - 1.5*log(0.5*(1.+v2)) + sqr(Constants::pi)/6.
- 0.5*sqr(lr) - (1.+v2)/v*(lr*log(1.+v2) + sqr(Constants::pi)/12.
-0.5*log(4.*mu2_)*lr + 0.25*sqr(lr)));
fNS = -0.5*(1.+2.*v2)*lr/v + 1.5*lr - 2./3.*sqr(Constants::pi) + 0.5*sqr(lr)
+ (1.+v2)/v*(Herwig::Math::ReLi2(r) + sqr(Constants::pi)/3. - 0.25*sqr(lr)
+ lr*log((2.*v/ (1.+v))));
VNS = 1.5*log(0.5*(1.+v2))
+ 0.5*(1.+v2)/v*( 2.*lr*log(2.*(1.+v2)/sqr(1.+v))
+ 2.*Herwig::Math::ReLi2(sqr(r))
- 2.*Herwig::Math::ReLi2(2.*v/(1.+v)) - sqr(Constants::pi)/6.)
+ log(1.-mu_) - 2.*log(1.-2.*mu_) - 4.*mu2_/(1.+v2)*log(mu_/(1.-mu_))
- mu_/(1.-mu_)
+ 4.*(2.*mu2_-mu_)/(1.+v2) + 0.5*sqr(Constants::pi);
f2 = CF_*aS_/Constants::pi*mu2_*lr/v;
}
// small mass limit
else {
f1 = -CF_*aS_/Constants::pi/6.*
( - 6. - 24.*lmu*mu2_ - 15.*mu4 - 12.*mu4*lmu - 24.*mu4*sqr(lmu)
+ 2.*mu4*sqr(Constants::pi) - 12.*mu2_*mu4 - 96.*mu2_*mu4*sqr(lmu)
+ 8.*mu2_*mu4*sqr(Constants::pi) - 80.*mu2_*mu4*lmu);
fNS = - mu2_/18.*( + 36.*lmu - 36. - 45.*mu2_ + 216.*lmu*mu2_ - 24.*mu2_*sqr(Constants::pi)
+ 72.*mu2_*sqr(lmu) - 22.*mu4 + 1032.*mu4 * lmu
- 96.*mu4*sqr(Constants::pi) + 288.*mu4*sqr(lmu));
VNS = - mu2_/1260.*(-6930. + 7560.*lmu + 2520.*mu_ - 16695.*mu2_
+ 1260.*mu2_*sqr(Constants::pi)
+ 12600.*lmu*mu2_ + 1344.*mu_*mu2_ - 52780.*mu4 + 36960.*mu4*lmu
+ 5040.*mu4*sqr(Constants::pi) - 12216.*mu_*mu4);
f2 = CF_*aS_*mu2_/Constants::pi*( 2.*lmu + 4.*mu2_*lmu + 2.*mu2_ + 12.*mu4*lmu + 7.*mu4);
}
// add up bits for f1
f1 += CF_*aS_/Constants::pi*(fNS+VNS);
double realFact(0.);
for(int iemit=0;iemit<2;++iemit) {
// now for the real correction
double phi = UseRandom::rnd()*Constants::twopi;
// calculate y
double yminus = 0.;
double yplus = 1.-2.*mu_*(1.-mu_)/(1.-2*mu2_);
double y = yminus + UseRandom::rnd()*(yplus-yminus);
// calculate z
double v1 = sqrt(sqr(2.*mu2_+(1.-2.*mu2_)*(1.-y))-4.*mu2_)/(1.-2.*mu2_)/(1.-y);
double zplus = (1.+v1)*(1.-2.*mu2_)*y/2./(mu2_ +(1.-2.*mu2_)*y);
double zminus = (1.-v1)*(1.-2.*mu2_)*y/2./(mu2_ +(1.-2.*mu2_)*y);
double z = zminus + UseRandom::rnd()*(zplus-zminus);
double jac = (1.-y)*(yplus-yminus)*(zplus-zminus);
// calculate x1,x2
double x2 = 1. - y*(1.-2.*mu2_);
double x1 = 1. - z*(x2-2.*mu2_);
// copy the particle objects over for calculateRealEmission
vector<PPtr> hardProcess(3);
hardProcess[0] = const_ptr_cast<PPtr>(&part);
hardProcess[1] = decay[0];
hardProcess[2] = decay[1];
// total real emission contribution
realFact += 0.25*jac*sqr(1.-2.*mu2_)/
sqrt(1.-4.*mu2_)/Constants::twopi
*2.*CF_*aS_*calculateRealEmission(x1, x2, hardProcess, phi,
iemit, true);
}
// the born + virtual + real
output = output*(1. + f1 + realFact) + f2*total;
return output;
}
void SMZDecayer::doinitrun() {
PerturbativeDecayer::doinitrun();
if(initialize()) {
for(unsigned int ix=0;ix<numberModes();++ix) {
if(ix<5) quarkWeight_ [ix ]=mode(ix)->maxWeight();
else if(ix<11) leptonWeight_[ix-5 ]=mode(ix)->maxWeight();
}
}
}
void SMZDecayer::dataBaseOutput(ofstream & output,
bool header) const {
if(header) output << "update decayers set parameters=\"";
for(unsigned int ix=0;ix<quarkWeight_.size();++ix) {
output << "newdef " << name() << ":QuarkMax " << ix << " "
<< quarkWeight_[ix] << "\n";
}
for(unsigned int ix=0;ix<leptonWeight_.size();++ix) {
output << "newdef " << name() << ":LeptonMax " << ix << " "
<< leptonWeight_[ix] << "\n";
}
// parameters for the PerturbativeDecayer base class
PerturbativeDecayer::dataBaseOutput(output,false);
if(header) output << "\n\" where BINARY ThePEGName=\""
<< fullName() << "\";" << endl;
}
InvEnergy2 SMZDecayer::
realEmissionME(unsigned int,const Particle &parent,
ParticleVector &children,
unsigned int iemitter,
double ctheta, double stheta,
const LorentzRotation & rot1,
const LorentzRotation & rot2) {
// check the number of products and parent
assert(children.size()==3 && parent.id()==ParticleID::Z0);
// the electric charge
double e = sqrt(SM().alphaEM()*4.*Constants::pi);
// azimuth of the photon
double phi = children[2]->momentum().phi();
// wavefunctions for the decaying particle in the rotated dipole frame
vector<VectorWaveFunction> vec1 = _vectors;
for(unsigned int ix=0;ix<vec1.size();++ix) {
vec1[ix].transform(rot1);
vec1[ix].transform(rot2);
}
// wavefunctions for the decaying particle in the rotated rest frame
vector<VectorWaveFunction> vec2 = _vectors;
for(unsigned int ix=0;ix<vec1.size();++ix) {
vec2[ix].transform(rot2);
}
// find the outgoing particle and antiparticle
unsigned int iferm(0),ianti(1);
if(children[iferm]->id()<0) swap(iferm,ianti);
// wavefunctions for the particles before the radiation
// wavefunctions for the outgoing fermion
SpinorBarWaveFunction wavebartemp;
Lorentz5Momentum ptemp = - _wavebar[0].momentum();
ptemp *= rot2;
if(ptemp.perp()/ptemp.e()<1e-10) {
ptemp.setX(ZERO);
ptemp.setY(ZERO);
}
wavebartemp = SpinorBarWaveFunction(ptemp,_wavebar[0].particle(),outgoing);
// wavefunctions for the outgoing antifermion
SpinorWaveFunction wavetemp;
ptemp = - _wave[0].momentum();
ptemp *= rot2;
if(ptemp.perp()/ptemp.e()<1e-10) {
ptemp.setX(ZERO);
ptemp.setY(ZERO);
}
wavetemp = SpinorWaveFunction(ptemp,_wave[0].particle(),outgoing);
// loop over helicities
vector<SpinorWaveFunction> wf_old;
vector<SpinorBarWaveFunction> wfb_old;
for(unsigned int ihel=0;ihel<2;++ihel) {
wavetemp.reset(ihel);
wf_old.push_back(wavetemp);
wavebartemp.reset(ihel);
wfb_old.push_back(wavebartemp);
}
// calculate the wave functions for the new fermions
// ensure the momenta have pT=0
for(unsigned int ix=0;ix<2;++ix) {
Lorentz5Momentum ptemp = children[ix]->momentum();
if(ptemp.perp()/ptemp.e()<1e-10) {
ptemp.setX(ZERO);
ptemp.setY(ZERO);
children[ix]->set5Momentum(ptemp);
}
}
// calculate the wavefunctions
vector<SpinorBarWaveFunction> wfb;
SpinorBarWaveFunction::calculateWaveFunctions(wfb,children[iferm],outgoing);
vector<SpinorWaveFunction> wf;
SpinorWaveFunction::calculateWaveFunctions (wf ,children[ianti],outgoing);
// wave functions for the photons
vector<VectorWaveFunction> photon;
VectorWaveFunction::calculateWaveFunctions(photon,children[2],outgoing,true);
// loop to calculate the matrix elements
Complex lome[3][2][2],diffme[3][2][2][2],summe[3][2][2][2];
Energy2 scale(sqr(parent.mass()));
Complex diff[2]={0.,0.};
Complex sum [2]={0.,0.};
for(unsigned int ifm=0;ifm<2;++ifm) {
for(unsigned int ia=0;ia<2;++ia) {
for(unsigned int vhel=0;vhel<3;++vhel) {
// calculation of the leading-order matrix element
Complex loamp = FFZVertex_->evaluate(scale,wf_old[ia],
wfb_old[ifm],vec2[vhel]);
Complex lotemp = FFZVertex_->evaluate(scale,wf[ia],
wfb[ifm],vec1[vhel]);
if(iferm>ianti) lome[vhel][ia][ifm] = loamp;
else lome[vhel][ifm][ia] = loamp;
// photon loop for the real emmision terms
for(unsigned int phel=0;phel<2;++phel) {
// radiation from the antifermion
// normal case with small angle treatment
if(children[2 ]->momentum().z()/
children[iferm]->momentum().z()>=ZERO && iemitter == iferm ) {
Complex dipole = e*double(children[iferm]->dataPtr()->iCharge())/3.*
UnitRemoval::E*loamp*
(children[iferm]->momentum()*photon[2*phel].wave())/
(children[iferm]->momentum()*children[2]->momentum());
// sum and difference
SpinorBarWaveFunction foff =
FFPVertex_->evaluateSmall(ZERO,3,children[iferm]->dataPtr()->CC(),
wfb[ifm],photon[2*phel],
ifm,2*phel,ctheta,phi,stheta,false);
diff[0] = FFZVertex_->evaluate(scale,wf[ia],foff,vec1[vhel]) +
e*double(children[iferm]->dataPtr()->iCharge())/3.*
UnitRemoval::E*(lotemp-loamp)*
(children[iferm]->momentum()*photon[2*phel].wave())/
(children[iferm]->momentum()*children[2]->momentum());
sum [0] = diff[0]+2.*dipole;
}
// special if fermion backwards
else {
SpinorBarWaveFunction foff =
FFPVertex_->evaluate(ZERO,3,children[iferm]->dataPtr()->CC(),
wfb[ifm],photon[2*phel]);
Complex diag =
FFZVertex_->evaluate(scale,wf[ia],foff,vec1[vhel]);
Complex dipole = e*double(children[iferm]->dataPtr()->iCharge())/3.*
UnitRemoval::E*loamp*
(children[iferm]->momentum()*photon[2*phel].wave())/
(children[iferm]->momentum()*children[2]->momentum());
diff[0] = diag-dipole;
sum [0] = diag+dipole;
}
// radiation from the anti fermion
// small angle case in general
if(children[2 ]->momentum().z()/
children[ianti]->momentum().z()>=ZERO && iemitter == ianti ) {
Complex dipole = e*double(children[ianti]->dataPtr()->iCharge())/3.*
UnitRemoval::E*loamp*
(children[ianti]->momentum()*photon[2*phel].wave())/
(children[ianti]->momentum()*children[2]->momentum());
// sum and difference
SpinorWaveFunction foff =
FFPVertex_->evaluateSmall(ZERO,3,children[ianti]->dataPtr()->CC(),
wf[ia],photon[2*phel],
ia,2*phel,ctheta,phi,stheta,false);
diff[1] = FFZVertex_->evaluate(scale,foff ,wfb[ifm],vec1[vhel]) +
e*double(children[ianti]->dataPtr()->iCharge())/3.*
UnitRemoval::E*(lotemp-loamp)*
(children[ianti]->momentum()*photon[2*phel].wave())/
(children[ianti]->momentum()*children[2]->momentum());
sum [1] = diff[1]+2.*dipole;
}
// special if fermion backwards after radiation
else {
SpinorWaveFunction foff =
FFPVertex_->evaluate(ZERO,3,children[ianti]->dataPtr()->CC(),
wf[ia],photon[2*phel]);
Complex diag =
FFZVertex_->evaluate(scale,foff ,wfb[ifm],vec1[vhel]);
Complex dipole = e*double(children[ianti]->dataPtr()->iCharge())/3.*
UnitRemoval::E*loamp*
(children[ianti]->momentum()*photon[2*phel].wave())/
(children[ianti]->momentum()*children[2]->momentum());
// sum and difference
diff[1] = diag - dipole;
sum [1] = diag + dipole;
}
// add to me
if(iferm>ianti) {
diffme[vhel][ia][ifm][phel] = diff[0] + diff[1];
summe [vhel][ia][ifm][phel] = sum[0] + sum[1] ;
}
else {
diffme [vhel][ifm][ia][phel] = diff[0] + diff[1];
summe [vhel][ifm][ia][phel] = sum[0] + sum[1] ;
}
}
}
}
}
// cerr << parent << "\n";
// for(unsigned int ix=0;ix<children.size();++ix) {
// cerr << *children[ix] << "\n";
// }
// _rho = RhoDMatrix(PDT::Spin1);
Complex lo(0.),difference(0.);
for(unsigned int vhel1=0;vhel1<3;++vhel1) {
for(unsigned int vhel2=0;vhel2<3;++vhel2) {
for(unsigned int ifm=0;ifm<2;++ifm) {
for(unsigned int ia=0;ia<2;++ia) {
lo += _rho(vhel1,vhel2)*lome[vhel1][ifm][ia]*conj(lome[vhel2][ifm][ia]);
for(unsigned int phel=0;phel<2;++phel) {
difference +=
_rho(vhel1,vhel2)*diffme[vhel1][ifm][ia][phel]*conj(summe[vhel2][ifm][ia][phel]);
}
}
}
}
}
// // analytic result
// double iCharge = children[0]->dataPtr()->iCharge()*
// children[1]->dataPtr()->iCharge()/9.;
// Energy2 ubar = 2.*children[0]->momentum()*children[2]->momentum();
// Energy2 tbar = 2.*children[1]->momentum()*children[2]->momentum();
// double mu2 = sqr(children[1]->mass()/parent.mass());
// double gL = (FFZVertex_->left() *FFZVertex_->norm()).real();
// double gR = (FFZVertex_->right()*FFZVertex_->norm()).real();
// Energy2 den = sqr(parent.mass())*(((sqr(gL)+sqr(gR))*(1-mu2)+6.*mu2*gL*gR));
// InvEnergy2 anal = -iCharge*( 2.*(ubar/tbar+tbar/ubar)/sqr(parent.mass())+
// 4.*mu2/den*((sqr(gL)+sqr(gR))*(1+ubar/tbar+tbar/ubar)
// -2.*gL*gR*(1.+2.*(ubar/tbar+tbar/ubar))));
// cerr << "testing ratio " << parent.PDGName()
// << " " << difference.real()/sqr(e)/lo.real()*UnitRemoval::InvE2/(anal) << "\n"
// << stheta << " " << ctheta << "\n";
return difference.real()/sqr(e)/lo.real()*UnitRemoval::InvE2;
}
double SMZDecayer::oneLoopVirtualME(unsigned int,
const Particle & parent,
const ParticleVector & children) {
assert(children.size()==2);
// velocities of the particles
double beta = sqrt(1.-4.*sqr(children[0]->mass()/parent.mass()));
double opb = 1.+beta;
double omb = 4.*sqr(children[0]->mass()/parent.mass())/opb;
// couplings
double gL = (FFZVertex_->left() *FFZVertex_->norm()).real();
double gR = (FFZVertex_->right()*FFZVertex_->norm()).real();
double gA = 0.5*(gL-gR);
double gV = 0.5*(gL+gR);
// correction terms
double ln = log(omb/opb);
double f1 = 1. + ln*beta;
double fA = 1. + ln/beta;
InvEnergy f2 = 0.5*sqrt(omb*opb)/parent.mass()/beta*ln;
// momentum difference for the loop
Lorentz5Momentum q = children[0]->momentum()-children[1]->momentum();
if(children[0]->id()<0) q *= -1.;
// spinors
vector<LorentzSpinor <SqrtEnergy> > sp;
vector<LorentzSpinorBar<SqrtEnergy> > sbar;
for(unsigned int ix=0;ix<2;++ix) {
sp .push_back( _wave[ix].dimensionedWave());
sbar.push_back(_wavebar[ix].dimensionedWave());
}
// polarization vectors
vector<LorentzPolarizationVector> pol;
for(unsigned int ix=0;ix<3;++ix)
pol.push_back(_vectors[ix].wave());
// matrix elements
complex<Energy> lome[3][2][2],loopme[3][2][2];
for(unsigned int vhel=0;vhel<3;++vhel) {
for(unsigned int ihel1=0;ihel1<2;++ihel1) {
for(unsigned int ihel2=0;ihel2<2;++ihel2) {
complex<Energy> vector =
sp[ihel1].generalCurrent(sbar[ihel2], 1.,1.).dot(pol[vhel]);
complex<Energy> axial =
sp[ihel1].generalCurrent(sbar[ihel2],-1.,1.).dot(pol[vhel]);
complex<Energy2> scalar =
sp[ihel1].scalar(sbar[ihel2])*(q*pol[vhel]);
lome [vhel][ihel1][ihel2] = gV* vector-gA* axial;
loopme[vhel][ihel1][ihel2] = gV*f1*vector-gA*fA*axial+scalar*f2*gV;
}
}
}
// sum sums
complex<Energy2> den(ZERO),num(ZERO);
for(unsigned int vhel1=0;vhel1<3;++vhel1) {
for(unsigned int vhel2=0;vhel2<3;++vhel2) {
for(unsigned int ihel1=0;ihel1<2;++ihel1) {
for(unsigned int ihel2=0;ihel2<2;++ihel2) {
num += _rho(vhel1,vhel2)*
( lome[vhel1][ihel1][ihel2]*conj(loopme[vhel2][ihel1][ihel2])+
loopme[vhel1][ihel1][ihel2]*conj( lome[vhel2][ihel1][ihel2]));
den += _rho(vhel1,vhel2)*
lome[vhel1][ihel1][ihel2]*conj(lome[vhel2][ihel1][ihel2]);
}
}
}
}
// prefactor
double iCharge = children[0]->dataPtr()->iCharge()*
children[1]->dataPtr()->iCharge()/9.;
double pre = 0.5*SM().alphaEM()*iCharge/Constants::pi;
// output
return pre*num.real()/den.real();
}
void SMZDecayer::
initializeMECorrection(RealEmissionProcessPtr born, double & initial,
double & final) {
// get the quark and antiquark
ParticleVector qq;
for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix)
qq.push_back(born->bornOutgoing()[ix]);
// ensure quark first
if(qq[0]->id()<0) swap(qq[0],qq[1]);
// centre of mass energy
d_Q_ = (qq[0]->momentum() + qq[1]->momentum()).m();
// quark mass
d_m_ = 0.5*(qq[0]->momentum().m()+qq[1]->momentum().m());
// set the other parameters
setRho(sqr(d_m_/d_Q_));
setKtildeSymm();
// otherwise can do it
initial=1.;
final =1.;
}
bool SMZDecayer::softMatrixElementVeto(PPtr parent,
PPtr progenitor,
const bool & ,
const Energy & highestpT,
const vector<tcPDPtr> & ids,
const double & d_z,
const Energy & d_qt,
const Energy &) {
// check we should be applying the veto
if(parent->id()!=progenitor->id()||
ids[0]->id()!=ids[1]->id()||
ids[2]->id()!=ParticleID::g) return false;
// calculate pt
Energy2 d_m2 = parent->momentum().m2();
Energy pPerp = (1.-d_z)*sqrt( sqr(d_z*d_qt) - d_m2);
// if not hardest so far don't apply veto
if(pPerp<highestpT) return false;
// calculate the weight
double weight = 0.;
if(parent->id()>0) weight = qWeightX(d_qt, d_z);
else weight = qbarWeightX(d_qt, d_z);
// compute veto from weight and return
return !UseRandom::rndbool(weight);
}
void SMZDecayer::setRho(double r)
{
d_rho_ = r;
d_v_ = sqrt(1.-4.*d_rho_);
}
void SMZDecayer::setKtildeSymm() {
d_kt1_ = (1. + sqrt(1. - 4.*d_rho_))/2.;
setKtilde2();
}
void SMZDecayer::setKtilde2() {
double num = d_rho_ * d_kt1_ + 0.25 * d_v_ *(1.+d_v_)*(1.+d_v_);
double den = d_kt1_ - d_rho_;
d_kt2_ = num/den;
}
double SMZDecayer::getZfromX(double x1, double x2) {
double uval = u(x2);
double num = x1 - (2. - x2)*uval;
double den = sqrt(x2*x2 - 4.*d_rho_);
return uval + num/den;
}
double SMZDecayer::getKfromX(double x1, double x2) {
double zval = getZfromX(x1, x2);
return (1.-x2)/(zval*(1.-zval));
}
double SMZDecayer::MEV(double x1, double x2) {
// Vector part
double num = (x1+2.*d_rho_)*(x1+2.*d_rho_) + (x2+2.*d_rho_)*(x2+2.*d_rho_)
- 8.*d_rho_*(1.+2.*d_rho_);
double den = (1.+2.*d_rho_)*(1.-x1)*(1.-x2);
return (num/den - 2.*d_rho_/((1.-x1)*(1.-x1))
- 2*d_rho_/((1.-x2)*(1.-x2)))/d_v_;
}
double SMZDecayer::MEA(double x1, double x2) {
// Axial part
double num = (x1+2.*d_rho_)*(x1+2.*d_rho_) + (x2+2.*d_rho_)*(x2+2.*d_rho_)
+ 2.*d_rho_*((5.-x1-x2)*(5.-x1-x2) - 19.0 + 4*d_rho_);
double den = d_v_*d_v_*(1.-x1)*(1.-x2);
return (num/den - 2.*d_rho_/((1.-x1)*(1.-x1))
- 2*d_rho_/((1.-x2)*(1.-x2)))/d_v_;
}
double SMZDecayer::u(double x2) {
return 0.5*(1. + d_rho_/(1.-x2+d_rho_));
}
void SMZDecayer::
getXXbar(double kti, double z, double &x, double &xbar) {
double w = sqr(d_v_) + kti*(-1. + z)*z*(2. + kti*(-1. + z)*z);
if (w < 0) {
x = -1.;
xbar = -1;
} else {
x = (1. + sqr(d_v_)*(-1. + z) + sqr(kti*(-1. + z))*z*z*z
+ z*sqrt(w)
- kti*(-1. + z)*z*(2. + z*(-2 + sqrt(w))))/
(1. - kti*(-1. + z)*z + sqrt(w));
xbar = 1. + kti*(-1. + z)*z;
}
}
double SMZDecayer::qWeight(double x, double xbar) {
double rval;
double xg = 2. - xbar - x;
// always return one in the soft gluon region
if(xg < EPS_) return 1.0;
// check it is in the phase space
if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return 0.0;
double k1 = getKfromX(x, xbar);
double k2 = getKfromX(xbar, x);
// Is it in the quark emission zone?
if(k1 < d_kt1_) {
rval = MEV(x, xbar)/PS(x, xbar);
// is it also in the anti-quark emission zone?
if(k2 < d_kt2_) rval *= 0.5;
return rval;
}
return 1.0;
}
double SMZDecayer::qbarWeight(double x, double xbar) {
double rval;
double xg = 2. - xbar - x;
// always return one in the soft gluon region
if(xg < EPS_) return 1.0;
// check it is in the phase space
if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return 0.0;
double k1 = getKfromX(x, xbar);
double k2 = getKfromX(xbar, x);
// Is it in the antiquark emission zone?
if(k2 < d_kt2_) {
rval = MEV(x, xbar)/PS(xbar, x);
// is it also in the quark emission zone?
if(k1 < d_kt1_) rval *= 0.5;
return rval;
}
return 1.0;
}
double SMZDecayer::qWeightX(Energy qtilde, double z) {
double x, xb;
getXXbar(sqr(qtilde/d_Q_), z, x, xb);
// if exceptionally out of phase space, leave this emission, as there
// is no good interpretation for the soft ME correction.
if (x < 0 || xb < 0) return 1.0;
return qWeight(x, xb);
}
double SMZDecayer::qbarWeightX(Energy qtilde, double z) {
double x, xb;
getXXbar(sqr(qtilde/d_Q_), z, xb, x);
// see above in qWeightX.
if (x < 0 || xb < 0) return 1.0;
return qbarWeight(x, xb);
}
double SMZDecayer::PS(double x, double xbar) {
double u = 0.5*(1. + d_rho_ / (1.-xbar+d_rho_));
double z = u + (x - (2.-xbar)*u)/sqrt(xbar*xbar - 4.*d_rho_);
double brack = (1.+z*z)/(1.-z)- 2.*d_rho_/(1-xbar);
// interesting: the splitting function without the subtraction
// term. Actually gives a much worse approximation in the collinear
// limit. double brack = (1.+z*z)/(1.-z);
double den = (1.-xbar)*sqrt(xbar*xbar - 4.*d_rho_);
return brack/den;
}
double SMZDecayer::matrixElementRatio(const Particle & inpart, const ParticleVector & decay2,
const ParticleVector & decay3, MEOption,
ShowerInteraction inter) {
// extract partons and LO momentas
vector<cPDPtr> partons(1,inpart.dataPtr());
vector<Lorentz5Momentum> lomom(1,inpart.momentum());
for(unsigned int ix=0;ix<2;++ix) {
partons.push_back(decay2[ix]->dataPtr());
lomom.push_back(decay2[ix]->momentum());
}
vector<Lorentz5Momentum> realmom(1,inpart.momentum());
for(unsigned int ix=0;ix<3;++ix) {
if(ix==2) partons.push_back(decay3[ix]->dataPtr());
realmom.push_back(decay3[ix]->momentum());
}
if(partons[0]->id()<0) {
swap(partons[1],partons[2]);
swap(lomom[1],lomom[2]);
swap(realmom[1],realmom[2]);
}
scale_ = sqr(inpart.mass());
double lome = loME(partons,lomom);
InvEnergy2 reme = realME(partons,realmom,inter);
double ratio = reme/lome*sqr(inpart.mass())*4.*Constants::pi;
if(inter==ShowerInteraction::QCD) ratio *= CF_;
return ratio;
}
double SMZDecayer::meRatio(vector<cPDPtr> partons,
vector<Lorentz5Momentum> momenta,
unsigned int iemitter, bool subtract) const {
Lorentz5Momentum q = momenta[1]+momenta[2]+momenta[3];
Energy2 Q2=q.m2();
Energy2 lambda = sqrt((Q2-sqr(momenta[1].mass()+momenta[2].mass()))*
(Q2-sqr(momenta[1].mass()-momenta[2].mass())));
InvEnergy2 D[2];
double lome[2];
for(unsigned int iemit=0;iemit<2;++iemit) {
unsigned int ispect = iemit==0 ? 1 : 0;
Energy2 pipj = momenta[3 ] * momenta[1+iemit ];
Energy2 pipk = momenta[3 ] * momenta[1+ispect];
Energy2 pjpk = momenta[1+iemit] * momenta[1+ispect];
double y = pipj/(pipj+pipk+pjpk);
double z = pipk/( pipk+pjpk);
Energy mij = sqrt(2.*pipj+sqr(momenta[1+iemit].mass()));
Energy2 lamB = sqrt((Q2-sqr(mij+momenta[1+ispect].mass()))*
(Q2-sqr(mij-momenta[1+ispect].mass())));
Energy2 Qpk = q*momenta[1+ispect];
Lorentz5Momentum pkt =
lambda/lamB*(momenta[1+ispect]-Qpk/Q2*q)
+0.5/Q2*(Q2+sqr(momenta[1+ispect].mass())-sqr(momenta[1+ispect].mass()))*q;
Lorentz5Momentum pijt =
q-pkt;
double muj = momenta[1+iemit ].mass()/sqrt(Q2);
double muk = momenta[1+ispect].mass()/sqrt(Q2);
double vt = sqrt((1.-sqr(muj+muk))*(1.-sqr(muj-muk)))/(1.-sqr(muj)-sqr(muk));
double v = sqrt(sqr(2.*sqr(muk)+(1.-sqr(muj)-sqr(muk))*(1.-y))-4.*sqr(muk))
/(1.-y)/(1.-sqr(muj)-sqr(muk));
// dipole term
D[iemit] = 0.5/pipj*(2./(1.-(1.-z)*(1.-y))
-vt/v*(2.-z+sqr(momenta[1+iemit].mass())/pipj));
// matrix element
vector<Lorentz5Momentum> lomom(3);
lomom[0] = momenta[0];
if(iemit==0) {
lomom[1] = pijt;
lomom[2] = pkt ;
}
else {
lomom[2] = pijt;
lomom[1] = pkt ;
}
lome[iemit] = loME(partons,lomom);
}
InvEnergy2 ratio = realME(partons,momenta,ShowerInteraction::QCD)*abs(D[iemitter])
/(abs(D[0]*lome[0])+abs(D[1]*lome[1]));
if(subtract)
return Q2*(ratio-2.*D[iemitter]);
else
return Q2*ratio;
}
double SMZDecayer::loME(const vector<cPDPtr> & partons,
const vector<Lorentz5Momentum> & momenta) const {
// compute the spinors
vector<VectorWaveFunction> vin;
vector<SpinorWaveFunction> aout;
vector<SpinorBarWaveFunction> fout;
VectorWaveFunction zin (momenta[0],partons[0],incoming);
SpinorBarWaveFunction qkout(momenta[1],partons[1],outgoing);
SpinorWaveFunction qbout(momenta[2],partons[2],outgoing);
for(unsigned int ix=0;ix<2;++ix){
qkout.reset(ix);
fout.push_back(qkout);
qbout.reset(ix);
aout.push_back(qbout);
}
for(unsigned int ix=0;ix<3;++ix){
zin.reset(ix);
vin.push_back(zin);
}
// temporary storage of the different diagrams
// sum over helicities to get the matrix element
double total(0.);
for(unsigned int inhel=0;inhel<3;++inhel) {
for(unsigned int outhel1=0;outhel1<2;++outhel1) {
for(unsigned int outhel2=0;outhel2<2;++outhel2) {
Complex diag1 = FFZVertex_->evaluate(scale_,aout[outhel2],fout[outhel1],vin[inhel]);
total += norm(diag1);
}
}
}
// return the answer
return total;
}
InvEnergy2 SMZDecayer::realME(const vector<cPDPtr> & partons,
const vector<Lorentz5Momentum> & momenta,
ShowerInteraction inter) const {
AbstractFFVVertexPtr vertex = inter==ShowerInteraction::QCD ?
FFGVertex_ : FFPVertex_;
// compute the spinors
vector<VectorWaveFunction> vin;
vector<SpinorWaveFunction> aout;
vector<SpinorBarWaveFunction> fout;
vector<VectorWaveFunction> gout;
VectorWaveFunction zin (momenta[0],partons[0],incoming);
SpinorBarWaveFunction qkout(momenta[1],partons[1],outgoing);
SpinorWaveFunction qbout(momenta[2],partons[2],outgoing);
VectorWaveFunction gluon(momenta[3],partons[3],outgoing);
for(unsigned int ix=0;ix<2;++ix){
qkout.reset(ix);
fout.push_back(qkout);
qbout.reset(ix);
aout.push_back(qbout);
gluon.reset(2*ix);
gout.push_back(gluon);
}
for(unsigned int ix=0;ix<3;++ix){
zin.reset(ix);
vin.push_back(zin);
}
vector<Complex> diag(2,0.);
double total(0.);
for(unsigned int inhel1=0;inhel1<3;++inhel1) {
for(unsigned int outhel1=0;outhel1<2;++outhel1) {
for(unsigned int outhel2=0;outhel2<2;++outhel2) {
for(unsigned int outhel3=0;outhel3<2;++outhel3) {
SpinorBarWaveFunction off1 =
vertex->evaluate(scale_,3,partons[1]->CC(),fout[outhel1],gout[outhel3]);
diag[0] = FFZVertex_->evaluate(scale_,aout[outhel2],off1,vin[inhel1]);
SpinorWaveFunction off2 =
vertex->evaluate(scale_,3,partons[2]->CC(),aout[outhel2],gout[outhel3]);
diag[1] = FFZVertex_->evaluate(scale_,off2,fout[outhel1],vin[inhel1]);
// sum of diagrams
Complex sum = std::accumulate(diag.begin(),diag.end(),Complex(0.));
// me2
total += norm(sum);
}
}
}
}
// divide out the coupling
total /= norm(vertex->norm());
// return the total
return total*UnitRemoval::InvE2;
}
double SMZDecayer::calculateRealEmission(double x1, double x2,
vector<PPtr> hardProcess,
double phi,
bool subtract) const {
// make partons data object for meRatio
vector<cPDPtr> partons (3);
for(int ix=0; ix<3; ++ix)
partons[ix] = hardProcess[ix]->dataPtr();
partons.push_back(gluon_);
// calculate x3
double x3 = 2.-x1-x2;
double xT = sqrt(max(0.,sqr(x3) -0.25*sqr(sqr(x2)+sqr(x3)-sqr(x1))/(sqr(x2)-4.*mu2_)));
// calculate the momenta
Energy M = mZ_;
Lorentz5Momentum pspect(ZERO,ZERO,-0.5*M*sqrt(max(sqr(x2)-4.*mu2_,0.)),0.5*M*x2,M*mu_);
Lorentz5Momentum pemit (-0.5*M*xT*cos(phi),-0.5*M*xT*sin(phi),
0.5*M*sqrt(max(sqr(x1)-sqr(xT)-4.*mu2_,0.)),0.5*M*x1,M*mu_);
Lorentz5Momentum pgluon(0.5*M*xT*cos(phi), 0.5*M*xT*sin(phi),
0.5*M*sqrt(max(sqr(x3)-sqr(xT),0.)),0.5*M*x3,ZERO);
if(abs(pspect.z()+pemit.z()-pgluon.z())/M<1e-6)
pgluon.setZ(-pgluon.z());
else if(abs(pspect.z()-pemit.z()+pgluon.z())/M<1e-6)
pemit .setZ(- pemit.z());
// loop over the possible emitting partons
double realwgt(0.);
for(unsigned int iemit=0;iemit<2;++iemit) {
// boost and rotate momenta
LorentzRotation eventFrame( ( hardProcess[1]->momentum() +
hardProcess[2]->momentum() ).findBoostToCM() );
Lorentz5Momentum spectator = eventFrame*hardProcess[iemit+1]->momentum();
eventFrame.rotateZ( -spectator.phi() );
eventFrame.rotateY( -spectator.theta() );
eventFrame.invert();
vector<Lorentz5Momentum> momenta(3);
momenta[0] = hardProcess[0]->momentum();
if(iemit==0) {
momenta[2] = eventFrame*pspect;
momenta[1] = eventFrame*pemit ;
}
else {
momenta[1] = eventFrame*pspect;
momenta[2] = eventFrame*pemit ;
}
momenta.push_back(eventFrame*pgluon);
// calculate the weight
if(1.-x1>1e-5 && 1.-x2>1e-5)
realwgt += meRatio(partons,momenta,iemit,subtract);
}
// total real emission contribution
return realwgt;
}
double SMZDecayer::calculateRealEmission(double x1, double x2,
vector<PPtr> hardProcess,
double phi,
bool subtract,
int emitter) const {
// make partons data object for meRatio
vector<cPDPtr> partons (3);
for(int ix=0; ix<3; ++ix)
partons[ix] = hardProcess[ix]->dataPtr();
partons.push_back(gluon_);
// calculate x3
double x3 = 2.-x1-x2;
double xT = sqrt(max(0.,sqr(x3) -0.25*sqr(sqr(x2)+sqr(x3)-sqr(x1))/(sqr(x2)-4.*mu2_)));
// calculate the momenta
Energy M = mZ_;
Lorentz5Momentum pspect(ZERO,ZERO,-0.5*M*sqrt(max(sqr(x2)-4.*mu2_,0.)),0.5*M*x2,M*mu_);
Lorentz5Momentum pemit (-0.5*M*xT*cos(phi),-0.5*M*xT*sin(phi),
0.5*M*sqrt(max(sqr(x1)-sqr(xT)-4.*mu2_,0.)),0.5*M*x1,M*mu_);
Lorentz5Momentum pgluon( 0.5*M*xT*cos(phi), 0.5*M*xT*sin(phi),
0.5*M*sqrt(max(sqr(x3)-sqr(xT),0.)),0.5*M*x3,ZERO);
if(abs(pspect.z()+pemit.z()-pgluon.z())/M<1e-6)
pgluon.setZ(-pgluon.z());
else if(abs(pspect.z()-pemit.z()+pgluon.z())/M<1e-6)
pemit .setZ(- pemit.z());
// boost and rotate momenta
LorentzRotation eventFrame( ( hardProcess[1]->momentum() +
hardProcess[2]->momentum() ).findBoostToCM() );
Lorentz5Momentum spectator = eventFrame*hardProcess[emitter+1]->momentum();
eventFrame.rotateZ( -spectator.phi() );
eventFrame.rotateY( -spectator.theta() );
eventFrame.invert();
vector<Lorentz5Momentum> momenta(3);
momenta[0] = hardProcess[0]->momentum();
if(emitter==0) {
momenta[2] = eventFrame*pspect;
momenta[1] = eventFrame*pemit ;
}
else {
momenta[1] = eventFrame*pspect;
momenta[2] = eventFrame*pemit ;
}
momenta.push_back(eventFrame*pgluon);
// calculate the weight
double realwgt(0.);
if(1.-x1>1e-5 && 1.-x2>1e-5)
realwgt = meRatio(partons,momenta,emitter,subtract);
// total real emission contribution
return realwgt;
}
diff --git a/Decay/Tau/TauDecayer.cc b/Decay/Tau/TauDecayer.cc
--- a/Decay/Tau/TauDecayer.cc
+++ b/Decay/Tau/TauDecayer.cc
@@ -1,359 +1,361 @@
// -*- C++ -*-
//
// TauDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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 TauDecayer class.
//
// Author: Peter Richardson
//
#include "TauDecayer.h"
#include "ThePEG/Utilities/DescribeClass.h"
#include "ThePEG/PDT/DecayMode.h"
#include "ThePEG/Interface/ClassDocumentation.h"
#include "ThePEG/Interface/Switch.h"
#include "ThePEG/Interface/ParVector.h"
#include "ThePEG/Interface/Reference.h"
#include "ThePEG/Persistency/PersistentOStream.h"
#include "ThePEG/Persistency/PersistentIStream.h"
#include "ThePEG/Interface/Parameter.h"
#include "ThePEG/Helicity/VectorSpinInfo.h"
#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h"
#include "ThePEG/Helicity/WaveFunction/SpinorBarWaveFunction.h"
#include "Herwig/Decay/DecayVertex.h"
#include "Herwig/Decay/GeneralDecayMatrixElement.h"
#include "ThePEG/Helicity/FermionSpinInfo.h"
#include "ThePEG/StandardModel/StandardModelBase.h"
using namespace Herwig;
using namespace ThePEG::Helicity;
void TauDecayer::doinit() {
DecayIntegrator::doinit();
// make sure the current got initialised
_current->init();
// set up the phase-space channels
DecayPhaseSpaceModePtr mode;
DecayPhaseSpaceChannelPtr channel;
tPDVector extpart,ptemp;
extpart.push_back(getParticleData(ParticleID::tauminus));
extpart.push_back(getParticleData(ParticleID::nu_tau));
Energy mtau(extpart[0]->mass());
double maxweight;
vector<double> channelwgts;
int iq(0),ia(0);
_modemap.clear();
unsigned int ix,iy;
bool done;
vector<double>::iterator start,end;
for(ix=0;ix<_current->numberOfModes();++ix) {
// get the external particles for this mode
extpart.resize(2);
ptemp=_current->particles(-3,ix,iq,ia);
for(iy=0;iy<ptemp.size();++iy) extpart.push_back(ptemp[iy]);
// create the mode
mode=new_ptr(DecayPhaseSpaceMode(extpart,this));
// create the first piece of the channel
channel = new_ptr(DecayPhaseSpaceChannel(mode));
channel->addIntermediate(extpart[0],0,0.0,-1,1);
done=_current->createMode(-3,ix,mode,2,1,channel,mtau);
if(done) {
// the maximum weight and the channel weights
// the maximum
maxweight = _wgtmax.size()>numberModes() ? _wgtmax[numberModes()] : 0;
// the weights for the channel
if(_wgtloc.size()>numberModes()&&
_wgtloc[numberModes()]+mode->numberChannels()<=_weights.size()) {
start=_weights.begin()+_wgtloc[numberModes()];
end = start+mode->numberChannels();
channelwgts=vector<double>(start,end);
}
else {
channelwgts.resize(mode->numberChannels(),1./(mode->numberChannels()));
}
_modemap.push_back(ix);
// special for the two body modes
if(extpart.size()==3) {
channelwgts.clear();
mode=new_ptr(DecayPhaseSpaceMode(extpart,this));
}
addMode(mode,maxweight,channelwgts);
}
}
_current->reset();
_current->touch();
_current->update();
}
void TauDecayer::doinitrun() {
_current->initrun();
DecayIntegrator::doinitrun();
if(initialize()) {
_weights.clear();_wgtloc.clear();_wgtmax.clear();
unsigned int ix,iy;
for(ix=0;ix<numberModes();++ix) {
_wgtmax.push_back(mode(ix)->maxWeight());
_wgtloc.push_back(_weights.size());
for(iy=0;iy<mode(ix)->numberChannels();++iy) {
_weights.push_back(mode(ix)->channelWeight(iy));
}
}
}
}
bool TauDecayer::accept(tcPDPtr parent, const tPDVector & children) const {
bool allowed(false);
// find the neutrino
int idnu(0),idtemp,idin(parent->id());
vector<int> idother;
tPDVector::const_iterator pit = children.begin();
tPDVector::const_iterator pend = children.end();
for( ; pit!=pend;++pit) {
idtemp=(**pit).id();
if(abs(idtemp)==16) idnu=idtemp;
else idother.push_back(idtemp);
}
if((idnu==ParticleID::nu_tau && idin==ParticleID::tauminus)||
(idnu==ParticleID::nu_taubar && idin==ParticleID::tauplus )) {
allowed=_current->accept(idother);
}
return allowed;
}
int TauDecayer::modeNumber(bool & cc,tcPDPtr parent, const tPDVector & children) const {
int imode(-1);
tPDVector::const_iterator pit = children.begin();
tPDVector::const_iterator pend = children.end();
int idtemp;vector<int> idother;
for( ; pit!=pend;++pit) {
idtemp=(**pit).id();
if(abs(idtemp)!=16) idother.push_back(idtemp);
}
unsigned int itemp=_current->decayMode(idother);
for(unsigned int ix=0;ix<_modemap.size();++ix) {
if(_modemap[ix]==itemp) imode=ix;
}
// perform the decay
cc=parent->id()==ParticleID::tauplus;
return imode;
}
void TauDecayer::persistentOutput(PersistentOStream & os) const {
os << _modemap << _current << _wgtloc
<< _wgtmax << _weights << _polOpt << _tauMpol << _tauPpol;
}
void TauDecayer::persistentInput(PersistentIStream & is, int) {
is >> _modemap >> _current >> _wgtloc
>> _wgtmax >> _weights >> _polOpt >> _tauMpol >> _tauPpol;
}
// The following static variable is needed for the type
// description system in ThePEG.
DescribeClass<TauDecayer,DecayIntegrator>
describeHerwigTauDecayer("Herwig::TauDecayer", "HwTauDecay.so");
void TauDecayer::Init() {
static ClassDocumentation<TauDecayer> documentation
("The TauDecayer class is designed to use a weak current"
" to perform the decay of the tau.");
static Reference<TauDecayer,WeakDecayCurrent> interfaceWeakCurrent
("WeakCurrent",
"The reference for the decay current to be used.",
&TauDecayer::_current, false, false, true, false, false);
static ParVector<TauDecayer,int> interfaceWeightLocation
("WeightLocation",
"The locations of the weights for a given channel in the vector",
&TauDecayer::_wgtloc,
0, 0, 0, 0, 10000, false, false, true);
static ParVector<TauDecayer,double> interfaceWeightMax
("MaximumWeight",
"The maximum weight for a given channel.",
&TauDecayer::_wgtmax,
0, 0, 0, 0., 100., false, false, true);
static ParVector<TauDecayer,double> interfaceWeights
("Weights",
"The weights for the integration.",
&TauDecayer::_weights,
0, 0, 0, 0., 1., false, false, true);
static Switch<TauDecayer,bool> interfacePolarizationOption
("PolarizationOption",
"Option of forcing the polarization of the tau leptons, N.B. you"
" should only use this option for making distributions for"
" comparision if you really know what you are doing.",
&TauDecayer::_polOpt, false, false, false);
static SwitchOption interfacePolarizationOptionDefault
(interfacePolarizationOption,
"Default",
"Don't force the polarization use the full spin density matrices"
" to get the right answer",
false);
static SwitchOption interfacePolarizationOptionForce
(interfacePolarizationOption,
"Force",
"Force the polarizations",
true);
static Parameter<TauDecayer,double> interfaceTauMinusPolarization
("TauMinusPolarization",
"The polarization of the tau-, left=-1, right=+1 if this is forced.",
&TauDecayer::_tauMpol, 0.0, -1.0, 1.0,
false, false, Interface::limited);
static Parameter<TauDecayer,double> interfaceTauPlusPolarization
("TauPlusPolarization",
"The polarization of the tau+, left=-1, right=+1 if this is forced.",
&TauDecayer::_tauPpol, 0.0, -1.0, 1.0,
false, false, Interface::limited);
}
// combine the currents to give the matrix element
double TauDecayer::me2(const int ichan,const Particle & inpart,
const ParticleVector & decay,
MEOption meopt) const {
// map the mode to those in the current
int mode(_modemap[imode()]);
// get the particles for the hadronic current
ParticleVector hadpart(decay.begin()+1,decay.end());
Energy q;
// extract info on the decaying particle
if(meopt==Initialize) {
// spin density matrix for the decaying particle
_rho = RhoDMatrix(PDT::Spin1Half);
if(inpart.id()==ParticleID::tauminus)
SpinorWaveFunction ::calculateWaveFunctions(_inspin,_rho,
const_ptr_cast<tPPtr>(&inpart),
incoming);
else
SpinorBarWaveFunction::calculateWaveFunctions(_inbar ,_rho,
const_ptr_cast<tPPtr>(&inpart),
incoming);
+ // fix rho if no correlations
+ fixRho(_rho);
if(_polOpt) {
_rho(0,1) = _rho(1,0) = 0.;
if(inpart.id()==ParticleID::tauminus) {
_rho(0,0) = 0.5*(1.-_tauMpol);
_rho(1,1) = 0.5*(1.+_tauMpol);
}
else {
_rho(0,0) = 0.5*(1.+_tauPpol);
_rho(1,1) = 0.5*(1.-_tauPpol);
}
}
// work out the mapping for the hadron vector
_constants = vector<unsigned int>(decay.size()+1);
_ispin = vector<PDT::Spin >(decay.size());
int itemp(1);
unsigned int ix(decay.size());
do {
--ix;
_ispin[ix] = decay[ix]->data().iSpin();
itemp *= _ispin[ix];
_constants[ix] = itemp;
}
while(ix>0);
_constants[decay.size()] = 1;
_constants[0 ] = _constants[1];
}
if(!ME())
ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,_ispin)));
// connect the spininfo up if needed
if(meopt==Terminate) {
if(inpart.id()==ParticleID::tauminus) {
SpinorWaveFunction ::
constructSpinInfo(_inspin,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorBarWaveFunction::
constructSpinInfo(_inbar,decay[0],outgoing,true);
}
else {
SpinorBarWaveFunction::
constructSpinInfo(_inbar ,const_ptr_cast<tPPtr>(&inpart),incoming,true);
SpinorWaveFunction::
constructSpinInfo(_inspin,decay[0],outgoing,true);
}
_current->current(mode,ichan,q,hadpart,meopt);
return 0.;
}
// calculate the spinors for the decay products
if(inpart.id()==ParticleID::tauminus)
SpinorBarWaveFunction::calculateWaveFunctions(_inbar ,decay[0],outgoing);
else
SpinorWaveFunction ::calculateWaveFunctions(_inspin,decay[0],outgoing);
// calculate the hadron current
vector<LorentzPolarizationVectorE>
hadron(_current->current(mode,ichan,q,hadpart,meopt));
// prefactor
double pre = sqr(pow(inpart.mass()/q,int(hadpart.size()-2)));
// calculate the lepton current
LorentzPolarizationVectorE lepton[2][2];
for(unsigned ix=0;ix<2;++ix) {
for(unsigned iy=0;iy<2;++iy) {
if(inpart.id()==15)
lepton[ix][iy]=2.*_inspin[ix].leftCurrent(_inbar[iy]);
else
lepton[iy][ix]=2.*_inspin[ix].leftCurrent(_inbar[iy]);
}
}
// compute the matrix element
vector<unsigned int> ihel(decay.size()+1);
for(unsigned int hhel=0;hhel<hadron.size();++hhel) {
// map the index for the hadrons to a helicity state
for(unsigned int ix=decay.size();ix>1;--ix) {
ihel[ix]=(hhel%_constants[ix-1])/_constants[ix];
}
// loop over the helicities of the tau and neutrino and set up the matrix
// element
for(ihel[1]=0;ihel[1]<2;++ihel[1]){
for(ihel[0]=0;ihel[0]<2;++ihel[0]) {
(*ME())(ihel)= lepton[ihel[0]][ihel[1]].dot(hadron[hhel])*
SM().fermiConstant();
}
}
}
// multiply by the CKM element
int iq,ia;
_current->decayModeInfo(mode,iq,ia);
double ckm(1.);
if(iq<=6) {
if(iq%2==0) ckm = SM().CKM(iq/2-1,(abs(ia)-1)/2);
else ckm = SM().CKM(abs(ia)/2-1,(iq-1)/2);
}
return 0.5*pre*ckm*(ME()->contract(_rho)).real();
}
// output the setup information for the particle database
void TauDecayer::dataBaseOutput(ofstream & output,bool header) const {
unsigned int ix;
if(header) output << "update decayers set parameters=\"";
DecayIntegrator::dataBaseOutput(output,false);
for(ix=0;ix<_wgtloc.size();++ix) {
output << "insert " << name() << ":WeightLocation " << ix << " "
<< _wgtloc[ix] << "\n";
}
for(ix=0;ix<_wgtmax.size();++ix) {
output << "insert " << name() << ":MaximumWeight " << ix << " "
<< _wgtmax[ix] << "\n";
}
for(ix=0;ix<_weights.size();++ix) {
output << "insert " << name() << ":Weights " << ix << " "
<< _weights[ix] << "\n";
}
_current->dataBaseOutput(output,false,true);
output << "newdef " << name() << ":WeakCurrent " << _current->name() << " \n";
output << "\n\" where BINARY ThePEGName=\"" << fullName() << "\";\n";
}
diff --git a/Shower/ShowerHandler.h b/Shower/ShowerHandler.h
--- a/Shower/ShowerHandler.h
+++ b/Shower/ShowerHandler.h
@@ -1,873 +1,880 @@
// -*- C++ -*-
//
// ShowerHandler.h is a part of Herwig - A multi-purpose Monte Carlo event generator
// Copyright (C) 2002-2017 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_ShowerHandler_H
#define HERWIG_ShowerHandler_H
//
// This is the declaration of the ShowerHandler class.
//
#include "ThePEG/Handlers/EventHandler.h"
#include "ThePEG/Handlers/CascadeHandler.h"
#include "ShowerVariation.h"
#include "Herwig/PDF/HwRemDecayer.fh"
#include "ThePEG/EventRecord/RemnantParticle.fh"
#include "UEBase.h"
#include "PerturbativeProcess.h"
#include "Herwig/MatrixElement/Matchbox/Matching/HardScaleProfile.h"
#include "ShowerHandler.fh"
namespace Herwig {
using namespace ThePEG;
/** \ingroup Shower
*
* This class is the main driver of the shower: it is responsible for
* the proper handling of all other specific collaborating classes
* and for the storing of the produced particles in the event record.
*
* @see \ref ShowerHandlerInterfaces "The interfaces"
*
* @see ThePEG::CascadeHandler
* @see MPIHandler
* @see HwRemDecayer
*/
class ShowerHandler: public CascadeHandler {
public:
/**
* Typedef for a pair of ThePEG::RemnantParticle pointers.
*/
typedef pair<tRemPPtr, tRemPPtr> RemPair;
public:
/**
* Default constructor
*/
ShowerHandler();
/**
* Destructor
*/
virtual ~ShowerHandler();
public:
/**
* The main method which manages the multiple interactions and starts
* the shower by calling cascade(sub, lastXC).
*/
virtual void cascade();
/**
* pointer to "this", the current ShowerHandler.
*/
static const tShowerHandlerPtr currentHandler() {
assert(currentHandler_);
return currentHandler_;
}
+ /**
+ * pointer to "this", the current ShowerHandler.
+ */
+ static bool currentHandlerIsSet() {
+ return currentHandler_;
+ }
+
public:
/**
* Hook to allow vetoing of event after showering hard sub-process
* as in e.g. MLM merging.
*/
virtual bool showerHardProcessVeto() const { return false; }
/**
* Return true, if this cascade handler will perform reshuffling from hard
* process masses.
*/
virtual bool isReshuffling() const { return true; }
/**
* Return true, if the shower handler can generate a truncated
* shower for POWHEG style events generated using Matchbox
*/
virtual bool canHandleMatchboxTrunc() const { return false; }
/**
* Get the PDF freezing scale
*/
Energy pdfFreezingScale() const { return pdfFreezingScale_; }
/**
* Get the local PDFs.
*/
PDFPtr getPDFA() const {return PDFA_;}
/**
* Get the local PDFs.
*/
PDFPtr getPDFB() const {return PDFB_;}
/**
* Return true if currently the primary subprocess is showered.
*/
bool firstInteraction() const {
if (!eventHandler()->currentCollision())return true;
return ( subProcess_ ==
eventHandler()->currentCollision()->primarySubProcess() );
}
/**
* Return the remnant decayer.
*/
tHwRemDecPtr remnantDecayer() const { return remDec_; }
/**
* Split the hard process into production and decays
* @param tagged The tagged particles from the StepHandler
* @param hard The hard perturbative process
* @param decay The decay particles
*/
void splitHardProcess(tPVector tagged, PerturbativeProcessPtr & hard,
DecayProcessMap & decay) const;
/**
* Information if the Showerhandler splits the hard process.
*/
bool doesSplitHardProcess()const {return splitHardProcess_;}
/**
* Decay a particle.
* radPhotons switches the generation of photon
* radiation on/off.
* Required for Dipole Shower but not QTilde Shower.
*/
tDMPtr decay(PerturbativeProcessPtr,
DecayProcessMap & decay,
bool radPhotons = false) const;
/**
* Cached lookup of decay modes.
* Generator::findDecayMode() is not efficient.
*/
tDMPtr findDecayMode(const string & tag) const;
/**
* A struct to order the particles in the same way as in the DecayMode's
*/
struct ParticleOrdering {
bool operator() (tcPDPtr p1, tcPDPtr p2);
};
/**
* A container for ordered particles required
* for constructing tags for decay mode lookup.
*/
typedef multiset<tcPDPtr,ParticleOrdering> OrderedParticles;
public:
/**
* @name Switches for initial- and final-state radiation
*/
//@{
/**
* Switch for any radiation
*/
bool doRadiation() const {return doFSR_ || doISR_;}
/**
* Switch on or off final state radiation.
*/
bool doFSR() const { return doFSR_;}
/**
* Switch on or off initial state radiation.
*/
bool doISR() const { return doISR_;}
//@}
public:
/**
* @name Switches for scales
*/
//@{
/**
* Return true if maximum pt should be deduced from the factorization scale
*/
bool hardScaleIsMuF() const { return maxPtIsMuF_; }
/**
* The factorization scale factor.
*/
double factorizationScaleFactor() const {
return factorizationScaleFactor_;
}
/**
* The renormalization scale factor.
*/
double renFac() const {
return renormalizationScaleFactor_;
}
/**
* The factorization scale factor.
*/
double facFac() const {
return factorizationScaleFactor_;
}
/**
* The renormalization scale factor.
*/
double renormalizationScaleFactor() const {
return renormalizationScaleFactor_;
}
/**
* The scale factor for the hard scale
*/
double hardScaleFactor() const {
return hardScaleFactor_;
}
/**
* Return true, if the phase space restrictions of the dipole shower should
* be applied.
*/
bool restrictPhasespace() const { return restrictPhasespace_; }
/**
* Return profile scales
*/
Ptr<HardScaleProfile>::tptr profileScales() const { return hardScaleProfile_; }
/**
* Return the relevant hard scale to be used in the profile scales
*/
virtual Energy hardScale() const;
/**
* Return information about shower phase space choices
*/
virtual int showerPhaseSpaceOption() const {
assert(false && "not implemented in general");
return -1;
}
//@}
public:
/**
* Access the shower variations
*/
map<string,ShowerVariation>& showerVariations() {
return showerVariations_;
}
/**
* Return the shower variations
*/
const map<string,ShowerVariation>& showerVariations() const {
return showerVariations_;
}
/**
* Access the current Weights
*/
map<string,double>& currentWeights() {
return currentWeights_;
}
/**
* Return the current Weights
*/
const map<string,double>& currentWeights() const {
return currentWeights_;
}
/**
* Change the current reweighting factor
*/
void reweight(double w) {
reweight_ = w;
}
/**
* Return the current reweighting factor
*/
double reweight() const {
return reweight_;
}
public :
/**
* Access to switches for spin correlations
*/
//@{
/**
* Spin Correlations
*/
unsigned int spinCorrelations() const {
return spinOpt_;
}
/**
* Any correlations
*/
virtual bool correlations() const {
return spinOpt_!=0;
}
//@}
public:
/**
* struct that is used to catch exceptions which are thrown
* due to energy conservation issues of additional scatters
*/
struct ExtraScatterVeto {};
/**
* struct that is used to catch exceptions which are thrown
* due to fact that the Shower has been invoked more than
* a defined threshold on a certain configuration
*/
struct ShowerTriesVeto {
/** variable to store the number of attempts */
const int tries;
/** constructor */
ShowerTriesVeto(int t) : tries(t) {}
};
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 Functions to perform the cascade
*/
//@{
/**
* The main method which manages the showering of a subprocess.
*/
virtual tPPair cascade(tSubProPtr sub, XCPtr xcomb);
/**
* Set up for the cascade
*/
void prepareCascade(tSubProPtr sub) {
current_ = currentStep();
subProcess_ = sub;
}
/**
* Boost all the particles in the collision so that the collision always occurs
* in the rest frame with the incoming particles along the z axis
*/
void boostCollision(bool boost);
//@}
protected:
/**
* Set/unset the current shower handler
*/
//@{
/**
* Set the current handler
*/
void setCurrentHandler() {
currentHandler_ = tShowerHandlerPtr(this);
}
/**
* Unset the current handler
*/
void unSetCurrentHandler() {
currentHandler_ = tShowerHandlerPtr();
}
//@}
protected:
/**
* @name Members relating to the underlying event and MPI
*/
//@{
/**
* Return true if multiple parton interactions are switched on
* and can be used for this beam setup.
*/
bool isMPIOn() const {
return MPIHandler_ && MPIHandler_->beamOK();
}
/**
* Access function for the MPIHandler, it should only be called after
* checking with isMPIOn.
*/
tUEBasePtr getMPIHandler() const {
assert(MPIHandler_);
return MPIHandler_;
}
/**
* Is a beam particle where hadronic structure is resolved
*/
bool isResolvedHadron(tPPtr);
/**
* Get the remnants from the ThePEG::PartonBinInstance es and
* do some checks.
*/
RemPair getRemnants(PBIPair incbins);
/**
* Reset the PDF's after the hard collision has been showered
*/
void setMPIPDFs();
//@}
public:
/**
* Check if a particle decays in the shower
* @param id The PDG code for the particle
*/
bool decaysInShower(long id) const {
return ( particlesDecayInShower_.find( abs(id) ) !=
particlesDecayInShower_.end() );
}
protected:
/**
* Members to handle splitting up of hard process and decays
*/
//@{
/**
* Find decay products from the hard process and create decay processes
* @param parent The parent particle
* @param hard The hard process
* @param decay The decay processes
*/
void findDecayProducts(PPtr parent, PerturbativeProcessPtr hard, DecayProcessMap & decay) const;
/**
* Find decay products from the hard process and create decay processes
* @param parent The parent particle
* @param hard The parent hard process
* @param decay The decay processes
*/
void createDecayProcess(PPtr parent,PerturbativeProcessPtr hard, DecayProcessMap & decay) const;
//@}
/**
* @name Functions to return information relevant to the process being showered
*/
//@{
/**
* Return the currently used SubProcess.
*/
tSubProPtr currentSubProcess() const {
assert(subProcess_);
return subProcess_;
}
/**
* Access to the incoming beam particles
*/
tPPair incomingBeams() const {
return incoming_;
}
//@}
protected:
/**
* Weight handling for shower variations
*/
//@
/**
* Combine the variation weights which have been encountered
*/
void combineWeights();
/**
* Initialise the weights in currentEvent()
*/
void initializeWeights();
/**
* Reset the current weights
*/
void resetWeights();
//@}
protected:
/**
* Return the maximum number of attempts for showering
* a given subprocess.
*/
unsigned int maxtry() const { return maxtry_; }
protected:
/**
* Parameters for the space-time model
*/
//@{
/**
* Whether or not to include spa-cetime distances in the shower
*/
bool includeSpaceTime() const {return includeSpaceTime_;}
/**
* The minimum virtuality for the space-time model
*/
Energy2 vMin() const {return vMin_;}
//@}
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();
/**
* Initialize this object. Called in the run phase just before
* a run begins.
*/
virtual void doinitrun();
/**
* Finalize this object. Called in the run phase just after a
* run has ended. Used eg. to write out statistics.
*/
virtual void dofinish();
//@}
private:
/**
* The assignment operator is private and must never be called.
* In fact, it should not even be implemented.
*/
ShowerHandler & operator=(const ShowerHandler &);
private:
/**
* pointer to "this", the current ShowerHandler.
*/
static tShowerHandlerPtr currentHandler_;
/**
* a MPIHandler to administer the creation of several (semihard)
* partonic interactions.
*/
UEBasePtr MPIHandler_;
/**
* Pointer to the HwRemDecayer
*/
HwRemDecPtr remDec_;
private:
/**
* Maximum tries for various stages of the showering process
*/
//@{
/**
* Maximum number of attempts for the
* main showering loop
*/
unsigned int maxtry_;
/**
* Maximum number of attempts for the regeneration of an additional
* scattering, before the number of scatters is reduced.
*/
unsigned int maxtryMPI_;
/**
* Maximum number of attempts for the regeneration of an additional
* hard scattering, before this event is vetoed.
*/
unsigned int maxtryDP_;
/**
* Maximum number of attempts to generate a decay
*/
unsigned int maxtryDecay_;
//@}
private:
/**
* Factors for the various scales
*/
//@{
/**
* The factorization scale factor.
*/
double factorizationScaleFactor_;
/**
* The renormalization scale factor.
*/
double renormalizationScaleFactor_;
/**
* The scale factor for the hard scale
*/
double hardScaleFactor_;
/**
* True, if the phase space restrictions of the dipole shower should
* be applied.
*/
bool restrictPhasespace_;
/**
* True if maximum pt should be deduced from the factorization scale
*/
bool maxPtIsMuF_;
/**
* The profile scales
*/
Ptr<HardScaleProfile>::ptr hardScaleProfile_;
//@}
/**
* Option to include spin correlations
*/
unsigned int spinOpt_;
private:
/**
* Storage of information about the current event
*/
//@{
/**
* The incoming beam particles for the current collision
*/
tPPair incoming_;
/**
* Boost to get back to the lab
*/
LorentzRotation boost_;
/**
* Const pointer to the currently handeled ThePEG::SubProcess
*/
tSubProPtr subProcess_;
/**
* Const pointer to the current step
*/
tcStepPtr current_;
//@}
private:
/**
* PDFs to be used for the various stages and related parameters
*/
//@{
/**
* The PDF freezing scale
*/
Energy pdfFreezingScale_;
/**
* PDFs to be used for the various stages and related parameters
*/
//@{
/**
* The PDF for beam particle A. Overrides the particle's own PDF setting.
*/
PDFPtr PDFA_;
/**
* The PDF for beam particle B. Overrides the particle's own PDF setting.
*/
PDFPtr PDFB_;
/**
* The PDF for beam particle A for remnant splitting. Overrides the particle's own PDF setting.
*/
PDFPtr PDFARemnant_;
/**
* The PDF for beam particle B for remnant splitting. Overrides the particle's own PDF setting.
*/
PDFPtr PDFBRemnant_;
/**
* The MPI PDF's to be used for secondary scatters.
*/
pair <PDFPtr, PDFPtr> mpipdfs_;
/**
* The MPI PDF's to be used for secondary scatters.
*/
pair <PDFPtr, PDFPtr> rempdfs_;
/**
* The MPI PDF's to be used for secondary scatters.
*/
pair <PDFPtr, PDFPtr> remmpipdfs_;
//@}
private:
/**
* @name Parameters for initial- and final-state radiation
*/
//@{
/**
* Switch on or off final state radiation.
*/
bool doFSR_;
/**
* Switch on or off initial state radiation.
*/
bool doISR_;
//@}
private:
/**
* @name Parameters for particle decays
*/
//@{
/**
* Whether or not to split into hard and decay trees
*/
bool splitHardProcess_;
/**
* PDG codes of the particles which decay during showering
* this is fast storage for use during running
*/
set<long> particlesDecayInShower_;
/**
* PDG codes of the particles which decay during showering
* this is a vector that is interfaced so they can be changed
*/
vector<long> inputparticlesDecayInShower_;
//@}
private:
/**
* Parameters for the space-time model
*/
//@{
/**
* Whether or not to include spa-cetime distances in the shower
*/
bool includeSpaceTime_;
/**
* The minimum virtuality for the space-time model
*/
Energy2 vMin_;
//@}
private:
/**
* Parameters relevant for reweight and variations
*/
//@{
/**
* The shower variations
*/
map<string,ShowerVariation> showerVariations_;
/**
* Command to add a shower variation
*/
string doAddVariation(string);
/**
* A reweighting factor applied by the showering
*/
double reweight_;
/**
* The shower variation weights
*/
map<string,double> currentWeights_;
//@}
};
}
#endif /* HERWIG_ShowerHandler_H */

File Metadata

Mime Type
text/x-diff
Expires
Tue, Nov 19, 2:43 PM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3791632
Default Alt Text
(518 KB)

Event Timeline