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 */