diff --git a/Decay/Perturbative/SMHiggsFermionsDecayer.cc b/Decay/Perturbative/SMHiggsFermionsDecayer.cc --- a/Decay/Perturbative/SMHiggsFermionsDecayer.cc +++ b/Decay/Perturbative/SMHiggsFermionsDecayer.cc @@ -1,645 +1,405 @@ // -*- C++ -*- // // SMHiggsFermionsDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator // Copyright (C) 2002-2017 The Herwig Collaboration // // Herwig is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // // // This is the implementation of the non-inlined, non-templated member // functions of the SMHiggsFermionsDecayer class. // #include "SMHiggsFermionsDecayer.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/ParVector.h" #include "ThePEG/Interface/Reference.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/PDT/DecayMode.h" #include "Herwig/Decay/DecayVertex.h" #include "ThePEG/Helicity/ScalarSpinInfo.h" #include "ThePEG/Helicity/FermionSpinInfo.h" #include "ThePEG/Helicity/WaveFunction/ScalarWaveFunction.h" #include "Herwig/Models/StandardModel/StandardModel.h" #include "Herwig/Decay/GeneralDecayMatrixElement.h" #include "Herwig/Utilities/Maths.h" #include "Herwig/Shower/RealEmissionProcess.h" #include "Herwig/Shower/Core/Couplings/ShowerAlpha.h" using namespace Herwig; using namespace ThePEG::Helicity; SMHiggsFermionsDecayer::SMHiggsFermionsDecayer() : CF_(4./3.), pTmin_(1.*GeV), NLO_(false) { _maxwgt.resize(9); _maxwgt[0]=0.; _maxwgt[1]=0; _maxwgt[2]=0; _maxwgt[3]=0.0194397; _maxwgt[4]=0.463542; _maxwgt[5]=0.; _maxwgt[6]=6.7048e-09; _maxwgt[7]=0.00028665; _maxwgt[8]=0.0809643; } void SMHiggsFermionsDecayer::doinit() { PerturbativeDecayer::doinit(); // get the vertices from the Standard Model object tcHwSMPtr hwsm=dynamic_ptr_cast<tcHwSMPtr>(standardModel()); if(!hwsm) throw InitException() << "SMHiggsFermionsDecayer needs the StandardModel class" << " to be either the Herwig one or a class inheriting" << " from it"; _hvertex = hwsm->vertexFFH(); // make sure they are initialized _hvertex->init(); // get the width generator for the higgs tPDPtr higgs = getParticleData(ParticleID::h0); // set up the decay modes vector<double> wgt(0); unsigned int imode=0; tPDVector extpart(3); DecayPhaseSpaceModePtr mode; int iy; extpart[0]=higgs; for(unsigned int istep=0;istep<11;istep+=10) { for(unsigned ix=1;ix<7;++ix) { if(istep<10||ix%2!=0) { iy = ix+istep; extpart[1]=getParticleData( iy); extpart[2]=getParticleData(-iy); mode = new_ptr(DecayPhaseSpaceMode(extpart,this)); addMode(mode,_maxwgt[imode],wgt); ++imode; } } } gluon_ = getParticleData(ParticleID::g); // Energy quarkMass = getParticleData(ParticleID::b )->mass(); // Energy higgsMass = getParticleData(ParticleID::h0)->mass(); // double mu = quarkMass/higgsMass; // double beta = sqrt(1.-4.*sqr(mu)); // double beta2 = sqr(beta); // double aS = SM().alphaS(sqr(higgsMass)); // double L = log((1.+beta)/(1.-beta)); // cerr << "testing " << beta << " " << mu << "\n"; // cerr << "testing " << aS << " " << L << "\n"; // double fact = // 6.-0.75*(1.+beta2)/beta2+12.*log(mu)-8.*log(beta) // +(5./beta-2.*beta+0.375*sqr(1.-beta2)/beta2/beta)*L // +(1.+beta2)/beta*(4.*L*log(0.5*(1.+beta)/beta) // -2.*log(0.5*(1.+beta))*log(0.5*(1.-beta)) // +8.*Herwig::Math::ReLi2((1.-beta)/(1.+beta)) // -4.*Herwig::Math::ReLi2(0.5*(1.-beta))); // cerr << "testing correction " // << 1.+4./3.*aS/Constants::twopi*fact // << "\n"; // double real = 4./3.*aS/Constants::twopi* // (8.-0.75*(1.+beta2)/beta2+8.*log(mu)-8.*log(beta) // +(3./beta+0.375*sqr(1.-beta2)/pow(beta,3))*L // +(1.+beta2)/beta*(-0.5*sqr(L)+4.*L*log(0.5*(1.+beta)) // -2.*L*log(beta)-2.*log(0.5*(1.+beta))*log(0.5*(1.-beta)) // +6.*Herwig::Math::ReLi2((1.-beta)/(1.+beta)) // -4.*Herwig::Math::ReLi2(0.5*(1.-beta)) // -2./3.*sqr(Constants::pi))); // double virt = 4./3.*aS/Constants::twopi* // (-2.+4.*log(mu)+(2./beta-2.*beta)*L // +(1.+beta2)/beta*(0.5*sqr(L)-2.*L*log(beta)+2.*sqr(Constants::pi)/3. // +2.*Herwig::Math::ReLi2((1.-beta)/(1.+beta)))); // cerr << "testing real " << real << "\n"; // cerr << "testing virtual " << virt << "\n"; // cerr << "testing total no mb corr " << 1.+real+virt << "\n"; // cerr << "testing total mb corr " << 1.+real+virt +(8./3. - 2.*log(sqr(mu)))*aS/Constants::pi << "\n"; // InvEnergy2 Gf = 1.166371e-5/GeV2; // Gf = sqrt(2.)*4*Constants::pi*SM().alphaEM(sqr(higgsMass))/8./SM().sin2ThetaW()/ // sqr(getParticleData(ParticleID::Wplus)->mass()); // cerr << "testing GF " << Gf*GeV2 << "\n"; // Energy LO = (3./8./Constants::pi)*sqrt(2)*sqr(quarkMass)*Gf*higgsMass*beta*beta*beta; // cerr << "testing LO " << LO/GeV << "\n"; // cerr << "testing quark mass " << quarkMass/GeV << "\n"; // cerr << "testing gamma " << (1.+real+virt)*LO/MeV << "\n"; } bool SMHiggsFermionsDecayer::accept(tcPDPtr parent, const tPDVector & children) const { if(parent->id()!=ParticleID::h0||children.size()!=2) return false; tPDVector::const_iterator pit = children.begin(); int id1=(**pit).id(); ++pit; int id2=(**pit).id(); if(id1==-id2&&(abs(id1)<=6||(abs(id1)>=11&&abs(id1)<=16))) return true; else return false; } ParticleVector SMHiggsFermionsDecayer::decay(const Particle & parent, const tPDVector & children) const { // id's of the decaying particles tPDVector::const_iterator pit(children.begin()); int id1((**pit).id()); int imode=-1; if(abs(id1)<=6) imode = abs(id1)-1; else if(abs(id1)>=11&&abs(id1)<=16) imode = (abs(id1)-11)/2+6; ParticleVector output(generate(false,false,imode,parent)); // set up the colour flow if(output[0]->hasColour()) output[0]->antiColourNeighbour(output[1]); else if(output[1]->hasColour()) output[1]->antiColourNeighbour(output[0]); return output; } void SMHiggsFermionsDecayer::persistentOutput(PersistentOStream & os) const { os << _maxwgt << _hvertex << NLO_ << alphaS_ << gluon_ << ounit( pTmin_, GeV ); } void SMHiggsFermionsDecayer::persistentInput(PersistentIStream & is, int) { is >> _maxwgt >> _hvertex >> NLO_ >> alphaS_ >> gluon_ >> iunit( pTmin_, GeV ); } // The following static variable is needed for the type // description system in ThePEG. DescribeClass<SMHiggsFermionsDecayer,PerturbativeDecayer> describeHerwigSMHiggsFermionsDecayer("Herwig::SMHiggsFermionsDecayer", "HwPerturbativeHiggsDecay.so"); void SMHiggsFermionsDecayer::Init() { static ClassDocumentation<SMHiggsFermionsDecayer> documentation ("The SMHiggsFermionsDecayer class implements the decat of the Standard Model" " Higgs boson to the Standard Model fermions"); static ParVector<SMHiggsFermionsDecayer,double> interfaceMaxWeights ("MaxWeights", "Maximum weights for the various decays", &SMHiggsFermionsDecayer::_maxwgt, 9, 1.0, 0.0, 10.0, false, false, Interface::limited); static Reference<SMHiggsFermionsDecayer,ShowerAlpha> interfaceCoupling ("Coupling", "The object calculating the strong coupling constant", &SMHiggsFermionsDecayer::alphaS_, false, false, true, false, false); static Parameter<SMHiggsFermionsDecayer, Energy> interfacePtMin ("minpT", "The pt cut on hardest emision generation", &SMHiggsFermionsDecayer::pTmin_, GeV, 1.*GeV, 0*GeV, 100000.0*GeV, false, false, Interface::limited); static Switch<SMHiggsFermionsDecayer,bool> interfaceNLO ("NLO", "Whether to return the LO or NLO result", &SMHiggsFermionsDecayer::NLO_, false, false, false); static SwitchOption interfaceNLOLO (interfaceNLO, "No", "Leading-order result", false); static SwitchOption interfaceNLONLO (interfaceNLO, "Yes", "NLO result", true); } // return the matrix element squared double SMHiggsFermionsDecayer::me2(const int, const Particle & part, const ParticleVector & decay, MEOption meopt) const { if(!ME()) ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin0,PDT::Spin1Half,PDT::Spin1Half))); int iferm(1),ianti(0); if(decay[0]->id()>0) swap(iferm,ianti); if(meopt==Initialize) { ScalarWaveFunction:: calculateWaveFunctions(_rho,const_ptr_cast<tPPtr>(&part),incoming); _swave = ScalarWaveFunction(part.momentum(),part.dataPtr(),incoming); } if(meopt==Terminate) { ScalarWaveFunction::constructSpinInfo(const_ptr_cast<tPPtr>(&part), incoming,true); SpinorBarWaveFunction:: constructSpinInfo(_wavebar,decay[iferm],outgoing,true); SpinorWaveFunction:: constructSpinInfo(_wave ,decay[ianti],outgoing,true); return 0.; } SpinorBarWaveFunction:: calculateWaveFunctions(_wavebar,decay[iferm],outgoing); SpinorWaveFunction:: calculateWaveFunctions(_wave ,decay[ianti],outgoing); Energy2 scale(sqr(part.mass())); unsigned int ifm,ia; for(ifm=0;ifm<2;++ifm) { for(ia=0;ia<2;++ia) { if(iferm>ianti) (*ME())(0,ia,ifm)=_hvertex->evaluate(scale,_wave[ia], _wavebar[ifm],_swave); else (*ME())(0,ifm,ia)=_hvertex->evaluate(scale,_wave[ia], _wavebar[ifm],_swave); } } int id = abs(decay[0]->id()); double output=(ME()->contract(_rho)).real()*UnitRemoval::E2/scale; if(id <=6) output*=3.; // test of the partial width // Ptr<Herwig::StandardModel>::transient_const_pointer // hwsm=dynamic_ptr_cast<Ptr<Herwig::StandardModel>::transient_const_pointer>(standardModel()); // double g2(hwsm->alphaEM(scale)*4.*Constants::pi/hwsm->sin2ThetaW()); // Energy mass(hwsm->mass(scale,decay[0]->dataPtr())), // mw(getParticleData(ParticleID::Wplus)->mass()); // double beta(sqrt(1.-4.*decay[0]->mass()*decay[0]->mass()/scale)); // cerr << "testing alpha " << hwsm->alphaEM(scale) << "\n"; // Energy test(g2*mass*mass*beta*beta*beta*part.mass()/32./Constants::pi/mw/mw); // if(abs(decay[0]->id())<=6){test *=3.;} // cout << "testing the answer " << output << " " // << test/GeV // << endl; // leading-order result if(!NLO_) return output; // fermion mass Energy particleMass = decay[0]->dataPtr()->mass(); // check decay products coloured, otherwise return if(!decay[0]->dataPtr()->coloured()|| particleMass==ZERO) return output; // inital masses, couplings etc // higgs mass mHiggs_ = part.mass(); // strong coupling aS_ = SM().alphaS(sqr(mHiggs_)); // reduced mass mu_ = particleMass/mHiggs_; mu2_ = sqr(mu_); // generate y double yminus = 0.; double yplus = 1.-2.*mu_*(1.-mu_)/(1.-2*mu2_); double y = yminus + UseRandom::rnd()*(yplus-yminus); //generate z for D31,2 double v = sqrt(sqr(2.*mu2_+(1.-2.*mu2_)*(1.-y))-4.*mu2_)/(1.-2.*mu2_)/(1.-y); double zplus = (1.+v)*(1.-2.*mu2_)*y/2./(mu2_ +(1.-2.*mu2_)*y); double zminus = (1.-v)*(1.-2.*mu2_)*y/2./(mu2_ +(1.-2.*mu2_)*y); double z = zminus + UseRandom::rnd()*(zplus-zminus); // map y,z to x1,x2 for both possible emissions double x2 = 1. - y*(1.-2.*mu2_); double x1 = 1. - z*(x2-2.*mu2_); //get the dipoles InvEnergy2 D1 = dipoleSubtractionTerm( x1, x2); InvEnergy2 D2 = dipoleSubtractionTerm( x2, x1); InvEnergy2 dipoleSum = abs(D1) + abs(D2); //jacobian double jac = (1.-y)*(yplus-yminus)*(zplus-zminus); //calculate real Energy2 realPrefactor = 0.25*sqr(mHiggs_)*sqr(1.-2.*mu2_) /sqrt(calculateLambda(1,mu2_,mu2_))/sqr(Constants::twopi); - InvEnergy2 realEmission = calculateRealEmission( x1, x2); + InvEnergy2 realEmission = 4.*Constants::pi*aS_*calculateRealEmission( x1, x2); // calculate the virtual double virtualTerm = calculateVirtualTerm(); // running mass correction virtualTerm += (8./3. - 2.*log(mu2_))*aS_/Constants::pi; //answer = (born + virtual + real)/born * LO output *= 1. + virtualTerm + 2.*jac*realPrefactor*(realEmission*abs(D1)/dipoleSum - D1); // return the answer return output; } void SMHiggsFermionsDecayer::dataBaseOutput(ofstream & os,bool header) const { if(header) os << "update decayers set parameters=\""; // parameters for the PerturbativeDecayer base class for(unsigned int ix=0;ix<_maxwgt.size();++ix) { os << "newdef " << name() << ":MaxWeights " << ix << " " << _maxwgt[ix] << "\n"; } PerturbativeDecayer::dataBaseOutput(os,false); if(header) os << "\n\" where BINARY ThePEGName=\"" << fullName() << "\";" << endl; } void SMHiggsFermionsDecayer::doinitrun() { PerturbativeDecayer::doinitrun(); if(initialize()) { for(unsigned int ix=0;ix<numberModes();++ix) { _maxwgt[ix] = mode(ix)->maxWeight(); } } } -RealEmissionProcessPtr SMHiggsFermionsDecayer:: -generateHardest(RealEmissionProcessPtr born) { - // check coloured - if(!born->bornOutgoing()[0]->dataPtr()->coloured()) return RealEmissionProcessPtr(); - assert(born->bornOutgoing().size()==2); - // extract required info - higgs_ = born->bornIncoming()[0]; - partons_.resize(2); - quark_.resize(2); - for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) { - partons_[ix] = born->bornOutgoing()[ix]->dataPtr(); - quark_[ix] = born->bornOutgoing()[ix]->momentum(); - quark_[ix].setMass(partons_[ix]->mass()); - } - bool order = partons_[0]->id()<0; - if(order) { - swap(partons_[0] ,partons_[1] ); - swap(quark_[0] ,quark_[1] ); - } - gauge_.setMass(0.*MeV); - // Get the Higgs boson mass. - mh2_ = (quark_[0] + quark_[1]).m2(); - mHiggs_ = sqrt(mh2_); - aS_ = SM().alphaS(sqr(mHiggs_)); - Energy particleMass = partons_[0]->mass(); - mu_ = particleMass/mHiggs_; - mu2_ = sqr(mu_); - // Generate emission and set _quark[0,1] and _gauge to be the - // momenta of q, qbar and g after the hardest emission: - if(!getEvent()) { - born->pT()[ShowerInteraction::QCD] = pTmin_; - return born; - } - // Ensure the energies are greater than the constituent masses: - for (int i=0; i<2; i++) { - if (quark_[i].e() < partons_[i]->constituentMass()) return RealEmissionProcessPtr(); - } - if (gauge_.e() < gluon_ ->constituentMass()) return RealEmissionProcessPtr(); - // set masses - quark_[0].setMass( partons_[0]->mass() ); - quark_[1].setMass( partons_[1]->mass() ); - gauge_ .setMass( ZERO ); - // assign the emitter based on evolution scales - unsigned int iemitter = quark_[0]*gauge_ > quark_[1]*gauge_ ? 2 : 1; - unsigned int ispectator = iemitter==1 ? 1 : 2; - // create new partices and insert - PPtr hboson = higgs_->dataPtr()->produceParticle(higgs_->momentum()); - born->incoming().push_back(hboson); - PPtr newq = partons_[0]->produceParticle(quark_[0]); - PPtr newa = partons_[1]->produceParticle(quark_[1]); - PPtr newg = gluon_->produceParticle(gauge_); - // make colour connections - newg->colourNeighbour(newq); - newa->colourNeighbour(newg); - // insert in output structure - if(!order) { - born->outgoing().push_back(newq); - born->outgoing().push_back(newa); - } - else { - born->outgoing().push_back(newa); - born->outgoing().push_back(newq); - swap(iemitter,ispectator); - } - born->outgoing().push_back(newg); - born->emitter (iemitter ); - born->spectator(ispectator); - born->emitted (3); - born->pT()[ShowerInteraction::QCD] = pT_; - // return process - born->interaction(ShowerInteraction::QCD); - return born; -} //calculate lambda double SMHiggsFermionsDecayer::calculateLambda(double x, double y, double z) const{ return sqr(x)+sqr(y)+sqr(z)-2.*x*y-2.*x*z-2.*y*z; } //calculates the dipole subtraction term for x1, D31,2 (Dij,k), // 2 is the spectator anti-fermion and 3 is the gluon InvEnergy2 SMHiggsFermionsDecayer:: dipoleSubtractionTerm(double x1, double x2) const{ InvEnergy2 commonPrefactor = CF_*8.*Constants::pi*aS_/sqr(mHiggs_); return commonPrefactor/(1.-x2)* (2.*(1.-2.*mu2_)/(2.-x1-x2)- sqrt((1.-4.*mu2_)/(sqr(x2)-4.*mu2_))* (x2-2.*mu2_)*(2.+(x1-1.)/(x2-2.*mu2_)+2.*mu2_/(1.-x2))/(1.-2.*mu2_)); } //return ME for real emission InvEnergy2 SMHiggsFermionsDecayer:: calculateRealEmission(double x1, double x2) const { - InvEnergy2 prefactor = CF_*8.*Constants::pi*aS_/sqr(mHiggs_)/(1.-4.*mu2_); + InvEnergy2 prefactor = 2.*CF_/sqr(mHiggs_)/(1.-4.*mu2_); return prefactor*(2. + (1.-x1)/(1.-x2) + (1.-x2)/(1.-x1) + 2.*(1.-2.*mu2_)*(1.-4.*mu2_)/(1.-x1)/(1.-x2) - 2.*(1.-4.*mu2_)*(1./(1.-x2)+1./(1.-x1)) - 2.*mu2_*(1.-4.*mu2_)*(1./sqr(1.-x2)+1./sqr(1.-x1))); } double SMHiggsFermionsDecayer:: calculateVirtualTerm() const { // logs and prefactors double beta = sqrt(1.-4.*mu2_); double L = log((1.+beta)/(1.-beta)); double prefactor = CF_*aS_/Constants::twopi; // non-singlet piece double nonSingletTerm = calculateNonSingletTerm(beta, L); double virtualTerm = -2.+4.*log(mu_)+(2./beta - 2.*beta)*L + (2.-4.*mu2_)/beta*(0.5*sqr(L) - 2.*L*log(beta) + 2.*Herwig::Math::ReLi2((1.-beta)/(1.+beta)) + 2.*sqr(Constants::pi)/3.); double iEpsilonTerm = 2.*(3.-sqr(Constants::pi)/2. + 0.5*log(mu2_) - 1.5*log(1.-2.*mu2_) -(1.-2.*mu2_)/beta*(0.5*sqr(L)+sqr(Constants::pi)/6. -2.*L*log(1.-2.*mu2_)) + nonSingletTerm); return prefactor*(virtualTerm+iEpsilonTerm); } //non-singlet piece of I(epsilon) insertion operator double SMHiggsFermionsDecayer:: calculateNonSingletTerm(double beta, double L) const { return 1.5*log(1.-2.*mu2_) + (1.-2.*mu2_)/beta*(- 2.*L*log(4.*(1.-2.*mu2_)/sqr(1.+beta))+ + 2.*Herwig::Math::ReLi2(sqr((1.-beta)/(1.+beta))) - 2.*Herwig::Math::ReLi2(2.*beta/(1.+beta)) - sqr(Constants::pi)/6.) + log(1.-mu_) - 2.*log(1.-2.*mu_) - 2.*mu2_/(1.-2.*mu2_)*log(mu_/(1.-mu_)) - mu_/(1.-mu_) + 2.*mu_*(2*mu_-1.)/(1.-2.*mu2_) + 0.5*sqr(Constants::pi); } -bool SMHiggsFermionsDecayer::getEvent() { - // max pT - Energy pTmax = 0.5*sqrt(mh2_); - // Define over valued y_max & y_min according to the associated pt_min cut. - double ymax = acosh(pTmax/pTmin_); - double ymin = -ymax; - // pt of the emmission - pT_ = pTmax; - // prefactor - double overEst = 4.; - double prefactor = overEst*alphaS_->overestimateValue()*4./3./Constants::twopi; - // loop to generate the pt and rapidity - bool reject; - - //arrays to hold the temporary probabilities whilst the for loop progresses - double probTemp[2][2]={{0.,0.},{0.,0.}}; - probTemp[0][0]=probTemp[0][1]=probTemp[1][0]=probTemp[1][1]=0.; - double x1Solution[2][2] = {{0.,0.},{0.,0.}}; - double x2Solution[2][2] = {{0.,0.},{0.,0.}}; - double x3Solution[2] = {0.,0.}; - Energy pT[2] = {pTmax,pTmax}; - double yTemp[2] = {0.,0.}; - for(int i=0; i<2; i++) { - do { - // reject the emission - reject = true; - // generate pt - pT[i] *= pow(UseRandom::rnd(),1./(prefactor*(ymax-ymin))); - Energy2 pT2 = sqr(pT[i]); - if(pT[i]<pTmin_) { - pT[i] = -GeV; - break; - } - // generate y - yTemp[i] = ymin + UseRandom::rnd()*(ymax-ymin); - //generate x3 & x1 from pT & y - double x1Plus = 1.; - double x1Minus = 2.*mu_; - x3Solution[i] = 2.*pT[i]*cosh(yTemp[i])/mHiggs_; - // prefactor - Energy2 weightPrefactor = mh2_/16./sqr(Constants::pi)/sqrt(1.-4.*mu2_); - weightPrefactor /= prefactor; - // calculate x1 & x2 solutions - Energy4 discrim2 = (sqr(x3Solution[i]*mHiggs_) - 4.*pT2)* - (mh2_*(x3Solution[i]-1.)*(4.*mu2_+x3Solution[i]-1.)-4.*mu2_*pT2); - //check discriminant2 is > 0 - if( discrim2 < ZERO) continue; - Energy2 discriminant = sqrt(discrim2); - Energy2 fact1 = 3.*mh2_*x3Solution[i]-2.*mh2_+2.*pT2*x3Solution[i]-4.*pT2-mh2_*sqr(x3Solution[i]); - Energy2 fact2 = 2.*mh2_*(x3Solution[i]-1.)-2.*pT2; - // two solns for x1 - x1Solution[i][0] = (fact1 + discriminant)/fact2; - x1Solution[i][1] = (fact1 - discriminant)/fact2; - x2Solution[i][0] = 2.-x3Solution[i]-x1Solution[i][0]; - x2Solution[i][1] = 2.-x3Solution[i]-x1Solution[i][1]; - bool found = false; - for(unsigned int j=0;j<2;++j) { - if(x1Solution[i][j]>=x1Minus && x1Solution[i][j]<=x1Plus && - checkZMomenta(x1Solution[i][j], x2Solution[i][j], x3Solution[i], yTemp[i], pT[i])) { - InvEnergy2 D1 = dipoleSubtractionTerm( x1Solution[i][j], x2Solution[i][j]); - InvEnergy2 D2 = dipoleSubtractionTerm( x2Solution[i][j], x1Solution[i][j]); - double dipoleFactor = abs(D1)/(abs(D1) + abs(D2)); - probTemp[i][j] = weightPrefactor*pT[i]*dipoleFactor* - calculateJacobian(x1Solution[i][j], x2Solution[i][j], pT[i])* - calculateRealEmission(x1Solution[i][j], x2Solution[i][j]); - - found = true; - } - else { - probTemp[i][j] = 0.; - } - } - if(!found) continue; - // alpha S piece - double wgt = (probTemp[i][0]+probTemp[i][1])*alphaS_->value(sqr(pT[i]))/aS_; - // matrix element weight - reject = UseRandom::rnd()>wgt; - } - while(reject); - } //end of emitter for loop - // no emission - if(pT[0]<ZERO&&pT[1]<ZERO) return false; - //pick the spectator and x1 x2 values - double x1,x2,y; - //particle 1 emits, particle 2 spectates - unsigned int iemit=0; - if(pT[0]>pT[1]){ - pT_ = pT[0]; - y=yTemp[0]; - if(probTemp[0][0]>UseRandom::rnd()*(probTemp[0][0]+probTemp[0][1])) { - x1 = x1Solution[0][0]; - x2 = x2Solution[0][0]; - } - else { - x1 = x1Solution[0][1]; - x2 = x2Solution[0][1]; - } - } - //particle 2 emits, particle 1 spectates - else { - iemit=1; - pT_ = pT[1]; - y=yTemp[1]; - if(probTemp[1][0]>UseRandom::rnd()*(probTemp[1][0]+probTemp[1][1])) { - x1 = x1Solution[1][0]; - x2 = x2Solution[1][0]; - } - else { - x1 = x1Solution[1][1]; - x2 = x2Solution[1][1]; - } - } - // find spectator - unsigned int ispect = iemit == 0 ? 1 : 0; - // Find the boost from the lab to the c.o.m with the spectator - // along the -z axis, and then invert it. - LorentzRotation eventFrame( ( quark_[0] + quark_[1] ).findBoostToCM() ); - Lorentz5Momentum spectator = eventFrame*quark_[ispect]; - eventFrame.rotateZ( -spectator.phi() ); - eventFrame.rotateY( -spectator.theta() - Constants::pi ); - eventFrame.invert(); - //generation of phi - double phi = UseRandom::rnd() * Constants::twopi; - // spectator - quark_[ispect].setT( 0.5*x2*mHiggs_ ); - quark_[ispect].setX( ZERO ); - quark_[ispect].setY( ZERO ); - quark_[ispect].setZ( -sqrt(0.25*mh2_*x2*x2-mh2_*mu2_) ); - // gluon - gauge_.setT( pT_*cosh(y) ); - gauge_.setX( pT_*cos(phi) ); - gauge_.setY( pT_*sin(phi) ); - gauge_.setZ( pT_*sinh(y) ); - gauge_.setMass(ZERO); - // emitter reconstructed from gluon & spectator - quark_[iemit] = - gauge_ - quark_[ispect]; - quark_[iemit].setT( 0.5*mHiggs_*x1 ); - // boost constructed vectors into the event frame - quark_[0] = eventFrame * quark_[0]; - quark_[1] = eventFrame * quark_[1]; - gauge_ = eventFrame * gauge_; - // need to reset masses because for whatever reason the boost - // touches the mass component of the five-vector and can make - // zero mass objects acquire a floating point negative mass(!). - gauge_.setMass( ZERO ); - quark_[iemit] .setMass(partons_[iemit ]->mass()); - quark_[ispect].setMass(partons_[ispect]->mass()); - - return true; +double SMHiggsFermionsDecayer::matrixElementRatio(const Particle & inpart, const ParticleVector & decay2, + const ParticleVector & decay3, MEOption) { + mHiggs_ = inpart.mass(); + mu_ = decay2[0]->mass()/mHiggs_; + mu2_ = sqr(mu_); + double x1 = 2.*decay3[0]->momentum().t()/mHiggs_; + double x2 = 2.*decay3[1]->momentum().t()/mHiggs_; + return calculateRealEmission(x1,x2)*4.*Constants::pi*sqr(mHiggs_); } - -InvEnergy SMHiggsFermionsDecayer::calculateJacobian(double x1, double x2, Energy pT) const{ - double xPerp = abs(2.*pT/mHiggs_); - Energy jac = mHiggs_*fabs((x1*x2-2.*mu2_*(x1+x2)+sqr(x2)-x2)/xPerp/pow(sqr(x2)-4.*mu2_,1.5)); - return 1./jac; //jacobian as defined is dptdy=jac*dx1dx2, therefore we have to divide by it -} - -bool SMHiggsFermionsDecayer::checkZMomenta(double x1, double x2, double x3, double y, Energy pT) const { - double xPerp2 = 4.*pT*pT/mHiggs_/mHiggs_; - static double tolerance = 1e-6; - bool isMomentaReconstructed = false; - - if(pT*sinh(y)>ZERO) { - if(abs(-sqrt(sqr(x2)-4.*mu2_)+sqrt(sqr(x3)-xPerp2) + sqrt(sqr(x1)-xPerp2 - 4.*mu2_)) <= tolerance || - abs(-sqrt(sqr(x2)-4.*mu2_)+sqrt(sqr(x3)-xPerp2) - sqrt(sqr(x1)-xPerp2 - 4.*mu2_)) <= tolerance) isMomentaReconstructed=true; - } - else if(pT*sinh(y) < ZERO){ - if(abs(-sqrt(sqr(x2)-4.*mu2_)-sqrt(sqr(x3)-xPerp2) + sqrt(sqr(x1)-xPerp2 - 4.*mu2_)) <= tolerance || - abs(-sqrt(sqr(x2)-4.*mu2_)-sqrt(sqr(x3)-xPerp2) - sqrt(sqr(x1)-xPerp2 - 4.*mu2_)) <= tolerance) isMomentaReconstructed=true; - } - else - if(abs(-sqrt(sqr(x2)-4.*mu2_)+ sqrt(sqr(x1)-xPerp2 - 4.*mu2_)) <= tolerance) isMomentaReconstructed=true; - - return isMomentaReconstructed; -} - diff --git a/Decay/Perturbative/SMHiggsFermionsDecayer.h b/Decay/Perturbative/SMHiggsFermionsDecayer.h --- a/Decay/Perturbative/SMHiggsFermionsDecayer.h +++ b/Decay/Perturbative/SMHiggsFermionsDecayer.h @@ -1,311 +1,297 @@ // -*- C++ -*- // // SMHiggsFermionsDecayer.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_SMHiggsFermionsDecayer_H #define HERWIG_SMHiggsFermionsDecayer_H // // This is the declaration of the SMHiggsFermionsDecayer class. // #include "Herwig/Decay/PerturbativeDecayer.h" #include "ThePEG/Helicity/Vertex/AbstractFFSVertex.h" #include "Herwig/Decay/DecayPhaseSpaceMode.h" #include "Herwig/Shower/Core/Couplings/ShowerAlpha.fh" namespace Herwig { using namespace ThePEG; /** * The SMHiggsFermionsDecayer class is designed to decay the Standard Model Higgs * to the Standard Model fermions. * * @see PerturbativeDecayer */ class SMHiggsFermionsDecayer: public PerturbativeDecayer { public: /** * The default constructor. */ SMHiggsFermionsDecayer(); /** * 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; /** * 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; /** * Has a POWHEG style correction */ virtual POWHEGType hasPOWHEGCorrection() {return FSR;} /** - * Apply the POWHEG style correction + * Calculate matrix element ratio R/B */ - virtual RealEmissionProcessPtr generateHardest(RealEmissionProcessPtr); + virtual double matrixElementRatio(const Particle & inpart, const ParticleVector & decay2, + const ParticleVector & decay3, MEOption meopt); public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const {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: /** * Calcluate the Kallen function */ double calculateLambda(double x, double y, double z) const; /** * Dipole subtraction term */ InvEnergy2 dipoleSubtractionTerm(double x1, double x2) const; /** * Real emission term */ InvEnergy2 calculateRealEmission(double x1, double x2) const; /** * Virtual term */ double calculateVirtualTerm() const; /** * Non-singlet term */ double calculateNonSingletTerm(double beta, double L) const; - /** - * Check the sign of the momentum in the \f$z\f$-direction is correct. - */ - bool checkZMomenta(double x1, double x2, double x3, double y, Energy pT) const; - - /** - * Calculate the Jacobian - */ - InvEnergy calculateJacobian(double x1, double x2, Energy pT) const; - - /** - * Generate a real emission event - */ - bool getEvent(); - protected: /** @name Standard Interfaced functions. */ //@{ /** * Initialize this object after the setup phase before saving an * EventGenerator to disk. * @throws InitException if object could not be initialized properly. */ virtual void doinit(); /** * Initialize this object. Called in the run phase just before * a run begins. */ virtual void doinitrun(); //@} private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ SMHiggsFermionsDecayer & operator=(const SMHiggsFermionsDecayer &); private: /** * Pointer to the Higgs vertex */ AbstractFFSVertexPtr _hvertex; /** * maximum weights for the different decay modes */ vector<double> _maxwgt; /** * Spin density matrix */ mutable RhoDMatrix _rho; /** * Scalar wavefunction */ mutable ScalarWaveFunction _swave; /** * Spinor wavefunction */ mutable vector<SpinorWaveFunction> _wave; /** * Barred spinor wavefunction */ mutable vector<SpinorBarWaveFunction> _wavebar; private: /** * The colour factor */ double CF_; /** * The Higgs mass */ mutable Energy mHiggs_; /** * The reduced mass */ mutable double mu_; /** * The square of the reduced mass */ mutable double mu2_; /** * The strong coupling */ mutable double aS_; /** * Stuff for the POWHEG correction */ //@{ /** * Pointer to the object calculating the strong coupling */ ShowerAlphaPtr alphaS_; /** * ParticleData object for the gluon */ tcPDPtr gluon_; /** * The cut off on pt, assuming massless quarks. */ Energy pTmin_; // radiative variables (pt,y) Energy pT_; /** * The ParticleData objects for the fermions */ vector<tcPDPtr> partons_; /** * The fermion momenta */ vector<Lorentz5Momentum> quark_; /** * The momentum of the radiated gauge boson */ Lorentz5Momentum gauge_; /** * The Higgs boson */ PPtr higgs_; /** * Higgs mass squared */ Energy2 mh2_; //@} /** * LO or NLO ? */ bool NLO_; }; } #endif /* HERWIG_SMHiggsFermionsDecayer_H */ diff --git a/Decay/Perturbative/SMTopDecayer.cc b/Decay/Perturbative/SMTopDecayer.cc --- a/Decay/Perturbative/SMTopDecayer.cc +++ b/Decay/Perturbative/SMTopDecayer.cc @@ -1,1198 +1,1198 @@ // -*- 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 << _wvertex << _wquarkwgt << _wleptonwgt << _wplus << _initialenhance << _finalenhance << _xg_sampling << _useMEforT2; } void SMTopDecayer::persistentInput(PersistentIStream & is, int) { is >> _wvertex >> _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 = _wvertex->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) = _wvertex->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 = _wvertex-> 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) = _wvertex->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()"; _wvertex = hwsm->vertexFFW(); //initialise _wvertex->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(_wvertex->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 *(coupling()->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::matrixElementRatio(const Particle & inpart, const ParticleVector & , const ParticleVector & decay3, MEOption ) { double f = (1. + sqr(e2()) - 2.*sqr(s2()) + s2() + s2()*e2() - 2.*e2()); double Nc = standardModel()->Nc(); double Cf = (sqr(Nc) - 1.) / (2.*Nc); 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; + return R/B*Constants::pi; } diff --git a/Decay/Perturbative/SMWDecayer.cc b/Decay/Perturbative/SMWDecayer.cc --- a/Decay/Perturbative/SMWDecayer.cc +++ b/Decay/Perturbative/SMWDecayer.cc @@ -1,924 +1,924 @@ // -*- C++ -*- // // SMWDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator // Copyright (C) 2002-2017 The Herwig Collaboration // // Herwig is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // // // This is the implementation of the non-inlined, non-templated member // functions of the SMWDecayer class. // #include "SMWDecayer.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/ParVector.h" #include "ThePEG/Interface/Reference.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/PDT/DecayMode.h" #include "Herwig/Decay/DecayVertex.h" #include "ThePEG/Helicity/VectorSpinInfo.h" #include "ThePEG/Helicity/FermionSpinInfo.h" #include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h" #include "Herwig/Models/StandardModel/StandardModel.h" #include "Herwig/Shower/Core/Base/ShowerProgenitor.h" #include "Herwig/Shower/Core/Base/ShowerParticle.h" #include "Herwig/Shower/Core/Base/Branching.h" #include "Herwig/Shower/RealEmissionProcess.h" #include "Herwig/Decay/GeneralDecayMatrixElement.h" #include <numeric> using namespace Herwig; using namespace ThePEG::Helicity; const double SMWDecayer::EPS_=0.00000001; SMWDecayer::SMWDecayer() : quarkWeight_(6,0.), leptonWeight_(3,0.), CF_(4./3.), NLO_(false) { quarkWeight_[0] = 1.01596; quarkWeight_[1] = 0.0537308; quarkWeight_[2] = 0.0538085; quarkWeight_[3] = 1.01377; quarkWeight_[4] = 1.45763e-05; quarkWeight_[5] = 0.0018143; leptonWeight_[0] = 0.356594; leptonWeight_[1] = 0.356593; leptonWeight_[2] = 0.356333; // intermediates generateIntermediates(false); } void SMWDecayer::doinit() { PerturbativeDecayer::doinit(); // get the vertices from the Standard Model object tcHwSMPtr hwsm=dynamic_ptr_cast<tcHwSMPtr>(standardModel()); if(!hwsm) throw InitException() << "Must have Herwig StandardModel object in" << "SMWDecayer::doinit()" << Exception::runerror; FFWVertex_ = hwsm->vertexFFW(); FFGVertex_ = hwsm->vertexFFG(); // make sure they are initialized FFGVertex_->init(); FFWVertex_->init(); // now set up the decay modes DecayPhaseSpaceModePtr mode; tPDVector extpart(3); vector<double> wgt(0); // W modes extpart[0]=getParticleData(ParticleID::Wplus); // loop for the quarks unsigned int iz=0; for(int ix=1;ix<6;ix+=2) { for(int iy=2;iy<6;iy+=2) { // check that the combination of particles is allowed if(!FFWVertex_->allowed(-ix,iy,ParticleID::Wminus)) throw InitException() << "SMWDecayer::doinit() the W vertex" << "cannot handle all the quark modes" << Exception::abortnow; extpart[1] = getParticleData(-ix); extpart[2] = getParticleData( iy); mode = new_ptr(DecayPhaseSpaceMode(extpart,this)); addMode(mode,quarkWeight_[iz],wgt); ++iz; } } // loop for the leptons for(int ix=11;ix<17;ix+=2) { // check that the combination of particles is allowed // if(!FFWVertex_->allowed(-ix,ix+1,ParticleID::Wminus)) // throw InitException() << "SMWDecayer::doinit() the W vertex" // << "cannot handle all the lepton modes" // << Exception::abortnow; extpart[1] = getParticleData(-ix); extpart[2] = getParticleData(ix+1); mode = new_ptr(DecayPhaseSpaceMode(extpart,this)); addMode(mode,leptonWeight_[(ix-11)/2],wgt); } gluon_ = getParticleData(ParticleID::g); } int SMWDecayer::modeNumber(bool & cc,tcPDPtr parent, const tPDVector & children) const { int imode(-1); if(children.size()!=2) return imode; int id0=parent->id(); tPDVector::const_iterator pit = children.begin(); int id1=(**pit).id(); ++pit; int id2=(**pit).id(); if(abs(id0)!=ParticleID::Wplus) return imode; int idd(0),idu(0); if(abs(id1)%2==1&&abs(id2)%2==0) { idd=abs(id1); idu=abs(id2); } else if(abs(id1)%2==0&&abs(id2)%2==1) { idd=abs(id2); idu=abs(id1); } if(idd==0&&idu==0) { return imode; } else if(idd<=5) { imode=idd+idu/2-2; } else { imode=(idd-1)/2+1; } cc= (id0==ParticleID::Wminus); return imode; } void SMWDecayer::persistentOutput(PersistentOStream & os) const { os << FFWVertex_ << quarkWeight_ << leptonWeight_ << FFGVertex_ << gluon_ << NLO_; } void SMWDecayer::persistentInput(PersistentIStream & is, int) { is >> FFWVertex_ >> quarkWeight_ >> leptonWeight_ >> FFGVertex_ >> gluon_ >> NLO_; } // The following static variable is needed for the type // description system in ThePEG. DescribeClass<SMWDecayer,PerturbativeDecayer> describeHerwigSMWDecayer("Herwig::SMWDecayer", "HwPerturbativeDecay.so"); void SMWDecayer::Init() { static ClassDocumentation<SMWDecayer> documentation ("The SMWDecayer class is the implementation of the decay" " of the W boson to the Standard Model fermions."); static ParVector<SMWDecayer,double> interfaceWquarkMax ("QuarkMax", "The maximum weight for the decay of the W to quarks", &SMWDecayer::quarkWeight_, 0, 0, 0, -10000, 10000, false, false, true); static ParVector<SMWDecayer,double> interfaceWleptonMax ("LeptonMax", "The maximum weight for the decay of the W to leptons", &SMWDecayer::leptonWeight_, 0, 0, 0, -10000, 10000, false, false, true); static Switch<SMWDecayer,bool> interfaceNLO ("NLO", "Whether to return the LO or NLO result", &SMWDecayer::NLO_, false, false, false); static SwitchOption interfaceNLOLO (interfaceNLO, "No", "Leading-order result", false); static SwitchOption interfaceNLONLO (interfaceNLO, "Yes", "NLO result", true); } // return the matrix element squared double SMWDecayer::me2(const int, const Particle & part, const ParticleVector & decay, MEOption meopt) const { if(!ME()) ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half))); int iferm(1),ianti(0); if(decay[0]->id()>0) swap(iferm,ianti); if(meopt==Initialize) { VectorWaveFunction::calculateWaveFunctions(vectors_,rho_, const_ptr_cast<tPPtr>(&part), incoming,false); } if(meopt==Terminate) { VectorWaveFunction::constructSpinInfo(vectors_,const_ptr_cast<tPPtr>(&part), incoming,true,false); SpinorBarWaveFunction:: constructSpinInfo(wavebar_,decay[iferm],outgoing,true); SpinorWaveFunction:: constructSpinInfo(wave_ ,decay[ianti],outgoing,true); return 0.; } SpinorBarWaveFunction:: calculateWaveFunctions(wavebar_,decay[iferm],outgoing); SpinorWaveFunction:: calculateWaveFunctions(wave_ ,decay[ianti],outgoing); // compute the matrix element Energy2 scale(sqr(part.mass())); for(unsigned int ifm=0;ifm<2;++ifm) { for(unsigned int ia=0;ia<2;++ia) { for(unsigned int vhel=0;vhel<3;++vhel) { if(iferm>ianti) (*ME())(vhel,ia,ifm)= FFWVertex_->evaluate(scale,wave_[ia],wavebar_[ifm],vectors_[vhel]); else (*ME())(vhel,ifm,ia)= FFWVertex_->evaluate(scale,wave_[ia],wavebar_[ifm],vectors_[vhel]); } } } double output=(ME()->contract(rho_)).real()*UnitRemoval::E2/scale; if(abs(decay[0]->id())<=6) output*=3.; if(decay[0]->hasColour()) decay[0]->antiColourNeighbour(decay[1]); else if(decay[1]->hasColour()) decay[1]->antiColourNeighbour(decay[0]); // leading-order result if(!NLO_) return output; // check decay products coloured, otherwise return if(!decay[0]->dataPtr()->coloured()) return output; // inital masses, couplings etc // W mass mW_ = part.mass(); // strong coupling aS_ = SM().alphaS(sqr(mW_)); // reduced mass double mu1 = (decay[0]->dataPtr()->mass())/mW_; double mu2 = (decay[1]->dataPtr()->mass())/mW_; // scale scale_ = sqr(mW_); // now for the nlo loop correction double virt = CF_*aS_/Constants::pi; // now for the real correction double realFact=0.; for(int iemit=0;iemit<2;++iemit) { double phi = UseRandom::rnd()*Constants::twopi; // set the emitter and the spectator double muj = iemit==0 ? mu1 : mu2; double muk = iemit==0 ? mu2 : mu1; double muj2 = sqr(muj); double muk2 = sqr(muk); // calculate y double yminus = 0.; double yplus = 1.-2.*muk*(1.-muk)/(1.-muj2-muk2); double y = yminus + UseRandom::rnd()*(yplus-yminus); double v = sqrt(sqr(2.*muk2 + (1.-muj2-muk2)*(1.-y))-4.*muk2) /(1.-muj2-muk2)/(1.-y); double zplus = (1.+v)*(1.-muj2-muk2)*y/2./(muj2+(1.-muj2-muk2)*y); double zminus = (1.-v)*(1.-muj2-muk2)*y/2./(muj2+(1.-muj2-muk2)*y); double z = zminus + UseRandom::rnd()*(zplus-zminus); double jac = (1.-y)*(yplus-yminus)*(zplus-zminus); // calculate x1,x2,x3,xT double x2 = 1.-y*(1.-muj2-muk2)-muj2+muk2; double x1 = 1.+muj2-muk2-z*(x2-2.*muk2); // copy the particle objects over for calculateRealEmission vector<PPtr> hardProcess(3); hardProcess[0] = const_ptr_cast<PPtr>(&part); hardProcess[1] = decay[0]; hardProcess[2] = decay[1]; realFact += 0.25*jac*sqr(1.-muj2-muk2)/ sqrt((1.-sqr(muj-muk))*(1.-sqr(muj+muk)))/Constants::twopi *2.*CF_*aS_*calculateRealEmission(x1, x2, hardProcess, phi, muj, muk, iemit, true); } // the born + virtual + real output *= (1. + virt + realFact); return output; } void SMWDecayer::doinitrun() { PerturbativeDecayer::doinitrun(); if(initialize()) { for(unsigned int ix=0;ix<numberModes();++ix) { if(ix<6) quarkWeight_ [ix]=mode(ix)->maxWeight(); else leptonWeight_[ix-6]=mode(ix)->maxWeight(); } } } void SMWDecayer::dataBaseOutput(ofstream & output, bool header) const { if(header) output << "update decayers set parameters=\""; for(unsigned int ix=0;ix<quarkWeight_.size();++ix) { output << "newdef " << name() << ":QuarkMax " << ix << " " << quarkWeight_[ix] << "\n"; } for(unsigned int ix=0;ix<leptonWeight_.size();++ix) { output << "newdef " << name() << ":LeptonMax " << ix << " " << leptonWeight_[ix] << "\n"; } // parameters for the PerturbativeDecayer base class PerturbativeDecayer::dataBaseOutput(output,false); if(header) output << "\n\" where BINARY ThePEGName=\"" << fullName() << "\";" << endl; } void SMWDecayer:: initializeMECorrection(RealEmissionProcessPtr born, double & initial, double & final) { // get the quark and antiquark ParticleVector qq; for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) qq.push_back(born->bornOutgoing()[ix]); // ensure quark first if(qq[0]->id()<0) swap(qq[0],qq[1]); // centre of mass energy d_Q_ = (qq[0]->momentum() + qq[1]->momentum()).m(); // quark mass d_m_ = 0.5*(qq[0]->momentum().m()+qq[1]->momentum().m()); // set the other parameters setRho(sqr(d_m_/d_Q_)); setKtildeSymm(); // otherwise can do it initial=1.; final =1.; } RealEmissionProcessPtr SMWDecayer:: applyHardMatrixElementCorrection(RealEmissionProcessPtr born) { // get the quark and antiquark ParticleVector qq; for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) qq.push_back(born->bornOutgoing()[ix]); if(!qq[0]->dataPtr()->coloured()) return RealEmissionProcessPtr(); // ensure quark first bool order = qq[0]->id()<0; if(order) swap(qq[0],qq[1]); // get the momenta vector<Lorentz5Momentum> newfs = applyHard(qq); // return if no emission if(newfs.size()!=3) return RealEmissionProcessPtr(); // perform final check to ensure energy greater than constituent mass for (int i=0; i<2; i++) { if (newfs[i].e() < qq[i]->data().constituentMass()) return RealEmissionProcessPtr(); } if (newfs[2].e() < getParticleData(ParticleID::g)->constituentMass()) return RealEmissionProcessPtr(); // set masses for (int i=0; i<2; i++) newfs[i].setMass(qq[i]->mass()); newfs[2].setMass(ZERO); // decide which particle emits bool firstEmits= newfs[2].vect().perp2(newfs[0].vect())< newfs[2].vect().perp2(newfs[1].vect()); // create the new quark, antiquark and gluon PPtr newg = getParticleData(ParticleID::g)->produceParticle(newfs[2]); PPtr newq = qq[0]->dataPtr()->produceParticle(newfs[0]); PPtr newa = qq[1]->dataPtr()->produceParticle(newfs[1]); // create the output real emission process for(unsigned int ix=0;ix<born->bornIncoming().size();++ix) { born->incoming().push_back(born->bornIncoming()[ix]); } if(!order) { born->outgoing().push_back(newq); born->outgoing().push_back(newa); born->outgoing().push_back(newg); } else { born->outgoing().push_back(newa); born->outgoing().push_back(newq); born->outgoing().push_back(newg); firstEmits = !firstEmits; } // make colour connections newg->colourNeighbour(newq); newa->colourNeighbour(newg); if(firstEmits) { born->emitter(1); born->spectator(2); } else { born->emitter(2); born->spectator(1); } born->emitted(3); born->interaction(ShowerInteraction::QCD); return born; } vector<Lorentz5Momentum> SMWDecayer:: applyHard(const ParticleVector &p) { double x, xbar; vector<Lorentz5Momentum> fs; // return if no emission if (getHard(x, xbar) < UseRandom::rnd() || p.size() != 2) return fs; // centre of mass energy Lorentz5Momentum pcm = p[0]->momentum() + p[1]->momentum(); // momenta of quark,antiquark and gluon Lorentz5Momentum pq, pa, pg; if (p[0]->id() > 0) { pq = p[0]->momentum(); pa = p[1]->momentum(); } else { pa = p[0]->momentum(); pq = p[1]->momentum(); } // boost to boson rest frame Boost beta = (pcm.findBoostToCM()); pq.boost(beta); pa.boost(beta); // return if fails ????? double xg = 2.-x-xbar; if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return fs; Axis u1, u2, u3; // moduli of momenta in units of Q and cos theta // stick to q direction? // p1 is the one that is kept, p2 is the other fermion, p3 the gluon. Energy e1, e2, e3; Energy pp1, pp2, pp3; bool keepq = true; if (UseRandom::rnd() > sqr(x)/(sqr(x)+sqr(xbar))) keepq = false; if (keepq) { pp1 = d_Q_*sqrt(sqr(x)-4.*d_rho_)/2.; pp2 = d_Q_*sqrt(sqr(xbar)-4.*d_rho_)/2.; e1 = d_Q_*x/2.; e2 = d_Q_*xbar/2.; u1 = pq.vect().unit(); } else { pp2 = d_Q_*sqrt(sqr(x)-4.*d_rho_)/2.; pp1 = d_Q_*sqrt(sqr(xbar)-4.*d_rho_)/2.; e2 = d_Q_*x/2.; e1 = d_Q_*xbar/2.; u1 = pa.vect().unit(); } pp3 = d_Q_*xg/2.; e3 = pp3; u2 = u1.orthogonal(); u2 /= u2.mag(); u3 = u1.cross(u2); u3 /= u3.mag(); double ct2=-2., ct3=-2.; if (pp1 == ZERO || pp2 == ZERO || pp3 == ZERO) { bool touched = false; if (pp1 == ZERO) { ct2 = 1; ct3 = -1; touched = true; } if (pp2 == ZERO || pp3 == ZERO) { ct2 = 1; ct3 = 1; touched = true; } if (!touched) throw Exception() << "SMWDecayer::applyHard()" << " did not set ct2/3" << Exception::abortnow; } else { ct3 = (sqr(pp1)+sqr(pp3)-sqr(pp2))/(2.*pp1*pp3); ct2 = (sqr(pp1)+sqr(pp2)-sqr(pp3))/(2.*pp1*pp2); } double phi = Constants::twopi*UseRandom::rnd(); double cphi = cos(phi); double sphi = sin(phi); double st2 = sqrt(1.-sqr(ct2)); double st3 = sqrt(1.-sqr(ct3)); ThreeVector<Energy> pv1, pv2, pv3; pv1 = pp1*u1; pv2 = -ct2*pp2*u1 + st2*cphi*pp2*u2 + st2*sphi*pp2*u3; pv3 = -ct3*pp3*u1 - st3*cphi*pp3*u2 - st3*sphi*pp3*u3; if (keepq) { pq = Lorentz5Momentum(pv1, e1); pa = Lorentz5Momentum(pv2, e2); } else { pa = Lorentz5Momentum(pv1, e1); pq = Lorentz5Momentum(pv2, e2); } pg = Lorentz5Momentum(pv3, e3); pq.boost(-beta); pa.boost(-beta); pg.boost(-beta); fs.push_back(pq); fs.push_back(pa); fs.push_back(pg); return fs; } double SMWDecayer::getHard(double &x1, double &x2) { double w = 0.0; double y1 = UseRandom::rnd(),y2 = UseRandom::rnd(); // simply double MC efficiency // -> weight has to be divided by two (Jacobian) if (y1 + y2 > 1) { y1 = 1.-y1; y2 = 1.-y2; } bool inSoft = false; if (y1 < 0.25) { if (y2 < 0.25) { inSoft = true; if (y1 < y2) { y1 = 0.25-y1; y2 = y1*(1.5 - 2.*y2); } else { y2 = 0.25 - y2; y1 = y2*(1.5 - 2.*y1); } } else { if (y2 < y1 + 2.*sqr(y1)) return w; } } else { if (y2 < 0.25) { if (y1 < y2 + 2.*sqr(y2)) return w; } } // inside PS? x1 = 1.-y1; x2 = 1.-y2; if(y1*y2*(1.-y1-y2) < d_rho_*sqr(y1+y2)) return w; double k1 = getKfromX(x1, x2); double k2 = getKfromX(x2, x1); // Is it in the quark emission zone? if (k1 < d_kt1_) return 0.0; // No...is it in the anti-quark emission zone? if (k2 < d_kt2_) return 0.0; // Point is in dead zone: compute q qbar g weight w = MEV(x1, x2); // for axial: // w = MEA(x1, x2); // Reweight soft region if (inSoft) { if (y1 < y2) w *= 2.*y1; else w *= 2.*y2; } // alpha and colour factors Energy2 pt2 = sqr(d_Q_)*(1.-x1)*(1.-x2); w *= 1./3./Constants::pi*coupling()->value(pt2); return w; } bool SMWDecayer:: softMatrixElementVeto(ShowerProgenitorPtr initial,ShowerParticlePtr parent,Branching br) { // check we should be applying the veto if(parent->id()!=initial->progenitor()->id()|| br.ids[0]!=br.ids[1]|| br.ids[2]->id()!=ParticleID::g) return false; // calculate pt double d_z = br.kinematics->z(); Energy d_qt = br.kinematics->scale(); Energy2 d_m2 = parent->momentum().m2(); Energy2 pPerp2 = sqr(d_z*d_qt) - d_m2; if(pPerp2<ZERO) { parent->vetoEmission(br.type,br.kinematics->scale()); return true; } Energy pPerp = (1.-d_z)*sqrt(pPerp2); // if not hardest so far don't apply veto if(pPerp<initial->highestpT()) return false; // calculate the weight double weight = 0.; if(parent->id()>0) weight = qWeightX(d_qt, d_z); else weight = qbarWeightX(d_qt, d_z); // compute veto from weight bool veto = !UseRandom::rndbool(weight); // if vetoing reset the scale if(veto) parent->vetoEmission(br.type,br.kinematics->scale()); // return the veto return veto; } void SMWDecayer::setRho(double r) { d_rho_ = r; d_v_ = sqrt(1.-4.*d_rho_); } void SMWDecayer::setKtildeSymm() { d_kt1_ = (1. + sqrt(1. - 4.*d_rho_))/2.; setKtilde2(); } void SMWDecayer::setKtilde2() { double num = d_rho_ * d_kt1_ + 0.25 * d_v_ *(1.+d_v_)*(1.+d_v_); double den = d_kt1_ - d_rho_; d_kt2_ = num/den; } double SMWDecayer::getZfromX(double x1, double x2) { double uval = u(x2); double num = x1 - (2. - x2)*uval; double den = sqrt(x2*x2 - 4.*d_rho_); return uval + num/den; } double SMWDecayer::getKfromX(double x1, double x2) { double zval = getZfromX(x1, x2); return (1.-x2)/(zval*(1.-zval)); } double SMWDecayer::MEV(double x1, double x2) { // Vector part double num = (x1+2.*d_rho_)*(x1+2.*d_rho_) + (x2+2.*d_rho_)*(x2+2.*d_rho_) - 8.*d_rho_*(1.+2.*d_rho_); double den = (1.+2.*d_rho_)*(1.-x1)*(1.-x2); return (num/den - 2.*d_rho_/((1.-x1)*(1.-x1)) - 2*d_rho_/((1.-x2)*(1.-x2)))/d_v_; } double SMWDecayer::MEA(double x1, double x2) { // Axial part double num = (x1+2.*d_rho_)*(x1+2.*d_rho_) + (x2+2.*d_rho_)*(x2+2.*d_rho_) + 2.*d_rho_*((5.-x1-x2)*(5.-x1-x2) - 19.0 + 4*d_rho_); double den = d_v_*d_v_*(1.-x1)*(1.-x2); return (num/den - 2.*d_rho_/((1.-x1)*(1.-x1)) - 2*d_rho_/((1.-x2)*(1.-x2)))/d_v_; } double SMWDecayer::u(double x2) { return 0.5*(1. + d_rho_/(1.-x2+d_rho_)); } void SMWDecayer:: getXXbar(double kti, double z, double &x, double &xbar) { double w = sqr(d_v_) + kti*(-1. + z)*z*(2. + kti*(-1. + z)*z); if (w < 0) { x = -1.; xbar = -1; } else { x = (1. + sqr(d_v_)*(-1. + z) + sqr(kti*(-1. + z))*z*z*z + z*sqrt(w) - kti*(-1. + z)*z*(2. + z*(-2 + sqrt(w))))/ (1. - kti*(-1. + z)*z + sqrt(w)); xbar = 1. + kti*(-1. + z)*z; } } double SMWDecayer::qWeight(double x, double xbar) { double rval; double xg = 2. - xbar - x; // always return one in the soft gluon region if(xg < EPS_) return 1.0; // check it is in the phase space if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return 0.0; double k1 = getKfromX(x, xbar); double k2 = getKfromX(xbar, x); // Is it in the quark emission zone? if(k1 < d_kt1_) { rval = MEV(x, xbar)/PS(x, xbar); // is it also in the anti-quark emission zone? if(k2 < d_kt2_) rval *= 0.5; return rval; } return 1.0; } double SMWDecayer::qbarWeight(double x, double xbar) { double rval; double xg = 2. - xbar - x; // always return one in the soft gluon region if(xg < EPS_) return 1.0; // check it is in the phase space if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return 0.0; double k1 = getKfromX(x, xbar); double k2 = getKfromX(xbar, x); // Is it in the antiquark emission zone? if(k2 < d_kt2_) { rval = MEV(x, xbar)/PS(xbar, x); // is it also in the quark emission zone? if(k1 < d_kt1_) rval *= 0.5; return rval; } return 1.0; } double SMWDecayer::qWeightX(Energy qtilde, double z) { double x, xb; getXXbar(sqr(qtilde/d_Q_), z, x, xb); // if exceptionally out of phase space, leave this emission, as there // is no good interpretation for the soft ME correction. if (x < 0 || xb < 0) return 1.0; return qWeight(x, xb); } double SMWDecayer::qbarWeightX(Energy qtilde, double z) { double x, xb; getXXbar(sqr(qtilde/d_Q_), z, xb, x); // see above in qWeightX. if (x < 0 || xb < 0) return 1.0; return qbarWeight(x, xb); } double SMWDecayer::PS(double x, double xbar) { double u = 0.5*(1. + d_rho_ / (1.-xbar+d_rho_)); double z = u + (x - (2.-xbar)*u)/sqrt(xbar*xbar - 4.*d_rho_); double brack = (1.+z*z)/(1.-z)- 2.*d_rho_/(1-xbar); // interesting: the splitting function without the subtraction // term. Actually gives a much worse approximation in the collinear // limit. double brack = (1.+z*z)/(1.-z); double den = (1.-xbar)*sqrt(xbar*xbar - 4.*d_rho_); return brack/den; } double SMWDecayer::matrixElementRatio(const Particle & inpart, const ParticleVector & decay2, const ParticleVector & decay3, MEOption) { // extract partons and LO momentas vector<cPDPtr> partons(1,inpart.dataPtr()); vector<Lorentz5Momentum> lomom(1,inpart.momentum()); for(unsigned int ix=0;ix<2;++ix) { partons.push_back(decay2[ix]->dataPtr()); lomom.push_back(decay2[ix]->momentum()); } vector<Lorentz5Momentum> realmom(1,inpart.momentum()); for(unsigned int ix=0;ix<3;++ix) { if(ix==2) partons.push_back(decay3[ix]->dataPtr()); realmom.push_back(decay3[ix]->momentum()); } if(partons[0]->id()<0) { swap(partons[1],partons[2]); swap(lomom[1],lomom[2]); swap(realmom[1],realmom[2]); } double lome = loME(partons,lomom); InvEnergy2 reme = realME(partons,realmom); - double ratio = CF_*reme/lome*sqr(inpart.mass()); + double ratio = CF_*reme/lome*sqr(inpart.mass())*4.*Constants::pi; return ratio; } double SMWDecayer::meRatio(vector<cPDPtr> partons, vector<Lorentz5Momentum> momenta, unsigned int iemitter, bool subtract) const { Lorentz5Momentum q = momenta[1]+momenta[2]+momenta[3]; Energy2 Q2=q.m2(); Energy2 lambda = sqrt((Q2-sqr(momenta[1].mass()+momenta[2].mass()))* (Q2-sqr(momenta[1].mass()-momenta[2].mass()))); InvEnergy2 D[2]; double lome(0.); for(unsigned int iemit=0;iemit<2;++iemit) { unsigned int ispect = iemit==0 ? 1 : 0; Energy2 pipj = momenta[3 ] * momenta[1+iemit ]; Energy2 pipk = momenta[3 ] * momenta[1+ispect]; Energy2 pjpk = momenta[1+iemit] * momenta[1+ispect]; double y = pipj/(pipj+pipk+pjpk); double z = pipk/( pipk+pjpk); Energy mij = sqrt(2.*pipj+sqr(momenta[1+iemit].mass())); Energy2 lamB = sqrt((Q2-sqr(mij+momenta[1+ispect].mass()))* (Q2-sqr(mij-momenta[1+ispect].mass()))); Energy2 Qpk = q*momenta[1+ispect]; Lorentz5Momentum pkt = lambda/lamB*(momenta[1+ispect]-Qpk/Q2*q) +0.5/Q2*(Q2+sqr(momenta[1+ispect].mass())-sqr(momenta[1+ispect].mass()))*q; Lorentz5Momentum pijt = q-pkt; double muj = momenta[1+iemit ].mass()/sqrt(Q2); double muk = momenta[1+ispect].mass()/sqrt(Q2); double vt = sqrt((1.-sqr(muj+muk))*(1.-sqr(muj-muk)))/(1.-sqr(muj)-sqr(muk)); double v = sqrt(sqr(2.*sqr(muk)+(1.-sqr(muj)-sqr(muk))*(1.-y))-4.*sqr(muk)) /(1.-y)/(1.-sqr(muj)-sqr(muk)); // dipole term D[iemit] = 0.5/pipj*(2./(1.-(1.-z)*(1.-y)) -vt/v*(2.-z+sqr(momenta[1+iemit].mass())/pipj)); // matrix element vector<Lorentz5Momentum> lomom(3); lomom[0] = momenta[0]; if(iemit==0) { lomom[1] = pijt; lomom[2] = pkt ; } else { lomom[2] = pijt; lomom[1] = pkt ; } if(iemit==0) lome = loME(partons,lomom); } InvEnergy2 ratio = realME(partons,momenta)/lome*abs(D[iemitter]) /(abs(D[0])+abs(D[1])); if(subtract) return Q2*(ratio-2.*D[iemitter]); else return Q2*ratio; } double SMWDecayer::loME(const vector<cPDPtr> & partons, const vector<Lorentz5Momentum> & momenta) const { // compute the spinors vector<VectorWaveFunction> vin; vector<SpinorWaveFunction> aout; vector<SpinorBarWaveFunction> fout; VectorWaveFunction win (momenta[0],partons[0],incoming); SpinorBarWaveFunction qkout(momenta[1],partons[1],outgoing); SpinorWaveFunction qbout(momenta[2],partons[2],outgoing); for(unsigned int ix=0;ix<2;++ix){ qkout.reset(ix); fout.push_back(qkout); qbout.reset(ix); aout.push_back(qbout); } for(unsigned int ix=0;ix<3;++ix){ win.reset(ix); vin.push_back(win); } // temporary storage of the different diagrams // sum over helicities to get the matrix element double total(0.); for(unsigned int inhel=0;inhel<3;++inhel) { for(unsigned int outhel1=0;outhel1<2;++outhel1) { for(unsigned int outhel2=0;outhel2<2;++outhel2) { Complex diag1 = FFWVertex()->evaluate(scale_,aout[outhel2],fout[outhel1],vin[inhel]); total += norm(diag1); } } } // return the answer return total; } InvEnergy2 SMWDecayer::realME(const vector<cPDPtr> & partons, const vector<Lorentz5Momentum> & momenta) const { // compute the spinors vector<VectorWaveFunction> vin; vector<SpinorWaveFunction> aout; vector<SpinorBarWaveFunction> fout; vector<VectorWaveFunction> gout; VectorWaveFunction win (momenta[0],partons[0],incoming); SpinorBarWaveFunction qkout(momenta[1],partons[1],outgoing); SpinorWaveFunction qbout(momenta[2],partons[2],outgoing); VectorWaveFunction gluon(momenta[3],partons[3],outgoing); for(unsigned int ix=0;ix<2;++ix){ qkout.reset(ix); fout.push_back(qkout); qbout.reset(ix); aout.push_back(qbout); gluon.reset(2*ix); gout.push_back(gluon); } for(unsigned int ix=0;ix<3;++ix){ win.reset(ix); vin.push_back(win); } vector<Complex> diag(2,0.); double total(0.); for(unsigned int inhel1=0;inhel1<3;++inhel1) { for(unsigned int outhel1=0;outhel1<2;++outhel1) { for(unsigned int outhel2=0;outhel2<2;++outhel2) { for(unsigned int outhel3=0;outhel3<2;++outhel3) { SpinorBarWaveFunction off1 = FFGVertex()->evaluate(scale_,3,partons[1],fout[outhel1],gout[outhel3]); diag[0] = FFWVertex()->evaluate(scale_,aout[outhel2],off1,vin[inhel1]); SpinorWaveFunction off2 = FFGVertex()->evaluate(scale_,3,partons[2],aout[outhel2],gout[outhel3]); diag[1] = FFWVertex()->evaluate(scale_,off2,fout[outhel1],vin[inhel1]); // sum of diagrams Complex sum = std::accumulate(diag.begin(),diag.end(),Complex(0.)); // me2 total += norm(sum); } } } } // divide out the coupling total /= norm(FFGVertex()->norm()); // return the total return total*UnitRemoval::InvE2; } double SMWDecayer::calculateRealEmission(double x1, double x2, vector<PPtr> hardProcess, double phi, double muj, double muk, int iemit, bool subtract) const { // make partons data object for meRatio vector<cPDPtr> partons (3); for(int ix=0; ix<3; ++ix) partons[ix] = hardProcess[ix]->dataPtr(); partons.push_back(gluon_); // calculate x3 double x3 = 2.-x1-x2; double xT = sqrt(max(0.,sqr(x3)-0.25*sqr(sqr(x2)+sqr(x3)-sqr(x1)-4.*sqr(muk)+4.*sqr(muj)) /(sqr(x2)-4.*sqr(muk)))); // calculate the momenta Energy M = mW_; Lorentz5Momentum pspect(ZERO,ZERO,-0.5*M*sqrt(max(sqr(x2)-4.*sqr(muk),0.)), 0.5*M*x2,M*muk); Lorentz5Momentum pemit (-0.5*M*xT*cos(phi),-0.5*M*xT*sin(phi), 0.5*M*sqrt(max(sqr(x1)-sqr(xT)-4.*sqr(muj),0.)), 0.5*M*x1,M*muj); Lorentz5Momentum pgluon(0.5*M*xT*cos(phi), 0.5*M*xT*sin(phi), 0.5*M*sqrt(max(sqr(x3)-sqr(xT),0.)),0.5*M*x3,ZERO); if(abs(pspect.z()+pemit.z()-pgluon.z())/M<1e-6) pgluon.setZ(-pgluon.z()); else if(abs(pspect.z()-pemit.z()+pgluon.z())/M<1e-6) pemit .setZ(- pemit.z()); // boost and rotate momenta LorentzRotation eventFrame( ( hardProcess[1]->momentum() + hardProcess[2]->momentum() ).findBoostToCM() ); Lorentz5Momentum spectator = eventFrame*hardProcess[iemit+1]->momentum(); eventFrame.rotateZ( -spectator.phi() ); eventFrame.rotateY( -spectator.theta() ); eventFrame.invert(); vector<Lorentz5Momentum> momenta(3); momenta[0] = hardProcess[0]->momentum(); if(iemit==0) { momenta[2] = eventFrame*pspect; momenta[1] = eventFrame*pemit ; } else { momenta[1] = eventFrame*pspect; momenta[2] = eventFrame*pemit ; } momenta.push_back(eventFrame*pgluon); // calculate the weight double realwgt(0.); if(1.-x1>1e-5 && 1.-x2>1e-5) realwgt = meRatio(partons,momenta,iemit,subtract); return realwgt; } diff --git a/Decay/Perturbative/SMZDecayer.cc b/Decay/Perturbative/SMZDecayer.cc --- a/Decay/Perturbative/SMZDecayer.cc +++ b/Decay/Perturbative/SMZDecayer.cc @@ -1,1331 +1,1331 @@ // -*- C++ -*- // // SMZDecayer.cc is a part of Herwig - A multi-purpose Monte Carlo event generator // Copyright (C) 2002-2017 The Herwig Collaboration // // Herwig is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // // // This is the implementation of the non-inlined, non-templated member // functions of the SMZDecayer class. // #include "SMZDecayer.h" #include "Herwig/Utilities/Maths.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/ParVector.h" #include "ThePEG/Interface/Reference.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/PDT/DecayMode.h" #include "Herwig/Decay/DecayVertex.h" #include "ThePEG/Helicity/VectorSpinInfo.h" #include "ThePEG/Helicity/FermionSpinInfo.h" #include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h" #include "Herwig/Models/StandardModel/StandardModel.h" #include "Herwig/Shower/Core/Base/ShowerProgenitor.h" #include "Herwig/Shower/Core/Base/ShowerParticle.h" #include "Herwig/Shower/Core/Base/Branching.h" #include "Herwig/Shower/RealEmissionProcess.h" #include "Herwig/Decay/GeneralDecayMatrixElement.h" #include <numeric> using namespace Herwig; using namespace ThePEG::Helicity; const double SMZDecayer::EPS_=0.00000001; SMZDecayer::SMZDecayer() : quarkWeight_(5,0.), leptonWeight_(6,0.), CF_(4./3.), NLO_(false) { quarkWeight_[0] = 0.488029; quarkWeight_[1] = 0.378461; quarkWeight_[2] = 0.488019; quarkWeight_[3] = 0.378027; quarkWeight_[4] = 0.483207; leptonWeight_[0] = 0.110709; leptonWeight_[1] = 0.220276; leptonWeight_[2] = 0.110708; leptonWeight_[3] = 0.220276; leptonWeight_[4] = 0.110458; leptonWeight_[5] = 0.220276; // intermediates generateIntermediates(false); // QED corrections hasRealEmissionME(true); hasOneLoopME(true); } void SMZDecayer::doinit() { PerturbativeDecayer::doinit(); // get the vertices from the Standard Model object tcHwSMPtr hwsm=dynamic_ptr_cast<tcHwSMPtr>(standardModel()); if(!hwsm) throw InitException() << "Must have Herwig StandardModel object in" << "SMZDecayer::doinit()" << Exception::runerror; // cast the vertices FFZVertex_ = dynamic_ptr_cast<FFVVertexPtr>(hwsm->vertexFFZ()); FFZVertex_->init(); FFGVertex_ = hwsm->vertexFFG(); FFGVertex_->init(); FFPVertex_ = hwsm->vertexFFP(); FFPVertex_->init(); gluon_ = getParticleData(ParticleID::g); // now set up the decay modes DecayPhaseSpaceModePtr mode; tPDVector extpart(3); vector<double> wgt(0); // the Z decay modes extpart[0]=getParticleData(ParticleID::Z0); // loop over the quarks and the leptons for(int istep=0;istep<11;istep+=10) { for(int ix=1;ix<7;++ix) { int iy=istep+ix; if(iy==6) continue; extpart[1] = getParticleData(-iy); extpart[2] = getParticleData( iy); mode = new_ptr(DecayPhaseSpaceMode(extpart,this)); if(iy<=6) addMode(mode, quarkWeight_.at(ix-1),wgt); else addMode(mode,leptonWeight_.at(iy-11),wgt); } } } int SMZDecayer::modeNumber(bool & cc,tcPDPtr parent, const tPDVector & children) const { int imode(-1); if(children.size()!=2) return imode; int id0=parent->id(); tPDVector::const_iterator pit = children.begin(); int id1=(**pit).id(); ++pit; int id2=(**pit).id(); // Z to quarks or leptons cc =false; if(id0!=ParticleID::Z0) return imode; if(abs(id1)<6&&id1==-id2) { imode=abs(id1)-1; } else if(abs(id1)>=11&&abs(id1)<=16&&id1==-id2) { imode=abs(id1)-6; } cc = false; return imode; } void SMZDecayer::persistentOutput(PersistentOStream & os) const { os << FFZVertex_ << FFPVertex_ << FFGVertex_ << quarkWeight_ << leptonWeight_ << NLO_ << gluon_; } void SMZDecayer::persistentInput(PersistentIStream & is, int) { is >> FFZVertex_ >> FFPVertex_ >> FFGVertex_ >> quarkWeight_ >> leptonWeight_ >> NLO_ >> gluon_; } // The following static variable is needed for the type // description system in ThePEG. DescribeClass<SMZDecayer,PerturbativeDecayer> describeHerwigSMZDecayer("Herwig::SMZDecayer", "HwPerturbativeDecay.so"); void SMZDecayer::Init() { static ClassDocumentation<SMZDecayer> documentation ("The SMZDecayer class is the implementation of the decay" " Z boson to the Standard Model fermions."); static ParVector<SMZDecayer,double> interfaceZquarkMax ("QuarkMax", "The maximum weight for the decay of the Z to quarks", &SMZDecayer::quarkWeight_, 0, 0, 0, -10000, 10000, false, false, true); static ParVector<SMZDecayer,double> interfaceZleptonMax ("LeptonMax", "The maximum weight for the decay of the Z to leptons", &SMZDecayer::leptonWeight_, 0, 0, 0, -10000, 10000, false, false, true); static Switch<SMZDecayer,bool> interfaceNLO ("NLO", "Whether to return the LO or NLO result", &SMZDecayer::NLO_, false, false, false); static SwitchOption interfaceNLOLO (interfaceNLO, "No", "Leading-order result", false); static SwitchOption interfaceNLONLO (interfaceNLO, "Yes", "NLO result", true); } // return the matrix element squared double SMZDecayer::me2(const int, const Particle & part, const ParticleVector & decay, MEOption meopt) const { if(!ME()) ME(new_ptr(GeneralDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half))); int iferm(1),ianti(0); if(decay[0]->id()>0) swap(iferm,ianti); if(meopt==Initialize) { VectorWaveFunction::calculateWaveFunctions(_vectors,_rho, const_ptr_cast<tPPtr>(&part), incoming,false); } if(meopt==Terminate) { VectorWaveFunction::constructSpinInfo(_vectors,const_ptr_cast<tPPtr>(&part), incoming,true,false); SpinorBarWaveFunction:: constructSpinInfo(_wavebar,decay[iferm],outgoing,true); SpinorWaveFunction:: constructSpinInfo(_wave ,decay[ianti],outgoing,true); return 0.; } SpinorBarWaveFunction:: calculateWaveFunctions(_wavebar,decay[iferm],outgoing); SpinorWaveFunction:: calculateWaveFunctions(_wave ,decay[ianti],outgoing); // compute the matrix element Energy2 scale(sqr(part.mass())); unsigned int ifm,ia,vhel; for(ifm=0;ifm<2;++ifm) { for(ia=0;ia<2;++ia) { for(vhel=0;vhel<3;++vhel) { if(iferm>ianti) (*ME())(vhel,ia,ifm)= FFZVertex_->evaluate(scale,_wave[ia],_wavebar[ifm],_vectors[vhel]); else (*ME())(vhel,ifm,ia)= FFZVertex_->evaluate(scale,_wave[ia],_wavebar[ifm],_vectors[vhel]); } } } double output=(ME()->contract(_rho)).real()*UnitRemoval::E2/scale; if(abs(decay[0]->id())<=6) output*=3.; if(decay[0]->hasColour()) decay[0]->antiColourNeighbour(decay[1]); else if(decay[1]->hasColour()) decay[1]->antiColourNeighbour(decay[0]); // if LO return if(!NLO_) return output; // check decay products coloured, otherwise return if(!decay[0]->dataPtr()->coloured()) return output; // inital masses, couplings etc // fermion mass Energy particleMass = decay[0]->dataPtr()->mass(); // Z mass mZ_ = part.mass(); // strong coupling aS_ = SM().alphaS(sqr(mZ_)); // reduced mass mu_ = particleMass/mZ_; mu2_ = sqr(mu_); // scale scale_ = sqr(mZ_); // compute the spinors vector<SpinorWaveFunction> aout; vector<SpinorBarWaveFunction> fout; vector<VectorWaveFunction> vin; SpinorBarWaveFunction qkout(decay[0]->momentum(),decay[0]->dataPtr(),outgoing); SpinorWaveFunction qbout(decay[1]->momentum(),decay[1]->dataPtr(),outgoing); VectorWaveFunction zin (part.momentum() ,part.dataPtr() ,incoming); for(unsigned int ix=0;ix<2;++ix){ qkout.reset(ix); fout.push_back(qkout); qbout.reset(ix); aout.push_back(qbout); } for(unsigned int ix=0;ix<3;++ix){ zin.reset(ix); vin.push_back(zin); } // temporary storage of the different diagrams // sum over helicities to get the matrix element double total=0.; if(mu_!=0.) { LorentzPolarizationVector momDiff = (decay[0]->momentum()-decay[1]->momentum())/2./ (decay[0]->momentum().mass()+decay[1]->momentum().mass()); // scalars Complex scalar1 = zin.wave().dot(momDiff); for(unsigned int outhel1=0;outhel1<2;++outhel1) { for(unsigned int outhel2=0;outhel2<2;++outhel2) { for(unsigned int inhel=0;inhel<3;++inhel) { // first the LO bit Complex diag1 = FFZVertex_->evaluate(scale_,aout[outhel2], fout[outhel1],vin[inhel]); // extra stuff for NLO LorentzPolarizationVector left = aout[outhel2].wave().leftCurrent(fout[outhel1].wave()); LorentzPolarizationVector right = aout[outhel2].wave().rightCurrent(fout[outhel1].wave()); Complex scalar = aout[outhel2].wave().scalar(fout[outhel1].wave()); // nlo specific pieces Complex diag3 = Complex(0.,1.)*FFZVertex_->norm()* (FFZVertex_->right()*( left.dot(zin.wave())) + FFZVertex_-> left()*(right.dot(zin.wave())) - ( FFZVertex_-> left()+FFZVertex_->right())*scalar1*scalar); // nlo piece total += real(diag1*conj(diag3) + diag3*conj(diag1)); } } } // rescale total *= UnitRemoval::E2/scale_; } else { total = ZERO; } // now for the NLO bit double mu4 = sqr(mu2_); double lmu = mu_!=0. ? log(mu_) : 0.; double v = sqrt(1.-4.*mu2_),v2(sqr(v)); double omv = 4.*mu2_/(1.+v); double f1,f2,fNS,VNS; double r = omv/(1.+v); double lr = mu_!=0. ? log(r) : 0.; // normal form if(mu_>1e-4) { f1 = CF_*aS_/Constants::pi* ( +1. + 3.*log(0.5*(1.+v)) - 1.5*log(0.5*(1.+v2)) + sqr(Constants::pi)/6. - 0.5*sqr(lr) - (1.+v2)/v*(lr*log(1.+v2) + sqr(Constants::pi)/12. -0.5*log(4.*mu2_)*lr + 0.25*sqr(lr))); fNS = -0.5*(1.+2.*v2)*lr/v + 1.5*lr - 2./3.*sqr(Constants::pi) + 0.5*sqr(lr) + (1.+v2)/v*(Herwig::Math::ReLi2(r) + sqr(Constants::pi)/3. - 0.25*sqr(lr) + lr*log((2.*v/ (1.+v)))); VNS = 1.5*log(0.5*(1.+v2)) + 0.5*(1.+v2)/v*( 2.*lr*log(2.*(1.+v2)/sqr(1.+v)) + 2.*Herwig::Math::ReLi2(sqr(r)) - 2.*Herwig::Math::ReLi2(2.*v/(1.+v)) - sqr(Constants::pi)/6.) + log(1.-mu_) - 2.*log(1.-2.*mu_) - 4.*mu2_/(1.+v2)*log(mu_/(1.-mu_)) - mu_/(1.-mu_) + 4.*(2.*mu2_-mu_)/(1.+v2) + 0.5*sqr(Constants::pi); f2 = CF_*aS_/Constants::pi*mu2_*lr/v; } // small mass limit else { f1 = -CF_*aS_/Constants::pi/6.* ( - 6. - 24.*lmu*mu2_ - 15.*mu4 - 12.*mu4*lmu - 24.*mu4*sqr(lmu) + 2.*mu4*sqr(Constants::pi) - 12.*mu2_*mu4 - 96.*mu2_*mu4*sqr(lmu) + 8.*mu2_*mu4*sqr(Constants::pi) - 80.*mu2_*mu4*lmu); fNS = - mu2_/18.*( + 36.*lmu - 36. - 45.*mu2_ + 216.*lmu*mu2_ - 24.*mu2_*sqr(Constants::pi) + 72.*mu2_*sqr(lmu) - 22.*mu4 + 1032.*mu4 * lmu - 96.*mu4*sqr(Constants::pi) + 288.*mu4*sqr(lmu)); VNS = - mu2_/1260.*(-6930. + 7560.*lmu + 2520.*mu_ - 16695.*mu2_ + 1260.*mu2_*sqr(Constants::pi) + 12600.*lmu*mu2_ + 1344.*mu_*mu2_ - 52780.*mu4 + 36960.*mu4*lmu + 5040.*mu4*sqr(Constants::pi) - 12216.*mu_*mu4); f2 = CF_*aS_*mu2_/Constants::pi*( 2.*lmu + 4.*mu2_*lmu + 2.*mu2_ + 12.*mu4*lmu + 7.*mu4); } // add up bits for f1 f1 += CF_*aS_/Constants::pi*(fNS+VNS); double realFact(0.); for(int iemit=0;iemit<2;++iemit) { // now for the real correction double phi = UseRandom::rnd()*Constants::twopi; // calculate y double yminus = 0.; double yplus = 1.-2.*mu_*(1.-mu_)/(1.-2*mu2_); double y = yminus + UseRandom::rnd()*(yplus-yminus); // calculate z double v1 = sqrt(sqr(2.*mu2_+(1.-2.*mu2_)*(1.-y))-4.*mu2_)/(1.-2.*mu2_)/(1.-y); double zplus = (1.+v1)*(1.-2.*mu2_)*y/2./(mu2_ +(1.-2.*mu2_)*y); double zminus = (1.-v1)*(1.-2.*mu2_)*y/2./(mu2_ +(1.-2.*mu2_)*y); double z = zminus + UseRandom::rnd()*(zplus-zminus); double jac = (1.-y)*(yplus-yminus)*(zplus-zminus); // calculate x1,x2 double x2 = 1. - y*(1.-2.*mu2_); double x1 = 1. - z*(x2-2.*mu2_); // copy the particle objects over for calculateRealEmission vector<PPtr> hardProcess(3); hardProcess[0] = const_ptr_cast<PPtr>(&part); hardProcess[1] = decay[0]; hardProcess[2] = decay[1]; // total real emission contribution realFact += 0.25*jac*sqr(1.-2.*mu2_)/ sqrt(1.-4.*mu2_)/Constants::twopi *2.*CF_*aS_*calculateRealEmission(x1, x2, hardProcess, phi, iemit, true); } // the born + virtual + real output = output*(1. + f1 + realFact) + f2*total; return output; } void SMZDecayer::doinitrun() { PerturbativeDecayer::doinitrun(); if(initialize()) { for(unsigned int ix=0;ix<numberModes();++ix) { if(ix<5) quarkWeight_ [ix ]=mode(ix)->maxWeight(); else if(ix<11) leptonWeight_[ix-5 ]=mode(ix)->maxWeight(); } } } void SMZDecayer::dataBaseOutput(ofstream & output, bool header) const { if(header) output << "update decayers set parameters=\""; for(unsigned int ix=0;ix<quarkWeight_.size();++ix) { output << "newdef " << name() << ":QuarkMax " << ix << " " << quarkWeight_[ix] << "\n"; } for(unsigned int ix=0;ix<leptonWeight_.size();++ix) { output << "newdef " << name() << ":LeptonMax " << ix << " " << leptonWeight_[ix] << "\n"; } // parameters for the PerturbativeDecayer base class PerturbativeDecayer::dataBaseOutput(output,false); if(header) output << "\n\" where BINARY ThePEGName=\"" << fullName() << "\";" << endl; } InvEnergy2 SMZDecayer:: realEmissionME(unsigned int,const Particle &parent, ParticleVector &children, unsigned int iemitter, double ctheta, double stheta, const LorentzRotation & rot1, const LorentzRotation & rot2) { // check the number of products and parent assert(children.size()==3 && parent.id()==ParticleID::Z0); // the electric charge double e = sqrt(SM().alphaEM()*4.*Constants::pi); // azimuth of the photon double phi = children[2]->momentum().phi(); // wavefunctions for the decaying particle in the rotated dipole frame vector<VectorWaveFunction> vec1 = _vectors; for(unsigned int ix=0;ix<vec1.size();++ix) { vec1[ix].transform(rot1); vec1[ix].transform(rot2); } // wavefunctions for the decaying particle in the rotated rest frame vector<VectorWaveFunction> vec2 = _vectors; for(unsigned int ix=0;ix<vec1.size();++ix) { vec2[ix].transform(rot2); } // find the outgoing particle and antiparticle unsigned int iferm(0),ianti(1); if(children[iferm]->id()<0) swap(iferm,ianti); // wavefunctions for the particles before the radiation // wavefunctions for the outgoing fermion SpinorBarWaveFunction wavebartemp; Lorentz5Momentum ptemp = - _wavebar[0].momentum(); ptemp *= rot2; if(ptemp.perp()/ptemp.e()<1e-10) { ptemp.setX(ZERO); ptemp.setY(ZERO); } wavebartemp = SpinorBarWaveFunction(ptemp,_wavebar[0].particle(),outgoing); // wavefunctions for the outgoing antifermion SpinorWaveFunction wavetemp; ptemp = - _wave[0].momentum(); ptemp *= rot2; if(ptemp.perp()/ptemp.e()<1e-10) { ptemp.setX(ZERO); ptemp.setY(ZERO); } wavetemp = SpinorWaveFunction(ptemp,_wave[0].particle(),outgoing); // loop over helicities vector<SpinorWaveFunction> wf_old; vector<SpinorBarWaveFunction> wfb_old; for(unsigned int ihel=0;ihel<2;++ihel) { wavetemp.reset(ihel); wf_old.push_back(wavetemp); wavebartemp.reset(ihel); wfb_old.push_back(wavebartemp); } // calculate the wave functions for the new fermions // ensure the momenta have pT=0 for(unsigned int ix=0;ix<2;++ix) { Lorentz5Momentum ptemp = children[ix]->momentum(); if(ptemp.perp()/ptemp.e()<1e-10) { ptemp.setX(ZERO); ptemp.setY(ZERO); children[ix]->set5Momentum(ptemp); } } // calculate the wavefunctions vector<SpinorBarWaveFunction> wfb; SpinorBarWaveFunction::calculateWaveFunctions(wfb,children[iferm],outgoing); vector<SpinorWaveFunction> wf; SpinorWaveFunction::calculateWaveFunctions (wf ,children[ianti],outgoing); // wave functions for the photons vector<VectorWaveFunction> photon; VectorWaveFunction::calculateWaveFunctions(photon,children[2],outgoing,true); // loop to calculate the matrix elements Complex lome[3][2][2],diffme[3][2][2][2],summe[3][2][2][2]; Energy2 scale(sqr(parent.mass())); Complex diff[2]={0.,0.}; Complex sum [2]={0.,0.}; for(unsigned int ifm=0;ifm<2;++ifm) { for(unsigned int ia=0;ia<2;++ia) { for(unsigned int vhel=0;vhel<3;++vhel) { // calculation of the leading-order matrix element Complex loamp = FFZVertex_->evaluate(scale,wf_old[ia], wfb_old[ifm],vec2[vhel]); Complex lotemp = FFZVertex_->evaluate(scale,wf[ia], wfb[ifm],vec1[vhel]); if(iferm>ianti) lome[vhel][ia][ifm] = loamp; else lome[vhel][ifm][ia] = loamp; // photon loop for the real emmision terms for(unsigned int phel=0;phel<2;++phel) { // radiation from the antifermion // normal case with small angle treatment if(children[2 ]->momentum().z()/ children[iferm]->momentum().z()>=ZERO && iemitter == iferm ) { Complex dipole = e*double(children[iferm]->dataPtr()->iCharge())/3.* UnitRemoval::E*loamp* (children[iferm]->momentum()*photon[2*phel].wave())/ (children[iferm]->momentum()*children[2]->momentum()); // sum and difference SpinorBarWaveFunction foff = FFPVertex_->evaluateSmall(ZERO,3,children[iferm]->dataPtr()->CC(), wfb[ifm],photon[2*phel], ifm,2*phel,ctheta,phi,stheta,false); diff[0] = FFZVertex_->evaluate(scale,wf[ia],foff,vec1[vhel]) + e*double(children[iferm]->dataPtr()->iCharge())/3.* UnitRemoval::E*(lotemp-loamp)* (children[iferm]->momentum()*photon[2*phel].wave())/ (children[iferm]->momentum()*children[2]->momentum()); sum [0] = diff[0]+2.*dipole; } // special if fermion backwards else { SpinorBarWaveFunction foff = FFPVertex_->evaluate(ZERO,3,children[iferm]->dataPtr()->CC(), wfb[ifm],photon[2*phel]); Complex diag = FFZVertex_->evaluate(scale,wf[ia],foff,vec1[vhel]); Complex dipole = e*double(children[iferm]->dataPtr()->iCharge())/3.* UnitRemoval::E*loamp* (children[iferm]->momentum()*photon[2*phel].wave())/ (children[iferm]->momentum()*children[2]->momentum()); diff[0] = diag-dipole; sum [0] = diag+dipole; } // radiation from the anti fermion // small angle case in general if(children[2 ]->momentum().z()/ children[ianti]->momentum().z()>=ZERO && iemitter == ianti ) { Complex dipole = e*double(children[ianti]->dataPtr()->iCharge())/3.* UnitRemoval::E*loamp* (children[ianti]->momentum()*photon[2*phel].wave())/ (children[ianti]->momentum()*children[2]->momentum()); // sum and difference SpinorWaveFunction foff = FFPVertex_->evaluateSmall(ZERO,3,children[ianti]->dataPtr()->CC(), wf[ia],photon[2*phel], ia,2*phel,ctheta,phi,stheta,false); diff[1] = FFZVertex_->evaluate(scale,foff ,wfb[ifm],vec1[vhel]) + e*double(children[ianti]->dataPtr()->iCharge())/3.* UnitRemoval::E*(lotemp-loamp)* (children[ianti]->momentum()*photon[2*phel].wave())/ (children[ianti]->momentum()*children[2]->momentum()); sum [1] = diff[1]+2.*dipole; } // special if fermion backwards after radiation else { SpinorWaveFunction foff = FFPVertex_->evaluate(ZERO,3,children[ianti]->dataPtr()->CC(), wf[ia],photon[2*phel]); Complex diag = FFZVertex_->evaluate(scale,foff ,wfb[ifm],vec1[vhel]); Complex dipole = e*double(children[ianti]->dataPtr()->iCharge())/3.* UnitRemoval::E*loamp* (children[ianti]->momentum()*photon[2*phel].wave())/ (children[ianti]->momentum()*children[2]->momentum()); // sum and difference diff[1] = diag - dipole; sum [1] = diag + dipole; } // add to me if(iferm>ianti) { diffme[vhel][ia][ifm][phel] = diff[0] + diff[1]; summe [vhel][ia][ifm][phel] = sum[0] + sum[1] ; } else { diffme [vhel][ifm][ia][phel] = diff[0] + diff[1]; summe [vhel][ifm][ia][phel] = sum[0] + sum[1] ; } } } } } // cerr << parent << "\n"; // for(unsigned int ix=0;ix<children.size();++ix) { // cerr << *children[ix] << "\n"; // } // _rho = RhoDMatrix(PDT::Spin1); Complex lo(0.),difference(0.); for(unsigned int vhel1=0;vhel1<3;++vhel1) { for(unsigned int vhel2=0;vhel2<3;++vhel2) { for(unsigned int ifm=0;ifm<2;++ifm) { for(unsigned int ia=0;ia<2;++ia) { lo += _rho(vhel1,vhel2)*lome[vhel1][ifm][ia]*conj(lome[vhel2][ifm][ia]); for(unsigned int phel=0;phel<2;++phel) { difference += _rho(vhel1,vhel2)*diffme[vhel1][ifm][ia][phel]*conj(summe[vhel2][ifm][ia][phel]); } } } } } // // analytic result // double iCharge = children[0]->dataPtr()->iCharge()* // children[1]->dataPtr()->iCharge()/9.; // Energy2 ubar = 2.*children[0]->momentum()*children[2]->momentum(); // Energy2 tbar = 2.*children[1]->momentum()*children[2]->momentum(); // double mu2 = sqr(children[1]->mass()/parent.mass()); // double gL = (FFZVertex_->left() *FFZVertex_->norm()).real(); // double gR = (FFZVertex_->right()*FFZVertex_->norm()).real(); // Energy2 den = sqr(parent.mass())*(((sqr(gL)+sqr(gR))*(1-mu2)+6.*mu2*gL*gR)); // InvEnergy2 anal = -iCharge*( 2.*(ubar/tbar+tbar/ubar)/sqr(parent.mass())+ // 4.*mu2/den*((sqr(gL)+sqr(gR))*(1+ubar/tbar+tbar/ubar) // -2.*gL*gR*(1.+2.*(ubar/tbar+tbar/ubar)))); // cerr << "testing ratio " << parent.PDGName() // << " " << difference.real()/sqr(e)/lo.real()*UnitRemoval::InvE2/(anal) << "\n" // << stheta << " " << ctheta << "\n"; return difference.real()/sqr(e)/lo.real()*UnitRemoval::InvE2; } double SMZDecayer::oneLoopVirtualME(unsigned int, const Particle & parent, const ParticleVector & children) { assert(children.size()==2); // velocities of the particles double beta = sqrt(1.-4.*sqr(children[0]->mass()/parent.mass())); double opb = 1.+beta; double omb = 4.*sqr(children[0]->mass()/parent.mass())/opb; // couplings double gL = (FFZVertex_->left() *FFZVertex_->norm()).real(); double gR = (FFZVertex_->right()*FFZVertex_->norm()).real(); double gA = 0.5*(gL-gR); double gV = 0.5*(gL+gR); // correction terms double ln = log(omb/opb); double f1 = 1. + ln*beta; double fA = 1. + ln/beta; InvEnergy f2 = 0.5*sqrt(omb*opb)/parent.mass()/beta*ln; // momentum difference for the loop Lorentz5Momentum q = children[0]->momentum()-children[1]->momentum(); if(children[0]->id()<0) q *= -1.; // spinors vector<LorentzSpinor <SqrtEnergy> > sp; vector<LorentzSpinorBar<SqrtEnergy> > sbar; for(unsigned int ix=0;ix<2;++ix) { sp .push_back( _wave[ix].dimensionedWave()); sbar.push_back(_wavebar[ix].dimensionedWave()); } // polarization vectors vector<LorentzPolarizationVector> pol; for(unsigned int ix=0;ix<3;++ix) pol.push_back(_vectors[ix].wave()); // matrix elements complex<Energy> lome[3][2][2],loopme[3][2][2]; for(unsigned int vhel=0;vhel<3;++vhel) { for(unsigned int ihel1=0;ihel1<2;++ihel1) { for(unsigned int ihel2=0;ihel2<2;++ihel2) { complex<Energy> vector = sp[ihel1].generalCurrent(sbar[ihel2], 1.,1.).dot(pol[vhel]); complex<Energy> axial = sp[ihel1].generalCurrent(sbar[ihel2],-1.,1.).dot(pol[vhel]); complex<Energy2> scalar = sp[ihel1].scalar(sbar[ihel2])*(q*pol[vhel]); lome [vhel][ihel1][ihel2] = gV* vector-gA* axial; loopme[vhel][ihel1][ihel2] = gV*f1*vector-gA*fA*axial+scalar*f2*gV; } } } // sum sums complex<Energy2> den(ZERO),num(ZERO); for(unsigned int vhel1=0;vhel1<3;++vhel1) { for(unsigned int vhel2=0;vhel2<3;++vhel2) { for(unsigned int ihel1=0;ihel1<2;++ihel1) { for(unsigned int ihel2=0;ihel2<2;++ihel2) { num += _rho(vhel1,vhel2)* ( lome[vhel1][ihel1][ihel2]*conj(loopme[vhel2][ihel1][ihel2])+ loopme[vhel1][ihel1][ihel2]*conj( lome[vhel2][ihel1][ihel2])); den += _rho(vhel1,vhel2)* lome[vhel1][ihel1][ihel2]*conj(lome[vhel2][ihel1][ihel2]); } } } } // prefactor double iCharge = children[0]->dataPtr()->iCharge()* children[1]->dataPtr()->iCharge()/9.; double pre = 0.5*SM().alphaEM()*iCharge/Constants::pi; // output return pre*num.real()/den.real(); } void SMZDecayer:: initializeMECorrection(RealEmissionProcessPtr born, double & initial, double & final) { // get the quark and antiquark ParticleVector qq; for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) qq.push_back(born->bornOutgoing()[ix]); // ensure quark first if(qq[0]->id()<0) swap(qq[0],qq[1]); // centre of mass energy d_Q_ = (qq[0]->momentum() + qq[1]->momentum()).m(); // quark mass d_m_ = 0.5*(qq[0]->momentum().m()+qq[1]->momentum().m()); // set the other parameters setRho(sqr(d_m_/d_Q_)); setKtildeSymm(); // otherwise can do it initial=1.; final =1.; } RealEmissionProcessPtr SMZDecayer:: applyHardMatrixElementCorrection(RealEmissionProcessPtr born) { // get the quark and antiquark ParticleVector qq; for(unsigned int ix=0;ix<born->bornOutgoing().size();++ix) qq.push_back(born->bornOutgoing()[ix]); if(!qq[0]->dataPtr()->coloured()) return RealEmissionProcessPtr(); // ensure quark first bool order = qq[0]->id()<0; if(order) swap(qq[0],qq[1]); // get the momenta vector<Lorentz5Momentum> newfs = applyHard(qq); // return if no emission if(newfs.size()!=3) return RealEmissionProcessPtr(); // perform final check to ensure energy greater than constituent mass for (int i=0; i<2; i++) { if (newfs[i].e() < qq[i]->data().constituentMass()) return RealEmissionProcessPtr(); } if (newfs[2].e() < getParticleData(ParticleID::g)->constituentMass()) return RealEmissionProcessPtr(); // set masses for (int i=0; i<2; i++) newfs[i].setMass(qq[i]->mass()); newfs[2].setMass(ZERO); // decide which particle emits bool firstEmits= newfs[2].vect().perp2(newfs[0].vect())< newfs[2].vect().perp2(newfs[1].vect()); // create the new quark, antiquark and gluon PPtr newg = getParticleData(ParticleID::g)->produceParticle(newfs[2]); PPtr newq = qq[0]->dataPtr()->produceParticle(newfs[0]); PPtr newa = qq[1]->dataPtr()->produceParticle(newfs[1]); // create the output real emission process for(unsigned int ix=0;ix<born->bornIncoming().size();++ix) { born->incoming().push_back(born->bornIncoming()[ix]); } if(!order) { born->outgoing().push_back(newq); born->outgoing().push_back(newa); born->outgoing().push_back(newg); } else { born->outgoing().push_back(newa); born->outgoing().push_back(newq); born->outgoing().push_back(newg); firstEmits = !firstEmits; } // make colour connections newg->colourNeighbour(newq); newa->colourNeighbour(newg); if(firstEmits) { born->emitter(1); born->spectator(2); } else { born->emitter(2); born->spectator(1); } born->emitted(3); born->interaction(ShowerInteraction::QCD); return born; } vector<Lorentz5Momentum> SMZDecayer:: applyHard(const ParticleVector &p) { double x, xbar; vector<Lorentz5Momentum> fs; // return if no emission if (getHard(x, xbar) < UseRandom::rnd() || p.size() != 2) return fs; // centre of mass energy Lorentz5Momentum pcm = p[0]->momentum() + p[1]->momentum(); // momenta of quark,antiquark and gluon Lorentz5Momentum pq, pa, pg; if (p[0]->id() > 0) { pq = p[0]->momentum(); pa = p[1]->momentum(); } else { pa = p[0]->momentum(); pq = p[1]->momentum(); } // boost to boson rest frame Boost beta = (pcm.findBoostToCM()); pq.boost(beta); pa.boost(beta); // return if fails ????? double xg = 2.-x-xbar; if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return fs; Axis u1, u2, u3; // moduli of momenta in units of Q and cos theta // stick to q direction? // p1 is the one that is kept, p2 is the other fermion, p3 the gluon. Energy e1, e2, e3; Energy pp1, pp2, pp3; bool keepq = true; if (UseRandom::rnd() > sqr(x)/(sqr(x)+sqr(xbar))) keepq = false; if (keepq) { pp1 = d_Q_*sqrt(sqr(x)-4.*d_rho_)/2.; pp2 = d_Q_*sqrt(sqr(xbar)-4.*d_rho_)/2.; e1 = d_Q_*x/2.; e2 = d_Q_*xbar/2.; u1 = pq.vect().unit(); } else { pp2 = d_Q_*sqrt(sqr(x)-4.*d_rho_)/2.; pp1 = d_Q_*sqrt(sqr(xbar)-4.*d_rho_)/2.; e2 = d_Q_*x/2.; e1 = d_Q_*xbar/2.; u1 = pa.vect().unit(); } pp3 = d_Q_*xg/2.; e3 = pp3; u2 = u1.orthogonal(); u2 /= u2.mag(); u3 = u1.cross(u2); u3 /= u3.mag(); double ct2=-2., ct3=-2.; if (pp1 == ZERO || pp2 == ZERO || pp3 == ZERO) { bool touched = false; if (pp1 == ZERO) { ct2 = 1; ct3 = -1; touched = true; } if (pp2 == ZERO || pp3 == ZERO) { ct2 = 1; ct3 = 1; touched = true; } if (!touched) throw Exception() << "SMZDecayer::applyHard()" << " did not set ct2/3" << Exception::abortnow; } else { ct3 = (sqr(pp1)+sqr(pp3)-sqr(pp2))/(2.*pp1*pp3); ct2 = (sqr(pp1)+sqr(pp2)-sqr(pp3))/(2.*pp1*pp2); } double phi = Constants::twopi*UseRandom::rnd(); double cphi = cos(phi); double sphi = sin(phi); double st2 = sqrt(1.-sqr(ct2)); double st3 = sqrt(1.-sqr(ct3)); ThreeVector<Energy> pv1, pv2, pv3; pv1 = pp1*u1; pv2 = -ct2*pp2*u1 + st2*cphi*pp2*u2 + st2*sphi*pp2*u3; pv3 = -ct3*pp3*u1 - st3*cphi*pp3*u2 - st3*sphi*pp3*u3; if (keepq) { pq = Lorentz5Momentum(pv1, e1); pa = Lorentz5Momentum(pv2, e2); } else { pa = Lorentz5Momentum(pv1, e1); pq = Lorentz5Momentum(pv2, e2); } pg = Lorentz5Momentum(pv3, e3); pq.boost(-beta); pa.boost(-beta); pg.boost(-beta); fs.push_back(pq); fs.push_back(pa); fs.push_back(pg); return fs; } double SMZDecayer::getHard(double &x1, double &x2) { double w = 0.0; double y1 = UseRandom::rnd(),y2 = UseRandom::rnd(); // simply double MC efficiency // -> weight has to be divided by two (Jacobian) if (y1 + y2 > 1) { y1 = 1.-y1; y2 = 1.-y2; } bool inSoft = false; if (y1 < 0.25) { if (y2 < 0.25) { inSoft = true; if (y1 < y2) { y1 = 0.25-y1; y2 = y1*(1.5 - 2.*y2); } else { y2 = 0.25 - y2; y1 = y2*(1.5 - 2.*y1); } } else { if (y2 < y1 + 2.*sqr(y1)) return w; } } else { if (y2 < 0.25) { if (y1 < y2 + 2.*sqr(y2)) return w; } } // inside PS? x1 = 1.-y1; x2 = 1.-y2; if(y1*y2*(1.-y1-y2) < d_rho_*sqr(y1+y2)) return w; double k1 = getKfromX(x1, x2); double k2 = getKfromX(x2, x1); // Is it in the quark emission zone? if (k1 < d_kt1_) return 0.0; // No...is it in the anti-quark emission zone? if (k2 < d_kt2_) return 0.0; // Point is in dead zone: compute q qbar g weight w = MEV(x1, x2); // for axial: // w = MEA(x1, x2); // Reweight soft region if (inSoft) { if (y1 < y2) w *= 2.*y1; else w *= 2.*y2; } // alpha and colour factors Energy2 pt2 = sqr(d_Q_)*(1.-x1)*(1.-x2); w *= 1./3./Constants::pi*coupling()->value(pt2); return w; } bool SMZDecayer:: softMatrixElementVeto(ShowerProgenitorPtr initial,ShowerParticlePtr parent,Branching br) { // check we should be applying the veto if(parent->id()!=initial->progenitor()->id()|| br.ids[0]->id()!=br.ids[1]->id()|| br.ids[2]->id()!=ParticleID::g) return false; // calculate pt double d_z = br.kinematics->z(); Energy d_qt = br.kinematics->scale(); Energy2 d_m2 = parent->momentum().m2(); Energy pPerp = (1.-d_z)*sqrt( sqr(d_z*d_qt) - d_m2); // if not hardest so far don't apply veto if(pPerp<initial->highestpT()) return false; // calculate the weight double weight = 0.; if(parent->id()>0) weight = qWeightX(d_qt, d_z); else weight = qbarWeightX(d_qt, d_z); // compute veto from weight bool veto = !UseRandom::rndbool(weight); // if vetoing reset the scale if(veto) parent->vetoEmission(br.type,br.kinematics->scale()); // return the veto return veto; } void SMZDecayer::setRho(double r) { d_rho_ = r; d_v_ = sqrt(1.-4.*d_rho_); } void SMZDecayer::setKtildeSymm() { d_kt1_ = (1. + sqrt(1. - 4.*d_rho_))/2.; setKtilde2(); } void SMZDecayer::setKtilde2() { double num = d_rho_ * d_kt1_ + 0.25 * d_v_ *(1.+d_v_)*(1.+d_v_); double den = d_kt1_ - d_rho_; d_kt2_ = num/den; } double SMZDecayer::getZfromX(double x1, double x2) { double uval = u(x2); double num = x1 - (2. - x2)*uval; double den = sqrt(x2*x2 - 4.*d_rho_); return uval + num/den; } double SMZDecayer::getKfromX(double x1, double x2) { double zval = getZfromX(x1, x2); return (1.-x2)/(zval*(1.-zval)); } double SMZDecayer::MEV(double x1, double x2) { // Vector part double num = (x1+2.*d_rho_)*(x1+2.*d_rho_) + (x2+2.*d_rho_)*(x2+2.*d_rho_) - 8.*d_rho_*(1.+2.*d_rho_); double den = (1.+2.*d_rho_)*(1.-x1)*(1.-x2); return (num/den - 2.*d_rho_/((1.-x1)*(1.-x1)) - 2*d_rho_/((1.-x2)*(1.-x2)))/d_v_; } double SMZDecayer::MEA(double x1, double x2) { // Axial part double num = (x1+2.*d_rho_)*(x1+2.*d_rho_) + (x2+2.*d_rho_)*(x2+2.*d_rho_) + 2.*d_rho_*((5.-x1-x2)*(5.-x1-x2) - 19.0 + 4*d_rho_); double den = d_v_*d_v_*(1.-x1)*(1.-x2); return (num/den - 2.*d_rho_/((1.-x1)*(1.-x1)) - 2*d_rho_/((1.-x2)*(1.-x2)))/d_v_; } double SMZDecayer::u(double x2) { return 0.5*(1. + d_rho_/(1.-x2+d_rho_)); } void SMZDecayer:: getXXbar(double kti, double z, double &x, double &xbar) { double w = sqr(d_v_) + kti*(-1. + z)*z*(2. + kti*(-1. + z)*z); if (w < 0) { x = -1.; xbar = -1; } else { x = (1. + sqr(d_v_)*(-1. + z) + sqr(kti*(-1. + z))*z*z*z + z*sqrt(w) - kti*(-1. + z)*z*(2. + z*(-2 + sqrt(w))))/ (1. - kti*(-1. + z)*z + sqrt(w)); xbar = 1. + kti*(-1. + z)*z; } } double SMZDecayer::qWeight(double x, double xbar) { double rval; double xg = 2. - xbar - x; // always return one in the soft gluon region if(xg < EPS_) return 1.0; // check it is in the phase space if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return 0.0; double k1 = getKfromX(x, xbar); double k2 = getKfromX(xbar, x); // Is it in the quark emission zone? if(k1 < d_kt1_) { rval = MEV(x, xbar)/PS(x, xbar); // is it also in the anti-quark emission zone? if(k2 < d_kt2_) rval *= 0.5; return rval; } return 1.0; } double SMZDecayer::qbarWeight(double x, double xbar) { double rval; double xg = 2. - xbar - x; // always return one in the soft gluon region if(xg < EPS_) return 1.0; // check it is in the phase space if((1.-x)*(1.-xbar)*(1.-xg) < d_rho_*xg*xg) return 0.0; double k1 = getKfromX(x, xbar); double k2 = getKfromX(xbar, x); // Is it in the antiquark emission zone? if(k2 < d_kt2_) { rval = MEV(x, xbar)/PS(xbar, x); // is it also in the quark emission zone? if(k1 < d_kt1_) rval *= 0.5; return rval; } return 1.0; } double SMZDecayer::qWeightX(Energy qtilde, double z) { double x, xb; getXXbar(sqr(qtilde/d_Q_), z, x, xb); // if exceptionally out of phase space, leave this emission, as there // is no good interpretation for the soft ME correction. if (x < 0 || xb < 0) return 1.0; return qWeight(x, xb); } double SMZDecayer::qbarWeightX(Energy qtilde, double z) { double x, xb; getXXbar(sqr(qtilde/d_Q_), z, xb, x); // see above in qWeightX. if (x < 0 || xb < 0) return 1.0; return qbarWeight(x, xb); } double SMZDecayer::PS(double x, double xbar) { double u = 0.5*(1. + d_rho_ / (1.-xbar+d_rho_)); double z = u + (x - (2.-xbar)*u)/sqrt(xbar*xbar - 4.*d_rho_); double brack = (1.+z*z)/(1.-z)- 2.*d_rho_/(1-xbar); // interesting: the splitting function without the subtraction // term. Actually gives a much worse approximation in the collinear // limit. double brack = (1.+z*z)/(1.-z); double den = (1.-xbar)*sqrt(xbar*xbar - 4.*d_rho_); return brack/den; } double SMZDecayer::matrixElementRatio(const Particle & inpart, const ParticleVector & decay2, const ParticleVector & decay3, MEOption) { // extract partons and LO momentas vector<cPDPtr> partons(1,inpart.dataPtr()); vector<Lorentz5Momentum> lomom(1,inpart.momentum()); for(unsigned int ix=0;ix<2;++ix) { partons.push_back(decay2[ix]->dataPtr()); lomom.push_back(decay2[ix]->momentum()); } vector<Lorentz5Momentum> realmom(1,inpart.momentum()); for(unsigned int ix=0;ix<3;++ix) { if(ix==2) partons.push_back(decay3[ix]->dataPtr()); realmom.push_back(decay3[ix]->momentum()); } if(partons[0]->id()<0) { swap(partons[1],partons[2]); swap(lomom[1],lomom[2]); swap(realmom[1],realmom[2]); } double lome = loME(partons,lomom); InvEnergy2 reme = realME(partons,realmom); - double ratio = CF_*reme/lome*sqr(inpart.mass()); + double ratio = CF_*reme/lome*sqr(inpart.mass())*4.*Constants::pi; return ratio; } double SMZDecayer::meRatio(vector<cPDPtr> partons, vector<Lorentz5Momentum> momenta, unsigned int iemitter, bool subtract) const { Lorentz5Momentum q = momenta[1]+momenta[2]+momenta[3]; Energy2 Q2=q.m2(); Energy2 lambda = sqrt((Q2-sqr(momenta[1].mass()+momenta[2].mass()))* (Q2-sqr(momenta[1].mass()-momenta[2].mass()))); InvEnergy2 D[2]; double lome[2]; for(unsigned int iemit=0;iemit<2;++iemit) { unsigned int ispect = iemit==0 ? 1 : 0; Energy2 pipj = momenta[3 ] * momenta[1+iemit ]; Energy2 pipk = momenta[3 ] * momenta[1+ispect]; Energy2 pjpk = momenta[1+iemit] * momenta[1+ispect]; double y = pipj/(pipj+pipk+pjpk); double z = pipk/( pipk+pjpk); Energy mij = sqrt(2.*pipj+sqr(momenta[1+iemit].mass())); Energy2 lamB = sqrt((Q2-sqr(mij+momenta[1+ispect].mass()))* (Q2-sqr(mij-momenta[1+ispect].mass()))); Energy2 Qpk = q*momenta[1+ispect]; Lorentz5Momentum pkt = lambda/lamB*(momenta[1+ispect]-Qpk/Q2*q) +0.5/Q2*(Q2+sqr(momenta[1+ispect].mass())-sqr(momenta[1+ispect].mass()))*q; Lorentz5Momentum pijt = q-pkt; double muj = momenta[1+iemit ].mass()/sqrt(Q2); double muk = momenta[1+ispect].mass()/sqrt(Q2); double vt = sqrt((1.-sqr(muj+muk))*(1.-sqr(muj-muk)))/(1.-sqr(muj)-sqr(muk)); double v = sqrt(sqr(2.*sqr(muk)+(1.-sqr(muj)-sqr(muk))*(1.-y))-4.*sqr(muk)) /(1.-y)/(1.-sqr(muj)-sqr(muk)); // dipole term D[iemit] = 0.5/pipj*(2./(1.-(1.-z)*(1.-y)) -vt/v*(2.-z+sqr(momenta[1+iemit].mass())/pipj)); // matrix element vector<Lorentz5Momentum> lomom(3); lomom[0] = momenta[0]; if(iemit==0) { lomom[1] = pijt; lomom[2] = pkt ; } else { lomom[2] = pijt; lomom[1] = pkt ; } lome[iemit] = loME(partons,lomom); } InvEnergy2 ratio = realME(partons,momenta)*abs(D[iemitter]) /(abs(D[0]*lome[0])+abs(D[1]*lome[1])); if(subtract) return Q2*(ratio-2.*D[iemitter]); else return Q2*ratio; } double SMZDecayer::loME(const vector<cPDPtr> & partons, const vector<Lorentz5Momentum> & momenta) const { // compute the spinors vector<VectorWaveFunction> vin; vector<SpinorWaveFunction> aout; vector<SpinorBarWaveFunction> fout; VectorWaveFunction zin (momenta[0],partons[0],incoming); SpinorBarWaveFunction qkout(momenta[1],partons[1],outgoing); SpinorWaveFunction qbout(momenta[2],partons[2],outgoing); for(unsigned int ix=0;ix<2;++ix){ qkout.reset(ix); fout.push_back(qkout); qbout.reset(ix); aout.push_back(qbout); } for(unsigned int ix=0;ix<3;++ix){ zin.reset(ix); vin.push_back(zin); } // temporary storage of the different diagrams // sum over helicities to get the matrix element double total(0.); for(unsigned int inhel=0;inhel<3;++inhel) { for(unsigned int outhel1=0;outhel1<2;++outhel1) { for(unsigned int outhel2=0;outhel2<2;++outhel2) { Complex diag1 = FFZVertex_->evaluate(scale_,aout[outhel2],fout[outhel1],vin[inhel]); total += norm(diag1); } } } // return the answer return total; } InvEnergy2 SMZDecayer::realME(const vector<cPDPtr> & partons, const vector<Lorentz5Momentum> & momenta) const { // compute the spinors vector<VectorWaveFunction> vin; vector<SpinorWaveFunction> aout; vector<SpinorBarWaveFunction> fout; vector<VectorWaveFunction> gout; VectorWaveFunction zin (momenta[0],partons[0],incoming); SpinorBarWaveFunction qkout(momenta[1],partons[1],outgoing); SpinorWaveFunction qbout(momenta[2],partons[2],outgoing); VectorWaveFunction gluon(momenta[3],partons[3],outgoing); for(unsigned int ix=0;ix<2;++ix){ qkout.reset(ix); fout.push_back(qkout); qbout.reset(ix); aout.push_back(qbout); gluon.reset(2*ix); gout.push_back(gluon); } for(unsigned int ix=0;ix<3;++ix){ zin.reset(ix); vin.push_back(zin); } vector<Complex> diag(2,0.); double total(0.); for(unsigned int inhel1=0;inhel1<3;++inhel1) { for(unsigned int outhel1=0;outhel1<2;++outhel1) { for(unsigned int outhel2=0;outhel2<2;++outhel2) { for(unsigned int outhel3=0;outhel3<2;++outhel3) { SpinorBarWaveFunction off1 = FFGVertex_->evaluate(scale_,3,partons[1],fout[outhel1],gout[outhel3]); diag[0] = FFZVertex_->evaluate(scale_,aout[outhel2],off1,vin[inhel1]); SpinorWaveFunction off2 = FFGVertex_->evaluate(scale_,3,partons[2],aout[outhel2],gout[outhel3]); diag[1] = FFZVertex_->evaluate(scale_,off2,fout[outhel1],vin[inhel1]); // sum of diagrams Complex sum = std::accumulate(diag.begin(),diag.end(),Complex(0.)); // me2 total += norm(sum); } } } } // divide out the coupling total /= norm(FFGVertex_->norm()); // return the total return total*UnitRemoval::InvE2; } double SMZDecayer::calculateRealEmission(double x1, double x2, vector<PPtr> hardProcess, double phi, bool subtract) const { // make partons data object for meRatio vector<cPDPtr> partons (3); for(int ix=0; ix<3; ++ix) partons[ix] = hardProcess[ix]->dataPtr(); partons.push_back(gluon_); // calculate x3 double x3 = 2.-x1-x2; double xT = sqrt(max(0.,sqr(x3) -0.25*sqr(sqr(x2)+sqr(x3)-sqr(x1))/(sqr(x2)-4.*mu2_))); // calculate the momenta Energy M = mZ_; Lorentz5Momentum pspect(ZERO,ZERO,-0.5*M*sqrt(max(sqr(x2)-4.*mu2_,0.)),0.5*M*x2,M*mu_); Lorentz5Momentum pemit (-0.5*M*xT*cos(phi),-0.5*M*xT*sin(phi), 0.5*M*sqrt(max(sqr(x1)-sqr(xT)-4.*mu2_,0.)),0.5*M*x1,M*mu_); Lorentz5Momentum pgluon(0.5*M*xT*cos(phi), 0.5*M*xT*sin(phi), 0.5*M*sqrt(max(sqr(x3)-sqr(xT),0.)),0.5*M*x3,ZERO); if(abs(pspect.z()+pemit.z()-pgluon.z())/M<1e-6) pgluon.setZ(-pgluon.z()); else if(abs(pspect.z()-pemit.z()+pgluon.z())/M<1e-6) pemit .setZ(- pemit.z()); // loop over the possible emitting partons double realwgt(0.); for(unsigned int iemit=0;iemit<2;++iemit) { // boost and rotate momenta LorentzRotation eventFrame( ( hardProcess[1]->momentum() + hardProcess[2]->momentum() ).findBoostToCM() ); Lorentz5Momentum spectator = eventFrame*hardProcess[iemit+1]->momentum(); eventFrame.rotateZ( -spectator.phi() ); eventFrame.rotateY( -spectator.theta() ); eventFrame.invert(); vector<Lorentz5Momentum> momenta(3); momenta[0] = hardProcess[0]->momentum(); if(iemit==0) { momenta[2] = eventFrame*pspect; momenta[1] = eventFrame*pemit ; } else { momenta[1] = eventFrame*pspect; momenta[2] = eventFrame*pemit ; } momenta.push_back(eventFrame*pgluon); // calculate the weight if(1.-x1>1e-5 && 1.-x2>1e-5) realwgt += meRatio(partons,momenta,iemit,subtract); } // total real emission contribution return realwgt; } double SMZDecayer::calculateRealEmission(double x1, double x2, vector<PPtr> hardProcess, double phi, bool subtract, int emitter) const { // make partons data object for meRatio vector<cPDPtr> partons (3); for(int ix=0; ix<3; ++ix) partons[ix] = hardProcess[ix]->dataPtr(); partons.push_back(gluon_); // calculate x3 double x3 = 2.-x1-x2; double xT = sqrt(max(0.,sqr(x3) -0.25*sqr(sqr(x2)+sqr(x3)-sqr(x1))/(sqr(x2)-4.*mu2_))); // calculate the momenta Energy M = mZ_; Lorentz5Momentum pspect(ZERO,ZERO,-0.5*M*sqrt(max(sqr(x2)-4.*mu2_,0.)),0.5*M*x2,M*mu_); Lorentz5Momentum pemit (-0.5*M*xT*cos(phi),-0.5*M*xT*sin(phi), 0.5*M*sqrt(max(sqr(x1)-sqr(xT)-4.*mu2_,0.)),0.5*M*x1,M*mu_); Lorentz5Momentum pgluon( 0.5*M*xT*cos(phi), 0.5*M*xT*sin(phi), 0.5*M*sqrt(max(sqr(x3)-sqr(xT),0.)),0.5*M*x3,ZERO); if(abs(pspect.z()+pemit.z()-pgluon.z())/M<1e-6) pgluon.setZ(-pgluon.z()); else if(abs(pspect.z()-pemit.z()+pgluon.z())/M<1e-6) pemit .setZ(- pemit.z()); // boost and rotate momenta LorentzRotation eventFrame( ( hardProcess[1]->momentum() + hardProcess[2]->momentum() ).findBoostToCM() ); Lorentz5Momentum spectator = eventFrame*hardProcess[emitter+1]->momentum(); eventFrame.rotateZ( -spectator.phi() ); eventFrame.rotateY( -spectator.theta() ); eventFrame.invert(); vector<Lorentz5Momentum> momenta(3); momenta[0] = hardProcess[0]->momentum(); if(emitter==0) { momenta[2] = eventFrame*pspect; momenta[1] = eventFrame*pemit ; } else { momenta[1] = eventFrame*pspect; momenta[2] = eventFrame*pemit ; } momenta.push_back(eventFrame*pgluon); // calculate the weight double realwgt(0.); if(1.-x1>1e-5 && 1.-x2>1e-5) realwgt = meRatio(partons,momenta,emitter,subtract); // total real emission contribution return realwgt; } diff --git a/Decay/PerturbativeDecayer.h b/Decay/PerturbativeDecayer.h --- a/Decay/PerturbativeDecayer.h +++ b/Decay/PerturbativeDecayer.h @@ -1,245 +1,245 @@ // -*- 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" 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}; public: /** * The default constructor. */ PerturbativeDecayer() : 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 R/B + * 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); /** * Work out the type of process */ bool identifyDipoles(vector<dipoleType> & dipoles, PPtr & aProgenitor, PPtr & bProgenitor, PPtr & cProgenitor) const; /** * Coupling for the generation of hard radiation */ ShowerAlphaPtr coupling() {return coupling_;} /** * Return the momenta including the hard emission */ vector<Lorentz5Momentum> hardMomenta(PPtr in, PPtr emitter, PPtr spectator, const vector<dipoleType> & dipoles, int i); /** * 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, const dipoleType & emittingDipole); /** * Return contribution to dipole that depends on the spin of the emitter */ double dipoleSpinFactor(const PPtr & emitter, double z); /** * Return the colour coefficient of the dipole */ double colourCoeff(const PDT::Colour emitter, const PDT::Colour spectator, const PDT::Colour other); /** * Set up the colour lines */ void getColourLines(RealEmissionProcessPtr real); 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 */ //@{ /** * Coupling for the generation of hard radiation */ ShowerAlphaPtr coupling_; /** * 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 */