diff --git a/Decay/Perturbative/SMTopDecayer.cc b/Decay/Perturbative/SMTopDecayer.cc --- a/Decay/Perturbative/SMTopDecayer.cc +++ b/Decay/Perturbative/SMTopDecayer.cc @@ -1,1335 +1,1296 @@ // -*- C++ -*- // // SMTopDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator // Copyright (C) 2002-2017 The Herwig Collaboration // // Herwig is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // // // This is the implementation of the non-inlined, non-templated member // functions of the SMTopDecayer class. // #include "SMTopDecayer.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/ParVector.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Interface/Reference.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/PDT/DecayMode.h" #include "Herwig/Decay/DecayVertex.h" #include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h" #include "Herwig/PDT/ThreeBodyAllOn1IntegralCalculator.h" #include "Herwig/Shower/RealEmissionProcess.h" #include "Herwig/Shower/Core/Base/ShowerProgenitor.h" #include "Herwig/Shower/Core/Base/ShowerParticle.h" #include "Herwig/Shower/Core/Base/Branching.h" #include "Herwig/Decay/GeneralDecayMatrixElement.h" using namespace Herwig; using namespace ThePEG::Helicity; SMTopDecayer::SMTopDecayer() : _wquarkwgt(6,0.),_wleptonwgt(3,0.), _xg_sampling(1.5), _initialenhance(1.), _finalenhance(2.3), _useMEforT2(true) { _wleptonwgt[0] = 0.302583; _wleptonwgt[1] = 0.301024; _wleptonwgt[2] = 0.299548; _wquarkwgt[0] = 0.851719; _wquarkwgt[1] = 0.0450162; _wquarkwgt[2] = 0.0456962; _wquarkwgt[3] = 0.859839; _wquarkwgt[4] = 3.9704e-06; _wquarkwgt[5] = 0.000489657; generateIntermediates(true); } bool SMTopDecayer::accept(tcPDPtr parent, const tPDVector & children) const { if(abs(parent->id()) != ParticleID::t) return false; int id0(0),id1(0),id2(0); for(tPDVector::const_iterator it = children.begin(); it != children.end();++it) { int id=(**it).id(),absid(abs(id)); if(absid==ParticleID::b&&double(id)/double(parent->id())>0) { id0=id; } else { switch (absid) { case ParticleID::nu_e: case ParticleID::nu_mu: case ParticleID::nu_tau: id1 = id; break; case ParticleID::eminus: case ParticleID::muminus: case ParticleID::tauminus: id2 = id; break; case ParticleID::b: case ParticleID::d: case ParticleID::s: id1 = id; break; case ParticleID::u: case ParticleID::c: id2=id; break; default : break; } } } if(id0==0||id1==0||id2==0) return false; if(double(id1)/double(id2)>0) return false; return true; } ParticleVector SMTopDecayer::decay(const Particle & parent, const tPDVector & children) const { int id1(0),id2(0); for(tPDVector::const_iterator it = children.begin(); it != children.end();++it) { int id=(**it).id(),absid=abs(id); if(absid == ParticleID::b && double(id)/double(parent.id())>0) continue; //leptons if(absid > 10 && absid%2==0) id1=absid; if(absid > 10 && absid%2==1) id2=absid; //quarks if(absid < 10 && absid%2==0) id2=absid; if(absid < 10 && absid%2==1) id1=absid; } unsigned int imode(0); if(id2 >=11 && id2<=16) imode = (id1-12)/2; else imode = id1+1+id2/2; bool cc = parent.id() == ParticleID::tbar; ParticleVector out(generate(true,cc,imode,parent)); //arrange colour flow PPtr pparent=const_ptr_cast<PPtr>(&parent); out[1]->incomingColour(pparent,out[1]->id()<0); ParticleVector products = out[0]->children(); if(products[0]->hasColour()) products[0]->colourNeighbour(products[1],true); else if(products[0]->hasAntiColour()) products[0]->colourNeighbour(products[1],false); return out; } void SMTopDecayer::persistentOutput(PersistentOStream & os) const { os << FFWVertex_ << FFGVertex_ << FFPVertex_ << WWWVertex_ << _wquarkwgt << _wleptonwgt << _wplus << _initialenhance << _finalenhance << _xg_sampling << _useMEforT2; } void SMTopDecayer::persistentInput(PersistentIStream & is, int) { is >> FFWVertex_ >> FFGVertex_ >> FFPVertex_ >> WWWVertex_ >> _wquarkwgt >> _wleptonwgt >> _wplus >> _initialenhance >> _finalenhance >> _xg_sampling >> _useMEforT2; } // The following static variable is needed for the type // description system in ThePEG. DescribeClass<SMTopDecayer,PerturbativeDecayer> describeHerwigSMTopDecayer("Herwig::SMTopDecayer", "HwPerturbativeDecay.so"); void SMTopDecayer::Init() { static ClassDocumentation<SMTopDecayer> documentation ("This is the implementation of the SMTopDecayer which " "decays top quarks into bottom quarks and either leptons " "or quark-antiquark pairs including the matrix element for top decay", "The matrix element correction for top decay \\cite{Hamilton:2006ms}.", "%\\cite{Hamilton:2006ms}\n" "\\bibitem{Hamilton:2006ms}\n" " K.~Hamilton and P.~Richardson,\n" " ``A simulation of QCD radiation in top quark decays,''\n" " JHEP {\\bf 0702}, 069 (2007)\n" " [arXiv:hep-ph/0612236].\n" " %%CITATION = JHEPA,0702,069;%%\n"); static ParVector<SMTopDecayer,double> interfaceQuarkWeights ("QuarkWeights", "Maximum weights for the hadronic decays", &SMTopDecayer::_wquarkwgt, 6, 1.0, 0.0, 10.0, false, false, Interface::limited); static ParVector<SMTopDecayer,double> interfaceLeptonWeights ("LeptonWeights", "Maximum weights for the semi-leptonic decays", &SMTopDecayer::_wleptonwgt, 3, 1.0, 0.0, 10.0, false, false, Interface::limited); static Parameter<SMTopDecayer,double> interfaceEnhancementFactor ("InitialEnhancementFactor", "The enhancement factor for initial-state radiation in the shower to ensure" " the weight for the matrix element correction is less than one.", &SMTopDecayer::_initialenhance, 1.0, 1.0, 10000.0, false, false, Interface::limited); static Parameter<SMTopDecayer,double> interfaceFinalEnhancementFactor ("FinalEnhancementFactor", "The enhancement factor for final-state radiation in the shower to ensure" " the weight for the matrix element correction is less than one", &SMTopDecayer::_finalenhance, 1.6, 1.0, 1000.0, false, false, Interface::limited); static Parameter<SMTopDecayer,double> interfaceSamplingTopHardMEC ("SamplingTopHardMEC", "The importance sampling power for choosing an initial xg, " "to sample xg according to xg^-_xg_sampling", &SMTopDecayer::_xg_sampling, 1.5, 1.2, 2.0, false, false, Interface::limited); static Switch<SMTopDecayer,bool> interfaceUseMEForT2 ("UseMEForT2", "Use the matrix element correction, if available to fill the T2" " region for the decay shower and don't fill using the shower", &SMTopDecayer::_useMEforT2, true, false, false); static SwitchOption interfaceUseMEForT2Shower (interfaceUseMEForT2, "Shower", "Use the shower to fill the T2 region", false); static SwitchOption interfaceUseMEForT2ME (interfaceUseMEForT2, "ME", "Use the Matrix element to fill the T2 region", true); } double SMTopDecayer::me2(const int, const Particle & inpart, const ParticleVector & decay, MEOption meopt) const { if(!ME()) ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half, PDT::Spin1Half,PDT::Spin1Half))); // spinors etc for the decaying particle if(meopt==Initialize) { // spinors and rho if(inpart.id()>0) SpinorWaveFunction ::calculateWaveFunctions(_inHalf,_rho, const_ptr_cast<tPPtr>(&inpart), incoming); else SpinorBarWaveFunction::calculateWaveFunctions(_inHalfBar,_rho, const_ptr_cast<tPPtr>(&inpart), incoming); } // setup spin info when needed if(meopt==Terminate) { // for the decaying particle if(inpart.id()>0) { SpinorWaveFunction:: constructSpinInfo(_inHalf,const_ptr_cast<tPPtr>(&inpart),incoming,true); SpinorBarWaveFunction::constructSpinInfo(_inHalfBar,decay[0],outgoing,true); SpinorWaveFunction ::constructSpinInfo(_outHalf ,decay[1],outgoing,true); SpinorBarWaveFunction::constructSpinInfo(_outHalfBar,decay[2],outgoing,true); } else { SpinorBarWaveFunction:: constructSpinInfo(_inHalfBar,const_ptr_cast<tPPtr>(&inpart),incoming,true); SpinorWaveFunction::constructSpinInfo(_inHalf,decay[0],outgoing,true); SpinorBarWaveFunction::constructSpinInfo(_outHalfBar,decay[1],outgoing,true); SpinorWaveFunction ::constructSpinInfo(_outHalf ,decay[2],outgoing,true); } } if ( ( decay[1]->momentum() + decay[2]->momentum() ).m() < decay[1]->data().constituentMass() + decay[2]->data().constituentMass() ) return 0.0; // spinors for the decay product if(inpart.id()>0) { SpinorBarWaveFunction::calculateWaveFunctions(_inHalfBar ,decay[0],outgoing); SpinorWaveFunction ::calculateWaveFunctions(_outHalf ,decay[1],outgoing); SpinorBarWaveFunction::calculateWaveFunctions(_outHalfBar,decay[2],outgoing); } else { SpinorWaveFunction ::calculateWaveFunctions(_inHalf ,decay[0],outgoing); SpinorBarWaveFunction::calculateWaveFunctions(_outHalfBar,decay[1],outgoing); SpinorWaveFunction ::calculateWaveFunctions(_outHalf ,decay[2],outgoing); } Energy2 scale(sqr(inpart.mass())); if(inpart.id() == ParticleID::t) { //Define intermediate vector wave-function for Wplus tcPDPtr Wplus(getParticleData(ParticleID::Wplus)); VectorWaveFunction inter; unsigned int thel,bhel,fhel,afhel; for(thel = 0;thel<2;++thel){ for(bhel = 0;bhel<2;++bhel){ inter = FFWVertex_->evaluate(scale,1,Wplus,_inHalf[thel], _inHalfBar[bhel]); for(afhel=0;afhel<2;++afhel){ for(fhel=0;fhel<2;++fhel){ (*ME())(thel,bhel,afhel,fhel) = FFWVertex_->evaluate(scale,_outHalf[afhel], _outHalfBar[fhel],inter); } } } } } else if(inpart.id() == ParticleID::tbar) { VectorWaveFunction inter; tcPDPtr Wminus(getParticleData(ParticleID::Wminus)); unsigned int tbhel,bbhel,afhel,fhel; for(tbhel = 0;tbhel<2;++tbhel){ for(bbhel = 0;bbhel<2;++bbhel){ inter = FFWVertex_-> evaluate(scale,1,Wminus,_inHalf[bbhel],_inHalfBar[tbhel]); for(afhel=0;afhel<2;++afhel){ for(fhel=0;fhel<2;++fhel){ (*ME())(tbhel,bbhel,fhel,afhel) = FFWVertex_->evaluate(scale,_outHalf[afhel], _outHalfBar[fhel],inter); } } } } } double output = (ME()->contract(_rho)).real(); if(abs(decay[1]->id())<=6) output *=3.; return output; } void SMTopDecayer::doinit() { PerturbativeDecayer::doinit(); //get vertices from SM object tcHwSMPtr hwsm = dynamic_ptr_cast<tcHwSMPtr>(standardModel()); if(!hwsm) throw InitException() << "Must have Herwig::StandardModel in " << "SMTopDecayer::doinit()"; FFWVertex_ = hwsm->vertexFFW(); FFGVertex_ = hwsm->vertexFFG(); FFPVertex_ = hwsm->vertexFFP(); WWWVertex_ = hwsm->vertexWWW(); //initialise FFWVertex_->init(); FFGVertex_->init(); FFPVertex_->init(); WWWVertex_->init(); //set up decay modes _wplus = getParticleData(ParticleID::Wplus); DecayPhaseSpaceModePtr mode; DecayPhaseSpaceChannelPtr Wchannel; tPDVector extpart(4); vector<double> wgt(1,1.0); extpart[0] = getParticleData(ParticleID::t); extpart[1] = getParticleData(ParticleID::b); //lepton modes for(int i=11; i<17;i+=2) { extpart[2] = getParticleData(-i); extpart[3] = getParticleData(i+1); mode = new_ptr(DecayPhaseSpaceMode(extpart,this)); Wchannel = new_ptr(DecayPhaseSpaceChannel(mode)); Wchannel->addIntermediate(extpart[0],0,0.0,-1,1); Wchannel->addIntermediate(_wplus,0,0.0,2,3); Wchannel->init(); mode->addChannel(Wchannel); addMode(mode,_wleptonwgt[(i-11)/2],wgt); } //quark modes unsigned int iz=0; for(int ix=1;ix<6;ix+=2) { for(int iy=2;iy<6;iy+=2) { // check that the combination of particles is allowed if(FFWVertex_->allowed(-ix,iy,ParticleID::Wminus)) { extpart[2] = getParticleData(-ix); extpart[3] = getParticleData( iy); mode = new_ptr(DecayPhaseSpaceMode(extpart,this)); Wchannel = new_ptr(DecayPhaseSpaceChannel(mode)); Wchannel->addIntermediate(extpart[0],0,0.0,-1,1); Wchannel->addIntermediate(_wplus,0,0.0,2,3); Wchannel->init(); mode->addChannel(Wchannel); addMode(mode,_wquarkwgt[iz],wgt); ++iz; } else { throw InitException() << "SMTopDecayer::doinit() the W vertex" << "cannot handle all the quark modes" << Exception::abortnow; } } } } void SMTopDecayer::dataBaseOutput(ofstream & os,bool header) const { if(header) os << "update decayers set parameters=\""; // parameters for the PerturbativeDecayer base class for(unsigned int ix=0;ix<_wquarkwgt.size();++ix) { os << "newdef " << name() << ":QuarkWeights " << ix << " " << _wquarkwgt[ix] << "\n"; } for(unsigned int ix=0;ix<_wleptonwgt.size();++ix) { os << "newdef " << name() << ":LeptonWeights " << ix << " " << _wleptonwgt[ix] << "\n"; } PerturbativeDecayer::dataBaseOutput(os,false); if(header) os << "\n\" where BINARY ThePEGName=\"" << fullName() << "\";" << endl; } void SMTopDecayer::doinitrun() { PerturbativeDecayer::doinitrun(); if(initialize()) { for(unsigned int ix=0;ix<numberModes();++ix) { if(ix<3) _wleptonwgt[ix ] = mode(ix)->maxWeight(); else _wquarkwgt [ix-3] = mode(ix)->maxWeight(); } } } WidthCalculatorBasePtr SMTopDecayer::threeBodyMEIntegrator(const DecayMode & dm) const { // identify W decay products int sign = dm.parent()->id() > 0 ? 1 : -1; int iferm(0),ianti(0); for(ParticleMSet::const_iterator pit=dm.products().begin(); pit!=dm.products().end();++pit) { int id = (**pit).id(); if(id*sign != ParticleID::b) { if (id*sign > 0 ) iferm = id*sign; else ianti = id*sign; } } assert(iferm!=0&&ianti!=0); // work out which mode we are doing int imode(-1); for(unsigned int ix=0;ix<numberModes();++ix) { if(mode(ix)->externalParticles(2)->id() == ianti && mode(ix)->externalParticles(3)->id() == iferm ) { imode = ix; break; } } assert(imode>=0); // get the masses we need Energy m[3] = {mode(imode)->externalParticles(1)->mass(), mode(imode)->externalParticles(3)->mass(), mode(imode)->externalParticles(2)->mass()}; return new_ptr(ThreeBodyAllOn1IntegralCalculator<SMTopDecayer> (3,_wplus->mass(),_wplus->width(),0.0,*this,imode,m[0],m[1],m[2])); } InvEnergy SMTopDecayer::threeBodydGammads(const int imode, const Energy2 mt2, const Energy2 mffb2, const Energy mb, const Energy mf, const Energy mfb) const { Energy mffb(sqrt(mffb2)); Energy mw(_wplus->mass()); Energy2 mw2(sqr(mw)),gw2(sqr(_wplus->width())); Energy mt(sqrt(mt2)); Energy Eb = 0.5*(mt2-mffb2-sqr(mb))/mffb; Energy Ef = 0.5*(mffb2-sqr(mfb)+sqr(mf))/mffb; Energy Ebm = sqrt(sqr(Eb)-sqr(mb)); Energy Efm = sqrt(sqr(Ef)-sqr(mf)); Energy2 upp = sqr(Eb+Ef)-sqr(Ebm-Efm); Energy2 low = sqr(Eb+Ef)-sqr(Ebm+Efm); InvEnergy width=(dGammaIntegrand(mffb2,upp,mt,mb,mf,mfb,mw)- dGammaIntegrand(mffb2,low,mt,mb,mf,mfb,mw)) /32./mt2/mt/8/pow(Constants::pi,3)/(sqr(mffb2-mw2)+mw2*gw2); // couplings width *= 0.25*sqr(4.*Constants::pi*generator()->standardModel()->alphaEM(mt2)/ generator()->standardModel()->sin2ThetaW()); width *= generator()->standardModel()->CKM(*mode(imode)->externalParticles(0), *mode(imode)->externalParticles(1)); if(abs(mode(imode)->externalParticles(2)->id())<=6) { width *=3.; if(abs(mode(imode)->externalParticles(2)->id())%2==0) width *=generator()->standardModel()->CKM(*mode(imode)->externalParticles(2), *mode(imode)->externalParticles(3)); else width *=generator()->standardModel()->CKM(*mode(imode)->externalParticles(3), *mode(imode)->externalParticles(2)); } // final spin average assert(!std::isnan(double(width*MeV))); return 0.5*width; } Energy6 SMTopDecayer::dGammaIntegrand(Energy2 mffb2, Energy2 mbf2, Energy mt, Energy mb, Energy mf, Energy mfb, Energy mw) const { Energy2 mt2(sqr(mt)) ,mb2(sqr(mb)) ,mf2(sqr(mf )),mfb2(sqr(mfb )),mw2(sqr(mw )); Energy4 mt4(sqr(mt2)),mb4(sqr(mb2)),mf4(sqr(mf2)),mfb4(sqr(mfb2)),mw4(sqr(mw2)); return -mbf2 * ( + 6 * mb2 * mf2 * mfb2 * mffb2 + 6 * mb2 * mt2 * mfb2 * mffb2 + 6 * mb2 * mt2 * mf2 * mffb2 + 12 * mb2 * mt2 * mf2 * mfb2 - 3 * mb2 * mfb4 * mffb2 + 3 * mb2 * mf2 * mffb2 * mffb2 - 3 * mb2 * mf4 * mffb2 - 6 * mb2 * mt2 * mfb4 - 6 * mb2 * mt2 * mf4 - 3 * mb4 * mfb2 * mffb2 - 3 * mb4 * mf2 * mffb2 - 6 * mb4 * mf2 * mfb2 + 3 * mt4 * mf4 + 3 * mb4 * mfb4 + 3 * mb4 * mf4 + 3 * mt4 * mfb4 + 3 * mb2 * mfb2 * mffb2 * mffb2 + 3 * mt2 * mfb2 * mffb2 * mffb2 - 3 * mt2 * mfb4 * mffb2 + 3 * mt2 * mf2 * mffb2 * mffb2 - 3 * mt2 * mf4 * mffb2 - 3 * mt4 * mfb2 * mffb2 - 3 * mt4 * mf2 * mffb2 - 6 * mt4 * mf2 * mfb2 + 6 * mt2 * mf2 * mfb2 * mffb2 + 12 * mt2 * mf2 * mw4 + 12 * mb2 * mfb2 * mw4 + 12 * mb2 * mt2 * mw4 + 6 * mw2 * mt2 * mfb2 * mbf2 - 12 * mw2 * mt2 * mf2 * mffb2 - 6 * mw2 * mt2 * mf2 * mbf2 - 12 * mw2 * mt2 * mf2 * mfb2 - 12 * mw2 * mb2 * mfb2 * mffb2 - 6 * mw2 * mb2 * mfb2 * mbf2 + 6 * mw2 * mb2 * mf2 * mbf2 - 12 * mw2 * mb2 * mf2 * mfb2 - 12 * mw2 * mb2 * mt2 * mfb2 - 12 * mw2 * mb2 * mt2 * mf2 + 12 * mf2 * mfb2 * mw4 + 4 * mbf2 * mbf2 * mw4 - 6 * mfb2 * mbf2 * mw4 - 6 * mf2 * mbf2 * mw4 - 6 * mt2 * mbf2 * mw4 - 6 * mb2 * mbf2 * mw4 + 12 * mw2 * mt2 * mf4 + 12 * mw2 * mt4 * mf2 + 12 * mw2 * mb2 * mfb4 + 12 * mw2 * mb4 * mfb2) /mw4 / 3.; } void SMTopDecayer::initializeMECorrection(RealEmissionProcessPtr born, double & initial, double & final) { // check the outgoing particles PPtr part[2]; for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) { part[ix]= born->bornOutgoing()[ix]; } // check the final-state particles and get the masses if(abs(part[0]->id())==ParticleID::Wplus&&abs(part[1]->id())==ParticleID::b) { _ma=part[0]->mass(); _mc=part[1]->mass(); } else if(abs(part[1]->id())==ParticleID::Wplus&&abs(part[0]->id())==ParticleID::b) { _ma=part[1]->mass(); _mc=part[0]->mass(); } else { return; } // set the top mass _mt=born->bornIncoming()[0]->mass(); // set the gluon mass _mg=getParticleData(ParticleID::g)->constituentMass(); // set the radiation enhancement factors initial = _initialenhance; final = _finalenhance; // reduced mass parameters _a=sqr(_ma/_mt); _g=sqr(_mg/_mt); _c=sqr(_mc/_mt); double lambda = sqrt(1.+sqr(_a)+sqr(_c)-2.*_a-2.*_c-2.*_a*_c); _ktb = 0.5*(3.-_a+_c+lambda); _ktc = 0.5*(1.-_a+3.*_c+lambda); useMe(); } RealEmissionProcessPtr SMTopDecayer::applyHardMatrixElementCorrection(RealEmissionProcessPtr born) { // Get b and a and put them in particle vector ba in that order... ParticleVector ba; for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) ba.push_back(born->bornOutgoing()[ix]); if(abs(ba[0]->id())!=5) swap(ba[0],ba[1]); assert(born->bornIncoming().size()==1); // Now decide if we get an emission into the dead region. // If there is an emission newfs stores momenta for a,c,g // according to NLO decay matrix element. vector<Lorentz5Momentum> newfs = applyHard(ba,_ktb,_ktc); // If there was no gluon emitted return. if(newfs.size()!=3) return RealEmissionProcessPtr(); // Sanity checks to ensure energy greater than mass etc :) bool check = true; tcPDPtr gluondata=getParticleData(ParticleID::g); if (newfs[0].e()<ba[0]->data().constituentMass()) check = false; if (newfs[1].e()<ba[1]->mass()) check = false; if (newfs[2].e()<gluondata->constituentMass()) check = false; // Return if insane: if (!check) return RealEmissionProcessPtr(); // // Set masses in 5-vectors: newfs[0].setMass(ba[0]->mass()); newfs[1].setMass(ba[1]->mass()); newfs[2].setMass(ZERO); // The next part of this routine sets the colour structure. // To do this for decays we assume that the gluon comes from c! // First create new particle objects for c, a and gluon: PPtr newg = gluondata->produceParticle(newfs[2]); PPtr newc = ba[0]->data().produceParticle(newfs[0]); PPtr newa = ba[1]->data().produceParticle(newfs[1]); born->spectator(0); born->emitted(3); // decaying particle born->incoming().push_back(born->bornIncoming()[0]->dataPtr()-> produceParticle(born->bornIncoming()[0]->momentum())); // colour flow newg->incomingColour(born->incoming()[0],ba[0]->id()<0); newg->colourConnect(newc ,ba[0]->id()<0); if(born->bornOutgoing()[0]->id()==newc->id()) { born->outgoing().push_back(newc); born->outgoing().push_back(newa); born->emitter(1); } else { born->outgoing().push_back(newa); born->outgoing().push_back(newc); born->emitter(2); } born->outgoing().push_back(newg); // boost for the W LorentzRotation trans(ba[1]->momentum().findBoostToCM()); trans.boost(newfs[1].boostVector()); born->transformation(trans); if(!inTheDeadRegion(_xg,_xa,_ktb,_ktc)) { generator()->log() << "SMTopDecayer::applyHardMatrixElementCorrection()\n" << "Just found a point that escaped from the dead region!\n" << " _xg: " << _xg << " _xa: " << _xa << " newfs.size(): " << newfs.size() << endl; } born->interaction(ShowerInteraction::QCD); return born; } vector<Lorentz5Momentum> SMTopDecayer:: applyHard(const ParticleVector &p,double ktb, double ktc) { // ********************************* // // First we see if we get a dead // // region event: _xa,_xg // // ********************************* // vector<Lorentz5Momentum> fs; // Return if there is no (NLO) gluon emission: double weight = getHard(ktb,ktc); if(weight>1.) { generator()->log() << "Weight greater than 1 for hard emission in " << "SMTopDecayer::applyHard xg = " << _xg << " xa = " << _xa << "\n"; weight=1.; } // Accept/Reject if (weight<UseRandom::rnd()||p.size()!= 2) return fs; // Drop events if getHard returned a negative weight // as in events that, somehow have escaped from the dead region // or, worse, the allowed region. if(weight<0.) return fs; // Calculate xc by momentum conservation: _xc = 2.-_xa-_xg; // ************************************ // // Now we get the boosts & rotations to // // go from lab to top rest frame with // // a in the +z direction. // // ************************************ // Lorentz5Momentum pa_lab,pb_lab,pc_lab,pg_lab; // Calculate momentum of b: pb_lab = p[0]->momentum() + p[1]->momentum(); // Define/assign momenta of c,a and the gluon: if(abs(p[0]->id())==5) { pc_lab = p[0]->momentum(); pa_lab = p[1]->momentum(); } else { pc_lab = p[1]->momentum(); pa_lab = p[0]->momentum(); } // Calculate the boost to the b rest frame: SpinOneLorentzRotation rot0(pb_lab.findBoostToCM()); // Calculate the rotation matrix to position a along the +z direction // in the rest frame of b and does a random rotation about z: SpinOneLorentzRotation rot1 = rotateToZ(rot0*pa_lab); // Calculate the boost from the b rest frame back to the lab: // and the inverse of the random rotation about the z-axis and the // rotation required to align a with +z: SpinOneLorentzRotation invrot = rot0.inverse()*rot1.inverse(); // ************************************ // // Now we construct the momenta in the // // b rest frame using _xa,_xg. // // First we construct b, then c and g, // // finally we generate a by momentum // // conservation. // // ************************************ // Lorentz5Momentum pa_brf, pb_brf(_mt), pc_brf, pg_brf; // First we set the top quark to being on-shell and at rest. // Second we set the energies of c and g, pc_brf.setE(0.5*_mt*(2.-_xa-_xg)); pg_brf.setE(0.5*_mt*_xg); // then their masses, pc_brf.setMass(_mc); pg_brf.setMass(ZERO); // Now set the z-component of c and g. For pg we simply start from // _xa and _xg, while for pc we assume it is equal to minus the sum // of the z-components of a (assumed to point in the +z direction) and g. double root=sqrt(_xa*_xa-4.*_a); pg_brf.setZ(_mt*(1.-_xa-_xg+0.5*_xa*_xg-_c+_a)/root); pc_brf.setZ(-1.*( pg_brf.z()+_mt*0.5*root)); // Now set the y-component of c and g's momenta pc_brf.setY(ZERO); pg_brf.setY(ZERO); // Now set the x-component of c and g's momenta pg_brf.setX(sqrt(sqr(pg_brf.t())-sqr(pg_brf.z()))); pc_brf.setX(-pg_brf.x()); // Momenta b,c,g are now set. Now we obtain a from momentum conservation, pa_brf = pb_brf-pc_brf-pg_brf; pa_brf.setMass(pa_brf.m()); pa_brf.rescaleEnergy(); // ************************************ // // Now we orient the momenta and boost // // them back to the original lab frame. // // ************************************ // // As in herwig6507 we assume that, in the rest frame // of b, we have aligned the W boson momentum in the // +Z direction by rot1*rot0*pa_lab, therefore // we obtain the new pa_lab by applying: // invrot*pa_brf. pa_lab = invrot*pa_brf; pb_lab = invrot*pb_brf; pc_lab = invrot*pc_brf; pg_lab = invrot*pg_brf; fs.push_back(pc_lab); fs.push_back(pa_lab); fs.push_back(pg_lab); return fs; } double SMTopDecayer::getHard(double ktb, double ktc) { // zero the variables _xg = 0.; _xa = 0.; _xc = 0.; // Get a phase space point in the dead region: double volume_factor = deadRegionxgxa(ktb,ktc); // if outside region return -1 if(volume_factor<0) return volume_factor; // Compute the weight for this phase space point: double weight = volume_factor*me(_xa,_xg)*(1.+_a-_c-_xa); // Alpha_S and colour factors - this hard wired Alpha_S needs removing. weight *= (4./3.)/Constants::pi *(alphaS()->value(_mt*_mt*_xg*(1.-_xa+_a-_c)/(2.-_xg-_xa-_c))); return weight; } bool SMTopDecayer::softMatrixElementVeto(ShowerProgenitorPtr initial, ShowerParticlePtr parent,Branching br) { // check if we need to apply the full correction long id[2]={abs(initial->progenitor()->id()),abs(parent->id())}; // the initial-state correction if(id[0]==ParticleID::t&&id[1]==ParticleID::t) { Energy pt=br.kinematics->pT(); // check if hardest so far // if not just need to remove effect of enhancement bool veto(false); // if not hardest so far if(pt<initial->highestpT()) veto=!UseRandom::rndbool(1./_initialenhance); // if hardest so far do calculation else { // values of kappa and z double z(br.kinematics->z()),kappa(sqr(br.kinematics->scale()/_mt)); // parameters for the translation double w(1.-(1.-z)*(kappa-1.)),u(1.+_a-_c-(1.-z)*kappa),v(sqr(u)-4.*_a*w*z); // veto if outside phase space if(v<0.) veto=true; // otherwise calculate the weight else { v = sqrt(v); double xa((0.5*(u+v)/w+0.5*(u-v)/z)),xg((1.-z)*kappa); double f(me(xa,xg)), J(0.5*(u+v)/sqr(w)-0.5*(u-v)/sqr(z)+_a*sqr(w-z)/(v*w*z)); double wgt(f*J*2./kappa/(1.+sqr(z)-2.*z/kappa)/_initialenhance); // This next `if' prevents the hardest emission from the // top shower ever entering the so-called T2 region of the // phase space if that region is to be populated by the hard MEC. if(_useMEforT2&&xg>xgbcut(_ktb)) wgt = 0.; if(wgt>1.) { generator()->log() << "Violation of maximum for initial-state " << " soft veto in " << "SMTopDecayer::softMatrixElementVeto" << "xg = " << xg << " xa = " << xa << "weight = " << wgt << "\n"; wgt=1.; } // compute veto from weight veto = !UseRandom::rndbool(wgt); } // if not vetoed reset max if(!veto) initial->highestpT(pt); } // if vetoing reset the scale if(veto) parent->vetoEmission(br.type,br.kinematics->scale()); // return the veto return veto; } // final-state correction else if(id[0]==ParticleID::b&&id[1]==ParticleID::b) { Energy pt=br.kinematics->pT(); // check if hardest so far // if not just need to remove effect of enhancement bool veto(false); // if not hardest so far if(pt<initial->highestpT()) return !UseRandom::rndbool(1./_finalenhance); // if hardest so far do calculation // values of kappa and z double z(br.kinematics->z()),kappa(sqr(br.kinematics->scale()/_mt)); // momentum fractions double xa(1.+_a-_c-z*(1.-z)*kappa),r(0.5*(1.+_c/(1.+_a-xa))),root(sqr(xa)-4.*_a); if(root<0.) { generator()->log() << "Imaginary root for final-state veto in " << "SMTopDecayer::softMatrixElementVeto" << "\nz = " << z << "\nkappa = " << kappa << "\nxa = " << xa << "\nroot^2= " << root; parent->vetoEmission(br.type,br.kinematics->scale()); return true; } root=sqrt(root); double xg((2.-xa)*(1.-r)-(z-r)*root); // xfact (below) is supposed to equal xg/(1-z). double xfact(z*kappa/2./(z*(1.-z)*kappa+_c)*(2.-xa-root)+root); // calculate the full result double f(me(xa,xg)); // jacobian double J(z*root); double wgt(f*J*2.*kappa/(1.+sqr(z)-2.*_c/kappa/z)/sqr(xfact)/_finalenhance); if(wgt>1.) { generator()->log() << "Violation of maximum for final-state soft veto in " << "SMTopDecayer::softMatrixElementVeto" << "xg = " << xg << " xa = " << xa << "weight = " << wgt << "\n"; wgt=1.; } // compute veto from weight veto = !UseRandom::rndbool(wgt); // if vetoing reset the scale if(veto) parent->vetoEmission(br.type,br.kinematics->scale()); // return the veto return veto; } // otherwise don't veto else return !UseRandom::rndbool(1./_finalenhance); } double SMTopDecayer::me(double xw,double xg) { double prop(1.+_a-_c-xw),xg2(sqr(xg)); double lambda=sqrt(1.+_a*_a+_c*_c-2.*_a-2.*_c-2.*_a*_c); double denom=(1.-2*_a*_a+_a+_c*_a+_c*_c-2.*_c); double wgt=-_c*xg2/prop+(1.-_a+_c)*xg-(prop*(1 - xg)+xg2) +(0.5*(1.+2.*_a+_c)*sqr(prop-xg)*xg+2.*_a*prop*xg2)/denom; return wgt/(lambda*prop); } // This function is auxiliary to the xab function. double SMTopDecayer::xgbr(int toggle) { return 1.+toggle*sqrt(_a)-_c*(1.-toggle*sqrt(_a))/(1.-_a); } // This function is auxiliary to the xab function. double SMTopDecayer::ktr(double xgb, int toggle) { return 2.*xgb/ (xgb+toggle*sqrt((1.-1./_a) *(xgb-xgbr( 1)) *(xgb-xgbr(-1)))); } // Function xab determines xa (2*W energy fraction) for a given value // of xg (2*gluon energy fraction) and kappa tilde (q tilde squared over // m_top squared). Hence this function allows you to draw 1: the total // phase space volume in the xa vs xg plane 2: for a given value of // kappa tilde (i.e. starting evolution scale) the associated contour // in the xa vs xg plane (and hence the regions that either shower can // populate). This calculation is done assuming the emission came from // the top quark i.e. kappa tilde here is the q tilde squared of the TOP // quark divided by m_top squared. double SMTopDecayer::xab(double xgb, double kt, int toggle) { double xab; if(toggle==2) { // This applies for g==0.&&kt==ktr(a,c,0.,xgb,1). xab = -2.*_a*(xgb-2.)/(1.+_a-_c-xgb); } else if(toggle==1) { // This applies for kt==1&&g==0. double lambda = sqrt(sqr(xgb-1.+_a+_c)-4.*_a*_c); xab = (0.5/(kt-xgb))*(kt*(1.+_a-_c-xgb)-lambda) + (0.5/(kt+xgb*(1.-kt)))*(kt*(1.+_a-_c-xgb)+lambda); } else { // This is the form of xab FOR _g=0. double ktmktrpktmktrm = kt*kt - 4.*_a*(kt-1.)*xgb*xgb / (sqr(1.-_a-_c-xgb)-4.*_a*_c); if(fabs(kt-(2.*xgb-2.*_g)/(xgb-sqrt(xgb*xgb-4.*_g)))/kt>1.e-6) { double lambda = sqrt((sqr(1.-_a-_c-xgb)-4.*_a*_c)*ktmktrpktmktrm); xab = (0.5/(kt-xgb))*(kt*(1.+_a-_c-xgb)-lambda) + (0.5/(kt+xgb*(1.-kt)))*(kt*(1.+_a-_c-xgb)+lambda); } else { // This is the value of xa as a function of xb when kt->infinity. // Where we take any kt > (2.*xgb-2.*_g)/(xgb-sqrt(xgb*xgb-4.*_g)) // as being effectively infinite. This kt value is actually the // maximum allowed value kt can have if the phase space is calculated // without the approximation of _g=0 (massless gluon). This formula // for xab below is then valid for _g=0 AND kt=infinity only. xab = ( 2.*_c+_a*(xgb-2.) + 3.*xgb - xgb*(_c+xgb+sqrt(_a*_a-2.*(_c-xgb+1.)*_a+sqr(_c+xgb-1.))) - 2. )/2./(xgb-1.); } } if(std::isnan(xab)) { double ktmktrpktmktrm = ( sqr(xgb*kt-2.*(xgb-_g)) -kt*kt*(1.-1./_a)*(xgb-xgbr( 1)-_g/(1.+sqrt(_a))) *(xgb-xgbr(-1)-_g/(1.-sqrt(_a))) )/ (xgb*xgb-(1.-1./_a)*(xgb-xgbr( 1)-_g/(1.+sqrt(_a))) *(xgb-xgbr(-1)-_g/(1.-sqrt(_a))) ); double lambda = sqrt((xgb-1.+sqr(sqrt(_a)+sqrt(_c-_g))) *(xgb-1.+sqr(sqrt(_a)-sqrt(_c-_g)))* ktmktrpktmktrm); xab = (0.5/(kt-xgb+_g))*(kt*(1.+_a-_c+_g-xgb)-lambda) + (0.5/(kt+xgb*(1.-kt)-_g))*(kt*(1.+_a-_c+_g-xgb)+lambda); if(std::isnan(xab)) throw Exception() << "TopMECorrection::xab complex x_a value.\n" << " xgb = " << xgb << "\n" << " xab = " << xab << "\n" << " toggle = " << toggle << "\n" << " ktmktrpktmktrm = " << ktmktrpktmktrm << Exception::eventerror; } return xab; } // xgbcut is the point along the xg axis where the upper bound on the // top quark (i.e. b) emission phase space goes back on itself in the // xa vs xg plane i.e. roughly mid-way along the xg axis in // the xa vs xg Dalitz plot. double SMTopDecayer::xgbcut(double kt) { double lambda2 = 1.+_a*_a+_c*_c-2.*_a-2.*_c-2.*_a*_c; double num1 = kt*kt*(1.-_a-_c); double num2 = 2.*kt*sqrt(_a*(kt*kt*_c+lambda2*(kt-1.))); return (num1-num2)/(kt*kt-4.*_a*(kt-1.)); } double SMTopDecayer::xaccut(double kt) { return 1.+_a-_c-0.25*kt; } double SMTopDecayer::z(double xac, double kt, int toggle1, int toggle2) { double z = -1.0; if(toggle2==0) { z = (kt+toggle1*sqrt(kt*(kt-4.*(1.+_a-_c-xac))))/(2.*kt); } else if(toggle2==1) { z = ((1.+_a+_c-xac)+toggle1*(1.+_a-_c-xac)) /(2.*(1.+_a-xac)); } else if(toggle2==2) { z = 0.5; } else { throw Exception() << "Cannot determine z in SMTopDecayer::z()" << Exception::eventerror; } return z; } double SMTopDecayer::xgc(double xac, double kt, int toggle1, int toggle2) { double tiny(1.e-6); double xaToMinBoundary(xac*xac-4.*_a); if(xaToMinBoundary<0) { if(fabs(xaToMinBoundary/(1.-_a)/(1.-_a))<tiny) xaToMinBoundary *= -1.; else throw Exception() << "SMTopDecayer::xgc xa not in phase space!" << Exception::eventerror; } return (2.-xac)*(1.-0.5*(1.+_c/(1.+_a-xac))) -(z(xac,kt,toggle1,toggle2)-0.5*(1.+_c/(1.+_a-xac))) *sqrt(xaToMinBoundary); } double SMTopDecayer::xginvc0(double xg , double kt) { // The function xg(kappa_tilde_c,xa) surely, enough, draws a // line of constant kappa_tilde_c in the xg, xa Dalitz plot. // Such a function can therefore draw the upper and lower // edges of the phase space for emission from c (the b-quark). // However, to sample the soft part of the dead zone effectively // we want to generate a value of xg first and THEN distribute // xa in the associated allowed part of the dead zone. Hence, the // function we want, to define the dead zone in xa for a given // xg, is the inverse of xg(kappa_tilde_c,xa). The full expression // for xg(kappa_tilde_c,xa) is complicated and, sure enough, // does not invert. Therefore we try to overestimate the size // of the dead zone initially, rejecting events which do not // fall exactly inside it afterwards, with the immediate aim // of getting an approximate version of xg(kappa_tilde_c,xa) // that can be inverted. We do this by simply setting c=0 i.e. // the b-quark mass to zero (and the gluon mass of course), in // the full expression xg(...). The result of inverting this // function is the output of this routine (a value of xa) hence // the name xginvc0. xginvc0 is calculated to be, // xginvc0 = (1./3.)*(1.+a+pow((U+sqrt(4.*V*V*V+U*U))/2.,1./3.) // -V*pow(2./(U+sqrt(4.*V*V*V+U*U)),1./3.) // ) // U = 2.*a*a*a - 66.*a*a + 9.*a*kt*xg + 18.*a*kt // - 66.*a + 27.*kt*xg*xg - 45.*kt*xg +18.*kt +2. ; // V = -1.-a*a-14.*a-3.kt*xg+3.*kt; // This function, as with many functions in this ME correction, // is plagued by cuts that have to handled carefully in numerical // implementation. We have analysed the cuts and hence we implement // it in the following way, with a series of 'if' statements. // // A useful -definition- to know in deriving the v<0 terms is // that tanh^-1(z) = 0.5*(log(1.+z)-log(1.-z)). double u,v,output; u = 2.*_a*_a*_a-66.*_a*_a +9.*xg*kt*_a+18.*kt*_a -66.*_a+27.*xg*xg*kt -45.*xg*kt+18.*kt+2.; v = -_a*_a-14.*_a-3.*xg*kt+3.*kt-1.; double u2=u*u,v3=v*v*v; if(v<0.) { if(u>0.&&(4.*v3+u2)<0.) output = cos( atan(sqrt(-4.*v3-u2)/u)/3.); else if(u>0.&&(4.*v3+u2)>0.) output = cosh(atanh(sqrt( 4.*v3+u2)/u)/3.); else output = cos(( atan(sqrt(-4.*v3-u2)/u) +Constants::pi)/3.); output *= 2.*sqrt(-v); } else { output = sinh(log((u+sqrt(4.*v3+u2))/(2.*sqrt(v3)))/3.); output *= 2.*sqrt(v); } if(!isfinite(output)) { throw Exception() << "TopMECorrection::xginvc0:\n" << "possible numerical instability detected.\n" << "\n v = " << v << " u = " << u << "\n4.*v3+u2 = " << 4.*v3+u2 << "\n_a = " << _a << " ma = " << sqrt(_a*_mt*_mt/GeV2) << "\n_c = " << _c << " mc = " << sqrt(_c*_mt*_mt/GeV2) << "\n_g = " << _g << " mg = " << sqrt(_g*_mt*_mt/GeV2) << Exception::eventerror; } return ( 1.+_a +output)/3.; } double SMTopDecayer::approxDeadMaxxa(double xg,double ktb,double ktc) { double maxxa(0.); double x = min(xginvc0(xg,ktc), xab(xg,(2.*xg-2.*_g)/(xg-sqrt(xg*xg-4.*_g)),0)); double y(-9999999999.); if(xg>2.*sqrt(_g)&&xg<=xgbcut(ktb)) { y = max(xab(xg,ktb,0),xab(xg,1.,1)); } else if(xg>=xgbcut(ktb)&&xg<=1.-sqr(sqrt(_a)+sqrt(_c))) { y = max(xab(xg,ktr(xg,1),2),xab(xg,1.,1)); } if(xg>2.*sqrt(_g)&&xg<=1.-sqr(sqrt(_a)+sqrt(_c))) { if(x>=y) { maxxa = x ; } else { maxxa = -9999999.; } } else { maxxa = -9999999.; } return maxxa; } double SMTopDecayer::approxDeadMinxa(double xg,double ktb,double ktc) { double minxa(0.); double x = min(xginvc0(xg,ktc), xab(xg,(2.*xg-2.*_g)/(xg-sqrt(xg*xg-4.*_g)),0)); double y(-9999999999.); if(xg>2.*sqrt(_g)&&xg<=xgbcut(ktb)) { y = max(xab(xg,ktb,0),xab(xg,1.,1)); } else if(xg>=xgbcut(ktb)&&xg<=1.-sqr(sqrt(_a)+sqrt(_c))) { if(_useMEforT2) y = xab(xg,1.,1); else y = max(xab(xg,ktr(xg,1),2),xab(xg,1.,1)); } if(xg>2.*sqrt(_g)&&xg<=1.-sqr(sqrt(_a)+sqrt(_c))) { if(x>=y) { minxa = y ; } else { minxa = 9999999.; } } else { minxa = 9999999.; } return minxa; } // This function returns true if the phase space point (xg,xa) is in the // kinematically allowed phase space. bool SMTopDecayer::inTheAllowedRegion(double xg , double xa) { bool output(true); if(xg<2.*sqrt(_g)||xg>1.-sqr(sqrt(_a)+sqrt(_c))) output = false; if(xa<xab(xg,1.,1)) output = false; if(xa>xab(xg,(2.*xg-2.*_g)/(xg-sqrt(xg*xg-4.*_g)),0)) output = false; return output; } // This function returns true if the phase space point (xg,xa) is in the // approximate (overestimated) dead region. bool SMTopDecayer::inTheApproxDeadRegion(double xg , double xa, double ktb, double ktc) { bool output(true); if(!inTheAllowedRegion(xg,xa)) output = false; if(xa<approxDeadMinxa(xg,ktb,ktc)) output = false; if(xa>approxDeadMaxxa(xg,ktb,ktc)) output = false; return output; } // This function returns true if the phase space point (xg,xa) is in the // dead region. bool SMTopDecayer::inTheDeadRegion(double xg , double xa, double ktb, double ktc) { bool output(true); if(!inTheApproxDeadRegion(xg,xa,ktb,ktc)) output = false; if(xa>xaccut(ktc)) { if(xg<xgc(max(xaccut(ktc),2.*sqrt(_a)),ktc, 1,2)&& xg>xgc(xa,ktc, 1,0)) { output = false; } if(xg>xgc(max(xaccut(ktc),2.*sqrt(_a)),ktc,-1,2)&& xg<xgc(xa,ktc,-1,0)) { output = false; } } return output; } // This function attempts to generate a phase space point in the dead // region and returns the associated phase space volume factor needed for // the associated event weight. double SMTopDecayer::deadRegionxgxa(double ktb,double ktc) { _xg=0.; _xa=0.; // Here we set limits on xg and generate a value inside the bounds. double xgmin(2.*sqrt(_g)),xgmax(1.-sqr(sqrt(_a)+sqrt(_c))); // Generate _xg. if(_xg_sampling==2.) { _xg=xgmin*xgmax/(xgmin+UseRandom::rnd()*(xgmax-xgmin)); } else { _xg=xgmin*xgmax/pow(( pow(xgmin,_xg_sampling-1.) + UseRandom::rnd()*(pow(xgmax,_xg_sampling-1.) -pow(xgmin,_xg_sampling-1.)) ),1./(_xg_sampling-1.)); } // Here we set the bounds on _xa for given _xg. if(_xg<xgmin||xgmin>xgmax) throw Exception() << "TopMECorrection::deadRegionxgxa:\n" << "upper xg bound is less than the lower xg bound.\n" << "\n_xg = " << _xg << "\n2.*sqrt(_g) = " << 2.*sqrt(_g) << "\n_a = " << _a << " ma = " << sqrt(_a*_mt*_mt/GeV2) << "\n_c = " << _c << " mc = " << sqrt(_c*_mt*_mt/GeV2) << "\n_g = " << _g << " mg = " << sqrt(_g*_mt*_mt/GeV2) << Exception::eventerror; double xamin(approxDeadMinxa(_xg,ktb,ktc)); double xamax(approxDeadMaxxa(_xg,ktb,ktc)); // Are the bounds sensible? If not return. if(xamax<=xamin) return -1.; _xa=1.+_a-(1.+_a-xamax)*pow((1.+_a-xamin)/(1.+_a-xamax),UseRandom::rnd()); // If outside the allowed region return -1. if(!inTheDeadRegion(_xg,_xa,ktb,ktc)) return -1.; // The integration volume for the weight double xg_vol,xa_vol; if(_xg_sampling==2.) { xg_vol = (xgmax-xgmin) / (xgmax*xgmin); } else { xg_vol = (pow(xgmax,_xg_sampling-1.)-pow(xgmin,_xg_sampling-1.)) / ((_xg_sampling-1.)*pow(xgmax*xgmin,_xg_sampling-1.)); } xa_vol = log((1.+_a-xamin)/(1.+_a-xamax)); // Here we return the integral volume factor multiplied by the part of the // weight left over which is not included in the BRACES function, i.e. // the part of _xg^-2 which is not absorbed in the integration measure. return xg_vol*xa_vol*pow(_xg,_xg_sampling-2.); } LorentzRotation SMTopDecayer::rotateToZ(Lorentz5Momentum v) { // compute the rotation matrix LorentzRotation trans; // rotate so in z-y plane trans.rotateZ(-atan2(v.y(),v.x())); // rotate so along Z trans.rotateY(-acos(v.z()/v.vect().mag())); // generate random rotation double c,s,cs; do { c = 2.*UseRandom::rnd()-1.; s = 2.*UseRandom::rnd()-1.; cs = c*c+s*s; } while(cs>1.||cs==0.); double cost=(c*c-s*s)/cs,sint=2.*c*s/cs; // apply random azimuthal rotation trans.rotateZ(atan2(sint,cost)); return trans; } -bool SMTopDecayer::deadZoneCheck(double xw, double xg){ - - //veto events not in the dead cone - double Lambda = sqrt(1. + sqr(s2()) + sqr(e2()) - 2.*s2() - 2.*e2() - 2.*s2()*e2()); - double kappa = e2() + 0.5*(1. - s2() + e2() + Lambda); - //invert xw for z values - double A = 1.; - double B = -1.; - double C = (1.+s2()-e2()-xw)/kappa; - if((sqr(B) - 4.*A*C) >= 0.){ - double z[2]; - z[0] = (-B + sqrt(sqr(B) - 4.*A*C))/(2.*A); - z[1] = (-B - sqrt(sqr(B) - 4.*A*C))/(2.*A); - double r = 0.5*(1. + e2()/(1. + s2()- xw)); - double xg_lims [2]; - xg_lims[0] = (2. - xw)*(1.-r) - (z[0]-r)*sqrt(sqr(xw) - 4.*s2()); - xg_lims[1] = (2. - xw)*(1.-r) - (z[1]-r)*sqrt(sqr(xw) - 4.*s2()); - double xg_low_lim = min(xg_lims[0], xg_lims[1]); - double xg_upp_lim = max(xg_lims[0], xg_lims[1]); - if (xg>=xg_low_lim && xg<=xg_upp_lim) return false; - } - - double kappa_t = 1. + 0.5*(1. - s2() + e2() + Lambda); - double z = 1. - xg/kappa_t; - double u = 1. + s2() - e2() - (1.-z)*kappa_t; - double y = 1. - (1.-z)*(kappa_t-1.); - if (sqr(u) - 4.*s2()*y*z >= 0.){ - double v = sqrt(sqr(u) - 4.*s2()*y*z); - double xw_lim = (u + v) / (2.*y) + (u - v) / (2.*z); - if (xw <= xw_lim) return false; - } - else if (sqr(u) - 4.*s2()*y*z < 0.){ - double xg_lim = (8.*s2() -2.*xw*(1-e2()+s2()))/(4.*s2()-2.*xw); - if (xg>=xg_lim) return false; - } - - return true; -} - double SMTopDecayer::loME(const Particle & inpart, const ParticleVector & decay) { // spinors vector<SpinorWaveFunction > swave; vector<SpinorBarWaveFunction> awave; vector<VectorWaveFunction> vwave; // spinors if(inpart.id()>0) { SpinorWaveFunction ::calculateWaveFunctions(swave,const_ptr_cast<tPPtr>(&inpart), incoming); SpinorBarWaveFunction::calculateWaveFunctions(awave,decay[0],outgoing); } else { SpinorBarWaveFunction::calculateWaveFunctions(awave,const_ptr_cast<tPPtr>(&inpart), incoming); SpinorWaveFunction ::calculateWaveFunctions(swave,decay[0],outgoing); } // polarization vectors VectorWaveFunction::calculateWaveFunctions(vwave,decay[1],outgoing,false); Energy2 scale(sqr(inpart.mass())); double me=0.; if(inpart.id() == ParticleID::t) { for(unsigned int thel = 0; thel < 2; ++thel) { for(unsigned int bhel = 0; bhel < 2; ++bhel) { for(unsigned int whel = 0; whel < 3; ++whel) { Complex diag = FFWVertex_->evaluate(scale,swave[thel],awave[bhel],vwave[whel]); me += norm(diag); } } } } else if(inpart.id() == ParticleID::tbar) { for(unsigned int thel = 0; thel < 2; ++thel) { for(unsigned int bhel = 0; bhel < 2; ++bhel){ for(unsigned int whel = 0; whel < 3; ++whel) { Complex diag = FFWVertex_->evaluate(scale,swave[bhel],awave[thel],vwave[whel]); me += norm(diag); } } } } return me; } double SMTopDecayer::realME(const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter) { // vertex for emission from fermions AbstractFFVVertexPtr vertex = inter==ShowerInteraction::QCD ? FFGVertex_ : FFPVertex_; // spinors vector<SpinorWaveFunction > swave; vector<SpinorBarWaveFunction> awave; vector<VectorWaveFunction> vwave,gwave; // spinors if(inpart.id()>0) { SpinorWaveFunction ::calculateWaveFunctions(swave,const_ptr_cast<tPPtr>(&inpart), incoming); SpinorBarWaveFunction::calculateWaveFunctions(awave,decay[0],outgoing); } else { SpinorBarWaveFunction::calculateWaveFunctions(awave,const_ptr_cast<tPPtr>(&inpart), incoming); SpinorWaveFunction ::calculateWaveFunctions(swave,decay[0],outgoing); } // polarization vectors VectorWaveFunction::calculateWaveFunctions(vwave,decay[1],outgoing,false); VectorWaveFunction::calculateWaveFunctions(gwave,decay[2],outgoing,true ); Energy2 scale(sqr(inpart.mass())); double me=0.; vector<Complex> diag(3,0.); if(inpart.id() == ParticleID::t) { for(unsigned int thel = 0; thel < 2; ++thel) { for(unsigned int bhel = 0; bhel < 2; ++bhel) { for(unsigned int whel = 0; whel < 3; ++whel) { for(unsigned int ghel =0; ghel <3; ghel+=2) { // emission from top SpinorWaveFunction interF = vertex->evaluate(scale,3,inpart.dataPtr(),swave[thel],gwave[ghel]); diag[0] = FFWVertex_->evaluate(scale,interF,awave[bhel],vwave[whel]); // emission from bottom SpinorBarWaveFunction interB = vertex->evaluate(scale,3,decay[0]->dataPtr(),awave[bhel],gwave[ghel]); diag[1] = FFWVertex_->evaluate(scale,swave[thel],interB,vwave[whel]); // emission from W if(inter==ShowerInteraction::QED) { VectorWaveFunction interV = WWWVertex_->evaluate(scale,3,decay[1]->dataPtr()->CC(),vwave[whel],gwave[ghel]); diag[1] = FFWVertex_->evaluate(scale,swave[thel],awave[bhel],interV); } Complex sum = std::accumulate(diag.begin(),diag.end(),Complex(0.)); me += norm(sum); } } } } } else if(inpart.id() == ParticleID::tbar) { for(unsigned int thel = 0; thel < 2; ++thel) { for(unsigned int bhel = 0; bhel < 2; ++bhel){ for(unsigned int whel = 0; whel < 3; ++whel) { for(unsigned int ghel =0; ghel <3; ghel+=2) { // emission from top SpinorBarWaveFunction interB = vertex->evaluate(scale,3,inpart.dataPtr(),awave[thel],gwave[ghel]); diag[1] = FFWVertex_->evaluate(scale,swave[bhel],interB,vwave[whel]); // emission from bottom SpinorWaveFunction interF = vertex->evaluate(scale,3,decay[0]->dataPtr(),swave[bhel],gwave[ghel]); diag[0] = FFWVertex_->evaluate(scale,interF,awave[thel],vwave[whel]); // emission from W if(inter==ShowerInteraction::QED) { VectorWaveFunction interV = WWWVertex_->evaluate(scale,3,decay[1]->dataPtr()->CC(),vwave[whel],gwave[ghel]); diag[1] = FFWVertex_->evaluate(scale,swave[bhel],awave[thel],interV); } Complex sum = std::accumulate(diag.begin(),diag.end(),Complex(0.)); me += norm(sum); } } } } } // divide out the coupling me /= norm(vertex->norm()); // return the total return me; } double SMTopDecayer::matrixElementRatio(const Particle & inpart, const ParticleVector & decay2, const ParticleVector & decay3, MEOption , ShowerInteraction inter) { double Nc = standardModel()->Nc(); double Cf = (sqr(Nc) - 1.) / (2.*Nc); // if(inter==ShowerInteraction::QED) return 0.; // double f = (1. + sqr(e2()) - 2.*sqr(s2()) + s2() + s2()*e2() - 2.*e2()); // // // double B = f/s2(); // Energy2 PbPg = decay3[0]->momentum()*decay3[2]->momentum(); // Energy2 PtPg = inpart.momentum()*decay3[2]->momentum(); // Energy2 PtPb = inpart.momentum()*decay3[0]->momentum(); // double R = Cf *((-4.*sqr(mb())*f/s2()) * ((sqr(mb())*e2()/sqr(PbPg)) + // (sqr(mb())/sqr(PtPg)) - 2.*(PtPb/(PtPg*PbPg))) + // (16. + 8./s2() + 8.*e2()/s2()) * ((PtPg/PbPg) + (PbPg/PtPg)) - // (16./s2()) * (1. + e2())); // return R/B*Constants::pi; double Bnew = loME(inpart,decay2); double Rnew = realME(inpart,decay3,inter); double output = Rnew/Bnew*4.*Constants::pi*sqr(inpart.mass())*UnitRemoval::InvE2; if(inter==ShowerInteraction::QCD) output *= Cf; return output; } diff --git a/Decay/Perturbative/SMTopDecayer.h b/Decay/Perturbative/SMTopDecayer.h --- a/Decay/Perturbative/SMTopDecayer.h +++ b/Decay/Perturbative/SMTopDecayer.h @@ -1,522 +1,516 @@ // -*- C++ -*- // // SMTopDecayer.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_SMTopDecayer_H #define HERWIG_SMTopDecayer_H // // This is the declaration of the SMTopDecayer class. // #include "Herwig/Decay/PerturbativeDecayer.h" #include "ThePEG/Helicity/Vertex/AbstractFFVVertex.h" #include "ThePEG/Helicity/Vertex/AbstractVVVVertex.h" #include "Herwig/Decay/DecayPhaseSpaceMode.h" #include "Herwig/Models/StandardModel/StandardModel.h" #include "Herwig/Shower/Core/Couplings/ShowerAlpha.fh" namespace Herwig { using namespace ThePEG; using namespace ThePEG::Helicity; /** * \ingroup Decay * * The SMTopDecayer performs decays of the top quark into * the bottom quark and qqbar pairs or to the bottom quark and lepton * neutrino pairs via W boson exchange. */ class SMTopDecayer: public PerturbativeDecayer { public: /** * The default constructor. */ SMTopDecayer(); public: /** * Virtual members to be overridden by inheriting classes * which implement hard corrections */ //@{ /** * Has an old fashioned ME correction */ virtual bool hasMECorrection() {return true;} /** * Initialize the ME correction */ virtual void initializeMECorrection(RealEmissionProcessPtr , double & , double & ); /** * Apply the hard matrix element correction to a given hard process or decay */ virtual RealEmissionProcessPtr applyHardMatrixElementCorrection(RealEmissionProcessPtr); /** * Apply the soft matrix element correction * @param initial The particle from the hard process which started the * shower * @param parent The initial particle in the current branching * @param br The branching struct * @return If true the emission should be vetoed */ virtual bool softMatrixElementVeto(ShowerProgenitorPtr initial, ShowerParticlePtr parent,Branching br); /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() {return FSR;} //@} public: /** * Which of the possible decays is required */ virtual int modeNumber(bool & , tcPDPtr , const tPDVector & ) const {return -1;} /** * Check if this decayer can perfom the decay for a particular mode. * Uses the modeNumber member but can be overridden * @param parent The decaying particle * @param children The decay products */ virtual bool accept(tcPDPtr parent, const tPDVector & children) const; /** * 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; /** * 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; /** * Method to return an object to calculate the 3 (or higher body) partial width * @param dm The DecayMode * @return A pointer to a WidthCalculatorBase object capable of calculating the width */ virtual WidthCalculatorBasePtr threeBodyMEIntegrator(const DecayMode & dm) const; /** * The differential three body decay rate with one integral performed. * @param imode The mode for which the matrix element is needed. * @param q2 The scale, \e i.e. the mass squared of the decaying particle. * @param s The invariant mass which still needs to be integrate over. * @param m1 The mass of the first outgoing particle. * @param m2 The mass of the second outgoing particle. * @param m3 The mass of the third outgoing particle. * @return The differential rate \f$\frac{d\Gamma}{ds}\f$ */ virtual InvEnergy threeBodydGammads(const int imode, const Energy2 q2, const Energy2 s, const Energy m1, const Energy m2, const Energy m3) const; /** * Output the setup information for the particle database * @param os The stream to output the information to * @param header Whether or not to output the information for MySQL */ virtual void dataBaseOutput(ofstream & os,bool header) const; 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: /** * The integrand for the integrate partial width */ Energy6 dGammaIntegrand(Energy2 mffb2, Energy2 mbf2, Energy mt, Energy mb, Energy mf, Energy mfb, Energy mw) const; protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const {return new_ptr(*this);} /** 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 {return new_ptr(*this);} //@} protected: /** @name Standard Interfaced functions. */ //@{ /** * Initialize this object after the setup phase before saving and * 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: /** * Apply the hard matrix element */ vector<Lorentz5Momentum> applyHard(const ParticleVector &p,double,double); /** * Get the weight for hard emission */ double getHard(double, double); /** * This function is auxiliary to the function \f$x_{a}\f$ (hXAB). */ double xgbr(int); /** * This function is auxiliary to the function \f$x_{a}\f$ (hXAB). */ double ktr(double,int); /** * This function determines \f$x_{a}\f$ as a function of \f$x_{g}\f$ * and \f$\kappa\f$ where \f$\kappa\f$ pertains to emissions from the * b. */ double xab(double,double,int); /** * This function determines the point (\f$x_{g}\f$) where the condition that * \f$x_{a}\f$ be real supersedes that due to the external input * \f$\tilde{\kappa}\f$ where, again, \f$\kappa\f$ pertains to emissions from the * b. */ double xgbcut(double); /** * This function determines the minimum value of \f$x_{a}\f$ * for a given \f$\tilde{\kappa}\f$ where \f$\kappa\f$ pertains to * emissions from the c. */ double xaccut(double); /** * This function is auxiliary to the function \f$x_{g}\f$ (hXGC). */ double z(double,double,int,int); /** * This function determines \f$x_{g}\f$ as a function of \f$x_{a}\f$ * and \f$\kappa\f$ where \f$\kappa\f$ pertains to emissions from the * c. It is multivalued, one selects a branch according to the * second to last integer flag (+/-1). The last integer flag * is used to select whether (1) or not (0) you wish to have the * function for the special case of the full phase space, in which * case the fifth argument \f$\kappa\f$ is irrelevant. */ double xgc(double,double,int,int); /** * This function, \f$x_{g,c=0}^{-1}\f$, returns \f$x_{a}\f$ as a function * of \f$x_{g}\f$ for the special case of c=0, for emissions from c * (the b-quark). The third input is \f$\tilde{\kappa}\f$ which pertains * to emissions from c. */ double xginvc0(double,double); /** * For a given value of \f$x_{g}\f$ this returns the maximum value of \f$x_{a}\f$ * in the dead region. */ double approxDeadMaxxa(double,double,double); /** * For a given value of \f$x_{g}\f$ this returns the maximum value of \f$x_{a}\f$ * in the dead region. */ double approxDeadMinxa(double,double,double); /** * This function returns true or false according to whether the values * xg,xa are in the allowed region, the kinematically accessible phase * space. */ bool inTheAllowedRegion(double,double); /** * This function returns true or false according to whether the values * xg,xa are exactly in the approximate dead region. */ bool inTheApproxDeadRegion(double,double, double,double); /** * This function returns true or false according to whether the values * xg,xa are exactly in the dead region. */ bool inTheDeadRegion(double,double, double,double); /** * This function returns values of (\f$x_{g}\f$,\f$x_{a}\f$) distributed * according to \f$\left(1+a-x_{a}\right)^{-1}x_{g}^{-2}\f$ in the * approximate dead region. */ double deadRegionxgxa(double,double); /** * This rotation takes a 5-momentum and returns a rotation matrix * such that it acts on the input 5-momentum so as to * make it point in the +Z direction. Finally it performs a randomn * rotation about the z-axis. */ LorentzRotation rotateToZ(Lorentz5Momentum); /** * Full matrix element with a factor of \f$\frac{\alpha_SC_F}{x_g^2\pi}\f$ removed. * @param xw The momentum fraction of the W boson * @param xg The momentum fraction of the gluon. */ double me(double xw, double xg); protected: - /** - * check if event is in dead region - */ - bool deadZoneCheck(double xw, double xg); - -protected: /** * Calculate matrix element ratio R/B */ virtual double matrixElementRatio(const Particle & inpart, const ParticleVector & decay2, const ParticleVector & decay3, MEOption meopt, ShowerInteraction inter); /** * LO matrix element for \f$t\to b W^\pm\f$ */ double loME(const Particle & inpart, const ParticleVector & decay); /** * LO matrix element for \f$t\to b W^\pm\f$ */ double realME(const Particle & inpart, const ParticleVector & decay, ShowerInteraction inter); private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ SMTopDecayer & operator=(const SMTopDecayer &); /** * Pointer to the W vertex */ AbstractFFVVertexPtr FFWVertex_; /** * Pointer to the gluon vertex */ AbstractFFVVertexPtr FFGVertex_; /** * Pointer to the photon vertex */ AbstractFFVVertexPtr FFPVertex_; /** * Pointer to the photon vertex */ AbstractVVVVertexPtr WWWVertex_; /** * Max weight for integration */ //@{ /** * Weight \f$W\to q\bar{q}'\f$ */ vector<double> _wquarkwgt; /** * Weight \f$W\to \ell \nu\f$ */ vector<double> _wleptonwgt; //@} /** * Pointer to the \f$W^\pm\f$ */ PDPtr _wplus; /** * Spin density matrix for the decay */ mutable RhoDMatrix _rho; /** * 1st spinor for the decay */ mutable vector<SpinorWaveFunction > _inHalf; /** * 2nd spinor for the decay */ mutable vector<SpinorWaveFunction > _outHalf; /** * 1st barred spinor for the decay */ mutable vector<SpinorBarWaveFunction> _inHalfBar; /** * 2nd barred spinor for the decay */ mutable vector<SpinorBarWaveFunction> _outHalfBar; /** * The mass of the W boson */ Energy _ma; /** * The mass of the bottom quark */ Energy _mc; /** * The top mass */ Energy _mt; /** * The gluon mass. */ Energy _mg; /** * The mass ratio for the W. */ double _a; /** * The mass ratio for the bottom. */ double _c; /** * The mass ratio for the gluon. */ double _g; /** * Two times the energy fraction of a. */ double _ktb; /** * Two times the energy fraction of the gluon. */ double _ktc; /** * Two times the energy fraction of the gluon. */ double _xg; /** * Two times the energy fraction of a. */ double _xa; /** * Two times the energy fraction of c. */ double _xc; /** * This determines the hard matrix element importance * sampling in _xg. _xg_sampling=2.0 samples as 1/xg^2. */ double _xg_sampling; /** * The enhancement factor for initial-state radiation */ double _initialenhance; /** * The enhancement factor for final-state radiation */ double _finalenhance; /** * This flag determines whether the T2 region in the decay shower * (JHEP12(2003)_045) is populated by the ME correction (true) or * the shower from the decaying particle. */ bool _useMEforT2; }; } #endif /* HERWIG_SMTopDecayer_H */ diff --git a/Decay/PerturbativeDecayer.cc b/Decay/PerturbativeDecayer.cc --- a/Decay/PerturbativeDecayer.cc +++ b/Decay/PerturbativeDecayer.cc @@ -1,822 +1,948 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the PerturbativeDecayer class. // #include "PerturbativeDecayer.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/EventRecord/Particle.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Interface/Reference.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Utilities/EnumIO.h" using namespace Herwig; void PerturbativeDecayer::persistentOutput(PersistentOStream & os) const { os << ounit(pTmin_,GeV) << oenum(inter_) << alphaS_ << alphaEM_; } void PerturbativeDecayer::persistentInput(PersistentIStream & is, int) { is >> iunit(pTmin_,GeV) >> ienum(inter_) >> alphaS_ >> alphaEM_; } // The following static variable is needed for the type // description system in ThePEG. DescribeAbstractClass<PerturbativeDecayer,DecayIntegrator> describeHerwigPerturbativeDecayer("Herwig::PerturbativeDecayer", "Herwig.so HwPerturbativeDecay.so"); void PerturbativeDecayer::Init() { static ClassDocumentation<PerturbativeDecayer> documentation ("The PerturbativeDecayer class is the mase class for perturbative decays in Herwig"); static Parameter<PerturbativeDecayer,Energy> interfacepTmin ("pTmin", "Minimum transverse momentum from gluon radiation", &PerturbativeDecayer::pTmin_, GeV, 1.0*GeV, 0.0*GeV, 10.0*GeV, false, false, Interface::limited); static Switch<PerturbativeDecayer,ShowerInteraction> interfaceInteractions ("Interactions", "which interactions to include for the hard corrections", &PerturbativeDecayer::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); static Reference<PerturbativeDecayer,ShowerAlpha> interfaceAlphaS ("AlphaS", "Object for the coupling in the generation of hard QCD radiation", &PerturbativeDecayer::alphaS_, false, false, true, true, false); static Reference<PerturbativeDecayer,ShowerAlpha> interfaceAlphaEM ("AlphaEM", "Object for the coupling in the generation of hard QED radiation", &PerturbativeDecayer::alphaEM_, false, false, true, true, false); } double PerturbativeDecayer::threeBodyME(const int , const Particle &, const ParticleVector &,MEOption) { throw Exception() << "Base class PerturbativeDecayer::threeBodyME() " << "called, should have an implementation in the inheriting class" << Exception::runerror; return 0.; } double PerturbativeDecayer::matrixElementRatio(const Particle & , const ParticleVector & , const ParticleVector & , MEOption , ShowerInteraction ) { throw Exception() << "Base class PerturbativeDecayer::matrixElementRatio() " << "called, should have an implementation in the inheriting class" << Exception::runerror; return 0.; } RealEmissionProcessPtr PerturbativeDecayer::generateHardest(RealEmissionProcessPtr born) { + return getHardEvent(born,false,inter_); +} + +RealEmissionProcessPtr PerturbativeDecayer::getHardEvent(RealEmissionProcessPtr born, + bool inDeadZone, + ShowerInteraction inter) { // check one incoming assert(born->bornIncoming().size()==1); // check exactly two outgoing particles assert(born->bornOutgoing().size()==2); // search for coloured particles bool colouredParticles=born->bornIncoming()[0]->dataPtr()->coloured(); - if(!colouredParticles) { - for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) { - if(born->bornOutgoing()[ix]->dataPtr()->coloured()) { - colouredParticles=true; - break; - } - } + bool chargedParticles=born->bornIncoming()[0]->dataPtr()->charged(); + for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) { + if(born->bornOutgoing()[ix]->dataPtr()->coloured()) + colouredParticles=true; + if(born->bornOutgoing()[ix]->dataPtr()->charged()) + chargedParticles=true; } - // if no coloured particles return - if ( !colouredParticles && inter_==ShowerInteraction::QCD ) return RealEmissionProcessPtr(); + // if no coloured/charged particles return + if ( !colouredParticles && !chargedParticles ) return RealEmissionProcessPtr(); + if ( !colouredParticles && inter==ShowerInteraction::QCD ) return RealEmissionProcessPtr(); + if ( ! chargedParticles && inter==ShowerInteraction::QED ) return RealEmissionProcessPtr(); // for decay b -> a c // set progenitors PPtr cProgenitor = born->bornOutgoing()[0]; PPtr aProgenitor = born->bornOutgoing()[1]; // get the decaying particle PPtr bProgenitor = born->bornIncoming()[0]; // identify which dipoles are required vector<DipoleType> dipoles; - if(!identifyDipoles(dipoles,aProgenitor,bProgenitor,cProgenitor)) { + if(!identifyDipoles(dipoles,aProgenitor,bProgenitor,cProgenitor,inter)) { return RealEmissionProcessPtr(); } Energy trialpT = pTmin_; LorentzRotation eventFrame; vector<Lorentz5Momentum> momenta; vector<Lorentz5Momentum> trialMomenta(4); PPtr finalEmitter, finalSpectator; PPtr trialEmitter, trialSpectator; DipoleType finalType(FFa,ShowerInteraction::QCD); for (int i=0; i<int(dipoles.size()); ++i) { // assign emitter and spectator based on current dipole if (dipoles[i].type==FFc || dipoles[i].type==IFc || dipoles[i].type==IFbc){ trialEmitter = cProgenitor; trialSpectator = aProgenitor; } else if (dipoles[i].type==FFa || dipoles[i].type==IFa || dipoles[i].type==IFba){ trialEmitter = aProgenitor; trialSpectator = cProgenitor; } // find rotation from lab to frame with the spectator along -z LorentzRotation trialEventFrame(bProgenitor->momentum().findBoostToCM()); Lorentz5Momentum pspectator = (trialEventFrame*trialSpectator->momentum()); trialEventFrame.rotateZ( -pspectator.phi() ); trialEventFrame.rotateY( -pspectator.theta() - Constants::pi ); // invert it trialEventFrame.invert(); // try to generate an emission pT_ = pTmin_; vector<Lorentz5Momentum> trialMomenta - = hardMomenta(bProgenitor, trialEmitter, trialSpectator, dipoles, i); + = hardMomenta(bProgenitor, trialEmitter, trialSpectator, + dipoles, i, inDeadZone); // select dipole which gives highest pT emission if(pT_>trialpT) { trialpT = pT_; momenta = trialMomenta; eventFrame = trialEventFrame; finalEmitter = trialEmitter; finalSpectator = trialSpectator; finalType = dipoles[i]; if (dipoles[i].type==FFc || dipoles[i].type==FFa ) { if((momenta[3]+momenta[1]).m2()-momenta[1].m2()> (momenta[3]+momenta[2]).m2()-momenta[2].m2()) { swap(finalEmitter,finalSpectator); swap(momenta[1],momenta[2]); } } } } pT_ = trialpT; // if no emission return if(momenta.empty()) { - if(inter_==ShowerInteraction::Both || inter_==ShowerInteraction::QCD) + if(inter==ShowerInteraction::Both || inter==ShowerInteraction::QCD) born->pT()[ShowerInteraction::QCD] = pTmin_; - if(inter_==ShowerInteraction::Both || inter_==ShowerInteraction::QED) + if(inter==ShowerInteraction::Both || inter==ShowerInteraction::QED) born->pT()[ShowerInteraction::QED] = pTmin_; return born; } // rotate momenta back to the lab for(unsigned int ix=0;ix<momenta.size();++ix) { momenta[ix] *= eventFrame; } // set maximum pT for subsequent branchings - if(inter_==ShowerInteraction::Both || inter_==ShowerInteraction::QCD) + if(inter==ShowerInteraction::Both || inter==ShowerInteraction::QCD) born->pT()[ShowerInteraction::QCD] = pT_; - if(inter_==ShowerInteraction::Both || inter_==ShowerInteraction::QED) + if(inter==ShowerInteraction::Both || inter==ShowerInteraction::QED) born->pT()[ShowerInteraction::QED] = pT_; // get ParticleData objects tcPDPtr b = bProgenitor ->dataPtr(); tcPDPtr e = finalEmitter ->dataPtr(); tcPDPtr s = finalSpectator->dataPtr(); tcPDPtr boson = getParticleData(finalType.interaction==ShowerInteraction::QCD ? ParticleID::g : ParticleID::gamma); // create new ShowerParticles PPtr emitter = e ->produceParticle(momenta[1]); PPtr spectator = s ->produceParticle(momenta[2]); PPtr gauge = boson->produceParticle(momenta[3]); PPtr incoming = b ->produceParticle(bProgenitor->momentum()); // insert the particles born->incoming().push_back(incoming); unsigned int iemit(0),ispect(0); for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) { if(born->bornOutgoing()[ix]==finalEmitter) { born->outgoing().push_back(emitter); iemit = born->outgoing().size(); } else if(born->bornOutgoing()[ix]==finalSpectator) { born->outgoing().push_back(spectator); ispect = born->outgoing().size(); } } born->outgoing().push_back(gauge); if(!spectator->dataPtr()->coloured() || (finalType.type != FFa && finalType.type!=FFc) ) ispect = 0; born->emitter(iemit); born->spectator(ispect); born->emitted(3); // set the interaction born->interaction(finalType.interaction); // set up colour lines getColourLines(born); // return the tree return born; } bool PerturbativeDecayer::identifyDipoles(vector<DipoleType> & dipoles, PPtr & aProgenitor, PPtr & bProgenitor, - PPtr & cProgenitor) const { + PPtr & cProgenitor, + ShowerInteraction inter) const { // identify any QCD dipoles - if(inter_==ShowerInteraction::QCD || - inter_==ShowerInteraction::Both) { + if(inter==ShowerInteraction::QCD || + inter==ShowerInteraction::Both) { PDT::Colour bColour = bProgenitor->dataPtr()->iColour(); PDT::Colour cColour = cProgenitor->dataPtr()->iColour(); PDT::Colour aColour = aProgenitor->dataPtr()->iColour(); // decaying colour singlet if (bColour==PDT::Colour0 ) { if ((cColour==PDT::Colour3 && aColour==PDT::Colour3bar) || (cColour==PDT::Colour3bar && aColour==PDT::Colour3) || (cColour==PDT::Colour8 && aColour==PDT::Colour8)){ dipoles.push_back(DipoleType(FFa,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(FFc,ShowerInteraction::QCD)); } } // decaying colour triplet else if (bColour==PDT::Colour3 ) { if (cColour==PDT::Colour3 && aColour==PDT::Colour0){ dipoles.push_back(DipoleType(IFbc,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFc ,ShowerInteraction::QCD)); } else if (cColour==PDT::Colour0 && aColour==PDT::Colour3){ dipoles.push_back(DipoleType(IFba,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFa ,ShowerInteraction::QCD)); } else if (cColour==PDT::Colour8 && aColour==PDT::Colour3){ dipoles.push_back(DipoleType(IFbc,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFc ,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(FFc ,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(FFa ,ShowerInteraction::QCD)); } else if (cColour==PDT::Colour3 && aColour==PDT::Colour8){ dipoles.push_back(DipoleType(IFba,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFa ,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(FFc ,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(FFa ,ShowerInteraction::QCD)); } } // decaying colour anti-triplet else if (bColour==PDT::Colour3bar) { if ((cColour==PDT::Colour3bar && aColour==PDT::Colour0)){ dipoles.push_back(DipoleType(IFbc,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFc ,ShowerInteraction::QCD)); } else if ((cColour==PDT::Colour0 && aColour==PDT::Colour3bar)){ dipoles.push_back(DipoleType(IFba,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFa ,ShowerInteraction::QCD)); } else if (cColour==PDT::Colour8 && aColour==PDT::Colour3bar){ dipoles.push_back(DipoleType(IFbc,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFc ,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(FFc ,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(FFa ,ShowerInteraction::QCD)); } else if (cColour==PDT::Colour3bar && aColour==PDT::Colour8){ dipoles.push_back(DipoleType(IFba,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFa ,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(FFc ,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(FFa ,ShowerInteraction::QCD)); } } // decaying colour octet else if (bColour==PDT::Colour8){ if ((cColour==PDT::Colour3 && aColour==PDT::Colour3bar) || (cColour==PDT::Colour3bar && aColour==PDT::Colour3)){ dipoles.push_back(DipoleType(IFba,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFbc,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFa,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFc,ShowerInteraction::QCD)); } else if (cColour==PDT::Colour8 && aColour==PDT::Colour0){ dipoles.push_back(DipoleType(IFbc,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFc,ShowerInteraction::QCD)); } else if (cColour==PDT::Colour0 && aColour==PDT::Colour8){ dipoles.push_back(DipoleType(IFba,ShowerInteraction::QCD)); dipoles.push_back(DipoleType(IFa,ShowerInteraction::QCD)); } } } // QED dipoles - if(inter_==ShowerInteraction::Both || - inter_==ShowerInteraction::QED) { + if(inter==ShowerInteraction::Both || + inter==ShowerInteraction::QED) { const bool & bCharged = bProgenitor->dataPtr()->charged(); const bool & cCharged = cProgenitor->dataPtr()->charged(); const bool & aCharged = aProgenitor->dataPtr()->charged(); // initial-final if(bCharged && aCharged) { dipoles.push_back(DipoleType(IFba,ShowerInteraction::QED)); dipoles.push_back(DipoleType(IFa ,ShowerInteraction::QED)); } if(bCharged && cCharged) { dipoles.push_back(DipoleType(IFbc,ShowerInteraction::QED)); dipoles.push_back(DipoleType(IFc ,ShowerInteraction::QED)); } // final-state if(aCharged && cCharged) { dipoles.push_back(DipoleType(FFa,ShowerInteraction::QED)); dipoles.push_back(DipoleType(FFc,ShowerInteraction::QED)); } } // check colour structure is allowed return !dipoles.empty(); } vector<Lorentz5Momentum> PerturbativeDecayer::hardMomenta(PPtr in, PPtr emitter, PPtr spectator, const vector<DipoleType> &dipoles, - int i) { + int i, bool inDeadZone) { double C = 6.3; double ymax = 10.; double ymin = -ymax; // get masses of the particles mb_ = in ->momentum().mass(); e_ = emitter ->momentum().mass()/mb_; s_ = spectator->momentum().mass()/mb_; e2_ = sqr(e_); s2_ = sqr(s_); vector<Lorentz5Momentum> particleMomenta (4); Energy2 lambda = sqr(mb_)*sqrt(1.+sqr(s2_)+sqr(e2_)-2.*s2_-2.*e2_-2.*s2_*e2_); // calculate A double A = (ymax-ymin)*C/Constants::twopi; if(dipoles[i].interaction==ShowerInteraction::QCD) A *= alphaS() ->overestimateValue(); else A *= alphaEM()->overestimateValue(); Energy pTmax = mb_*(sqr(1.-s_)-e2_)/(2.*(1.-s_)); // if no possible branching return if ( pTmax < pTmin_ ) { particleMomenta.clear(); return particleMomenta; } while (pTmax >= pTmin_) { // generate pT, y and phi values Energy pT = pTmax*pow(UseRandom::rnd(),(1./A)); if (pT < pTmin_) { particleMomenta.clear(); break; } double phi = UseRandom::rnd()*Constants::twopi; double y = ymin+UseRandom::rnd()*(ymax-ymin); double weight[2] = {0.,0.}; double xs[2], xe[2], xe_z[2], xg; for (unsigned int j=0; j<2; j++) { // check if the momenta are physical if (!calcMomenta(j, pT, y, phi, xg, xs[j], xe[j], xe_z[j], particleMomenta)) continue; // check if point lies within phase space if (!psCheck(xg, xs[j])) continue; - + // check if point lies within the dead-zone (if required) + if(inDeadZone) { + if(!inTotalDeadZone(xg,xs[j],dipoles,i)) continue; + } // decay products for 3 body decay PPtr inpart = in ->dataPtr()->produceParticle(particleMomenta[0]); ParticleVector decay3; decay3.push_back(emitter ->dataPtr()->produceParticle(particleMomenta[1])); decay3.push_back(spectator->dataPtr()->produceParticle(particleMomenta[2])); if(dipoles[i].interaction==ShowerInteraction::QCD) decay3.push_back(getParticleData(ParticleID::g )->produceParticle(particleMomenta[3])); else decay3.push_back(getParticleData(ParticleID::gamma)->produceParticle(particleMomenta[3])); // decay products for 2 body decay Lorentz5Momentum p1(ZERO,ZERO, lambda/2./mb_,(mb_/2.)*(1.+e2_-s2_),mb_*e_); Lorentz5Momentum p2(ZERO,ZERO,-lambda/2./mb_,(mb_/2.)*(1.+s2_-e2_),mb_*s_); ParticleVector decay2; decay2.push_back(emitter ->dataPtr()->produceParticle(p1)); decay2.push_back(spectator->dataPtr()->produceParticle(p2)); if (dipoles[i].type==FFc || dipoles[i].type==IFc || dipoles[i].type==IFbc){ swap(decay2[0],decay2[1]); swap(decay3[0],decay3[1]); } // calculate matrix element ratio R/B double meRatio = matrixElementRatio(*inpart,decay2,decay3,Initialize,dipoles[i].interaction); // calculate dipole factor InvEnergy2 dipoleSum = ZERO; InvEnergy2 numerator = ZERO; for (int k=0; k<int(dipoles.size()); ++k) { // skip dipoles which are not of the interaction being considered if(dipoles[k].interaction!=dipoles[i].interaction) continue; InvEnergy2 dipole = abs(calculateDipole(dipoles[k],*inpart,decay3)); dipoleSum += dipole; if (k==i) numerator = dipole; } meRatio *= numerator/dipoleSum; // calculate jacobian Energy2 denom = (mb_-particleMomenta[3].e())*particleMomenta[2].vect().mag() - particleMomenta[2].e()*particleMomenta[3].z(); InvEnergy2 J = (particleMomenta[2].vect().mag2())/(lambda*denom); // calculate weight weight[j] = meRatio*fabs(sqr(pT)*J)/C/Constants::twopi; if(dipoles[i].interaction==ShowerInteraction::QCD) weight[j] *= alphaS() ->ratio(pT*pT); else weight[j] *= alphaEM()->ratio(pT*pT); } // accept point if weight > R if (weight[0] + weight[1] > UseRandom::rnd()) { if (weight[0] > (weight[0] + weight[1])*UseRandom::rnd()) { particleMomenta[1].setE( (mb_/2.)*xe [0]); particleMomenta[1].setZ( (mb_/2.)*xe_z[0]); particleMomenta[2].setE( (mb_/2.)*xs [0]); particleMomenta[2].setZ(-(mb_/2.)*sqrt(sqr(xs[0])-4.*s2_)); } else { particleMomenta[1].setE( (mb_/2.)*xe [1]); particleMomenta[1].setZ( (mb_/2.)*xe_z[1]); particleMomenta[2].setE( (mb_/2.)*xs [1]); particleMomenta[2].setZ(-(mb_/2.)*sqrt(sqr(xs[1])-4.*s2_)); } pT_ = pT; break; } // if there's no branching lower the pT pTmax = pT; } return particleMomenta; } bool PerturbativeDecayer::calcMomenta(int j, Energy pT, double y, double phi, double& xg, double& xs, double& xe, double& xe_z, vector<Lorentz5Momentum>& particleMomenta){ // calculate xg xg = 2.*pT*cosh(y) / mb_; if (xg>(1. - sqr(e_ + s_)) || xg<0.) return false; // calculate the two values of xs double xT = 2.*pT / mb_; double A = 4.-4.*xg+sqr(xT); double B = 4.*(3.*xg-2.+2.*e2_-2.*s2_-sqr(xg)-xg*e2_+xg*s2_); double L = 1.+sqr(s2_)+sqr(e2_)-2.*s2_-2.*e2_-2.*s2_*e2_; double det = 16.*( -L*sqr(xT)+pow(xT,4)*s2_+2.*xg*sqr(xT)*(1.-s2_-e2_)+ L*sqr(xg)-sqr(xg*xT)*(1.+s2_)+pow(xg,4)+ 2.*pow(xg,3)*(-1.+s2_+e2_) ); if (det<0.) return false; if (j==0) xs = (-B+sqrt(det))/(2.*A); if (j==1) xs = (-B-sqrt(det))/(2.*A); // check value of xs is physical if (xs>(1.+s2_-e2_) || xs<2.*s_) return false; // calculate xe xe = 2.-xs-xg; // check value of xe is physical if (xe>(1.+e2_-s2_) || xe<2.*e_) return false; // calculate xe_z double root1 = sqrt(max(0.,sqr(xs)-4.*s2_)), root2 = sqrt(max(0.,sqr(xe)-4.*e2_-sqr(xT))); double epsilon_p = -root1+xT*sinh(y)+root2; double epsilon_m = -root1+xT*sinh(y)-root2; // find direction of emitter if (fabs(epsilon_p) < 1.e-10) xe_z = sqrt(sqr(xe)-4.*e2_-sqr(xT)); else if (fabs(epsilon_m) < 1.e-10) xe_z = -sqrt(sqr(xe)-4.*e2_-sqr(xT)); else return false; // check the emitter is on shell if (fabs((sqr(xe)-sqr(xT)-sqr(xe_z)-4.*e2_))>1.e-10) return false; // calculate 4 momenta particleMomenta[0].setE ( mb_); particleMomenta[0].setX ( ZERO); particleMomenta[0].setY ( ZERO); particleMomenta[0].setZ ( ZERO); particleMomenta[0].setMass( mb_); particleMomenta[1].setE ( mb_*xe/2.); particleMomenta[1].setX (-pT*cos(phi)); particleMomenta[1].setY (-pT*sin(phi)); particleMomenta[1].setZ ( mb_*xe_z/2.); particleMomenta[1].setMass( mb_*e_); particleMomenta[2].setE ( mb_*xs/2.); particleMomenta[2].setX ( ZERO); particleMomenta[2].setY ( ZERO); particleMomenta[2].setZ (-mb_*sqrt(sqr(xs)-4.*s2_)/2.); particleMomenta[2].setMass( mb_*s_); particleMomenta[3].setE ( pT*cosh(y)); particleMomenta[3].setX ( pT*cos(phi)); particleMomenta[3].setY ( pT*sin(phi)); particleMomenta[3].setZ ( pT*sinh(y)); particleMomenta[3].setMass( ZERO); return true; } bool PerturbativeDecayer::psCheck(const double xg, const double xs) { // check is point is in allowed region of phase space double xe_star = (1.-s2_+e2_-xg)/sqrt(1.-xg); double xg_star = xg/sqrt(1.-xg); if ((sqr(xe_star)-4.*e2_) < 1e-10) return false; double xs_max = (4.+4.*s2_-sqr(xe_star+xg_star)+ sqr(sqrt(sqr(xe_star)-4.*e2_)+xg_star))/ 4.; double xs_min = (4.+4.*s2_-sqr(xe_star+xg_star)+ sqr(sqrt(sqr(xe_star)-4.*e2_)-xg_star))/ 4.; if (xs < xs_min || xs > xs_max) return false; return true; } InvEnergy2 PerturbativeDecayer::calculateDipole(const DipoleType & dipoleId, const Particle & inpart, const ParticleVector & decay3) { // calculate dipole for decay b->ac InvEnergy2 dipole = ZERO; double x1 = 2.*decay3[0]->momentum().e()/mb_; double x2 = 2.*decay3[1]->momentum().e()/mb_; double xg = 2.*decay3[2]->momentum().e()/mb_; double mu12 = sqr(decay3[0]->mass()/mb_); double mu22 = sqr(decay3[1]->mass()/mb_); tcPDPtr part[3] = {inpart.dataPtr(),decay3[0]->dataPtr(),decay3[1]->dataPtr()}; if(dipoleId.type==FFc || dipoleId.type == IFc || dipoleId.type == IFbc) { swap(part[1],part[2]); swap(x1,x2); swap(mu12,mu22); } // radiation from b with initial-final connection if (dipoleId.type==IFba || dipoleId.type==IFbc) { dipole = -2./sqr(mb_*xg); dipole *= colourCoeff(part[0],part[1],part[2],dipoleId); } // radiation from a/c with initial-final connection else if (dipoleId.type==IFa || dipoleId.type==IFc) { double z = 1. - xg/(1.-mu22+mu12); dipole = (-2.*mu12/sqr(1.-x2+mu22-mu12)/sqr(mb_) + (1./(1.-x2+mu22-mu12)/sqr(mb_))* (2./(1.-z)-dipoleSpinFactor(part[1],z))); dipole *= colourCoeff(part[1],part[0],part[2],dipoleId); } // radiation from a/c with final-final connection else if (dipoleId.type==FFa || dipoleId.type==FFc) { double z = 1. + ((x1-1.+mu22-mu12)/(x2-2.*mu22)); double y = (1.-x2-mu12+mu22)/(1.-mu12-mu22); double vt = sqrt((1.-sqr(e_+s_))*(1.-sqr(e_-s_)))/(1.-mu12-mu22); double v = sqrt(sqr(2.*mu22+(1.-mu12-mu22)*(1.-y))-4.*mu22) /(1.-y)/(1.-mu12-mu22); if(part[1]->iSpin()!=PDT::Spin1) { dipole = (1./(1.-x2+mu22-mu12)/sqr(mb_))* ((2./(1.-z*(1.-y)))-vt/v*(dipoleSpinFactor(part[1],z)+(2.*mu12/(1.+mu22-mu12-x2)))); } else { dipole = (1./(1.-x2+mu22-mu12)/sqr(mb_))* (1./(1.-z*(1.-y))+1./(1.-(1.-z)*(1.-y))+(z*(1.-z)-2.)/v-vt/v*(2.*mu12/(1.+mu22-mu12-x2))); } dipole *= colourCoeff(part[1],part[2],part[0],dipoleId); } // coupling prefactors dipole *= 8.*Constants::pi; if(dipoleId.interaction==ShowerInteraction::QCD) dipole *= alphaS() ->value(mb_*mb_); else dipole *= alphaEM()->value(mb_*mb_); // return the answer return dipole; } double PerturbativeDecayer::dipoleSpinFactor(tcPDPtr part, double z){ // calculate the spin dependent component of the dipole if (part->iSpin()==PDT::Spin0) return 2.; else if (part->iSpin()==PDT::Spin1Half) return (1. + z); else if (part->iSpin()==PDT::Spin1) return -(z*(1.-z) - 1./(1.-z) + 1./z -2.); return 0.; } double PerturbativeDecayer::colourCoeff(tcPDPtr emitter, tcPDPtr spectator, tcPDPtr other, DipoleType dipole) { if(dipole.interaction==ShowerInteraction::QCD) { // calculate the colour factor of the dipole double numerator=1.; double denominator=1.; if (emitter->iColour()!=PDT::Colour0 && spectator->iColour()!=PDT::Colour0 && other->iColour()!=PDT::Colour0) { if (emitter->iColour() ==PDT::Colour3 || emitter->iColour() ==PDT::Colour3bar) numerator=-4./3; else if (emitter->iColour() ==PDT::Colour8) numerator=-3. ; denominator=-1.*numerator; if (spectator->iColour()==PDT::Colour3 || spectator->iColour()==PDT::Colour3bar) numerator-=4./3; else if (spectator->iColour()==PDT::Colour8) numerator-=3. ; if (other->iColour() ==PDT::Colour3 || other->iColour() ==PDT::Colour3bar) numerator+=4./3; else if (other->iColour() ==PDT::Colour8) numerator+=3. ; numerator*=(-1./2.); } if (emitter->iColour()==PDT::Colour3 || emitter->iColour()== PDT::Colour3bar) numerator*=4./3.; else if (emitter->iColour()==PDT::Colour8 && spectator->iColour()!=PDT::Colour8) numerator*=3.; else if (emitter->iColour()==PDT::Colour8 && spectator->iColour()==PDT::Colour8) numerator*=6.; return (numerator/denominator); } else { double val = double(emitter->iCharge()*spectator->iCharge())/9.; // FF dipoles if(dipole.type==FFa || dipole.type == FFc) { return val; } else { return -val; } } } void PerturbativeDecayer::getColourLines(RealEmissionProcessPtr real) { // extract the particles vector<PPtr> branchingPart; branchingPart.push_back(real->incoming()[0]); for(unsigned int ix=0;ix<real->outgoing().size();++ix) { branchingPart.push_back(real->outgoing()[ix]); } vector<unsigned int> sing,trip,atrip,oct; for (size_t ib=0;ib<branchingPart.size()-1;++ib) { if (branchingPart[ib]->dataPtr()->iColour()==PDT::Colour0 ) sing. push_back(ib); else if(branchingPart[ib]->dataPtr()->iColour()==PDT::Colour3 ) trip. push_back(ib); else if(branchingPart[ib]->dataPtr()->iColour()==PDT::Colour3bar) atrip.push_back(ib); else if(branchingPart[ib]->dataPtr()->iColour()==PDT::Colour8 ) oct. push_back(ib); } // decaying colour singlet if (branchingPart[0]->dataPtr()->iColour()==PDT::Colour0) { // 0 -> 3 3bar if (trip.size()==1 && atrip.size()==1) { if(real->interaction()==ShowerInteraction::QCD) { branchingPart[atrip[0]]->colourConnect(branchingPart[ 3 ]); branchingPart[ 3 ]->colourConnect(branchingPart[trip[0]]); } else { branchingPart[atrip[0]]->colourConnect(branchingPart[trip[0]]); } } // 0 -> 8 8 else if (oct.size()==2 ) { if(real->interaction()==ShowerInteraction::QCD) { bool col = UseRandom::rndbool(); branchingPart[oct[0]]->colourConnect(branchingPart[ 3 ],col); branchingPart[ 3 ]->colourConnect(branchingPart[oct[1]],col); branchingPart[oct[1]]->colourConnect(branchingPart[oct[0]],col); } else { branchingPart[oct[0]]->colourConnect(branchingPart[oct[1]]); branchingPart[oct[1]]->colourConnect(branchingPart[oct[0]]); } } else assert(real->interaction()==ShowerInteraction::QED); } // decaying colour triplet else if (branchingPart[0]->dataPtr()->iColour()==PDT::Colour3 ){ // 3 -> 3 0 if (trip.size()==2 && sing.size()==1) { if(real->interaction()==ShowerInteraction::QCD) { branchingPart[3]->incomingColour(branchingPart[trip[0]]); branchingPart[3]-> colourConnect(branchingPart[trip[1]]); } else { branchingPart[trip[1]]->incomingColour(branchingPart[trip[0]]); } } // 3 -> 3 8 else if (trip.size()==2 && oct.size()==1) { if(real->interaction()==ShowerInteraction::QCD) { // 8 emit incoming partner if(real->emitter()==oct[0]&&real->spectator()==0) { branchingPart[ 3 ]->incomingColour(branchingPart[trip[0]]); branchingPart[ 3 ]-> colourConnect(branchingPart[oct[0] ]); branchingPart[oct[0]]-> colourConnect(branchingPart[trip[1]]); } // 8 emit final spectator or vice veras else { branchingPart[oct[0]]->incomingColour(branchingPart[trip[0]]); branchingPart[oct[0]]-> colourConnect(branchingPart[ 3 ]); branchingPart[ 3 ]-> colourConnect(branchingPart[trip[1]]); } } else { branchingPart[oct[0]]->incomingColour(branchingPart[trip[0]]); branchingPart[oct[0]]-> colourConnect(branchingPart[trip[1]]); } } else assert(false); } // decaying colour anti-triplet else if (branchingPart[0]->dataPtr()->iColour()==PDT::Colour3bar) { // 3bar -> 3bar 0 if (atrip.size()==2 && sing.size()==1) { if(real->interaction()==ShowerInteraction::QCD) { branchingPart[3]->incomingColour(branchingPart[atrip[0]],true); branchingPart[3]-> colourConnect(branchingPart[atrip[1]],true); } else { branchingPart[atrip[1]]->incomingColour(branchingPart[atrip[0]],true); } } // 3 -> 3 8 else if (atrip.size()==2 && oct.size()==1){ if(real->interaction()==ShowerInteraction::QCD) { // 8 emit incoming partner if(real->emitter()==oct[0]&&real->spectator()==0) { branchingPart[ 3 ]->incomingColour(branchingPart[atrip[0]],true); branchingPart[ 3 ]-> colourConnect(branchingPart[oct[0] ],true); branchingPart[oct[0]]-> colourConnect(branchingPart[atrip[1]],true); } // 8 emit final spectator or vice veras else { if(real->interaction()==ShowerInteraction::QCD) { branchingPart[oct[0]]->incomingColour(branchingPart[atrip[0]],true); branchingPart[oct[0]]-> colourConnect(branchingPart[ 3 ],true); branchingPart[3]-> colourConnect(branchingPart[atrip[1]] ,true); } } } else { branchingPart[oct[0]]->incomingColour(branchingPart[atrip[0]],true); branchingPart[oct[0]]-> colourConnect(branchingPart[atrip[1]],true); } } else assert(false); } // decaying colour octet else if(branchingPart[0]->dataPtr()->iColour()==PDT::Colour8 ) { // 8 -> 3 3bar if (trip.size()==1 && atrip.size()==1) { if(real->interaction()==ShowerInteraction::QCD) { // 3 emits if(trip[0]==real->emitter()) { branchingPart[3] ->incomingColour(branchingPart[oct[0]] ); branchingPart[3] -> colourConnect(branchingPart[trip[0]]); branchingPart[atrip[0]]->incomingColour(branchingPart[oct[0]],true); } // 3bar emits else { branchingPart[3] ->incomingColour(branchingPart[oct[0]] ,true); branchingPart[3] -> colourConnect(branchingPart[atrip[0]],true); branchingPart[trip[0]]->incomingColour(branchingPart[oct[0]] ); } } else { branchingPart[trip[0]]->incomingColour(branchingPart[oct[0]] ); branchingPart[atrip[0]]->incomingColour(branchingPart[oct[0]],true); } } // 8 -> 8 0 else if (sing.size()==1 && oct.size()==2) { if(real->interaction()==ShowerInteraction::QCD) { bool col = UseRandom::rndbool(); branchingPart[ 3 ]->colourConnect (branchingPart[oct[1]], col); branchingPart[ 3 ]->incomingColour(branchingPart[oct[0]], col); branchingPart[oct[1]]->incomingColour(branchingPart[oct[0]],!col); } else { branchingPart[oct[1]]->incomingColour(branchingPart[oct[0]]); branchingPart[oct[1]]->incomingColour(branchingPart[oct[0]],true); } } else assert(false); } } + +PerturbativeDecayer::phaseSpaceRegion +PerturbativeDecayer::inInitialFinalDeadZone(double xg, double xa, + double a, double c) const { + double lam = sqrt(1.+a*a+c*c-2.*a-2.*c-2.*a*c); + double kappab = 1.+0.5*(1.-a+c+lam); + double kappac = kappab-1.+c; + double kappa(0.); + // check whether or not in the region for emission from c + double r = 0.5; + if(c!=0.) r += 0.5*c/(1.+a-xa); + double pa = sqrt(sqr(xa)-4.*a); + double z = ((2.-xa)*(1.-r)+r*pa-xg)/pa; + if(z<1. && z>0.) { + kappa = (1.+a-c-xa)/(z*(1.-z)); + if(kappa<kappac) + return emissionFromC; + } + // check in region for emission from b (T1) + double cq = sqr(1.+a-c)-4*a; + double bq = -2.*kappab*(1.-a-c); + double aq = sqr(kappab)-4.*a*(kappab-1); + double dis = sqr(bq)-4.*aq*cq; + z=1.-(-bq-sqrt(dis))/2./aq; + double w = 1.-(1.-z)*(kappab-1.); + double xgmax = (1.-z)*kappab; + // possibly in T1 region + if(xg<xgmax) { + z = 1.-xg/kappab; + kappa=kappab; + } + // possibly in T2 region + else { + aq = 4.*a; + bq = -4.*a*(2.-xg); + cq = sqr(1.+a-c-xg); + dis = sqr(bq)-4.*aq*cq; + z = (-bq-sqrt(dis))/2./aq; + kappa = xg/(1.-z); + } + // compute limit on xa + double u = 1.+a-c-(1.-z)*kappa; + w = 1.-(1.-z)*(kappa-1.); + double v = sqr(u)-4.*z*a*w; + if(v<0. && v>-1e-10) v= 0.; + v = sqrt(v); + if(xa<0.5*((u+v)/w+(u-v)/z)) + return xg<xgmax ? emissionFromA1 : emissionFromA2; + else + return deadZone; +} + +PerturbativeDecayer::phaseSpaceRegion +PerturbativeDecayer::inFinalFinalDeadZone(double xb, double xc, + double b, double c) const { + // basic kinematics + double lam = sqrt(1.+b*b+c*c-2.*b-2.*c-2.*b*c); + // check whether or not in the region for emission from b + double r = 0.5; + if(b!=0.) r+=0.5*b/(1.+c-xc); + double pc = sqrt(sqr(xc)-4.*c); + double z = -((2.-xc)*r-r*pc-xb)/pc; + if(z<1. and z>0.) { + if((1.-b+c-xc)/(z*(1.-z))<0.5*(1.+b-c+lam)) return emissionFromB; + } + // check whether or not in the region for emission from c + r = 0.5; + if(c!=0.) r+=0.5*c/(1.+b-xb); + double pb = sqrt(sqr(xb)-4.*b); + z = -((2.-xb)*r-r*pb-xc)/pb; + if(z<1. and z>0.) { + if((1.-c+b-xb)/(z*(1.-z))<0.5*(1.-b+c+lam)) return emissionFromC; + } + return deadZone; +} + +bool PerturbativeDecayer::inTotalDeadZone(double xg, double xs, + const vector<DipoleType> & dipoles, + int i) { + double xb,xc,b,c; + if(dipoles[i].type==FFa || dipoles[i].type == IFa || dipoles[i].type == IFba) { + xc = xs; + xb = 2.-xg-xs; + b = e2_; + c = s2_; + } + else { + xb = xs; + xc = 2.-xg-xs; + b = s2_; + c = e2_; + } + for(unsigned int ix=0;ix<dipoles.size();++ix) { + if(dipoles[ix].interaction!=dipoles[i].interaction) + continue; + // should also remove negative QED dipoles but shouldn't be an issue unless we + // support QED ME corrections + switch (dipoles[ix].type) { + case FFa : + if(inFinalFinalDeadZone(xb,xc,b,c)!=deadZone) return false; + break; + case FFc : + if(inFinalFinalDeadZone(xc,xb,c,b)!=deadZone) return false; + break; + case IFa : case IFba: + if(inInitialFinalDeadZone(xg,xc,c,b)!=deadZone) return false; + break; + case IFc : case IFbc: + if(inInitialFinalDeadZone(xg,xb,b,c)!=deadZone) return false; + break; + } + } + return true; +} diff --git a/Decay/PerturbativeDecayer.h b/Decay/PerturbativeDecayer.h --- a/Decay/PerturbativeDecayer.h +++ b/Decay/PerturbativeDecayer.h @@ -1,279 +1,310 @@ // -*- C++ -*- #ifndef Herwig_PerturbativeDecayer_H #define Herwig_PerturbativeDecayer_H // // This is the declaration of the PerturbativeDecayer class. // #include "Herwig/Decay/DecayIntegrator.h" #include "Herwig/Shower/Core/Couplings/ShowerAlpha.h" #include "Herwig/Shower/Core/ShowerInteraction.h" namespace Herwig { using namespace ThePEG; /** * The PerturbativeDecayer class is the base class for perturbative decays in * Herwig and implements the functuality for the POWHEG corrections * * @see \ref PerturbativeDecayerInterfaces "The interfaces" * defined for PerturbativeDecayer. */ class PerturbativeDecayer: public DecayIntegrator { protected: /** * Type of dipole */ enum dipoleType {FFa, FFc, IFa, IFc, IFba, IFbc}; /** + * Phase-space region for an emission (assumes \f$a\to b,c\f$ + */ + enum phaseSpaceRegion {emissionFromB,emissionFromC,emissionFromA1,emissionFromA2,deadZone}; + + /** * Type of dipole */ struct DipoleType { DipoleType() {} DipoleType(dipoleType a, ShowerInteraction b) : type(a), interaction(b) {} dipoleType type; ShowerInteraction interaction; }; public: /** * The default constructor. */ PerturbativeDecayer() : inter_(ShowerInteraction::QCD), pTmin_(GeV), pT_(ZERO), mb_(ZERO), e_(0.), s_(0.), e2_(0.), s2_(0.) {} /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() {return No;} /** * Member to generate the hardest emission in the POWHEG scheme */ virtual RealEmissionProcessPtr generateHardest(RealEmissionProcessPtr); 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: /** * Three-body matrix element including additional QCD radiation */ virtual double threeBodyME(const int , const Particle & inpart, const ParticleVector & decay, MEOption meopt); /** * Calculate matrix element ratio \f$\frac{M^2}{\alpha_S}\frac{|\overline{\rm{ME}}_3|}{|\overline{\rm{ME}}_2|}\f$ */ virtual double matrixElementRatio(const Particle & inpart, const ParticleVector & decay2, const ParticleVector & decay3, MEOption meopt, ShowerInteraction inter); /** * Work out the type of process */ bool identifyDipoles(vector<DipoleType> & dipoles, PPtr & aProgenitor, PPtr & bProgenitor, - PPtr & cProgenitor) const; + PPtr & cProgenitor, + ShowerInteraction inter) const; /** * Coupling for the generation of hard QCD radiation */ ShowerAlphaPtr alphaS() {return alphaS_;} /** * Coupling for the generation of hard QED radiation */ ShowerAlphaPtr alphaEM() {return alphaEM_;} /** * Return the momenta including the hard emission */ vector<Lorentz5Momentum> hardMomenta(PPtr in, PPtr emitter, PPtr spectator, - const vector<DipoleType> & dipoles, int i); + const vector<DipoleType> & dipoles, + int i, bool inDeadZone); /** * Calculate momenta of all the particles */ bool calcMomenta(int j, Energy pT, double y, double phi, double& xg, double& xs, double& xe, double& xe_z, vector<Lorentz5Momentum>& particleMomenta); /** * Check the calculated momenta are physical */ bool psCheck(const double xg, const double xs); /** * Return dipole corresponding to the DipoleType dipoleId */ InvEnergy2 calculateDipole(const DipoleType & dipoleId, const Particle & inpart, const ParticleVector & decay3); /** * Return contribution to dipole that depends on the spin of the emitter */ double dipoleSpinFactor(tcPDPtr emitter, double z); /** * Return the colour coefficient of the dipole */ double colourCoeff(tcPDPtr emitter, tcPDPtr spectator, tcPDPtr other, DipoleType dipole); /** * Set up the colour lines */ void getColourLines(RealEmissionProcessPtr real); + /** + * Generate a hard emission + */ + RealEmissionProcessPtr getHardEvent(RealEmissionProcessPtr born, + bool inDeadZone, + ShowerInteraction inter); + + /** + * Is the \f$x_g,x_s\f$ point in the dead-zone for all the dipoles + */ + bool inTotalDeadZone(double xg, double xs, + const vector<DipoleType> & dipoles, + int i); + + /** + * Is the \f$x_g,x_a\f$ point in the dead-zone for an initial-final colour connection + */ + phaseSpaceRegion inInitialFinalDeadZone(double xg, double xa, double a, double c) const; + + /** + * Is the \f$x_b,x_c\f$ point in the dead-zone for a final-final colour connection + */ + phaseSpaceRegion inFinalFinalDeadZone(double xb, double xc, double b, double c) const; + protected: /** * Access to the kinematics for inheriting classes */ //@{ /** * Transverse momentum of the emission */ const Energy & pT() const { return pT_;} /** * Mass of decaying particle */ const Energy & mb() const {return mb_;} /** * Reduced mass of emitter child particle */ const double & e() const {return e_;} /** * Reduced mass of spectator child particle */ const double & s() const {return s_;} /** * Reduced mass of emitter child particle squared */ const double & e2() const {return e2_;} /** * Reduced mass of spectator child particle squared */ const double & s2() const {return s2_;} //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ PerturbativeDecayer & operator=(const PerturbativeDecayer &); private: /** * Members for the generation of the hard radiation */ //@{ /** * Which types of radiation to generate */ ShowerInteraction inter_; /** * Coupling for the generation of hard QCD radiation */ ShowerAlphaPtr alphaS_; /** * Coupling for the generation of hard QED radiation */ ShowerAlphaPtr alphaEM_; /** * Minimum \f$p_T\f$ */ Energy pTmin_; //@} private: /** * Mmeber variables for the kinematics of the hard emission */ //@{ /** * Transverse momentum of the emission */ Energy pT_; /** * Mass of decaying particle */ Energy mb_; /** * Reduced mass of emitter child particle */ double e_; /** * Reduced mass of spectator child particle */ double s_; /** * Reduced mass of emitter child particle squared */ double e2_; /** * Reduced mass of spectator child particle squared */ double s2_; //@} }; } #endif /* Herwig_PerturbativeDecayer_H */