diff --git a/Decay/General/FFSDecayer.cc b/Decay/General/FFSDecayer.cc --- a/Decay/General/FFSDecayer.cc +++ b/Decay/General/FFSDecayer.cc @@ -1,444 +1,463 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> ) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractFFSVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<FFSVertexPtr> (vertex); + 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(); } } // 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) = - vertex_->evaluate(scale,wave_[if1],wavebar_[if2],scal); - else (*ME())(if2, if1, 0) = - vertex_->evaluate(scale,wave_[if1],wavebar_[if2],scal); + 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_) { + 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_->setCoupling(sqr(inpart.second), in, outa.first, outb.first); + perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in, outa.first, outb.first); } else { mu1 = outb.second/inpart.second; mu2 = outa.second/inpart.second; - perturbativeVertex_->setCoupling(sqr(inpart.second), in, outb.first, outa.first); + perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in, outb.first, outa.first); } - double c2 = norm(perturbativeVertex_->norm()); - Complex cl = perturbativeVertex_->left(); - Complex cr = perturbativeVertex_->right(); + 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 = vertex_->evaluate(scale,spinorInter,wavebar3_[ifo],swave3_); + 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 = vertex_->evaluate(scale,wave3_[ifo], spinorBarInter,swave3_); + 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 = vertex_->evaluate(scale,wave3_[ifi],spinorBarInter,swave3_); + 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 = vertex_->evaluate(scale,spinorInter,wavebar3_[ifi],swave3_); + 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 = vertex_->evaluate(scale,wave3_[ifi],wavebar3_[ifo],scalarInter); + diag = 0.; + for(auto vertex :vertex_) + diag += vertex->evaluate(scale,wave3_[ifi],wavebar3_[ifo],scalarInter); } else { - diag = vertex_->evaluate(scale,wave3_[ifo],wavebar3_[ifi],scalarInter); + 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/FFSDecayer.h b/Decay/General/FFSDecayer.h --- a/Decay/General/FFSDecayer.h +++ b/Decay/General/FFSDecayer.h @@ -1,210 +1,218 @@ // -*- C++ -*- // // FFSDecayer.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_FFSDecayer_H #define HERWIG_FFSDecayer_H // // This is the declaration of the FFSDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Scalar/FFSVertex.h" #include "ThePEG/Helicity/Vertex/Scalar/VSSVertex.h" #include "ThePEG/Helicity/Vertex/Vector/FFVVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::FFSVertexPtr; /** \ingroup Decay * The FFSDecayer class implements the decay of a fermion * to a fermion and a vector in a general model. It holds an FFVVertex * pointer that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class FFSDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ FFSDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FFSDecayer & operator=(const FFSDecayer &); private: /** * Abstract pointer to AbstractFFSVertex */ - AbstractFFSVertexPtr vertex_; + vector<AbstractFFSVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - FFSVertexPtr perturbativeVertex_; + vector<FFSVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from incoming (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> outgoingVertexF_; /** * Abstract pointer to AbstractVSSVertex for QCD radiation from outgoing scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> outgoingVertexS_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Spinor wavefunctions */ mutable vector<SpinorWaveFunction> wave_ ; /** * Barred spinor wavefunctions */ mutable vector<SpinorBarWaveFunction> wavebar_; /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Scalar wavefunction for 3 body decay */ mutable ScalarWaveFunction swave3_; /** * Spinor wavefunction for 3 body decay */ mutable vector<SpinorWaveFunction> wave3_; /** * Barred spinor wavefunction for 3 body decay */ mutable vector<SpinorBarWaveFunction> wavebar3_; /** * Vector wavefunction for 3 body decay */ mutable vector<VectorWaveFunction> gluon_; }; } #endif /* HERWIG_FFSDecayer_H */ diff --git a/Decay/General/FFVDecayer.cc b/Decay/General/FFVDecayer.cc --- a/Decay/General/FFVDecayer.cc +++ b/Decay/General/FFVDecayer.cc @@ -1,433 +1,456 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> ) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractFFVVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<FFVVertexPtr> (vertex); + 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(); } } // 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) = - vertex_->evaluate(scale,wave_[if1],wavebar_[if2],vector_[vhel]); + (*ME())(if1, if2,vhel) = 0.; else - (*ME())(if2, if1, vhel) = - vertex_->evaluate(scale,wave_[if1],wavebar_[if2],vector_[vhel]); + (*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_) { + 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_->setCoupling(sqr(inpart.second), in, + perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in, outa.first, outb.first); else { swap(mu1,mu2); - perturbativeVertex_->setCoupling(sqr(inpart.second),in, + perturbativeVertex_[0]->setCoupling(sqr(inpart.second),in, outb.first,outa.first); } - Complex cl(perturbativeVertex_->left()),cr(perturbativeVertex_->right()); + 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_->norm())*me2*pcm/16./Constants::pi; + 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 = vertex_->evaluate(scale,spinorInter,wavebar3_[ifo],vector3_[iv]); + 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 = vertex_->evaluate(scale,wave3_[ifo], spinorBarInter,vector3_[iv]); + 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 = vertex_->evaluate(scale,wave3_[ifi],spinorBarInter,vector3_[iv]); + 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 = vertex_->evaluate(scale,spinorInter,wavebar3_[ifi],vector3_[iv]); + 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 = vertex_->evaluate(scale,wave3_[ifi],wavebar3_[ifo],vectorInter); - else - diag = vertex_->evaluate(scale,wave3_[ifo],wavebar3_[ifi],vectorInter); + 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/FFVDecayer.h b/Decay/General/FFVDecayer.h --- a/Decay/General/FFVDecayer.h +++ b/Decay/General/FFVDecayer.h @@ -1,216 +1,223 @@ // -*- C++ -*- // // FFVDecayer.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_FFVDecayer_H #define HERWIG_FFVDecayer_H // // This is the declaration of the FFVDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Vector/FFVVertex.h" #include "ThePEG/Helicity/Vertex/Vector/VVVVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::FFVVertexPtr; /** \ingroup Decay * The FFVDecayer class implements the decay of a fermion * to a fermion and a vector in a general model. It holds an FFVVertex * pointer that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class FFVDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ FFVDecayer() {} public: /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FFVDecayer & operator=(const FFVDecayer &); private: /** * Abstract pointer to AbstractFFVVertex */ - AbstractFFVVertexPtr vertex_; + vector<AbstractFFVVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - FFVVertexPtr perturbativeVertex_; + vector<FFVVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from incoming (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> outgoingVertexF_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from outgoing vector */ map<ShowerInteraction,AbstractVVVVertexPtr> outgoingVertexV_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Spinor wavefunction */ mutable vector<SpinorWaveFunction> wave_ ; /** * Barred spinor wavefunction */ mutable vector<SpinorBarWaveFunction> wavebar_; /** * Polarization vectors */ mutable vector<VectorWaveFunction> vector_; /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * vector wavefunction for 3 body decay */ mutable vector<VectorWaveFunction> vector3_; /** * Spinor wavefunction for 3 body decay */ mutable vector<SpinorWaveFunction> wave3_; /** * Barred spinor wavefunction for 3 body decay */ mutable vector<SpinorBarWaveFunction> wavebar3_; /** * Vector wavefunction for gluon in 3 body decay */ mutable vector<VectorWaveFunction> gluon_; }; } #endif /* HERWIG_FFVDecayer_H */ diff --git a/Decay/General/FRSDecayer.cc b/Decay/General/FRSDecayer.cc --- a/Decay/General/FRSDecayer.cc +++ b/Decay/General/FRSDecayer.cc @@ -1,183 +1,189 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractRFSVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<RFSVertexPtr> (vertex); + 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(); } } // 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) { - if(ferm) (*ME())(if1, if2, 0) = - vertex_->evaluate(scale,wave_[if1],RSwavebar_[if2],scal); - else (*ME())(if1, if2, 0) = - vertex_->evaluate(scale,RSwave_[if2],wavebar_[if1],scal); + (*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_) { + 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_->setCoupling(sqr(inpart.second), outa.first, + perturbativeVertex_[0]->setCoupling(sqr(inpart.second), outa.first, in, outb.first); - Complex left = perturbativeVertex_-> left()*perturbativeVertex_-> norm(); - Complex right = perturbativeVertex_->right()*perturbativeVertex_-> norm(); + 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/FRSDecayer.h b/Decay/General/FRSDecayer.h --- a/Decay/General/FRSDecayer.h +++ b/Decay/General/FRSDecayer.h @@ -1,163 +1,164 @@ // -*- C++ -*- // // FRSDecayer.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_FRSDecayer_H #define HERWIG_FRSDecayer_H // // This is the declaration of the FRSDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Scalar/RFSVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::RFSVertexPtr; /** \ingroup Decay * The FRSDecayer class implements the decay of a fermion * to a spin-3/2 fermion and a vector in a general model. It holds an RFVVertex * pointer that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class FRSDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ FRSDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FRSDecayer & operator=(const FRSDecayer &); private: /** * Abstract pointer to AbstractFRSVertex */ - AbstractRFSVertexPtr vertex_; + vector<AbstractRFSVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - RFSVertexPtr perturbativeVertex_; + vector<RFSVertexPtr> perturbativeVertex_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Spinor wavefunctions */ mutable vector<SpinorWaveFunction> wave_ ; /** * Barred spinor wavefunctions */ mutable vector<SpinorBarWaveFunction> wavebar_; /** * RS Spinor wavefunctions */ mutable vector<RSSpinorWaveFunction> RSwave_ ; /** * Barred RS spinor wavefunctions */ mutable vector<RSSpinorBarWaveFunction> RSwavebar_; }; } #endif /* HERWIG_FRSDecayer_H */ diff --git a/Decay/General/FRVDecayer.cc b/Decay/General/FRVDecayer.cc --- a/Decay/General/FRVDecayer.cc +++ b/Decay/General/FRVDecayer.cc @@ -1,213 +1,219 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractRFVVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<RFVVertexPtr> (vertex); + 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(); } } // 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; - if(ferm) - (*ME())(if1, if2,vhel) = - vertex_->evaluate(scale,wave_[if1], - RSwavebar_[if2],vector_[vhel]); - else - (*ME())(if1, if2, vhel) = - vertex_->evaluate(scale,RSwave_[if2], - wavebar_[if1],vector_[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_) { + 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_->setCoupling(sqr(inpart.second), outa.first, + perturbativeVertex_[0]->setCoupling(sqr(inpart.second), outa.first, in, outb.first); - 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; + 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/FRVDecayer.h b/Decay/General/FRVDecayer.h --- a/Decay/General/FRVDecayer.h +++ b/Decay/General/FRVDecayer.h @@ -1,170 +1,171 @@ // -*- C++ -*- // // FRVDecayer.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_FRVDecayer_H #define HERWIG_FRVDecayer_H // // This is the declaration of the FRVDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Vector/RFVVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::RFVVertexPtr; /** \ingroup Decay * The FRVDecayer class implements the decay of a fermion * to a spin-3/2 fermion and a vector in a general model. It holds an RFVVertex * pointer that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class FRVDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ FRVDecayer() {} public: /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FRVDecayer & operator=(const FRVDecayer &); private: /** * Abstract pointer to AbstractFRVVertex */ - AbstractRFVVertexPtr vertex_; + vector<AbstractRFVVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - RFVVertexPtr perturbativeVertex_; + vector<RFVVertexPtr> perturbativeVertex_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Spinor wavefunction */ mutable vector<SpinorWaveFunction> wave_ ; /** * Barred spinor wavefunction */ mutable vector<SpinorBarWaveFunction> wavebar_; /** * RS Spinor wavefunction */ mutable vector<RSSpinorWaveFunction> RSwave_ ; /** * Barred RS spinor wavefunction */ mutable vector<RSSpinorBarWaveFunction> RSwavebar_; /** * Polarization vectors */ mutable vector<VectorWaveFunction> vector_; }; } #endif /* HERWIG_FRVDecayer_H */ diff --git a/Decay/General/GeneralTwoBodyDecayer.h b/Decay/General/GeneralTwoBodyDecayer.h --- a/Decay/General/GeneralTwoBodyDecayer.h +++ b/Decay/General/GeneralTwoBodyDecayer.h @@ -1,305 +1,306 @@ // -*- C++ -*- // // GeneralTwoBodyDecayer.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_GeneralTwoBodyDecayer_H #define HERWIG_GeneralTwoBodyDecayer_H // // This is the declaration of the GeneralTwoBodyDecayer class. // #include "Herwig/Decay/PerturbativeDecayer.h" #include "Herwig/Decay/DecayPhaseSpaceMode.h" #include "ThePEG/Helicity/Vertex/VertexBase.h" #include "GeneralTwoBodyDecayer.fh" namespace Herwig { using namespace ThePEG; using Helicity::VertexBasePtr; /** \ingroup Decay * The GeneralTwoBodyDecayer class is designed to be the base class * for 2 body decays for some general model. It inherits from * PerturbativeDecayer and implements the modeNumber() virtual function * that is the same for all of the decays. A decayer for * a specific spin configuration should inherit from this and implement * the me2() and partialWidth() member functions. The colourConnections() * member should be called from inside me2() in the inheriting decayer * to set up the colour lines. * * @see \ref GeneralTwoBodyDecayerInterfaces "The interfaces" * defined for GeneralTwoBodyDecayer. * @see PerturbativeDecayer */ class GeneralTwoBodyDecayer: public PerturbativeDecayer { public: /** A ParticleData ptr and (possible) mass pair.*/ typedef pair<tcPDPtr, Energy> PMPair; public: /** * The default constructor. */ GeneralTwoBodyDecayer() : maxWeight_(1.), colour_(1,DVector(1,1.)) {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * For a given decay mode and a given particle instance, perform the * decay and return the decay products. As this is the base class this * is not implemented. * @return The vector of particles produced in the decay. */ virtual ParticleVector decay(const Particle & parent, const tPDVector & children) const; /** * Which of the possible decays is required * @param cc Is this mode the charge conjugate * @param parent The decaying particle * @param children The decay products */ virtual int modeNumber(bool & cc, tcPDPtr parent,const tPDVector & children) const; /** * Return the matrix element squared for a given mode and phase-space channel * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int , const Particle & part, const ParticleVector & decay, MEOption meopt) const = 0; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Specify the \f$1\to2\f$ matrix element to be used in the running width * calculation. * @param dm The DecayMode * @param mecode The code for the matrix element as described * in the GenericWidthGenerator class. * @param coupling The coupling for the matrix element. * @return True if the the order of the particles in the * decayer is the same as the DecayMode tag. */ virtual bool twoBodyMEcode(const DecayMode & dm, int & mecode, double & coupling) const; /** * An overidden member to calculate a branching ratio for a certain * particle instance. * @param dm The DecayMode of the particle * @param p The particle object * @param oldbrat The branching fraction given in the DecayMode object */ virtual double brat(const DecayMode & dm, const Particle & p, double oldbrat) const; //@} /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>) =0; protected: /** @name Functions used by inheriting decayers. */ //@{ /** * Set integration weight * @param wgt Maximum integration weight */ void setWeight(double wgt) { maxWeight_ = wgt; } /** * Set colour connections * @param parent Parent particle * @param out Particle vector containing particles to * connect colour lines */ void colourConnections(const Particle & parent, const ParticleVector & out) const; /** * Compute the spin and colour factor */ double colourFactor(tcPDPtr in, tcPDPtr out1, tcPDPtr out2) const; /** * Calculate matrix element ratio R/B */ double matrixElementRatio(const Particle & inpart, const ParticleVector & decay2, const ParticleVector & decay3, MEOption meopt, ShowerInteraction inter); /** * Set the information on the decay */ void decayInfo(PDPtr incoming, PDPair outgoing); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Standard Interfaced functions. */ //@{ /** * Initialize this object after the setup phase before saving an * EventGenerator to disk. * @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(); //@} protected: /** * Member for the generation of additional hard radiation */ //@{ /** * Return the matrix of colour factors */ typedef vector<pair<int,double > > CFlowPairVec; typedef vector<CFlowPairVec> CFlow; const vector<DVector> & getColourFactors(const Particle & inpart, const ParticleVector & decay, unsigned int & nflow); const CFlow & colourFlows(const Particle & inpart, const ParticleVector & decay); /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ GeneralTwoBodyDecayer & operator=(const GeneralTwoBodyDecayer &); private: /** * Store the incoming particle */ PDPtr incoming_; /** * Outgoing particles */ vector<PDPtr> outgoing_; /** * Maximum weight for integration */ double maxWeight_; /** * Store colour factors for ME calc. */ vector<DVector> colour_; }; /** * Write a map with ShowerInteraction as the key */ template<typename T, typename Cmp, typename A> inline PersistentOStream & operator<<(PersistentOStream & os, const map<ShowerInteraction,T,Cmp,A> & m) { os << m.size(); if(m.find(ShowerInteraction::QCD)!=m.end()) { os << 0 << m.at(ShowerInteraction::QCD); } if(m.find(ShowerInteraction::QED)!=m.end()) { os << 1 << m.at(ShowerInteraction::QED); } return os; } /** * Read a map with ShowerInteraction as the key */ template <typename T, typename Cmp, typename A> inline PersistentIStream & operator>>(PersistentIStream & is, map<ShowerInteraction,T,Cmp,A> & m) { m.clear(); long size; int k; is >> size; while ( size-- && is ) { is >> k; if(k==0) is >> m[ShowerInteraction::QCD]; else if(k==1) is >> m[ShowerInteraction::QED]; else assert(false); } return is; } } #endif /* HERWIG_GeneralTwoBodyDecayer_H */ diff --git a/Decay/General/SFFDecayer.cc b/Decay/General/SFFDecayer.cc --- a/Decay/General/SFFDecayer.cc +++ b/Decay/General/SFFDecayer.cc @@ -1,478 +1,491 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> ) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractFFSVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<FFSVertexPtr> (vertex); + 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); } 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) = vertex_->evaluate(scale,wave_[ia], - wavebar_[ifm],swave_); - } - else { - (*ME())(0, ifm, ia) = vertex_->evaluate(scale,wave_[ia], - wavebar_[ifm],swave_); + 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_) { + if(perturbativeVertex_.size()==1 && + perturbativeVertex_[0]) { tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first; - perturbativeVertex_->setCoupling(sqr(inpart.second), outb.first, outa.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_->norm()); - Complex al(perturbativeVertex_->left()), ar(perturbativeVertex_->right()); + 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 = vertex_->evaluate(scale,wave3_[ia], - wavebar3_[ifm],scalarInter); + 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 = vertex_->evaluate(scale,wave3_[ia], interS,swave3_); + 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 = vertex_->evaluate(scale,interS,wavebar3_[ifm],swave3_); + 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/SFFDecayer.h b/Decay/General/SFFDecayer.h --- a/Decay/General/SFFDecayer.h +++ b/Decay/General/SFFDecayer.h @@ -1,226 +1,234 @@ // -*- C++ -*- // // SFFDecayer.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_SFFDecayer_H #define HERWIG_SFFDecayer_H // // This is the declaration of the SFFDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Scalar/FFSVertex.h" #include "ThePEG/Helicity/Vertex/Scalar/VSSVertex.h" #include "ThePEG/Helicity/Vertex/Vector/FFVVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::FFSVertexPtr; /** \ingroup Decay * The SFFDecayer class implements the decay of a scalar to 2 * fermions in a general model. It holds an FFSVertex pointer that * must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class SFFDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ SFFDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); /** * Indentify outgoing vertices for the fermion and antifermion */ void identifyVertices(const int iferm, const int ianti, const Particle & inpart, const ParticleVector & decay, AbstractFFVVertexPtr & outgoingVertexF, AbstractFFVVertexPtr & outgoingVertexA, ShowerInteraction inter); /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ SFFDecayer & operator=(const SFFDecayer &); private: /** * Abstract pointer to AbstractFFSVertex */ - AbstractFFSVertexPtr vertex_; + vector<AbstractFFSVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - FFSVertexPtr perturbativeVertex_; + vector<FFSVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractVSSVertex for QCD radiation from incoming scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> outgoingVertex1_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> outgoingVertex2_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Scalar wavefunction */ mutable ScalarWaveFunction swave_; /** * Spinor wavefunction */ mutable vector<SpinorWaveFunction> wave_; /** * Barred spinor wavefunction */ mutable vector<SpinorBarWaveFunction> wavebar_; /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Scalar wavefunction for 3 body decay */ mutable ScalarWaveFunction swave3_; /** * Spinor wavefunction for 3 body decay */ mutable vector<SpinorWaveFunction> wave3_; /** * Barred spinor wavefunction for 3 body decay */ mutable vector<SpinorBarWaveFunction> wavebar3_; /** * Vector wavefunction for 3 body decay */ mutable vector<VectorWaveFunction> gluon_; }; } #endif /* HERWIG_SFFDecayer_H */ diff --git a/Decay/General/SRFDecayer.cc b/Decay/General/SRFDecayer.cc --- a/Decay/General/SRFDecayer.cc +++ b/Decay/General/SRFDecayer.cc @@ -1,181 +1,196 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractRFSVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<RFSVertexPtr> (vertex); + 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); } 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) = vertex_->evaluate(scale,wave_[ia], - RSwavebar_[ifm],swave_); - else - (*ME())(0, ifm, ia) = vertex_->evaluate(scale,RSwave_[ifm], - wavebar_[ia],swave_); + 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) = vertex_->evaluate(scale,wave_[ia], - RSwavebar_[ifm],swave_); - else - (*ME())(0, ia, ifm) = vertex_->evaluate(scale,RSwave_[ifm], - wavebar_[ia],swave_); + 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_) { + 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_->setCoupling(sqr(inpart.second),outb.first, + perturbativeVertex_[0]->setCoupling(sqr(inpart.second),outb.first, outa.first, in); } else { - perturbativeVertex_->setCoupling(sqr(inpart.second),outa.first, + perturbativeVertex_[0]->setCoupling(sqr(inpart.second),outa.first, outb.first, in); } - Complex left = perturbativeVertex_-> left()*perturbativeVertex_-> norm(); - Complex right = perturbativeVertex_->right()*perturbativeVertex_-> norm(); + 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/SRFDecayer.h b/Decay/General/SRFDecayer.h --- a/Decay/General/SRFDecayer.h +++ b/Decay/General/SRFDecayer.h @@ -1,170 +1,171 @@ // -*- C++ -*- // // SRFDecayer.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_SRFDecayer_H #define HERWIG_SRFDecayer_H // // This is the declaration of the SRFDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Scalar/RFSVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::RFSVertexPtr; /** \ingroup Decay * The SRFDecayer class implements the decay of a scalar to spin-3/2 * and spin-1/2 fermion in a general model. It holds an RFSVertex pointer that * must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class SRFDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ SRFDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ SRFDecayer & operator=(const SRFDecayer &); private: /** * Abstract pointer to AbstractFFSVertex */ - AbstractRFSVertexPtr vertex_; + vector<AbstractRFSVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - RFSVertexPtr perturbativeVertex_; + vector<RFSVertexPtr> perturbativeVertex_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Scalar wavefunction */ mutable ScalarWaveFunction swave_; /** * Spinor wavefunction */ mutable vector<SpinorWaveFunction> wave_; /** * Barred spinor wavefunction */ mutable vector<SpinorBarWaveFunction> wavebar_; /** * RS Spinor wavefunction */ mutable vector<RSSpinorWaveFunction> RSwave_; /** * Barred RS spinor wavefunction */ mutable vector<RSSpinorBarWaveFunction> RSwavebar_; }; } #endif /* HERWIG_SRFDecayer_H */ diff --git a/Decay/General/SSSDecayer.cc b/Decay/General/SSSDecayer.cc --- a/Decay/General/SSSDecayer.cc +++ b/Decay/General/SSSDecayer.cc @@ -1,398 +1,410 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> ) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractSSSVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<SSSVertexPtr> (vertex); + 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); } 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) = vertex_->evaluate(scale,s1,s2,swave_); + (*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_ && !perturbativeVertex_->kinematics()) { + 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_->setCoupling(scale, in, outa.first, outb.first); + perturbativeVertex_[0]->setCoupling(scale, in, outa.first, outb.first); Energy pcm = Kinematics::pstarTwoBodyDecay(inpart.second, outa.second, outb.second); - double c2 = norm(perturbativeVertex_->norm()); + 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 = vertex_->evaluate(scale,scal,anti,scalarInter); + 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 = vertex_->evaluate(scale,swave3_,anti,scalarInter); + 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 = vertex_->evaluate(scale,swave3_,scal,scalarInter); + 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/SSSDecayer.h b/Decay/General/SSSDecayer.h --- a/Decay/General/SSSDecayer.h +++ b/Decay/General/SSSDecayer.h @@ -1,203 +1,211 @@ // -*- C++ -*- // // SSSDecayer.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_SSSDecayer_H #define HERWIG_SSSDecayer_H // // This is the declaration of the SSSDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Scalar/SSSVertex.h" #include "ThePEG/Helicity/Vertex/Scalar/VSSVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::SSSVertexPtr; /** \ingroup Decay * The SSDecayer class implements the decay of a scalar * to 2 scalars in a general model. It holds a SSSVertex * pointer that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class SSSDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ SSSDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); /** * Indentify outgoing vertices for the scalar and anti scalar */ void identifyVertices(const int iscal, const int ianti, const Particle & inpart, const ParticleVector & decay, AbstractVSSVertexPtr & abstractOutgoingVertexS, AbstractVSSVertexPtr & abstractOutgoingVertexA, ShowerInteraction inter); /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ SSSDecayer & operator=(const SSSDecayer &); private: /** * Abstract pointer to AbstractSSSVertex */ - AbstractSSSVertexPtr vertex_; + vector<AbstractSSSVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - SSSVertexPtr perturbativeVertex_; + vector<SSSVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractVSSVertex for QCD radiation from incoming scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractVSSVertex for QCD radiation from outgoing scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> outgoingVertex1_; /** * Abstract pointer to AbstractVSSVertex for QCD radiation from outgoing scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> outgoingVertex2_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Scalar wavefunctions */ mutable Helicity::ScalarWaveFunction swave_; /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Scalar wavefunction for 3 body decay */ mutable Helicity::ScalarWaveFunction swave3_; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> gluon_; }; } #endif /* HERWIG_SSSDecayer_H */ diff --git a/Decay/General/SSVDecayer.cc b/Decay/General/SSVDecayer.cc --- a/Decay/General/SSVDecayer.cc +++ b/Decay/General/SSVDecayer.cc @@ -1,351 +1,366 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> fourV) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractVSSVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<VSSVertexPtr> (vertex); + 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); } 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) = vertex_->evaluate(scale,vector_[ix],sca, swave_); + 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) = vertex_->evaluate(scale,vector_[ix],sca,swave_); + 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_) { + 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_->setCoupling(sqr(inpart.second), outb.first, outa.first,in); + perturbativeVertex_[0]->setCoupling(sqr(inpart.second), outb.first, outa.first,in); } else { swap(mu1sq,mu2sq); - perturbativeVertex_->setCoupling(sqr(inpart.second), outa.first, outb.first,in); + 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_->norm())/8./Constants::pi; + 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 = vertex_->evaluate(scale,vector3_[iv],scal,scalarInter); + 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 = vertex_->evaluate(scale,vector3_[iv],scalarInter,swave3_); + 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 = vertex_->evaluate(scale,vectorInter,scal,swave3_); + 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/SSVDecayer.h b/Decay/General/SSVDecayer.h --- a/Decay/General/SSVDecayer.h +++ b/Decay/General/SSVDecayer.h @@ -1,216 +1,224 @@ // -*- C++ -*- // // SSVDecayer.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_SSVDecayer_H #define HERWIG_SSVDecayer_H // // This is the declaration of the SSVDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Helicity/Vertex/Scalar/VSSVertex.h" #include "ThePEG/Helicity/Vertex/Vector/VVVVertex.h" #include "ThePEG/Helicity/Vertex/Scalar/VVSSVertex.h" #include "ThePEG/Repository/EventGenerator.h" namespace Herwig { using namespace ThePEG; using Helicity::VSSVertexPtr; /** \ingroup Decay * The SSVDecayer class implements the decay of a scalar to a vector * and a scalar in a general model. It holds an VSSVertex pointer * that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class SSVDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ SSVDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ SSVDecayer & operator=(const SSVDecayer &); private: /** * Abstract pointer to AbstractFFVVertex */ - AbstractVSSVertexPtr vertex_; + vector<AbstractVSSVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - VSSVertexPtr perturbativeVertex_; + vector<VSSVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractVSSVertex for QCD radiation from incoming scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractVSSVertex for QCD radiation from outgoing scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> outgoingVertexS_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from outgoing vector */ map<ShowerInteraction,AbstractVVVVertexPtr> outgoingVertexV_; /** * Abstract pointer to AbstractVVSSVertex for QCD radiation from 4 point vertex */ map<ShowerInteraction,AbstractVVSSVertexPtr> fourPointVertex_; /** * Spinor density matrix */ mutable RhoDMatrix rho_; /** * Scalar wavefunction */ mutable Helicity::ScalarWaveFunction swave_; /** * Vector wavefunction */ mutable vector<Helicity::VectorWaveFunction> vector_; /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Scalar wavefunction for 3 body decay */ mutable Helicity::ScalarWaveFunction swave3_; /** * Scalar wavefunction for 3 body decay */ mutable Helicity::ScalarWaveFunction scal_; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> vector3_; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> gluon_; }; } #endif /* HERWIG_SSVDecayer_H */ diff --git a/Decay/General/SVVDecayer.cc b/Decay/General/SVVDecayer.cc --- a/Decay/General/SVVDecayer.cc +++ b/Decay/General/SVVDecayer.cc @@ -1,421 +1,432 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> ) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractVVSVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<VVSVertexPtr> (vertex); + 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); } 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) = vertex_->evaluate(scale,vectors_[0][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_) { + if(perturbativeVertex_.size()==1 && + perturbativeVertex_[0]) { Energy2 scale(sqr(inpart.second)); tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first; - perturbativeVertex_->setCoupling(scale, outa.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_->norm())* + 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 = vertex_->evaluate(scale,vectors3_[0][iv1], - vectors3_[1][iv2],scalarInter); + 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 =vertex_->evaluate(scale,vectorInter,vectors3_[1][iv2],swave3_); + 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 = vertex_->evaluate(scale,vectors3_[0][iv1],vectorInter,swave3_); + 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/SVVDecayer.h b/Decay/General/SVVDecayer.h --- a/Decay/General/SVVDecayer.h +++ b/Decay/General/SVVDecayer.h @@ -1,222 +1,230 @@ // -*- C++ -*- // // SVVDecayer.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_SVVDecayer_H #define HERWIG_SVVDecayer_H // // This is the declaration of the SVVDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Scalar/VVSVertex.h" #include "ThePEG/Helicity/WaveFunction/ScalarWaveFunction.h" #include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h" namespace Herwig { using namespace ThePEG; using Helicity::VVSVertexPtr; /** \ingroup Decay * This SVVDecayer class implements the decay of a scalar to * 2 vector bosons using either the tree level VVSVertex or the loop vertex. * It inherits from * GeneralTwoBodyDecayer and implements the virtual member functions me2() * and partialWidth(). It also stores a pointer to the VVSVertex. * * @see GeneralTwoBodyDecayer * */ class SVVDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ SVVDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} protected: /** * Find the vertices for the decay */ void identifyVertices(const Particle & inpart, const ParticleVector & decay, AbstractVVVVertexPtr & outgoingVertex1, AbstractVVVVertexPtr & outgoingVertex2, ShowerInteraction inter); private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ SVVDecayer & operator=(const SVVDecayer &); private: /** * Abstract pointer to general VVS vertex */ - AbstractVVSVertexPtr vertex_; + vector<AbstractVVSVertexPtr> vertex_; /** * Pointer to the perturbative form */ - VVSVertexPtr perturbativeVertex_; + vector<VVSVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractVSSVertex for QCD radiation from incoming scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from the 1st outgoing vector */ map<ShowerInteraction,AbstractVVVVertexPtr> outgoingVertex1_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from the 2nd outgoing vector */ map<ShowerInteraction,AbstractVVVVertexPtr> outgoingVertex2_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Scalar wavefunction */ mutable Helicity::ScalarWaveFunction swave_; /** * Vector wavefunctions */ mutable vector<Helicity::VectorWaveFunction> vectors_[2]; private: /** * Member for the POWHEG correction */ //@{ /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Scalar wavefunction for 3 body decay */ mutable ScalarWaveFunction swave3_; /** * Vector wavefunctions */ mutable vector<Helicity::VectorWaveFunction> vectors3_[2]; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> gluon_; //@} }; } #endif /* HERWIG_SVVDecayer_H */ diff --git a/Decay/General/TFFDecayer.cc b/Decay/General/TFFDecayer.cc --- a/Decay/General/TFFDecayer.cc +++ b/Decay/General/TFFDecayer.cc @@ -1,340 +1,351 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> fourV) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractFFTVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<FFTVertexPtr> (vertex); + 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); } 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) = - vertex_->evaluate(scale,wave_[ahel], - wavebar_[fhel],tensors_[thel]); + (*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) = - vertex_->evaluate(scale,wave_[ahel], - wavebar_[fhel],tensors_[thel]); + (*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_) { + if(perturbativeVertex_.size()==1 && + perturbativeVertex_[0]) { Energy2 scale = sqr(inpart.second); tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first; - perturbativeVertex_->setCoupling(scale, in, outa.first, outb.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_->norm())*me2*pcm/(8.*Constants::pi); + 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 = vertex_->evaluate(scale,wave3_[ia], interS,tensors3_[it]); + 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 = vertex_->evaluate(scale,interS,wavebar3_[ifm],tensors3_[it]); + 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/TFFDecayer.h b/Decay/General/TFFDecayer.h --- a/Decay/General/TFFDecayer.h +++ b/Decay/General/TFFDecayer.h @@ -1,215 +1,223 @@ // -*- C++ -*- // // TFFDecayer.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_TFFDecayer_H #define HERWIG_TFFDecayer_H // // This is the declaration of the TFFDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Tensor/FFTVertex.h" #include "ThePEG/Helicity/Vertex/Vector/FFVVertex.h" #include "ThePEG/Helicity/Vertex/Tensor/FFVTVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::FFTVertexPtr; /** \ingroup Decay * The TFFDecayer class implements the decay of a tensor * to 2 fermions in a general model. It holds an FFTVertex pointer * that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class TFFDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ TFFDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ TFFDecayer & operator=(const TFFDecayer &); private: /** * Abstract pointer to AbstractFFTVertex */ - AbstractFFTVertexPtr vertex_; + vector<AbstractFFTVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - FFTVertexPtr perturbativeVertex_; + vector<FFTVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> outgoingVertex1_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> outgoingVertex2_; /** * Abstract pointer to AbstractFFVTVertex for QCD radiation from 4 point vertex */ map<ShowerInteraction,AbstractFFVTVertexPtr> fourPointVertex_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Polarization tensors for the decaying particle */ mutable vector<TensorWaveFunction> tensors_; /** * Spinors for the decay products */ mutable vector<SpinorWaveFunction> wave_; /** * Barred spinors for the decay products */ mutable vector<SpinorBarWaveFunction> wavebar_; /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Tensor wavefunction for 3 body decay */ mutable vector<TensorWaveFunction> tensors3_; /** * Spinor wavefunction for 3 body decay */ mutable vector<SpinorWaveFunction> wave3_; /** * Barred spinor wavefunction for 3 body decay */ mutable vector<SpinorBarWaveFunction> wavebar3_; /** * Vector wavefunction for 3 body decay */ mutable vector<VectorWaveFunction> gluon_; }; } #endif /* HERWIG_TFFDecayer_H */ diff --git a/Decay/General/TSSDecayer.cc b/Decay/General/TSSDecayer.cc --- a/Decay/General/TSSDecayer.cc +++ b/Decay/General/TSSDecayer.cc @@ -1,125 +1,130 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & , const vector<map<ShowerInteraction,VertexBasePtr> > & , map<ShowerInteraction,VertexBasePtr> ) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractSSTVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<SSTVertexPtr> (vertex); + 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); } 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) = vertex_->evaluate(scale,sca1,sca2,tensors_[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_) { + if(perturbativeVertex_.size()==1 && + perturbativeVertex_[0]) { Energy2 scale(sqr(inpart.second)); tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first; - perturbativeVertex_->setCoupling(scale, outa.first, outb.first, in); + 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_->norm())*me2*pcm/(8.*Constants::pi); + 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/TSSDecayer.h b/Decay/General/TSSDecayer.h --- a/Decay/General/TSSDecayer.h +++ b/Decay/General/TSSDecayer.h @@ -1,150 +1,151 @@ // -*- C++ -*- // // TSSDecayer.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_TSSDecayer_H #define HERWIG_TSSDecayer_H // // This is the declaration of the TSSDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Tensor/SSTVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::SSTVertexPtr; /** \ingroup Decay * The TSSDecayer class implements the decay of a tensor * to 2 scalars in a general model. It holds an SSTVertex pointer * that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class TSSDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ TSSDecayer() {} public: /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ TSSDecayer & operator=(const TSSDecayer &); private: /** * Abstract pointer to AbstractSSTVertex */ - AbstractSSTVertexPtr vertex_; + vector<AbstractSSTVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - SSTVertexPtr perturbativeVertex_; + vector<SSTVertexPtr> perturbativeVertex_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Polarization tensors of the decaying particle */ mutable vector<Helicity::TensorWaveFunction> tensors_; }; } #endif /* HERWIG_TSSDecayer_H */ diff --git a/Decay/General/TVVDecayer.cc b/Decay/General/TVVDecayer.cc --- a/Decay/General/TVVDecayer.cc +++ b/Decay/General/TVVDecayer.cc @@ -1,329 +1,338 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> fourV) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractVVTVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<VVTVertexPtr> (vertex); + 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); } 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) = vertex_->evaluate(scale, - vectors_[0][v1hel], - vectors_[1][v2hel], - tensors_[thel]); + (*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_) { + if(perturbativeVertex_.size()==1 && + perturbativeVertex_[0]) { Energy2 scale(sqr(inpart.second)); tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first; - perturbativeVertex_->setCoupling(scale, outa.first, outb.first, in); + 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_->norm())*me2*pcm + 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 = vertex_->evaluate(scale,vectors3_[1][iv1], - vectInter,tensors3_[it]); + 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 = vertex_->evaluate(scale,vectInter,vectors3_[0][iv0], - tensors3_[it]); + 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/TVVDecayer.h b/Decay/General/TVVDecayer.h --- a/Decay/General/TVVDecayer.h +++ b/Decay/General/TVVDecayer.h @@ -1,205 +1,213 @@ // -*- C++ -*- // // TVVDecayer.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_TVVDecayer_H #define HERWIG_TVVDecayer_H // // This is the declaration of the TVVDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Vector/VVVVertex.h" #include "ThePEG/Helicity/Vertex/Tensor/VVTVertex.h" #include "ThePEG/Helicity/Vertex/Tensor/VVVTVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::VVTVertexPtr; /** \ingroup Decay * The TVVDecayer class implements the decay of a tensor * to 2 vector bosons in a general model. It holds a VVTVertex pointer * that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class TVVDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ TVVDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ TVVDecayer & operator=(const TVVDecayer &); private: /** * Abstract pointer to AbstractVVTVertex */ - AbstractVVTVertexPtr vertex_; + vector<AbstractVVTVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - VVTVertexPtr perturbativeVertex_; + vector<VVTVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from outgoing vector */ map<ShowerInteraction,AbstractVVVVertexPtr> outgoingVertex1_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from outgoing vector */ map<ShowerInteraction,AbstractVVVVertexPtr> outgoingVertex2_; /** * Abstract pointer to AbstractVVVTVertex for QCD radiation from 4 point vertex */ map<ShowerInteraction,AbstractVVVTVertexPtr> fourPointVertex_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Polarization tensors of decaying particle */ mutable vector<Helicity::TensorWaveFunction> tensors_; /** * Polarization vectors of outgoing vector bosons */ mutable vector<Helicity::VectorWaveFunction> vectors_[2]; /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Tensor wavefunction for 3 body decay */ mutable vector<Helicity::TensorWaveFunction> tensors3_; /** * Polarization vectors of outgoing vector bosons */ mutable vector<Helicity::VectorWaveFunction> vectors3_[2]; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> gluon_; }; } #endif /* HERWIG_TVVDecayer_H */ diff --git a/Decay/General/VFFDecayer.cc b/Decay/General/VFFDecayer.cc --- a/Decay/General/VFFDecayer.cc +++ b/Decay/General/VFFDecayer.cc @@ -1,456 +1,470 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> ) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractFFVVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<FFVVertexPtr> (vertex); + 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); } 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) = - vertex_->evaluate(scale,wave_[ia], - wavebar_[ifm],vectors_[vhel]); + (*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)= - vertex_->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_) { + 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_->setCoupling(sqr(inpart.second), outa.first, outb.first,in); - Complex cl(perturbativeVertex_->left()), cr(perturbativeVertex_->right()); + 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_->norm())*me2*pcm / + 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 = vertex_->evaluate(scale,wave3_[ia],wavebar3_[ifm],vectorInter); + 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 = vertex_->evaluate(scale,wave3_[ia], interS,vector3_[iv]); + 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 = vertex_->evaluate(scale,interS,wavebar3_[ifm],vector3_[iv]); + 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/VFFDecayer.h b/Decay/General/VFFDecayer.h --- a/Decay/General/VFFDecayer.h +++ b/Decay/General/VFFDecayer.h @@ -1,224 +1,232 @@ // -*- C++ -*- // // VFFDecayer.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_VFFDecayer_H #define HERWIG_VFFDecayer_H // // This is the declaration of the VFFDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Vector/FFVVertex.h" #include "ThePEG/Helicity/Vertex/Vector/VVVVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::FFVVertexPtr; /** \ingroup Decay * The VFFDecayer class implements the decay of a vector * to 2 fermions in a general model. It holds an FFVVertex pointer * that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class VFFDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ VFFDecayer() {} public: /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); /** * Indentify outgoing vertices for the fermion and antifermion */ void identifyVertices(const int iferm, const int ianti, const Particle & inpart, const ParticleVector & decay, AbstractFFVVertexPtr & abstractOutgoingVertexF, AbstractFFVVertexPtr & abstractOutgoingVertexA, ShowerInteraction inter); /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ VFFDecayer & operator=(const VFFDecayer &); private: /** * Abstract pointer to AbstractFFVVertex */ - AbstractFFVVertexPtr vertex_; + vector<AbstractFFVVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - FFVVertexPtr perturbativeVertex_; + vector<FFVVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from incoming vector */ map<ShowerInteraction,AbstractVVVVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> outgoingVertex1_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing (anti)fermion */ map<ShowerInteraction,AbstractFFVVertexPtr> outgoingVertex2_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Polarization vectors for the decaying particle */ mutable vector<VectorWaveFunction> vectors_; /** * Spinors for the decay products */ mutable vector<SpinorWaveFunction> wave_; /** * Barred spinors for the decay products */ mutable vector<SpinorBarWaveFunction> wavebar_; /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Scalar wavefunction for 3 body decay */ mutable vector<VectorWaveFunction> vector3_; /** * Spinor wavefunction for 3 body decay */ mutable vector<SpinorWaveFunction> wave3_; /** * Barred spinor wavefunction for 3 body decay */ mutable vector<SpinorBarWaveFunction> wavebar3_; /** * Vector wavefunction for 3 body decay */ mutable vector<VectorWaveFunction> gluon_; }; } #endif /* HERWIG_VFFDecayer_H */ diff --git a/Decay/General/VSSDecayer.cc b/Decay/General/VSSDecayer.cc --- a/Decay/General/VSSDecayer.cc +++ b/Decay/General/VSSDecayer.cc @@ -1,405 +1,416 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> ) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractVSSVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<VSSVertexPtr> (vertex); + 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); } 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) = vertex_->evaluate(scale,vectors_[ix],sca1,sca2); + (*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_) { + if(perturbativeVertex_.size()==1 && + perturbativeVertex_[0]) { tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first; - perturbativeVertex_->setCoupling(sqr(inpart.second), in, outa.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_->norm())*me2*pcm / + 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 = vertex_->evaluate(scale,vectorInter,scal,anti); + 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 =vertex_->evaluate(scale,vector3_[iv],anti,scalarInter); + 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 = vertex_->evaluate(scale,vector3_[iv],scal,scalarInter); + 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/VSSDecayer.h b/Decay/General/VSSDecayer.h --- a/Decay/General/VSSDecayer.h +++ b/Decay/General/VSSDecayer.h @@ -1,203 +1,211 @@ // -*- C++ -*- // // VSSDecayer.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_VSSDecayer_H #define HERWIG_VSSDecayer_H // // This is the declaration of the VSSDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Helicity/Vertex/Scalar/VSSVertex.h" #include "ThePEG/Helicity/Vertex/Vector/VVVVertex.h" #include "ThePEG/Repository/EventGenerator.h" namespace Herwig { using namespace ThePEG; using Helicity::VSSVertexPtr; /** \ingroup Decay * The VSSDecayer class implements the decay of a vector * to 2 scalars in a general model. It holds an VSSVertex pointer * that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class VSSDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ VSSDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); /** * Indentify outgoing vertices for the scalar and antiscalar */ void identifyVertices(const int iscal, const int ianti, const Particle & inpart, const ParticleVector & decay, AbstractVSSVertexPtr & abstractOutgoingVertexS, AbstractVSSVertexPtr & abstractOutgoingVertexA, ShowerInteraction inter); /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ VSSDecayer & operator=(const VSSDecayer &); private: /** * Abstract pointer to AbstractVSSVertex */ - AbstractVSSVertexPtr vertex_; + vector<AbstractVSSVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - VSSVertexPtr perturbativeVertex_; + vector<VSSVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from incoming vector */ map<ShowerInteraction,AbstractVVVVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> outgoingVertex1_; /** * Abstract pointer to AbstractFFVVertex for QCD radiation from outgoing scalar */ map<ShowerInteraction,AbstractVSSVertexPtr> outgoingVertex2_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Polarization vectors for the decaying particle */ mutable vector<Helicity::VectorWaveFunction> vectors_; /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> vector3_; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> gluon_; }; } #endif /* HERWIG_VSSDecayer_H */ diff --git a/Decay/General/VVSDecayer.cc b/Decay/General/VVSDecayer.cc --- a/Decay/General/VVSDecayer.cc +++ b/Decay/General/VVSDecayer.cc @@ -1,302 +1,313 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr>) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractVVSVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<VVSVertexPtr> (vertex); + 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); } 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) = - vertex_->evaluate(scale,vectors_[0][in],vectors_[1][out],sca); + (*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_) { + 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_->setCoupling(sqr(inpart.second), in, + perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in, outa.first, outb.first); else { - perturbativeVertex_->setCoupling(sqr(inpart.second), in, + perturbativeVertex_[0]->setCoupling(sqr(inpart.second), in, outb.first, outa.first); swap(mu1sq, mu2sq); } - double vn = norm(perturbativeVertex_->norm()); + 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 = vertex_->evaluate(scale,vectorInter,vectors3_[1][iv1],scal); + 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 =vertex_->evaluate(scale,vectors3_[0][iv0],vectorInter,scal); + 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 = vertex_->evaluate(scale,vectors3_[0][iv0],vectors3_[1][iv1],scalarInter); + 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/VVSDecayer.h b/Decay/General/VVSDecayer.h --- a/Decay/General/VVSDecayer.h +++ b/Decay/General/VVSDecayer.h @@ -1,193 +1,201 @@ // -*- C++ -*- #ifndef THEPEG_VVSDecayer_H #define THEPEG_VVSDecayer_H // // This is the declaration of the VVSDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Helicity/Vertex/Scalar/VVSVertex.h" #include "ThePEG/Repository/EventGenerator.h" namespace Herwig { using namespace ThePEG; using Helicity::VVSVertexPtr; /** \ingroup Decay * The VVSDecayer class implements the decay of a vector to a * vector and a scalar in a general model. It holds an VVSVertex pointer * that must be typecast from the VertexBase pointer helid in the * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see \ref VVSDecayerInterfaces "The interfaces" * defined for VVSDecayer. */ class VVSDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ VVSDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ VVSDecayer & operator=(const VVSDecayer &); private: /** * Abstract pointer to AbstractVVSVertex */ - AbstractVVSVertexPtr vertex_; + vector<AbstractVVSVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - VVSVertexPtr perturbativeVertex_; + vector<VVSVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from incoming vector */ map<ShowerInteraction,AbstractVVVVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from the first outgoing vector */ map<ShowerInteraction,AbstractVVVVertexPtr> outgoingVertexV_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from the second outgoing vector */ map<ShowerInteraction,AbstractVSSVertexPtr> outgoingVertexS_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Vector wavefunctions */ mutable vector<Helicity::VectorWaveFunction> vectors_[2]; private: /** * Members for the POWHEG correction */ //@{ /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Vector wavefunctions */ mutable vector<Helicity::VectorWaveFunction> vectors3_[2]; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> gluon_; //@} }; } #endif /* THEPEG_VVSDecayer_H */ diff --git a/Decay/General/VVVDecayer.cc b/Decay/General/VVVDecayer.cc --- a/Decay/General/VVVDecayer.cc +++ b/Decay/General/VVVDecayer.cc @@ -1,429 +1,441 @@ // -*- 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, - VertexBasePtr vertex, + vector<VertexBasePtr> vertex, map<ShowerInteraction,VertexBasePtr> & inV, const vector<map<ShowerInteraction,VertexBasePtr> > & outV, map<ShowerInteraction,VertexBasePtr> fourV) { decayInfo(incoming,outgoing); - vertex_ = dynamic_ptr_cast<AbstractVVVVertexPtr>(vertex); - perturbativeVertex_ = dynamic_ptr_cast<VVVVertexPtr> (vertex); + 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); } 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) = vertex_-> - evaluate(scale,vectors_[1][iv2],vectors_[2][iv3],vectors_[0][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_) { + if(perturbativeVertex_.size()==1 && + perturbativeVertex_[0]) { tcPDPtr in = inpart.first->CC() ? tcPDPtr(inpart.first->CC()) : inpart.first; - perturbativeVertex_->setCoupling(sqr(inpart.second), in, - outa.first, outb.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_->norm()); + 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 = vertex_->evaluate(scale,vectorInter,vectors3_[0][iv1], - vectors3_[1][iv2]); + 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 =vertex_->evaluate(scale,vector3_[iv0],vectorInter,vectors3_[1][iv2]); + 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 = vertex_->evaluate(scale,vector3_[iv0],vectors3_[0][iv1],vectorInter); + 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/VVVDecayer.h b/Decay/General/VVVDecayer.h --- a/Decay/General/VVVDecayer.h +++ b/Decay/General/VVVDecayer.h @@ -1,220 +1,228 @@ // -*- C++ -*- // // VVVDecayer.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_VVVDecayer_H #define HERWIG_VVVDecayer_H // // This is the declaration of the VVVDecayer class. // #include "GeneralTwoBodyDecayer.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Helicity/Vertex/Vector/VVVVertex.h" #include "ThePEG/Helicity/Vertex/AbstractVVVVVertex.h" namespace Herwig { using namespace ThePEG; using Helicity::VVVVertexPtr; /** \ingroup Decay * The VVVDecayer class implements the decay of a vector * to 2 vectors in a general model. It holds an VVVVertex pointer * that must be typecast from the VertexBase pointer held in * GeneralTwoBodyDecayer. It implents the virtual functions me2() and * partialWidth(). * * @see GeneralTwoBodyDecayer */ class VVVDecayer: public GeneralTwoBodyDecayer { public: /** * The default constructor. */ VVVDecayer() {} /** @name Virtual functions required by the Decayer class. */ //@{ /** * Return the matrix element squared for a given mode and phase-space channel. * @param ichan The channel we are calculating the matrix element for. * @param part The decaying Particle. * @param decay The particles produced in the decay. * @param meopt Option for the calculation of the matrix element * @return The matrix element squared for the phase-space configuration. */ virtual double me2(const int ichan, const Particle & part, const ParticleVector & decay, MEOption meopt) const; /** * Function to return partial Width * @param inpart The decaying particle. * @param outa One of the decay products. * @param outb The other decay product. */ virtual Energy partialWidth(PMPair inpart, PMPair outa, PMPair outb) const; /** * Set the information on the decay */ - virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, VertexBasePtr, + virtual void setDecayInfo(PDPtr incoming, PDPair outgoing, + vector<VertexBasePtr>, map<ShowerInteraction,VertexBasePtr> &, const vector<map<ShowerInteraction,VertexBasePtr> > &, map<ShowerInteraction,VertexBasePtr>); /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() { - return (vertex_->orderInGem()+vertex_->orderInGs())==1 ? FSR : No; + POWHEGType output = FSR; + for(auto vertex : vertex_) { + if(vertex->orderInAllCouplings()!=1) { + output = No; + break; + } + } + return output; } /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter, MEOption meopt); //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} protected: /** * Find the vertices for the decay */ void identifyVertices(const Particle & inpart, const ParticleVector & decay, AbstractVVVVertexPtr & outgoingVertex1, AbstractVVVVertexPtr & outgoingVertex2, ShowerInteraction inter); private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ VVVDecayer & operator=(const VVVDecayer &); private: /** * Abstract pointer to AbstractVVVVertex */ - AbstractVVVVertexPtr vertex_; + vector<AbstractVVVVertexPtr> vertex_; /** * Pointer to the perturbative vertex */ - VVVVertexPtr perturbativeVertex_; + vector<VVVVertexPtr> perturbativeVertex_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from incoming vector */ map<ShowerInteraction,AbstractVVVVertexPtr> incomingVertex_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from the first outgoing vector */ map<ShowerInteraction,AbstractVVVVertexPtr> outgoingVertex1_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from the second outgoing vector */ map<ShowerInteraction,AbstractVVVVertexPtr> outgoingVertex2_; /** * Abstract pointer to AbstractVVVVertex for QCD radiation from the 4-point vertex */ map<ShowerInteraction,AbstractVVVVVertexPtr> fourPointVertex_; /** * Spin density matrix */ mutable RhoDMatrix rho_; /** * Vector wavefunctions */ mutable vector<Helicity::VectorWaveFunction> vectors_[3]; private: /** * Members for the POWHEG correction */ //@{ /** * Spin density matrix for 3 body decay */ mutable RhoDMatrix rho3_; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> vector3_; /** * Vector wavefunctions */ mutable vector<Helicity::VectorWaveFunction> vectors3_[2]; /** * Vector wavefunction for 3 body decay */ mutable vector<Helicity::VectorWaveFunction> gluon_; //@} }; } #endif /* HERWIG_VVVDecayer_H */ diff --git a/Models/General/TwoBodyDecayConstructor.cc b/Models/General/TwoBodyDecayConstructor.cc --- a/Models/General/TwoBodyDecayConstructor.cc +++ b/Models/General/TwoBodyDecayConstructor.cc @@ -1,432 +1,432 @@ // -*- C++ -*- // // TwoBodyDecayConstructor.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 TwoBodyDecayConstructor class. // #include "TwoBodyDecayConstructor.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Reference.h" #include "ThePEG/Interface/Switch.h" #include "Herwig/Decay/General/GeneralTwoBodyDecayer.h" #include "Herwig/Models/StandardModel/StandardModel.h" #include "ThePEG/PDT/EnumParticles.h" #include "DecayConstructor.h" #include "ThePEG/Utilities/Throw.h" #include "ThePEG/Utilities/EnumIO.h" #include "ThePEG/Helicity/Vertex/AbstractFFVVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractFFSVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractVVSVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractVSSVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractVVTVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractFFTVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractSSTVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractSSSVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractVVVVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractRFSVertex.fh" #include "ThePEG/Helicity/Vertex/AbstractRFVVertex.fh" using namespace Herwig; using ThePEG::Helicity::VertexBasePtr; IBPtr TwoBodyDecayConstructor::clone() const { return new_ptr(*this); } IBPtr TwoBodyDecayConstructor::fullclone() const { return new_ptr(*this); } void TwoBodyDecayConstructor::persistentOutput(PersistentOStream & os) const { os << alphaQCD_ << alphaQED_ << oenum(inter_); } void TwoBodyDecayConstructor::persistentInput(PersistentIStream & is, int) { is >> alphaQCD_ >> alphaQED_>> ienum(inter_); } // The following static variable is needed for the type // description system in ThePEG. DescribeClass<TwoBodyDecayConstructor,NBodyDecayConstructorBase> describeHerwigTwoBodyDecayConstructor("Herwig::TwoBodyDecayConstructor", "Herwig.so"); void TwoBodyDecayConstructor::Init() { static ClassDocumentation<TwoBodyDecayConstructor> documentation ("The TwoBodyDecayConstructor implements to creation of 2 body decaymodes " "and decayers that do not already exist for the given set of vertices."); static Reference<TwoBodyDecayConstructor,ShowerAlpha> interfaceShowerAlphaQCD ("AlphaQCD", "The coupling for QCD corrections", &TwoBodyDecayConstructor::alphaQCD_, false, false, true, false, false); static Reference<TwoBodyDecayConstructor,ShowerAlpha> interfaceShowerAlphaQED ("AlphaQED", "The coupling for QED corrections", &TwoBodyDecayConstructor::alphaQED_, false, false, true, false, false); static Switch<TwoBodyDecayConstructor,ShowerInteraction> interfaceInteractions ("Interactions", "which interactions to include for the hard corrections", &TwoBodyDecayConstructor::inter_, ShowerInteraction::QCD, false, false); static SwitchOption interfaceInteractionsQCD (interfaceInteractions, "QCD", "QCD Only", ShowerInteraction::QCD); static SwitchOption interfaceInteractionsQED (interfaceInteractions, "QED", "QED only", ShowerInteraction::QED); static SwitchOption interfaceInteractionsQCDandQED (interfaceInteractions, "QCDandQED", "Both QCD and QED", ShowerInteraction::Both); } void TwoBodyDecayConstructor::DecayList(const set<PDPtr> & particles) { if( particles.empty() ) return; tHwSMPtr model = dynamic_ptr_cast<tHwSMPtr>(generator()->standardModel()); unsigned int nv(model->numberOfVertices()); for(set<PDPtr>::const_iterator ip=particles.begin(); ip!=particles.end();++ip) { tPDPtr parent = *ip; if ( Debug::level > 0 ) Repository::cout() << "Constructing 2-body decays for " << parent->PDGName() << '\n'; multiset<TwoBodyDecay> decays; for(unsigned int iv = 0; iv < nv; ++iv) { if(excluded(model->vertex(iv)) || model->vertex(iv)->getNpoint()>3) continue; for(unsigned int il = 0; il < 3; ++il) createModes(parent, model->vertex(iv), il,decays); } if( !decays.empty() ) createDecayMode(decays); } } void TwoBodyDecayConstructor:: createModes(tPDPtr inpart, VertexBasePtr vertex, unsigned int list, multiset<TwoBodyDecay> & modes) { if( !vertex->isIncoming(inpart) || vertex->getNpoint() != 3 ) return; Energy m1(inpart->mass()); tPDPtr ccpart = inpart->CC() ? inpart->CC() : inpart; long id = ccpart->id(); tPDVector decaylist = vertex->search(list, ccpart); tPDVector::size_type nd = decaylist.size(); for( tPDVector::size_type i = 0; i < nd; i += 3 ) { tPDPtr pa(decaylist[i]), pb(decaylist[i + 1]), pc(decaylist[i + 2]); if( pb->id() == id ) swap(pa, pb); if( pc->id() == id ) swap(pa, pc); //allowed on-shell decay? if( m1 <= pb->mass() + pc->mass() ) continue; //vertices are defined with all particles incoming modes.insert( TwoBodyDecay(inpart,pb, pc, vertex) ); } } GeneralTwoBodyDecayerPtr TwoBodyDecayConstructor::createDecayer(TwoBodyDecay decay, - vector<tVertexBasePtr> vertices) { + vector<VertexBasePtr> vertices) { string name; using namespace Helicity::VertexType; PDT::Spin in = decay.parent_->iSpin(); // PDT::Spin out1 = decay.children_.first ->iSpin(); PDT::Spin out2 = decay.children_.second->iSpin(); switch(decay.vertex_->getName()) { case FFV : if(in == PDT::Spin1Half) { name = "FFVDecayer"; if(out2==PDT::Spin1Half) swap(decay.children_.first,decay.children_.second); } else { name = "VFFDecayer"; } break; case FFS : if(in == PDT::Spin1Half) { name = "FFSDecayer"; if(out2==PDT::Spin1Half) swap(decay.children_.first,decay.children_.second); } else { name = "SFFDecayer"; } break; case VVS : if(in == PDT::Spin1) { name = "VVSDecayer"; if(out2==PDT::Spin1) swap(decay.children_.first,decay.children_.second); } else { name = "SVVDecayer"; } break; case VSS : if(in == PDT::Spin1) { name = "VSSDecayer"; } else { name = "SSVDecayer"; if(out2==PDT::Spin0) swap(decay.children_.first,decay.children_.second); } break; case VVT : name = in==PDT::Spin2 ? "TVVDecayer" : "Unknown"; break; case FFT : name = in==PDT::Spin2 ? "TFFDecayer" : "Unknown"; break; case SST : name = in==PDT::Spin2 ? "TSSDecayer" : "Unknown"; break; case SSS : name = "SSSDecayer"; break; case VVV : name = "VVVDecayer"; break; case RFS : if(in==PDT::Spin1Half) { name = "FRSDecayer"; if(out2==PDT::Spin3Half) swap(decay.children_.first,decay.children_.second); } else if(in==PDT::Spin0) { name = "SRFDecayer"; if(out2==PDT::Spin3Half) swap(decay.children_.first,decay.children_.second); } else { name = "Unknown"; } break; case RFV : if(in==PDT::Spin1Half) { name = "FRVDecayer"; if(out2==PDT::Spin3Half) swap(decay.children_.first,decay.children_.second); } else name = "Unknown"; break; default : Throw<NBodyDecayConstructorError>() << "Error: Cannot assign " << decay.vertex_->fullName() << " to a decayer. " << "Decay is " << decay.parent_->PDGName() << " -> " << decay.children_.first ->PDGName() << " " << decay.children_.second->PDGName() << Exception::runerror; } if(name=="Unknown") Throw<NBodyDecayConstructorError>() << "Error: Cannot assign " << decay.vertex_->fullName() << " to a decayer. " << "Decay is " << decay.parent_->PDGName() << " -> " << decay.children_.first ->PDGName() << " " << decay.children_.second->PDGName() << Exception::runerror; ostringstream fullname; fullname << "/Herwig/Decays/" << name << "_" << decay.parent_->PDGName() << "_" << decay.children_.first ->PDGName() << "_" << decay.children_.second->PDGName(); string classname = "Herwig::" + name; GeneralTwoBodyDecayerPtr decayer; decayer = dynamic_ptr_cast<GeneralTwoBodyDecayerPtr> (generator()->preinitCreate(classname,fullname.str())); if(!decayer) Throw<NBodyDecayConstructorError>() << "Error: Cannot assign " << decay.vertex_->fullName() << " to a decayer. " << "Decay is " << decay.parent_->PDGName() << " -> " << decay.children_.first ->PDGName() << " " << decay.children_.second->PDGName() << Exception::runerror; // set the strong coupling for radiation generator()->preinitInterface(decayer, "AlphaS" , "set", alphaQCD_->fullName()); // set the EM coupling for radiation generator()->preinitInterface(decayer, "AlphaEM", "set", alphaQED_->fullName()); // set the type of interactions for the correction if(inter_==ShowerInteraction::QCD) generator()->preinitInterface(decayer, "Interactions", "set", "QCD"); else if(inter_==ShowerInteraction::QED) generator()->preinitInterface(decayer, "Interactions", "set", "QED"); else generator()->preinitInterface(decayer, "Interactions", "set", "QCDandQED"); // get the vertices for radiation from the external legs map<ShowerInteraction,VertexBasePtr> inRad,fourRad; vector<map<ShowerInteraction,VertexBasePtr> > outRad(2); vector<ShowerInteraction> itemp={ShowerInteraction::QCD,ShowerInteraction::QED}; for(auto & inter : itemp) { inRad[inter] = radiationVertex(decay.parent_,inter); outRad[0][inter] = radiationVertex(decay.children_.first ,inter); outRad[1][inter] = radiationVertex(decay.children_.second,inter); // get any contributing 4 point vertices fourRad[inter] = radiationVertex(decay.parent_,inter, decay.children_); } // set info on decay - decayer->setDecayInfo(decay.parent_,decay.children_,decay.vertex_, + decayer->setDecayInfo(decay.parent_,decay.children_,vertices, inRad,outRad,fourRad); // initialised the decayer setDecayerInterfaces(fullname.str()); decayer->init(); return decayer; } void TwoBodyDecayConstructor:: createDecayMode(multiset<TwoBodyDecay> & decays) { tPDPtr inpart = decays.begin()->parent_; for( multiset<TwoBodyDecay>::iterator dit = decays.begin(); dit != decays.end(); ) { TwoBodyDecay mode = *dit; // get all the moees with the same in and outgoing particles pair<multiset<TwoBodyDecay>::iterator, multiset<TwoBodyDecay>::iterator> range = decays.equal_range(mode); // construct the decay mode tPDPtr pb((mode).children_.first), pc((mode).children_.second); string tag = inpart->name() + "->" + pb->name() + "," + pc->name() + ";"; // Does it exist already ? tDMPtr dm = generator()->findDecayMode(tag); // find the vertices - vector<tVertexBasePtr> vertices; + vector<VertexBasePtr> vertices; for ( multiset<TwoBodyDecay>::iterator dit2 = range.first; dit2 != range.second; ++dit2) { vertices.push_back(dit2->vertex_); } dit=range.second; // now create DecayMode objects that do not already exist if( createDecayModes() && (!dm || inpart->id() == ParticleID::h0) ) { tDMPtr ndm = generator()->preinitCreateDecayMode(tag); if(ndm) { inpart->stable(false); GeneralTwoBodyDecayerPtr decayer=createDecayer(mode,vertices); if(!decayer) continue; generator()->preinitInterface(ndm, "Decayer", "set", decayer->fullName()); generator()->preinitInterface(ndm, "Active", "set", "Yes"); Energy width = decayer->partialWidth(make_pair(inpart,inpart->mass()), make_pair(pb,pb->mass()) , make_pair(pc,pc->mass())); setBranchingRatio(ndm, width); if(width==ZERO || ndm->brat()<decayConstructor()->minimumBR()) { generator()->preinitInterface(decayer->fullName(), "Initialize", "set","0"); } } else Throw<NBodyDecayConstructorError>() << "TwoBodyDecayConstructor::createDecayMode - Needed to create " << "new decaymode but one could not be created for the tag " << tag << Exception::warning; } else if( dm ) { if(dm->brat()<decayConstructor()->minimumBR()) { continue; } if((dm->decayer()->fullName()).find("Mambo") != string::npos) { inpart->stable(false); GeneralTwoBodyDecayerPtr decayer=createDecayer(mode,vertices); if(!decayer) continue; generator()->preinitInterface(dm, "Decayer", "set", decayer->fullName()); Energy width = decayer->partialWidth(make_pair(inpart,inpart->mass()), make_pair(pb,pb->mass()) , make_pair(pc,pc->mass())); if(width/(dm->brat()*inpart->width())<1e-10) { string message = "Herwig calculation of the partial width for the decay mode " + inpart->PDGName() + " -> " + pb->PDGName() + " " + pc->PDGName() + " is zero.\n This will cause problems with the calculation of" + " spin correlations.\n It is probably due to inconsistent parameters" + " and decay modes being passed to Herwig via the SLHA file.\n" + " Zeroing the branching ratio for this mode."; setBranchingRatio(dm,ZERO); generator()->logWarning(NBodyDecayConstructorError(message,Exception::warning)); } } } } // update CC mode if it exists if( inpart->CC() ) inpart->CC()->synchronize(); } VertexBasePtr TwoBodyDecayConstructor::radiationVertex(tPDPtr particle, ShowerInteraction inter, tPDPair children) { tHwSMPtr model = dynamic_ptr_cast<tHwSMPtr>(generator()->standardModel()); map<tPDPtr,VertexBasePtr>::iterator rit = radiationVertices_[inter].find(particle); tPDPtr cc = particle->CC() ? particle->CC() : particle; if(children==tPDPair() && rit!=radiationVertices_[inter].end()) return rit->second; unsigned int nv(model->numberOfVertices()); long bosonID = inter==ShowerInteraction::QCD ? ParticleID::g : ParticleID::gamma; tPDPtr gluon = getParticleData(bosonID); // look for radiation vertices for incoming and outgoing particles for(unsigned int iv=0;iv<nv;++iv) { VertexBasePtr vertex = model->vertex(iv); // look for 3 point vertices if (children==tPDPair()){ if( !vertex->isIncoming(particle) || vertex->getNpoint() != 3 || !vertex->isOutgoing(particle) || !vertex->isOutgoing(gluon)) continue; for(unsigned int list=0;list<3;++list) { tPDVector decaylist = vertex->search(list, particle); for( tPDVector::size_type i = 0; i < decaylist.size(); i += 3 ) { tPDPtr pa(decaylist[i]), pb(decaylist[i + 1]), pc(decaylist[i + 2]); if( pb->id() == bosonID ) swap(pa, pb); if( pc->id() == bosonID ) swap(pa, pc); if( pb->id() != particle->id()) swap(pb, pc); if( pa->id() != bosonID) continue; if( pb != particle) continue; if( pc != cc) continue; radiationVertices_[inter][particle] = vertex; return vertex; } } } // look for 4 point vertex including a gluon else { if( !vertex->isIncoming(particle) || vertex->getNpoint()!=4 || !vertex->isOutgoing(children.first) || !vertex->isOutgoing(children.second) || !vertex->isOutgoing(gluon)) continue; for(unsigned int list=0;list<4;++list) { tPDVector decaylist = vertex->search(list, particle); for( tPDVector::size_type i = 0; i < decaylist.size(); i += 4 ) { tPDPtr pa(decaylist[i]), pb(decaylist[i+1]), pc(decaylist[i+2]), pd(decaylist[i+3]); // order so that a = g, b = parent if( pb->id() == bosonID ) swap(pa, pb); if( pc->id() == bosonID ) swap(pa, pc); if( pd->id() == bosonID ) swap(pa, pd); if( pc->id() == particle->id()) swap(pb, pc); if( pd->id() == particle->id()) swap(pb, pd); if( pa->id() != bosonID) continue; if( pb->id() != particle->id()) continue; if( !((abs(pd->id()) == abs(children. first->id()) && abs(pc->id()) == abs(children.second->id())) || (abs(pc->id()) == abs(children. first->id()) && abs(pd->id()) == abs(children.second->id())))) continue; return vertex; } } } } return VertexBasePtr(); } diff --git a/Models/General/TwoBodyDecayConstructor.h b/Models/General/TwoBodyDecayConstructor.h --- a/Models/General/TwoBodyDecayConstructor.h +++ b/Models/General/TwoBodyDecayConstructor.h @@ -1,177 +1,177 @@ // -*- C++ -*- // // TwoBodyDecayConstructor.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_TwoBodyDecayConstructor_H #define HERWIG_TwoBodyDecayConstructor_H // // This is the declaration of the TwoBodyDecayConstructor class. // #include "NBodyDecayConstructorBase.h" #include "ThePEG/Helicity/Vertex/VertexBase.h" #include "Herwig/Decay/General/GeneralTwoBodyDecayer.fh" #include "Herwig/Shower/ShowerAlpha.h" #include "TwoBodyDecay.h" namespace Herwig { using namespace ThePEG; using Helicity::VertexBasePtr; using Helicity::tVertexBasePtr; /** * The TwoBodyDecayConstructor class inherits from the dummy base class * NBodyDecayConstructorBase and implements the necessary functions in * order to create the 2 body decay modes for a given set of vertices * stored in a Model class. * * @see \ref TwoBodyDecayConstructorInterfaces "The interfaces" * defined for TwoBodyDecayConstructor. * @see NBodyDecayConstructor **/ class TwoBodyDecayConstructor: public NBodyDecayConstructorBase { public: /** * The default constructor. */ TwoBodyDecayConstructor() : inter_(ShowerInteraction::Both) { radiationVertices_[ShowerInteraction::QCD] = map<tPDPtr,VertexBasePtr>(); radiationVertices_[ShowerInteraction::QED] = map<tPDPtr,VertexBasePtr>(); } /** * Function used to determine allowed decaymodes *@param part vector of ParticleData pointers containing particles in model */ virtual void DecayList(const set<PDPtr> & part); /** * Number of outgoing lines. Required for correct ordering. */ virtual unsigned int numBodies() const { return 2; } public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ TwoBodyDecayConstructor & operator=(const TwoBodyDecayConstructor &); private: /** @name Functions to create decayers and decaymodes. */ //@{ /** * Function to create decays * @param inpart Incoming particle * @param vert The vertex to create decays for * @param ilist Which list to search * @param iv Row number in _theExistingDecayers member * @return A vector a decay modes */ void createModes(tPDPtr inpart, VertexBasePtr vert, unsigned int ilist, multiset<TwoBodyDecay> & modes); /** * Function to create decayer for specific vertex * @param decay decay mode for this decay * member variable */ GeneralTwoBodyDecayerPtr createDecayer(TwoBodyDecay decay, - vector<tVertexBasePtr> ); + vector<VertexBasePtr> ); /** * Create decay mode(s) from given part and decay modes * @param decays The vector of decay modes * @param decayer The decayer responsible for this decay */ void createDecayMode(multiset<TwoBodyDecay> & decays); //@} /** * Get the vertex for QED/QCD radiation */ VertexBasePtr radiationVertex(tPDPtr particle,ShowerInteraction inter, tPDPair children = tPDPair ()); private: /** * Map of particles and the vertices which generate their QCD * radiation */ map<ShowerInteraction,map<tPDPtr,VertexBasePtr> > radiationVertices_; /** * Default choice for the strong coupling object for hard QCD radiation */ ShowerAlphaPtr alphaQCD_; /** * Default choice for the strong coupling object for hard QED radiation */ ShowerAlphaPtr alphaQED_; /** * Which type of corrections to the decays to include */ ShowerInteraction inter_; }; } #endif /* HERWIG_TwoBodyDecayConstructor_H */