diff --git a/MatrixElement/Matchbox/Base/MatchboxMEBase.cc b/MatrixElement/Matchbox/Base/MatchboxMEBase.cc --- a/MatrixElement/Matchbox/Base/MatchboxMEBase.cc +++ b/MatrixElement/Matchbox/Base/MatchboxMEBase.cc @@ -1,1658 +1,1663 @@ // -*- C++ -*- // // MatchboxMEBase.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 MatchboxMEBase class. // #include "MatchboxMEBase.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/PDF/PDF.h" #include "ThePEG/PDT/PDT.h" #include "ThePEG/StandardModel/StandardModelBase.h" #include "ThePEG/Cuts/Cuts.h" #include "ThePEG/Handlers/StdXCombGroup.h" #include "ThePEG/EventRecord/SubProcess.h" #include "Herwig/MatrixElement/Matchbox/Dipoles/SubtractionDipole.h" #include "Herwig/MatrixElement/Matchbox/Utility/DiagramDrawer.h" #include "Herwig/MatrixElement/Matchbox/MatchboxFactory.h" #include "Herwig/MatrixElement/Matchbox/Base/MergerBase.h" #include "Herwig/API/RunDirectories.h" #include "Herwig/MatrixElement/ProductionMatrixElement.h" #include "Herwig/MatrixElement/HardVertex.h" #include #include using std::ostream_iterator; using namespace Herwig; MatchboxMEBase::MatchboxMEBase() : MEBase(), theOneLoop(false), theOneLoopNoBorn(false), theOneLoopNoLoops(false), theNoCorrelations(false), theHavePDFs(false,false), checkedPDFs(false) {} MatchboxMEBase::~MatchboxMEBase() {} Ptr::tptr MatchboxMEBase::factory() const { return MatchboxFactory::currentFactory(); } Ptr::tptr MatchboxMEBase::diagramGenerator() const { return factory()->diagramGenerator(); } Ptr::tptr MatchboxMEBase::processData() const { return factory()->processData(); } unsigned int MatchboxMEBase::getNLight() const { return factory()->nLight(); } vector MatchboxMEBase::getNLightJetVec() const { return factory()->nLightJetVec(); } vector MatchboxMEBase::getNHeavyJetVec() const { return factory()->nHeavyJetVec(); } vector MatchboxMEBase::getNLightProtonVec() const { return factory()->nLightProtonVec(); } double MatchboxMEBase::factorizationScaleFactor() const { return factory()->factorizationScaleFactor(); } double MatchboxMEBase::renormalizationScaleFactor() const { return factory()->renormalizationScaleFactor(); } bool MatchboxMEBase::fixedCouplings() const { return factory()->fixedCouplings(); } bool MatchboxMEBase::fixedQEDCouplings() const { return factory()->fixedQEDCouplings(); } bool MatchboxMEBase::checkPoles() const { return factory()->checkPoles(); } bool MatchboxMEBase::verbose() const { return factory()->verbose(); } bool MatchboxMEBase::initVerbose() const { return factory()->initVerbose(); } void MatchboxMEBase::getDiagrams() const { if ( diagramGenerator() && processData() ) { vector::ptr> diags; vector::ptr>& res = processData()->diagramMap()[subProcess().legs]; if ( res.empty() ) { res = diagramGenerator()->generate(subProcess().legs,orderInAlphaS(),orderInAlphaEW()); } copy(res.begin(),res.end(),back_inserter(diags)); processData()->fillMassGenerators(subProcess().legs); if ( diags.empty() ) return; for (auto const & d : diags ) add(d); return; } throw Exception() << "MatchboxMEBase::getDiagrams() expects a Tree2toNGenerator and ProcessData object.\n" << "Please check your setup." << Exception::runerror; } Selector MatchboxMEBase::diagrams(const DiagramVector & diags) const { if ( phasespace() ) { return phasespace()->selectDiagrams(diags); } throw Exception() << "MatchboxMEBase::diagrams() expects a MatchboxPhasespace object.\n" << "Please check your setup." << Exception::runerror; return Selector(); } Selector MatchboxMEBase::colourGeometries(tcDiagPtr diag) const { if ( matchboxAmplitude() ) { if ( matchboxAmplitude()->haveColourFlows() ) { if ( matchboxAmplitude()->treeAmplitudes() ) matchboxAmplitude()->prepareAmplitudes(this); return matchboxAmplitude()->colourGeometries(diag); } } Ptr::tcptr tdiag = dynamic_ptr_cast::tcptr>(diag); assert(diag && processData()); vector& flows = processData()->colourFlowMap()[tdiag]; if ( flows.empty() ) { list > > > cflows = ColourBasis::colourFlows(tdiag); for ( auto const & fit : cflows) flows.push_back(new ColourLines(ColourBasis::cfstring(fit))); } Selector res; for ( auto const & f : flows ) res.insert(1.0,f); return res; } void MatchboxMEBase::constructVertex(tSubProPtr sub, const ColourLines* cl) { if ( !canFillRhoMatrix() || !factory()->spinCorrelations() ) return; assert(matchboxAmplitude()); assert(matchboxAmplitude()->colourBasis()); // get the colour structure for the selected colour flow size_t cStructure = matchboxAmplitude()->colourBasis()->tensorIdFromFlow(lastXComb().lastDiagram(),cl); // hard process for processing the spin info tPVector hard; hard.push_back(sub->incoming().first); hard.push_back(sub->incoming().second); vector out; for ( auto const & p : sub->outgoing() ) { out.push_back(p->data().iSpin()); hard.push_back(p); } // calculate dummy wave functions to fill the spin info static vector dummyPolarizations; static vector dummySpinors; static vector dummyBarSpinors; for ( size_t k = 0; k < hard.size(); ++k ) { if ( hard[k]->data().iSpin() == PDT::Spin1Half ) { if ( hard[k]->id() > 0 && k > 1 ) { SpinorBarWaveFunction(dummyBarSpinors,hard[k], outgoing, true); } else if ( hard[k]->id() < 0 && k > 1 ) { SpinorWaveFunction(dummySpinors,hard[k], outgoing, true); } else if ( hard[k]->id() > 0 && k < 2 ) { SpinorWaveFunction(dummySpinors,hard[k], incoming, false); } else if ( hard[k]->id() < 0 && k < 2 ) { SpinorBarWaveFunction(dummyBarSpinors,hard[k], incoming, false); } } else if ( hard[k]->data().iSpin() == PDT::Spin1 ) { VectorWaveFunction(dummyPolarizations,hard[k], k > 1 ? outgoing : incoming, k > 1 ? true : false, hard[k]->data().hardProcessMass() == ZERO); } else if (hard[k]->data().iSpin() == PDT::Spin0 ) { ScalarWaveFunction(hard[k],k > 1 ? outgoing : incoming, k > 1 ? true : false); } else assert(false); } // fill the production matrix element ProductionMatrixElement pMe(mePartonData()[0]->iSpin(), mePartonData()[1]->iSpin(), out); for ( map,CVector>::const_iterator lamp = lastLargeNAmplitudes().begin(); lamp != lastLargeNAmplitudes().end(); ++lamp ) { vector pMeHelicities = matchboxAmplitude()->physicalHelicities(lamp->first); pMe(pMeHelicities) = lamp->second[cStructure]; } // set the spin information HardVertexPtr hardvertex = new_ptr(HardVertex()); hardvertex->ME(pMe); if ( sub->incoming().first->spinInfo() ) sub->incoming().first->spinInfo()->productionVertex(hardvertex); if ( sub->incoming().second->spinInfo() ) sub->incoming().second->spinInfo()->productionVertex(hardvertex); for ( auto const & p : sub->outgoing() ) if ( p->spinInfo() ) p->spinInfo()->productionVertex(hardvertex); } unsigned int MatchboxMEBase::orderInAlphaS() const { return subProcess().orderInAlphaS; } unsigned int MatchboxMEBase::orderInAlphaEW() const { return subProcess().orderInAlphaEW; } void MatchboxMEBase::setXComb(tStdXCombPtr xc) { MEBase::setXComb(xc); lastMatchboxXComb(xc); if ( phasespace() ) phasespace()->setXComb(xc); if ( scaleChoice() ) scaleChoice()->setXComb(xc); if ( matchboxAmplitude() ) matchboxAmplitude()->setXComb(xc); if (theMerger){ theMerger->setME(this); theMerger->setXComb( xc ); } } double MatchboxMEBase::generateIncomingPartons(const double* r1, const double* r2) { // shamelessly stolen from PartonExtractor.cc Energy2 shmax = lastCuts().sHatMax(); Energy2 shmin = lastCuts().sHatMin(); Energy2 sh = shmin*pow(shmax/shmin, *r1); double ymax = lastCuts().yHatMax(); double ymin = lastCuts().yHatMin(); double km = log(shmax/shmin); ymax = min(ymax, log(lastCuts().x1Max()*sqrt(lastS()/sh))); ymin = max(ymin, -log(lastCuts().x2Max()*sqrt(lastS()/sh))); double y = ymin + (*r2)*(ymax - ymin); double x1 = exp(-0.5*log(lastS()/sh) + y); double x2 = exp(-0.5*log(lastS()/sh) - y); Lorentz5Momentum P1 = lastParticles().first->momentum(); LorentzMomentum p1 = lightCone((P1.rho() + P1.e())*x1, Energy()); p1.rotateY(P1.theta()); p1.rotateZ(P1.phi()); meMomenta()[0] = p1; Lorentz5Momentum P2 = lastParticles().second->momentum(); LorentzMomentum p2 = lightCone((P2.rho() + P2.e())*x2, Energy()); p2.rotateY(P2.theta()); p2.rotateZ(P2.phi()); meMomenta()[1] = p2; lastXCombPtr()->lastX1X2(make_pair(x1,x2)); lastXCombPtr()->lastSHat((meMomenta()[0]+meMomenta()[1]).m2()); return km*(ymax - ymin); } bool MatchboxMEBase::generateKinematics(const double * r) { if ( phasespace() ) { jacobian(phasespace()->generateKinematics(r,meMomenta())); if ( jacobian() == 0.0 ) return false; setScale(); if (theMerger&&!theMerger->generateKinematics(r)){ return false; } logGenerateKinematics(r); assert(lastMatchboxXComb()); if ( nDimAmplitude() > 0 ) { amplitudeRandomNumbers().resize(nDimAmplitude()); copy(r + nDimPhasespace(), r + nDimPhasespace() + nDimAmplitude(), amplitudeRandomNumbers().begin()); } if ( nDimInsertions() > 0 ) { insertionRandomNumbers().resize(nDimInsertions()); copy(r + nDimPhasespace() + nDimAmplitude(), r + nDimPhasespace() + nDimAmplitude() + nDimInsertions(), insertionRandomNumbers().begin()); } return true; } throw Exception() << "MatchboxMEBase::generateKinematics() expects a MatchboxPhasespace object.\n" << "Please check your setup." << Exception::runerror; return false; } int MatchboxMEBase::nDim() const { if ( lastMatchboxXComb() ) return nDimPhasespace() + nDimAmplitude() + nDimInsertions(); int ampAdd = 0; if ( matchboxAmplitude() ) { ampAdd = matchboxAmplitude()->nDimAdditional(); } int insertionAdd = 0; for ( auto const & v : virtuals() ) { insertionAdd = max(insertionAdd,v->nDimAdditional()); } return nDimBorn() + ampAdd + insertionAdd; } int MatchboxMEBase::nDimBorn() const { if ( lastMatchboxXComb() ) return nDimPhasespace(); if ( phasespace() ) return phasespace()->nDim(diagrams().front()->partons()); throw Exception() << "MatchboxMEBase::nDim() expects a MatchboxPhasespace object.\n" << "Please check your setup." << Exception::runerror; return 0; } void MatchboxMEBase::setScale(Energy2 ren, Energy2 fac) const { if ( haveX1X2() ) { lastXCombPtr()->lastSHat((meMomenta()[0]+meMomenta()[1]).m2()); } Energy2 fcscale = (fac == ZERO) ? factorizationScale() : fac; Energy2 fscale = fcscale*sqr(factorizationScaleFactor()); Energy2 rscale = (ren == ZERO ? renormalizationScale() : ren)*sqr(renormalizationScaleFactor()); Energy2 ewrscale = renormalizationScaleQED(); lastXCombPtr()->lastScale(fscale); lastXCombPtr()->lastCentralScale(fcscale); lastXCombPtr()->lastShowerScale(showerScale()); lastMatchboxXComb()->lastRenormalizationScale(rscale); if ( !fixedCouplings() ) { if ( rscale > lastCuts().scaleMin() ) lastXCombPtr()->lastAlphaS(SM().alphaS(rscale)); else lastXCombPtr()->lastAlphaS(SM().alphaS(lastCuts().scaleMin())); } else { lastXCombPtr()->lastAlphaS(SM().alphaS()); } if ( !fixedQEDCouplings() ) { lastXCombPtr()->lastAlphaEM(SM().alphaEMME(ewrscale)); } else { lastXCombPtr()->lastAlphaEM(SM().alphaEMMZ()); } logSetScale(); } Energy2 MatchboxMEBase::factorizationScale() const { if ( scaleChoice() ) { return scaleChoice()->factorizationScale(); } throw Exception() << "MatchboxMEBase::factorizationScale() expects a MatchboxScaleChoice object.\n" << "Please check your setup." << Exception::runerror; return ZERO; } Energy2 MatchboxMEBase::renormalizationScale() const { if ( scaleChoice() ) { return scaleChoice()->renormalizationScale(); } throw Exception() << "MatchboxMEBase::renormalizationScale() expects a MatchboxScaleChoice object.\n" << "Please check your setup." << Exception::runerror; return ZERO; } Energy2 MatchboxMEBase::renormalizationScaleQED() const { if ( scaleChoice() ) { return scaleChoice()->renormalizationScaleQED(); } return renormalizationScale(); } Energy2 MatchboxMEBase::showerScale() const { if ( scaleChoice() ) { return scaleChoice()->showerScale(); } throw Exception() << "MatchboxMEBase::showerScale() expects a MatchboxScaleChoice object.\n" << "Please check your setup." << Exception::runerror; return ZERO; } void MatchboxMEBase::setVetoScales(tSubProPtr) const {} bool MatchboxMEBase::havePDFWeight1() const { if ( checkedPDFs ) return theHavePDFs.first; theHavePDFs.first = factory()->isIncoming(mePartonData()[0]) && lastXCombPtr()->partonBins().first->pdf(); theHavePDFs.second = factory()->isIncoming(mePartonData()[1]) && lastXCombPtr()->partonBins().second->pdf(); checkedPDFs = true; return theHavePDFs.first; } bool MatchboxMEBase::havePDFWeight2() const { if ( checkedPDFs ) return theHavePDFs.second; theHavePDFs.first = factory()->isIncoming(mePartonData()[0]) && lastXCombPtr()->partonBins().first->pdf(); theHavePDFs.second = factory()->isIncoming(mePartonData()[1]) && lastXCombPtr()->partonBins().second->pdf(); checkedPDFs = true; return theHavePDFs.second; } void MatchboxMEBase::getPDFWeight(Energy2 factorizationScale) const { if ( !havePDFWeight1() && !havePDFWeight2() ) { lastMEPDFWeight(1.0); logPDFWeight(); return; } double w = 1.; if ( havePDFWeight1() ) w *= pdf1(factorizationScale); if ( havePDFWeight2() ) w *= pdf2(factorizationScale); lastMEPDFWeight(w); logPDFWeight(); } double MatchboxMEBase::pdf1(Energy2 fscale, double xEx, double xFactor) const { assert(lastXCombPtr()->partonBins().first->pdf()); if ( xEx < 1. && lastX1()*xFactor >= xEx ) { return ( ( 1. - lastX1()*xFactor ) / ( 1. - xEx ) ) * lastXCombPtr()->partonBins().first->pdf()->xfx(lastParticles().first->dataPtr(), lastPartons().first->dataPtr(), fscale == ZERO ? lastScale() : fscale, xEx)/xEx; } return lastXCombPtr()->partonBins().first->pdf()->xfx(lastParticles().first->dataPtr(), lastPartons().first->dataPtr(), fscale == ZERO ? lastScale() : fscale, lastX1()*xFactor)/lastX1()/xFactor; } double MatchboxMEBase::pdf2(Energy2 fscale, double xEx, double xFactor) const { assert(lastXCombPtr()->partonBins().second->pdf()); if ( xEx < 1. && lastX2()*xFactor >= xEx ) { return ( ( 1. - lastX2()*xFactor ) / ( 1. - xEx ) ) * lastXCombPtr()->partonBins().second->pdf()->xfx(lastParticles().second->dataPtr(), lastPartons().second->dataPtr(), fscale == ZERO ? lastScale() : fscale, xEx)/xEx; } return lastXCombPtr()->partonBins().second->pdf()->xfx(lastParticles().second->dataPtr(), lastPartons().second->dataPtr(), fscale == ZERO ? lastScale() : fscale, lastX2()*xFactor)/lastX2()/xFactor; } double MatchboxMEBase::me2() const { if ( matchboxAmplitude() ) { if ( matchboxAmplitude()->treeAmplitudes() ) matchboxAmplitude()->prepareAmplitudes(this); double res = matchboxAmplitude()->me2()* me2Norm(); return res; } throw Exception() << "MatchboxMEBase::me2() expects a MatchboxAmplitude object.\n" << "Please check your setup." << Exception::runerror; return 0.; } double MatchboxMEBase::largeNME2(Ptr::tptr largeNBasis) const { if ( matchboxAmplitude() ) { if ( matchboxAmplitude()->treeAmplitudes() ) { largeNBasis->prepare(mePartonData(),false); matchboxAmplitude()->prepareAmplitudes(this); } double res = matchboxAmplitude()->largeNME2(largeNBasis)* me2Norm(); return res; } throw Exception() << "MatchboxMEBase::largeNME2() expects a MatchboxAmplitude object.\n" << "Please check your setup." << Exception::runerror; return 0.; } double MatchboxMEBase::finalStateSymmetry() const { if ( symmetryFactor() > 0.0 ) return symmetryFactor(); double sFactor = 1.; map counts; cPDVector checkData; copy(mePartonData().begin()+2,mePartonData().end(),back_inserter(checkData)); cPDVector::iterator p = checkData.begin(); while ( !checkData.empty() ) { if ( counts.find((**p).id()) != counts.end() ) { counts[(**p).id()] += 1; } else { counts[(**p).id()] = 1; } checkData.erase(p); p = checkData.begin(); continue; } for ( auto const & c : counts) { if ( c.second == 1 ) continue; if ( c.second == 2 ) sFactor /= 2.; else if ( c.second == 3 ) sFactor /= 6.; else if ( c.second == 4 ) sFactor /= 24.; } symmetryFactor(sFactor); return symmetryFactor(); } double MatchboxMEBase::me2Norm(unsigned int addAlphaS) const { // assume that we always have incoming // spin-1/2 or massless spin-1 particles double fac = 1./4.; if ( hasInitialAverage() ) fac = 1.; double couplings = 1.0; if ( (orderInAlphaS() > 0 || addAlphaS != 0) && !hasRunningAlphaS() ) { fac *= pow(lastAlphaS()/SM().alphaS(),double(orderInAlphaS()+addAlphaS)); couplings *= pow(lastAlphaS(),double(orderInAlphaS()+addAlphaS)); } if ( orderInAlphaEW() > 0 && !hasRunningAlphaEW() ) { fac *= pow(lastAlphaEM()/SM().alphaEMMZ(),double(orderInAlphaEW())); couplings *= pow(lastAlphaEM(),double(orderInAlphaEW())); } lastMECouplings(couplings); if ( !hasInitialAverage() ) { if ( mePartonData()[0]->iColour() == PDT::Colour3 || mePartonData()[0]->iColour() == PDT::Colour3bar ) fac /= SM().Nc(); else if ( mePartonData()[0]->iColour() == PDT::Colour8 ) fac /= (SM().Nc()*SM().Nc()-1.); if ( mePartonData()[1]->iColour() == PDT::Colour3 || mePartonData()[1]->iColour() == PDT::Colour3bar ) fac /= SM().Nc(); else if ( mePartonData()[1]->iColour() == PDT::Colour8 ) fac /= (SM().Nc()*SM().Nc()-1.); } return !hasFinalStateSymmetry() ? finalStateSymmetry()*fac : fac; } CrossSection MatchboxMEBase::prefactor()const{ return (sqr(hbarc)/(2.*lastSHat())) *jacobian()* lastMEPDFWeight(); } CrossSection MatchboxMEBase::dSigHatDRB() const { getPDFWeight(); lastME2(me2()); return oneLoopNoBorn()?ZERO:prefactor() * lastME2(); } CrossSection MatchboxMEBase::dSigHatDRV() const { getPDFWeight(); lastME2(me2()); return ( oneLoop() && !oneLoopNoLoops() )?(prefactor() * oneLoopInterference()):ZERO; } CrossSection MatchboxMEBase::dSigHatDRI() const { getPDFWeight(); lastME2(me2()); CrossSection res=ZERO; if (oneLoop() &&!onlyOneLoop()) { for ( auto const & v : virtuals()) { v->setXComb(lastXCombPtr()); res += v->dSigHatDR(); } if ( checkPoles() && oneLoop() ) logPoles(); } return res; } CrossSection MatchboxMEBase::dSigHatDRAlphaDiff(double alpha) const { getPDFWeight(); lastME2(me2()); CrossSection res=ZERO; for ( auto const & v: virtuals() ) { v->setXComb(lastXCombPtr()); res+=v->dSigHatDRAlphaDiff( alpha); } return res; } + + + + + CrossSection MatchboxMEBase::dSigHatDR() const { getPDFWeight(); if (theMerger){ lastMECrossSection(theMerger->MergingDSigDR()); return lastMECrossSection(); } else if (lastXCombPtr()->willPassCuts() ) { lastME2(me2()); CrossSection _dSigHatDRB, _dSigHatDRV, _dSigHatDRI, res = ZERO; // ----- dSigHatDRB ----- _dSigHatDRB = oneLoopNoBorn()?ZERO:prefactor() * lastME2(); // ----- dSigHatDRV ----- _dSigHatDRV = ( oneLoop() && !oneLoopNoLoops() )?(prefactor() * oneLoopInterference()):ZERO; // ----- dSigHatDRI ----- if (oneLoop() &&!onlyOneLoop()) { for ( auto const & v : virtuals()) { v->setXComb(lastXCombPtr()); res += v->dSigHatDR(); } if ( checkPoles() && oneLoop() ) logPoles(); } _dSigHatDRI = res; // ----- finalizing ----- lastMECrossSection(_dSigHatDRB + _dSigHatDRV + _dSigHatDRI); return lastMECrossSection(); } else { lastME2(ZERO); lastMECrossSection(ZERO); return lastMECrossSection(); } } double MatchboxMEBase::oneLoopInterference() const { if ( matchboxAmplitude() ) { if ( matchboxAmplitude()->oneLoopAmplitudes() ) matchboxAmplitude()->prepareOneLoopAmplitudes(this); double res = matchboxAmplitude()->oneLoopInterference()* me2Norm(1); return res; } throw Exception() << "MatchboxMEBase::oneLoopInterference() expects a MatchboxAmplitude object.\n" << "Please check your setup." << Exception::runerror; return 0.; } MatchboxMEBase::AccuracyHistogram::AccuracyHistogram(double low, double up, unsigned int nbins) : lower(low), upper(up), sameSign(0), oppositeSign(0), nans(0), overflow(0), underflow(0) { double step = (up-low)/nbins; for ( unsigned int k = 1; k <= nbins; ++k ) bins[lower + k*step] = 0.0; } void MatchboxMEBase::AccuracyHistogram::book(double a, double b) { if ( ! (isfinite(a) && isfinite(b)) ) { ++nans; return; } if ( a*b >= 0. ) ++sameSign; if ( a*b < 0. ) ++oppositeSign; double r = 1.; if ( abs(a) != 0.0 ) r = abs(1.-abs(b/a)); else if ( abs(b) != 0.0 ) r = abs(b); if ( log10(r) < lower || r == 0.0 ) { ++underflow; return; } if ( log10(r) > upper ) { ++overflow; return; } map::iterator bin = bins.upper_bound(log10(r)); if ( bin == bins.end() ) return; bin->second += 1.; } void MatchboxMEBase::AccuracyHistogram::dump(const std::string& folder, const std::string& prefix, const cPDVector& proc) const { ostringstream fname(""); for ( cPDVector::const_iterator p = proc.begin(); p != proc.end(); ++p ) fname << (**p).PDGName(); ofstream out((folder+"/"+prefix+fname.str()+".dat").c_str()); out << "# same sign : " << sameSign << " opposite sign : " << oppositeSign << " nans : " << nans << " overflow : " << overflow << " underflow : " << underflow << "\n"; for ( map::const_iterator b = bins.begin(); b != bins.end(); ++b ) { map::const_iterator bp = b; --bp; if ( b->second != 0. ) { if ( b != bins.begin() ) out << bp->first; else out << lower; out << " " << b->first << " " << b->second << "\n" << flush; } } ofstream gpout((folder+"/"+prefix+fname.str()+".gp").c_str()); gpout << "set terminal png\n" << "set xlabel 'accuracy of pole cancellation [decimal places]'\n" << "set ylabel 'counts\n" << "set xrange [-20:0]\n" << "set output '" << prefix << fname.str() << ".png'\n" << "plot '" << prefix << fname.str() << ".dat' using (0.5*($1+$2)):3 with linespoints pt 7 ps 1 not"; } void MatchboxMEBase::AccuracyHistogram::persistentOutput(PersistentOStream& os) const { os << lower << upper << bins << sameSign << oppositeSign << nans << overflow << underflow; } void MatchboxMEBase::AccuracyHistogram::persistentInput(PersistentIStream& is) { is >> lower >> upper >> bins >> sameSign >> oppositeSign >> nans >> overflow >> underflow; } void MatchboxMEBase::logPoles() const { double res2me = oneLoopDoublePole(); double res1me = oneLoopSinglePole(); double res2i = 0.; double res1i = 0.; for ( auto const & v : virtuals()) { res2i += v->oneLoopDoublePole(); res1i += v->oneLoopSinglePole(); } if (res2me != 0.0 || res2i != 0.0) epsilonSquarePoleHistograms[mePartonData()].book(res2me,res2i); if (res1me != 0.0 || res1i != 0.0) epsilonPoleHistograms[mePartonData()].book(res1me,res1i); } bool MatchboxMEBase::haveOneLoop() const { if ( matchboxAmplitude() ) return matchboxAmplitude()->haveOneLoop(); return false; } bool MatchboxMEBase::onlyOneLoop() const { if ( matchboxAmplitude() ) return matchboxAmplitude()->onlyOneLoop(); return false; } bool MatchboxMEBase::isDRbar() const { if ( matchboxAmplitude() ) return matchboxAmplitude()->isDRbar(); return false; } bool MatchboxMEBase::isDR() const { if ( matchboxAmplitude() ) return matchboxAmplitude()->isDR(); return false; } bool MatchboxMEBase::isCS() const { if ( matchboxAmplitude() ) return matchboxAmplitude()->isCS(); return false; } bool MatchboxMEBase::isBDK() const { if ( matchboxAmplitude() ) return matchboxAmplitude()->isBDK(); return false; } bool MatchboxMEBase::isExpanded() const { if ( matchboxAmplitude() ) return matchboxAmplitude()->isExpanded(); return false; } Energy2 MatchboxMEBase::mu2() const { if ( matchboxAmplitude() ) return matchboxAmplitude()->mu2(); return 0*GeV2; } double MatchboxMEBase::oneLoopDoublePole() const { if ( matchboxAmplitude() ) { return matchboxAmplitude()->oneLoopDoublePole()* me2Norm(1); } return 0.; } double MatchboxMEBase::oneLoopSinglePole() const { if ( matchboxAmplitude() ) { return matchboxAmplitude()->oneLoopSinglePole()* me2Norm(1); } return 0.; } vector MatchboxMEBase::getDipoles(const vector& dipoles, const vector & borns,bool slim) const { vector res; // keep track of the dipoles we already did set up set,int>,pair::tptr,Ptr::tptr> > > done; cPDVector rep = diagrams().front()->partons(); int nreal = rep.size(); // now loop over configs for ( int emitter = 0; emitter < nreal; ++emitter ) { list matchDipoles; for ( auto const & d : dipoles ) { if ( ! d->canHandleEmitter(rep,emitter) ) continue; matchDipoles.push_back(d); } if ( matchDipoles.empty() ) continue; for ( int emission = 2; emission < nreal; ++emission ) { if ( emission == emitter ) continue; list matchDipoles2; for ( auto const & d : matchDipoles ) { if ( !d->canHandleSplitting(rep,emitter,emission) ) continue; matchDipoles2.push_back(d); } if ( matchDipoles2.empty() ) continue; map::ptr,SubtractionDipole::MergeInfo> mergeInfo; for ( auto const & d : diagrams() ) { Ptr::ptr check = new_ptr(Tree2toNDiagram(*dynamic_ptr_cast::ptr>(d))); map theMergeLegs; for ( unsigned int i = 0; i < check->external().size(); ++i ) theMergeLegs[i] = -1; int theEmitter = check->mergeEmission(emitter,emission,theMergeLegs); // no underlying Born if ( theEmitter == -1 ) continue; SubtractionDipole::MergeInfo info; info.diagram = check; info.emitter = theEmitter; info.mergeLegs = theMergeLegs; mergeInfo[d] = info; } if ( mergeInfo.empty() ) continue; for ( int spectator = 0; spectator < nreal; ++spectator ) { if ( spectator == emitter || spectator == emission ) continue; list matchDipoles3; for ( auto const & d : matchDipoles2 ) { if ( ! d->canHandleSpectator(rep,spectator) ) continue; matchDipoles3.push_back(d); } if ( matchDipoles3.empty() ) continue; if ( noDipole(emitter,emission,spectator) ) continue; for ( auto const & d : matchDipoles3 ) { if ( !d->canHandle(rep,emitter,emission,spectator) ) continue; for ( auto const & b : borns ) { if ( b->onlyOneLoop() ) continue; if ( done.find(make_pair(make_pair(make_pair(emitter,emission),spectator),make_pair(b,d))) != done.end() ) continue; // now get to work d->clearBookkeeping(); d->realEmitter(emitter); d->realEmission(emission); d->realSpectator(spectator); d->realEmissionME(const_cast(this)); d->underlyingBornME(b); d->setupBookkeeping(mergeInfo,slim); if ( ! d->empty() ) { res.push_back( d->cloneMe() ); Ptr::tptr nDipole = res.back(); done.insert(make_pair(make_pair(make_pair(emitter,emission),spectator),make_pair(b,d))); if ( nDipole->isSymmetric() ) done.insert(make_pair(make_pair(make_pair(emission,emitter),spectator),make_pair(b,d))); ostringstream dname; if ( theMerger) { dname << fullName(); if (theOneLoopNoBorn) dname << ".virtual" << "." ; dname << b->name() << "." << d->name() << ".[(" << emitter << "," << emission << ")," << spectator << "]"; } else { dname << fullName() << "." << b->name() << "." << d->name() << ".[(" << emitter << "," << emission << ")," << spectator << "]"; } if ( ! (generator()->preinitRegister(nDipole,dname.str()) ) ) throw Exception() << "MatchboxMEBase::getDipoles(): Dipole " << dname.str() << " already existing." << Exception::runerror; if ( !factory()->reweighters().empty() ) { for ( auto const & rw : factory()->reweighters()) nDipole->addReweighter(rw); } if ( !factory()->preweighters().empty() ) { for ( auto const & rw : factory()->preweighters() ) nDipole->addPreweighter(rw); } nDipole->cloneDependencies(dname.str(),slim); } } } } } } vector::tptr> partners; copy(res.begin(),res.end(),back_inserter(partners)); for ( auto const & d : res ) d->partnerDipoles(partners); return res; } double MatchboxMEBase::colourCorrelatedME2(pair ij) const { if ( matchboxAmplitude() ) { if ( matchboxAmplitude()->treeAmplitudes() ) matchboxAmplitude()->prepareAmplitudes(this); double res = matchboxAmplitude()->colourCorrelatedME2(ij)* me2Norm(); return res; } throw Exception() << "MatchboxMEBase::colourCorrelatedME2() expects a MatchboxAmplitude object.\n" << "Please check your setup." << Exception::runerror; return 0.; } double MatchboxMEBase::largeNColourCorrelatedME2(pair ij, Ptr::tptr largeNBasis) const { if ( matchboxAmplitude() ) { if ( matchboxAmplitude()->treeAmplitudes() ) { largeNBasis->prepare(mePartonData(),false); matchboxAmplitude()->prepareAmplitudes(this); } double res = matchboxAmplitude()->largeNColourCorrelatedME2(ij,largeNBasis)* me2Norm(); return res; } throw Exception() << "MatchboxMEBase::largeNColourCorrelatedME2() expects a MatchboxAmplitude object.\n" << "Please check your setup." << Exception::runerror; return 0.; } double MatchboxMEBase::spinColourCorrelatedME2(pair ij, const SpinCorrelationTensor& c) const { if ( matchboxAmplitude() ) { if ( matchboxAmplitude()->treeAmplitudes() ) matchboxAmplitude()->prepareAmplitudes(this); double res = matchboxAmplitude()->spinColourCorrelatedME2(ij,c)* me2Norm(); return res; } throw Exception() << "MatchboxMEBase::spinColourCorrelatedME2() expects a MatchboxAmplitude object.\n" << "Please check your setup." << Exception::runerror; return 0.; } double MatchboxMEBase::spinCorrelatedME2(pair ij, const SpinCorrelationTensor& c) const { if ( matchboxAmplitude() ) { if ( matchboxAmplitude()->treeAmplitudes() ) matchboxAmplitude()->prepareAmplitudes(this); double res = matchboxAmplitude()->spinCorrelatedME2(ij,c)* me2Norm(); return res; } throw Exception() << "MatchboxMEBase::spinCorrelatedME2() expects a MatchboxAmplitude object.\n" << "Please check your setup." << Exception::runerror; return 0.; } void MatchboxMEBase::flushCaches() { if ( theMerger )theMerger->flushCaches(); MEBase::flushCaches(); if ( matchboxAmplitude() ) matchboxAmplitude()->flushCaches(); for ( auto const & r : reweights() ) r->flushCaches(); for ( auto const & v : virtuals()) v->flushCaches(); } void MatchboxMEBase::setKinematics() { MEBase::setKinematics(); if ( theMerger ) theMerger->setKinematics(); } void MatchboxMEBase::clearKinematics() { MEBase::clearKinematics(); if ( theMerger ) theMerger->clearKinematics(); } const MergerBasePtr MatchboxMEBase::merger() const { return theMerger; } MergerBasePtr MatchboxMEBase::merger() { return theMerger; } void MatchboxMEBase::merger(MergerBasePtr v) { theMerger = v; } void MatchboxMEBase::print(ostream& os) const { os << "--- MatchboxMEBase setup -------------------------------------------------------\n"; os << " '" << name() << "' for subprocess:\n"; os << " "; for ( PDVector::const_iterator pp = subProcess().legs.begin(); pp != subProcess().legs.end(); ++pp ) { os << (**pp).PDGName() << " "; if ( pp == subProcess().legs.begin() + 1 ) os << "-> "; } os << "\n"; os << " including " << (oneLoop() ? "" : "no ") << "virtual corrections"; if ( oneLoopNoBorn() ) os << " without Born contributions"; if ( oneLoopNoLoops() ) os << " without loop contributions"; os << "\n"; if ( oneLoop() && !onlyOneLoop() ) { os << " using insertion operators\n"; for ( vector::ptr>::const_iterator v = virtuals().begin(); v != virtuals().end(); ++v ) { os << " '" << (**v).name() << "' with " << ((**v).isDR() ? "" : "C") << "DR/"; if ( (**v).isCS() ) os << "CS"; if ( (**v).isBDK() ) os << "BDK"; if ( (**v).isExpanded() ) os << "expanded"; os << " conventions\n"; } } os << "--------------------------------------------------------------------------------\n"; os << flush; } void MatchboxMEBase::printLastEvent(ostream& os) const { os << "--- MatchboxMEBase last event information --------------------------------------\n"; os << " for matrix element '" << name() << "'\n"; os << " process considered:\n "; int in = 0; for ( cPDVector::const_iterator p = mePartonData().begin(); p != mePartonData().end(); ++p ) { os << (**p).PDGName() << " "; if ( ++in == 2 ) os << " -> "; } os << " kinematic environment as set by the XComb " << lastXCombPtr() << ":\n" << " sqrt(shat)/GeV = " << sqrt(lastSHat()/GeV2) << " x1 = " << lastX1() << " x2 = " << lastX2() << " alphaS = " << lastAlphaS() << "\n"; os << " momenta/GeV generated from random numbers\n "; copy(lastXComb().lastRandomNumbers().begin(), lastXComb().lastRandomNumbers().end(),ostream_iterator(os," ")); os << ":\n "; for ( vector::const_iterator p = meMomenta().begin(); p != meMomenta().end(); ++p ) { os << (*p/GeV) << "\n "; } os << "last cross section/nb calculated was:\n " << (lastMECrossSection()/nanobarn) << " (pdf weight " << lastMEPDFWeight() << ")\n"; os << "--------------------------------------------------------------------------------\n"; os << flush; } void MatchboxMEBase::logGenerateKinematics(const double * r) const { if ( !verbose() ) return; generator()->log() << "'" << name() << "' generated kinematics\nfrom " << nDim() << " random numbers:\n"; copy(r,r+nDim(),ostream_iterator(generator()->log()," ")); generator()->log() << "\n"; generator()->log() << "storing phase space information in XComb " << lastXCombPtr() << "\n"; generator()->log() << "generated phase space point (in GeV):\n"; vector::const_iterator pit = meMomenta().begin(); cPDVector::const_iterator dit = mePartonData().begin(); for ( ; pit != meMomenta().end() ; ++pit, ++dit ) generator()->log() << (**dit).PDGName() << " : " << (*pit/GeV) << "\n"; generator()->log() << "with x1 = " << lastX1() << " x2 = " << lastX2() << "\n" << "and Jacobian = " << jacobian() << " sHat/GeV2 = " << (lastSHat()/GeV2) << "\n" << flush; } void MatchboxMEBase::logSetScale() const { if ( !verbose() ) return; generator()->log() << "'" << name() << "' set scales using XComb " << lastXCombPtr() << ":\n" << "scale/GeV2 = " << (scale()/GeV2) << " xi_R = " << renormalizationScaleFactor() << " xi_F = " << factorizationScaleFactor() << "\n" << "alpha_s = " << lastAlphaS() << "\n" << flush; } void MatchboxMEBase::logPDFWeight() const { if ( !verbose() ) return; generator()->log() << "'" << name() << "' calculated pdf weight = " << lastMEPDFWeight() << " from XComb " << lastXCombPtr() << "\n" << "x1 = " << lastX1() << " (" << (mePartonData()[0]->coloured() ? "" : "not ") << "used) " << "x2 = " << lastX2() << " (" << (mePartonData()[1]->coloured() ? "" : "not ") << "used)\n" << flush; } void MatchboxMEBase::logME2() const { if ( !verbose() ) return; generator()->log() << "'" << name() << "' evaluated me2 using XComb " << lastXCombPtr() << "\n" << "and phase space point (in GeV):\n"; vector::const_iterator pit = meMomenta().begin(); cPDVector::const_iterator dit = mePartonData().begin(); for ( ; pit != meMomenta().end() ; ++pit, ++dit ) generator()->log() << (**dit).PDGName() << " : " << (*pit/GeV) << "\n"; generator()->log() << "with x1 = " << lastX1() << " x2 = " << lastX2() << "\n" << "sHat/GeV2 = " << (lastSHat()/GeV2) << "\n" << flush; } void MatchboxMEBase::logDSigHatDR() const { if ( !verbose() ) return; generator()->log() << "'" << name() << "' evaluated cross section using XComb " << lastXCombPtr() << "\n" << "Jacobian = " << jacobian() << " sHat/GeV2 = " << (lastSHat()/GeV2) << " dsig/nb = " << (lastMECrossSection()/nanobarn) << "\n" << flush; } void MatchboxMEBase::cloneDependencies(const std::string& prefix,bool slim) { if ( phasespace() && !slim ) { Ptr::ptr myPhasespace = phasespace()->cloneMe(); ostringstream pname; pname << (prefix == "" ? fullName() : prefix) << "/" << myPhasespace->name(); if ( ! (generator()->preinitRegister(myPhasespace,pname.str()) ) ) throw Exception() << "MatchboxMEBase::cloneDependencies(): Phasespace generator " << pname.str() << " already existing." << Exception::runerror; myPhasespace->cloneDependencies(pname.str()); phasespace(myPhasespace); } theAmplitude = dynamic_ptr_cast::ptr>(amplitude()); if ( matchboxAmplitude() ) { Ptr::ptr myAmplitude = matchboxAmplitude()->cloneMe(); ostringstream pname; pname << (prefix == "" ? fullName() : prefix) << "/" << myAmplitude->name(); if ( ! (generator()->preinitRegister(myAmplitude,pname.str()) ) ){ throw Exception() << "MatchboxMEBase::cloneDependencies(): Amplitude " << pname.str() << " already existing." << Exception::runerror; } myAmplitude->cloneDependencies(pname.str(),slim); matchboxAmplitude(myAmplitude); amplitude(myAmplitude); matchboxAmplitude()->orderInGs(orderInAlphaS()); matchboxAmplitude()->orderInGem(orderInAlphaEW()); } if ( scaleChoice() &&!slim ) { Ptr::ptr myScaleChoice = scaleChoice()->cloneMe(); ostringstream pname; pname << (prefix == "" ? fullName() : prefix) << "/" << myScaleChoice->name(); if ( ! (generator()->preinitRegister(myScaleChoice,pname.str()) ) ) throw Exception() << "MatchboxMEBase::cloneDependencies(): Scale choice " << pname.str() << " already existing." << Exception::runerror; scaleChoice(myScaleChoice); } for ( auto & rw : theReweights ) { Ptr::ptr myReweight = rw->cloneMe(); ostringstream pname; pname << (prefix == "" ? fullName() : prefix) << "/" << rw->name(); if ( ! (generator()->preinitRegister(myReweight,pname.str()) ) ) throw Exception() << "MatchboxMEBase::cloneDependencies(): Reweight " << pname.str() << " already existing." << Exception::runerror; myReweight->cloneDependencies(pname.str()); rw = myReweight; } for ( auto & v : virtuals()) { Ptr::ptr myIOP = v->cloneMe(); ostringstream pname; pname << (prefix == "" ? fullName() : prefix) << "/" << v->name(); if ( ! (generator()->preinitRegister(myIOP,pname.str()) ) ) throw Exception() << "MatchboxMEBase::cloneDependencies(): Insertion operator " << pname.str() << " already existing." << Exception::runerror; v = myIOP; } } void MatchboxMEBase::prepareXComb(MatchboxXCombData& xc) const { // fixme We need to pass on the partons from the xcmob here, not // assuming one subprocess per matrix element if ( phasespace() ) xc.nDimPhasespace(phasespace()->nDim(diagrams().front()->partons())); if ( matchboxAmplitude() ) { xc.nDimAmplitude(matchboxAmplitude()->nDimAdditional()); if ( matchboxAmplitude()->colourBasis() ) { size_t cdim = matchboxAmplitude()->colourBasis()->prepare(diagrams(),noCorrelations()); xc.colourBasisDim(cdim); } if ( matchboxAmplitude()->isExternal() ) { xc.externalId(matchboxAmplitude()->externalId(diagrams().front()->partons())); } } int insertionAdd = 0; for ( auto const & v : virtuals() ) insertionAdd = max(insertionAdd,v->nDimAdditional()); xc.nDimInsertions(insertionAdd); xc.nLight(getNLight()); if(xc.nLightJetVec().empty()) for (auto const & id : getNLightJetVec()) xc.nLightJetVec( id ); if(xc.nHeavyJetVec().empty()) for (auto const & id :getNHeavyJetVec()) xc.nHeavyJetVec(id); if(xc.nLightProtonVec().empty()) for (auto const & id : getNLightProtonVec()) xc.nLightProtonVec(id); xc.olpId(olpProcess()); if ( initVerbose() ) { ostringstream fname_strm; // only allow alphanumeric, / and _ in filename for (const char c : name()) { switch (c) { case '+' : fname_strm << "+"; break; case '-' : fname_strm << "-"; break; case '~' : fname_strm << "_tilde"; break; case ']' : break; case ',' : fname_strm << "__"; break; default : fname_strm << (isalnum(c) ? c : '_'); break; } } fname_strm << ".diagrams"; const string fname = fname_strm.str(); ifstream test(fname.c_str()); if ( !test ) { test.close(); ofstream out(fname.c_str()); for ( vector::ptr>::const_iterator d = diagrams().begin(); d != diagrams().end(); ++d ) { DiagramDrawer::drawDiag(out,dynamic_cast(**d)); out << "\n"; } } } } StdXCombPtr MatchboxMEBase::makeXComb(Energy newMaxEnergy, const cPDPair & inc, tEHPtr newEventHandler,tSubHdlPtr newSubProcessHandler, tPExtrPtr newExtractor, tCascHdlPtr newCKKW, const PBPair & newPartonBins, tCutsPtr newCuts, const DiagramVector & newDiagrams, bool mir, const PartonPairVec&, tStdXCombPtr newHead, tMEPtr newME) { if ( !newME ) newME = this; Ptr::ptr xc = new_ptr(MatchboxXComb(newMaxEnergy, inc, newEventHandler, newSubProcessHandler, newExtractor, newCKKW, newPartonBins, newCuts, newME, newDiagrams, mir, newHead)); prepareXComb(*xc); return xc; } StdXCombPtr MatchboxMEBase::makeXComb(tStdXCombPtr newHead, const PBPair & newPartonBins, const DiagramVector & newDiagrams, tMEPtr newME) { if ( !newME ) newME = this; Ptr::ptr xc = new_ptr(MatchboxXComb(newHead, newPartonBins, newME, newDiagrams)); prepareXComb(*xc); return xc; } void MatchboxMEBase::persistentOutput(PersistentOStream & os) const { os << theLastXComb << thePhasespace << theAmplitude << theScaleChoice << theVirtuals << theReweights << theSubprocess << theOneLoop << theOneLoopNoBorn << theOneLoopNoLoops << epsilonSquarePoleHistograms << epsilonPoleHistograms << theMerger << theOLPProcess << theNoCorrelations << theHavePDFs << checkedPDFs; } void MatchboxMEBase::persistentInput(PersistentIStream & is, int) { is >> theLastXComb >> thePhasespace >> theAmplitude >> theScaleChoice >> theVirtuals >> theReweights >> theSubprocess >> theOneLoop >> theOneLoopNoBorn >> theOneLoopNoLoops >> epsilonSquarePoleHistograms >> epsilonPoleHistograms >> theMerger >> theOLPProcess >> theNoCorrelations >> theHavePDFs >> checkedPDFs; lastMatchboxXComb(theLastXComb); } void MatchboxMEBase::Init() { static ClassDocumentation documentation ("MatchboxMEBase is the base class for matrix elements " "in the context of the matchbox NLO interface."); } IBPtr MatchboxMEBase::clone() const { return new_ptr(*this); } IBPtr MatchboxMEBase::fullclone() const { return new_ptr(*this); } void MatchboxMEBase::doinit() { MEBase::doinit(); if ( !theAmplitude ) theAmplitude = dynamic_ptr_cast::ptr>(amplitude()); if ( matchboxAmplitude() ) matchboxAmplitude()->init(); if ( phasespace() ) { phasespace()->init(); matchboxAmplitude()->checkReshuffling(phasespace()); } if ( scaleChoice() ) { scaleChoice()->init(); } for (auto const & rw : theReweights) rw->init(); for (auto const & v : virtuals() ) v->init(); } void MatchboxMEBase::doinitrun() { MEBase::doinitrun(); if ( matchboxAmplitude() ) matchboxAmplitude()->initrun(); if ( phasespace() ) phasespace()->initrun(); if ( scaleChoice() ) scaleChoice()->initrun(); for (auto const & rw : theReweights) rw->initrun(); for (auto const & v : virtuals() ) v->initrun(); } void MatchboxMEBase::dofinish() { MEBase::dofinish(); for (auto const & b : epsilonSquarePoleHistograms ) { b.second.dump(factory()->poleData(),"epsilonSquarePoles-",b.first); } for (auto const & b : epsilonPoleHistograms ) { b.second.dump(factory()->poleData(),"epsilonPoles-",b.first); } } // *** Attention *** The following static variable is needed for the type // description system in ThePEG. Please check that the template arguments // are correct (the class and its base class), and that the constructor // arguments are correct (the class name and the name of the dynamically // loadable library where the class implementation can be found). DescribeClass describeHerwigMatchboxMEBase("Herwig::MatchboxMEBase", "Herwig.so"); diff --git a/MatrixElement/Matchbox/CVolver/ColourFlowBasis.cc b/MatrixElement/Matchbox/CVolver/ColourFlowBasis.cc --- a/MatrixElement/Matchbox/CVolver/ColourFlowBasis.cc +++ b/MatrixElement/Matchbox/CVolver/ColourFlowBasis.cc @@ -1,248 +1,276 @@ // -*- C++ -*- // // ColourFlowBasis.cc is a part of CVolver // Copyright (C) 2013-2017 Simon Platzer, The Herwig Collaboration // // CVolver 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 ColourFlowBasis class. // #include "ColourFlowBasis.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/EventRecord/Particle.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace CVolver; ColourFlowBasis::ColourFlowBasis() {} ColourFlowBasis::~ColourFlowBasis() {} IBPtr ColourFlowBasis::clone() const { return new_ptr(*this); } IBPtr ColourFlowBasis::fullclone() const { return new_ptr(*this); } void ColourFlowBasis::clear() { ColourBasis::clear(); } map > > ColourFlowBasis::basisList(const vector& basisId) const { assert(theCrossings.find(basisId) != theCrossings.end()); const ColourFlowCrossing& crossing = theCrossings.find(basisId)->second; size_t n = crossing.nFlows(); assert(theFlows.find(n) != theFlows.end()); map > > res; const vector& flows = theFlows.find(n)->second; for ( size_t n = 0; n < flows.size(); ++n ) { vector fundamental; vector antiFundamental; for ( size_t k = 0; k < flows[n].nLegs(); ++k ) { fundamental.push_back(crossing.colourLeg(k)); size_t ac = flows[n].antiColour(k); antiFundamental.push_back(crossing.antiColourLeg(ac)); } res[n].push_back(fundamental); res[n].push_back(antiFundamental); } return res; } size_t ColourFlowBasis::prepareBasis(const vector& sub) { useMe(); map,ColourFlowCrossing>::const_iterator cross = theCrossings.find(sub); if ( cross != theCrossings.end() ) return cross->second.nFlows(); ColourFlowCrossing newCrossing(sub,false); theCrossings[sub] = newCrossing; if ( theFlows.find(newCrossing.nFlows()) != theFlows.end() ) return newCrossing.nFlows(); set flows = ColourFlow::allFlows(newCrossing.nFlows()); copy(flows.begin(),flows.end(), back_inserter(theFlows[newCrossing.nFlows()])); return newCrossing.nFlows(); } void ColourFlowBasis::readBasisDetails(const vector& sub) { prepareBasis(sub); } double ColourFlowBasis::scalarProduct(size_t i, size_t j, const vector& abBasis) const { if ( largeN() && i != j ) return 0.; assert(theCrossings.find(abBasis) != theCrossings.end()); size_t n = theCrossings.find(abBasis)->second.nFlows(); assert(theFlows.find(n) != theFlows.end()); const ColourFlow& iflow = theFlows.find(n)->second[i]; const ColourFlow& jflow = theFlows.find(n)->second[j]; return pow(3.,(double)(iflow.scalarProduct(jflow))); } double ColourFlowBasis::tMatrixElement(size_t m, size_t a, size_t b, const vector& aBasis, - const vector& bBasis) const { + const vector& bBasis, + size_t k, size_t l, + const map& dict + ) const { + // Check indices k and l + assert( k == m ); + assert( l == bBasis.size() ); + // Check that dict is the standardMap + assert( dict.size()+1 == bBasis.size() ); + map::const_iterator tmp; + for ( size_t ii = 0; ii < bBasis.size(); ii++ ) + if ( ii != m ) { + tmp = dict.find(ii); + assert( tmp != dict.end() ); + assert( tmp->second == ii ); + } assert(theCrossings.find(bBasis) != theCrossings.end()); const ColourFlowCrossing& bcrossing = theCrossings.find(bBasis)->second; assert(theFlows.find(bcrossing.nFlows()) != theFlows.end()); const ColourFlow& bflow = theFlows.find(bcrossing.nFlows())->second[b]; assert(theCrossings.find(aBasis) != theCrossings.end()); const ColourFlowCrossing& acrossing = theCrossings.find(aBasis)->second; assert(theFlows.find(acrossing.nFlows()) != theFlows.end()); const ColourFlow& aflow = theFlows.find(acrossing.nFlows())->second[a]; size_t bEmitterLine = bBasis[m] == PDT::Colour3 || bBasis[m] == PDT::Colour8 ? bcrossing.colourLine(m) : bcrossing.antiColourLine(m); size_t bSpectatorLine = bBasis[m] == PDT::Colour3 || bBasis[m] == PDT::Colour8 ? bflow.antiColour(bEmitterLine) : bflow.colour(bEmitterLine); size_t aEmitterLine = bBasis[m] == PDT::Colour3 || bBasis[m] == PDT::Colour8 ? acrossing.colourLine(m) : acrossing.antiColourLine(m); size_t aSpectatorLine = bBasis[m] == PDT::Colour3 || bBasis[m] == PDT::Colour8 ? aflow.antiColour(aEmitterLine) : aflow.colour(aEmitterLine); assert(aEmitterLine == bEmitterLine); if ( bBasis[m] == PDT::Colour3 ) { if ( bSpectatorLine != aSpectatorLine ) { return 1./2.; } else { return -1./2./3.; } } if ( bBasis[m] == PDT::Colour3bar ) { if ( bSpectatorLine != aSpectatorLine ) { return -1./2.; } else { return 1./2./3.; } } if ( bBasis[m] == PDT::Colour8 ) { if ( bSpectatorLine != aSpectatorLine ) { return 1.; } else { return -1.; } } return 0.0; } +double ColourFlowBasis::sMatrixElement(size_t, size_t, size_t, + const vector&, + const vector&, + size_t, size_t, + const map&) const { + + throw Exception() << "ATTENTION this is missing on the CVolver API" + << Exception::runerror; + + return 0.; + +} + bool ColourFlowBasis::colourConnected(const cPDVector& sub, const vector& basisId, const pair& first, const pair& second, size_t tensor) const { assert(theCrossings.find(basisId) != theCrossings.end()); const ColourFlowCrossing& crossing = theCrossings.find(basisId)->second; // translate process to basis ids map >::const_iterator trans = indexMap().find(sub); assert(trans != indexMap().end()); size_t idColoured = first.second ? second.first : first.first; idColoured = trans->second.find(idColoured)->second; size_t idAntiColoured = first.second ? first.first : second.first; idAntiColoured = trans->second.find(idAntiColoured)->second; size_t colourLine = crossing.colourLine(idColoured); size_t antiColourLine = crossing.antiColourLine(idAntiColoured); size_t n = crossing.nFlows(); assert(theFlows.find(n) != theFlows.end()); const ColourFlow& iflow = theFlows.find(n)->second[tensor]; return antiColourLine == iflow.antiColour(colourLine); } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void ColourFlowBasis::persistentOutput(PersistentOStream &) const {} void ColourFlowBasis::persistentInput(PersistentIStream & , int) {} // *** Attention *** The following static variable is needed for the type // description system in ThePEG. Please check that the template arguments // are correct (the class and its base class), and that the constructor // arguments are correct (the class name and the name of the dynamically // loadable library where the class implementation can be found). DescribeClass describeColourFlowBasis("CVolver::ColourFlowBasis", "HwCVolver.so"); void ColourFlowBasis::Init() { static ClassDocumentation documentation ("ColourFlowBasis implements the colour flow basis.", "The colour algebra has been performed using CVolver \\cite{Platzer:2013fha}", "%\\cite{Platzer:2013fha}\n" "\\bibitem{Platzer:2013fha}\n" "S.~Platzer,\n" "``Summming Large-N Towers in Colour Flow Evolution,''\n" "arXiv:1312.2448 [hep-ph].\n" "%%CITATION = ARXIV:1312.2448;%%"); } diff --git a/MatrixElement/Matchbox/CVolver/ColourFlowBasis.h b/MatrixElement/Matchbox/CVolver/ColourFlowBasis.h --- a/MatrixElement/Matchbox/CVolver/ColourFlowBasis.h +++ b/MatrixElement/Matchbox/CVolver/ColourFlowBasis.h @@ -1,211 +1,231 @@ // -*- C++ -*- // // ColourFlowBasis.h is a part of CVolver // Copyright (C) 2013-2017 Simon Platzer, The Herwig Collaboration // // CVolver is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // #ifndef CVOLVER_ColourFlowBasis_H #define CVOLVER_ColourFlowBasis_H // // This is the declaration of the ColourFlowBasis class. // #include "Herwig/MatrixElement/Matchbox/Utility/ColourBasis.h" #include "ColourFlows.h" namespace CVolver { using namespace ThePEG; using namespace Herwig; /** * Specify particle data traits for ThePEG */ template<> struct ParticleDataTraits { /** * Return true, if singlet */ static bool isSinglet(const ThePEG::PDT::Colour& pd) { return pd == PDT::Colour0; } /** * Return true, if anti-fundamental */ static bool isAntiFundamental(const ThePEG::PDT::Colour& pd) { return pd == PDT::Colour3bar; } /** * Return true, if fundamental */ static bool isFundamental(const ThePEG::PDT::Colour& pd) { return pd == PDT::Colour3; } /** * Return true, if adjoint */ static bool isAdjoint(const ThePEG::PDT::Colour& pd) { return pd == PDT::Colour8; } }; /** * ColourFlowBasis implements the colour flow basis. * * @see \ref ColourFlowBasisInterfaces "The interfaces" * defined for ColourFlowBasis. */ class ColourFlowBasis: public Herwig::ColourBasis { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ ColourFlowBasis(); /** * The destructor. */ virtual ~ColourFlowBasis(); //@} public: /** * Clear this colour basis */ virtual void clear(); /** * Return a map of basis tensor indices to vectors identifying a * certain ordering corresponding to the given colour structure. May * not be supported by all colour basis implementations. */ virtual map > > basisList(const vector&) const; /** * Prepare the basis for the normal ordered legs and return the * dimensionality of the basis. */ virtual size_t prepareBasis(const vector&); /** * Gather any implementation dependend details when reading a basis */ virtual void readBasisDetails(const vector&); /** * Return the scalar product of basis tensors labelled a and b in * the basis used for the given normal ordered legs. */ virtual double scalarProduct(size_t a, size_t b, const vector& abBasis) const; /** * Return the matrix element of a colour charge * between basis tensors a and b, with * respect to aBasis and bBasis */ virtual double tMatrixElement(size_t i, size_t a, size_t b, const vector& aBasis, - const vector& bBasis) const; + const vector& bBasis, + size_t k, size_t l, + const map& dict) const; + + /** + * Return true, if this colour basis supports gluon splittings. + */ + virtual bool canSplitGluons() const { + return false; + } + + /** + * Return the matrix element of a quark splitting matrix + * between basis tensors a and b, with + * respect to aBasis and bBasis + */ + virtual double sMatrixElement(size_t i, size_t a, size_t b, + const vector& aBasis, + const vector& bBasis, + size_t k, size_t l, + const map& dict) const; /** * Return true, if the colour basis is capable of assigning colour * flows. */ virtual bool haveColourFlows() const { return true; } /** * Return true, if a large-N colour connection exists for the * given external legs and basis tensor. */ virtual bool colourConnected(const cPDVector&, const vector&, const pair&, const pair&, size_t) const; public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * Map colour signatures to colour crossings */ map,ColourFlowCrossing> theCrossings; /** * Colour flows indexed by basis size; note this is a unique * assignment */ map > theFlows; private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ ColourFlowBasis & operator=(const ColourFlowBasis &) = delete; }; } #endif /* CVOLVER_ColourFlowBasis_H */ diff --git a/MatrixElement/Matchbox/ColorFull/Col_amp.cc b/MatrixElement/Matchbox/ColorFull/Col_amp.cc --- a/MatrixElement/Matchbox/ColorFull/Col_amp.cc +++ b/MatrixElement/Matchbox/ColorFull/Col_amp.cc @@ -1,1143 +1,1210 @@ // -*- C++ -*- /* * Col_amp.cc * Contains the definition of the class Col_amp and related operators * Author: Malin Sjodahl */ #include "Col_amp.h" #include #include #include namespace ColorFull { std::ostream& operator<<(std::ostream& out, const col_amp & ca) { int max = ca.size(); for (int i = 0; i < max; i++) { if(i != 0) out <<" + "; out << ca.at(i) ; } return out; } void Col_amp::normal_order_col_strs() { for (uint m = 0; m < ca.size(); m++) { ca.at(m).normal_order(); } } void Col_amp::normal_order() { // First normal order the Col_strs normal_order_col_strs(); // To contain the Col_strs in order col_amp ca_ordered; // Order the different Col_strs in the Col_amp // Do this by moving the Cs one by one to ca_ordered while ( ca.size() > 0 ) { // Next Cs to put in place (the last Cs in ca) Col_str Cs_next = ca.at( ca.size() - 1 ); // Then insert the Cs among the ordered Col_strs // Count how many steps left Cs_next should be moved in ca_ordered uint steps_left = 0; while ( (steps_left < (ca_ordered.size())) && Cs_next.smallest( Cs_next, ca_ordered.at(ca_ordered.size()-1-steps_left )) ==1) { steps_left++; } // Insert the Cs in the right place among the ordered Col_strs col_amp::iterator it=ca_ordered.end()-steps_left; ca_ordered.insert( it, Cs_next); // Erase the Cs from ca ca.erase( ca.end()-1 ) ; } ca=ca_ordered; } void Col_amp::erase( int i ) { ca.erase(ca.begin() + i); } -void Col_amp::append(col_amp ca_in) { +void Col_amp::append( col_amp ca_in ) { for (uint m = 0; m < ca_in.size(); m++) { ca.push_back(ca_in.at(m)); } } void Col_amp::read_in_Col_amp( std::string filename) { // Read in file std::ifstream fin( filename.c_str() ); // Check that file exists if( !fin ){ std::cerr << "Col_amp::read_in_Col_amp: The file " << filename << " could not be opened." << std::endl; assert( 0 ); } // Erase current information ca.clear(); Scalar.clear(); // Copy info from file to string std::string str((std::istreambuf_iterator(fin)), std::istreambuf_iterator()); - str.erase(str.size()-1); + + // Skip lines starting with # + while( str.at(0)== '#' ){ + while (str.at(0) != '\n'){ + str.erase(str.begin()); + } + // erase endl sign(s) + while(str.at(0)== '\n'){ + str.erase(str.begin()); + } + } + + // Remove endl chars at the end of the file + while( str.at(str.size()-1) == '\n' ) str.erase(str.size()-1); + Col_amp_of_str( str ); } + void Col_amp::write_out_Col_amp( std::string filename ) const { if ((ca.size() == 0)) { std::cout << "Col_amp::write_out_Col_amp: The Col_amp is empty." << std::endl; std::cout.flush(); return; } std::ofstream outfile(filename.c_str()); if ( !outfile ) std::cerr << "Col_amp::write_out_Col_amp: Cannot write out Col_amp as the file \"" << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; outfile << *this; } void Col_amp::Col_amp_of_str( const std::string str ){ // First split the string into Col_str and Polynomial part uint j=0; // Check that left and right normal brackets match up int left_brackets=0,right_brackets=0; while (j < str.size()) { if(str.at(j)=='(') left_brackets++; if(str.at(j)==')') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Col_amp::Col_amp_of_str: The normal brackets, (), in the Col_amp \"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } // Check that left and right curly brackets match up left_brackets=0,right_brackets=0; j=0; while (j < str.size()) { if(str.at(j)=='{') left_brackets++; if(str.at(j)=='}') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Col_amp::Col_amp_of_str: The curly brackets in the Col_amp \"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } // Check that left and right [] brackets match up j=0; left_brackets=0, right_brackets=0; while (j < str.size()) { if(str.at(j)=='[') left_brackets++; if(str.at(j)==']') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Col_amp::Col_amp_of_str: The square brackets, [], in the string \"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } if(left_brackets < 1){ std::cerr << "Col_amp::Col_amp_of_str: The Col_amp string constructor requires at least one Col_str, i.e." << " at least one set of square brackets, []. The string \"" << str << "\" contains " << left_brackets << std::endl; assert( 0 ); } // Find various Col_strs, read until final ] j=0; std::string Cs_string; while (j< str.size() ){ // Skip some white spaces while (j< str.size() and str.at(j)==' ') j++; Cs_string.clear(); while (j< str.size() and str.at(j)!=']'){ Cs_string.push_back( str.at(j) ); j++; } // get fina l ] if( j< str.size() and str.at(j)==']' ) Cs_string.push_back( str.at(j) ); std::cout.flush(); j++; if( !Cs_string.empty() ) ca.push_back( Col_str(Cs_string) ); } Scalar=Scalar*0; } void Col_amp::remove_1_rings() { // Loop Col_strs, if empty, remove for (uint m = 0; m < ca.size(); m++) { // Try removing 1-rings in Col_str // If there was a 1-ring, the col_str will multiply 0 after Quark_line.remove_1_rings() ca.at(m).remove_1_rings(); // If the result is multiplying only 0 (in the form of a monomial=0), remove the Col_str if (ca.at(m).Poly.size() == 1 && ca.at(m).Poly.at(0).int_part == 0) erase(m); } } void Col_amp::remove_0_rings() { // Loop Col_strs, and remove empty Quark_lines for (uint m = 0; m < ca.size(); m++) { // Try removing 0-rings in Col_str ca.at(m).remove_0_rings(); // If the result is empty (no Quark_lines), keep the Polynomial (in scalar), // but remove that Col_str if (ca.at(m).cs.empty()) { - Scalar = Scalar + ca.at(m).Poly; + Scalar += ca.at(m).Poly; erase(m); } } } void Col_amp::remove_empty_Col_strs() { // Loop Col_strs for (uint m = 0; m < ca.size(); m++) { if ( ca.at(m).cs.empty() ){ - Scalar=Scalar+ca.at(m).Poly; + Scalar+=ca.at(m).Poly; erase(m); } } } bool Col_amp::gluons_only() const { // Loop over Col_strs for (uint m = 0; m < ca.size(); m++) { if (!ca.at(m).gluons_only()) return false; } // If all Cs had gluons only return true; } int Col_amp::n_gluon() const { if (ca.size()>0) return ca.at(0).n_gluon(); else{ std::cerr << "Col_amp::n_gluon(): ca has no Col_str " << std::endl; return 0; } } int Col_amp::n_quark() const { if (ca.size()>0) return ca.at(0).n_quark(); else{ std::cerr << "Col_amp::n_quark(): ca has no Col_str " << std::endl; return 0; } } int Col_amp::n_gluon_check() const { int ng=n_gluon(); for(uint n=0; n< ca.size(); n++) if( ca.at(n).n_gluon() != ng) { std::cerr << "Col_amp::n_gluon_check: The Col_strs in " << ca << " have differently many gluons." << std::endl; } return ng; } int Col_amp::n_quark_check() const { int nq=n_quark(); for(uint n=0; n< ca.size(); n++) if( ca.at(n).n_quark() != nq) { std::cerr << "Col_amp::n_quark_check: The Col_strs in " << ca << " have differently many quarks." << std::endl; } return nq; } int Col_amp::longest_quark_line() const{ int length=0; for ( uint j = 0; j < ca.size(); j++ ) { if( static_cast ( at(j).longest_quark_line() )> length ) length=static_cast ( at(j).longest_quark_line() ); } return length; } void Col_amp::collect_col_strs() { normal_order_col_strs(); // Special case of empty Col_amp, do nothing if (ca.empty()) return; // Resulting col_amp col_amp ca_out; // Move first Cs to ca_out ca_out.push_back(ca.at(0)); ca.erase(ca.begin()); // Move elements (starting from the beginning) in ca to ca_out // and sort as long as elements remain to sort while (!ca.empty()) { // Compare next =0th col_str to all terms in ca_out bool was_found = false; for (uint i = 0; (i < ca_out.size() && !was_found); i++) { if (ca.at(0).cs == ca_out.at(i).cs) { was_found = true; // Add Polynomial multiplying the Monomial to Polynomial multiplying // existing term - ca_out.at(i).Poly = ca_out.at(i).Poly + ca.at(0).Poly; + ca_out.at(i).Poly += ca.at(0).Poly; // See if Polynomial can be simplified //ca_out.at(i).Poly=simplify( ca_out.at(i).Poly ); } } // If the Col_str wasn't represented add info if (!was_found) ca_out.push_back(ca.at(0)); // Erase Cs after saving info ca.erase(ca.begin()); } ca = ca_out; } void Col_amp::simplify() { remove_1_rings(); remove_0_rings(); // Simplify scalar factor Scalar.simplify(); // Collect similar col_str's collect_col_strs(); // Loop over Col_strs for (uint m = 0; (m < ca.size() ); m++) { // Simplify Col_strs ca.at(m).simplify(); // If there's only one term in the Polynomial, and the term is 0 // remove Col_str if (ca.at(m).Poly.size() == 1 && ca.at(m).Poly.at(0).int_part == 0){ erase(m); } } } void Col_amp::conjugate( ) { Scalar.conjugate(); // Take conjugate by reversing order of all partons in each Quark_line // Loop over Col_strs for (uint i=0; i < ca.size(); i++ ){ ca.at(i).conjugate(); } } void Col_amp::contract_next_neighboring_gluons( ) { // Loop over Col_str's for (uint m = 0; m < ca.size(); m++) { ca.at(m).contract_next_neighboring_gluons( ); } return; } void Col_amp::contract_2_rings( ){ // Loop over Col_str's for (uint m = 0; m < size(); m++) { at(m).contract_2_rings( ); } return; } void Col_amp::contract_Quark_line_gluons( Quark_line & Ql ) { // Check that all quark indices have been removed if (Ql.open){ std::cerr << "Col_amp::contract_Quark_line_gluons(Ql): all quark indices were not contracted in " << Ql << std::endl; } if( !ca.empty() or (Scalar.size()==!1 or Scalar.at(0).int_part!=0 ) ){ std::cerr << "Col_amp::contract_Quark_line_gluons(Ql): This member function " << "stores the result from contracting the Quark_line in the Col_amp itself. " << "It therefore expects an empty initially Col_amp, but it was:" << *this << std::endl; } // If the Quark_line was empty, return a Col_amp with info in Col_str if (Ql.empty()) { Col_str Cs; // Move Polynomial to Col_str Cs.Poly = Ql.Poly; Ql.Poly.clear(); Cs.cs.push_back(Ql); ca.push_back(Cs); return; } // To keep track if partner was found or not bool found_pair = false; // Take first term and look for partner for (uint j1 = 0; j1 < Ql.ql.size() - 1; j1++) { int the_g = Ql.ql.at(j1); // Look for same g for (uint j2 = j1 + 1; j2 < Ql.ql.size(); j2++) { // if same g found if (Ql.ql.at(j2) == the_g) { // Special case that the gluons are neighbors if (j2 == j1 + 1 or (!Ql.open && j1 == 0 && j2 == Ql.ql.size() - 1)) { Ql.contract_neighboring_gluons( j1 ); // Make a Col_str out of the Quark_line Col_str Cs; Cs.cs.push_back(Ql); ca.push_back(Cs); simplify(); return; } // Special case that the gluons are next to neighbors else if (j2 == j1 + 2 //normal case or (!Ql.open && j1 == 0 && j2 == Ql.ql.size()- 2) //first and second last or (!Ql.open && j1 == 1 && j2 == Ql.ql.size()- 1) //second and last ) { if (j2 == j1 + 2) Ql.contract_next_neighboring_gluons( j1 ); if ((!Ql.open && j1 == 0 && j2 == Ql.ql.size() - 2)) Ql.contract_next_neighboring_gluons( Ql.ql.size() - 2); if( (!Ql.open && j1 == 1 && j2 == Ql.ql.size()- 1) ) Ql.contract_next_neighboring_gluons( Ql.ql.size() - 1); // Make a Col_str out of the Quark_line Col_str Cs; Cs.cs.push_back(Ql); ca.push_back(Cs); simplify(); return; } // normal case, not neighbors or next to neighbors else{ // Make a Col_str out of the Quark_line Col_str Cs; // Move color factor to multiply Cs instead of Ql Cs.Poly = Ql.Poly; Ql.Poly.clear(); Cs.cs.push_back(Ql); // Make two copies to store both terms ca.push_back(Cs); ca.push_back(Cs); std::pair Ql_parts=ca.at(0).cs.at(0).split_Quark_line( j1, j2 ); Col_str Cs_Ql_parts; Cs_Ql_parts.cs.push_back(Ql_parts.first); Cs_Ql_parts.cs.push_back(Ql_parts.second); ca.at(0)=Cs_Ql_parts; // This is multiplying a factor TR Monomial Mon_tmp; Mon_tmp.pow_TR = 1; - ca.at(0).Poly = ca.at(0).Poly * Mon_tmp; + ca.at(0).Poly *= Mon_tmp; // The split can generate new (next to) neighbors ca.at(0).contract_next_neighboring_gluons( ); // The second Nc suppressed term, obtained by just removing gluon // remove g quark_line::iterator it1 = ca.at(1).cs.at(0).ql.begin() + j1; quark_line::iterator it2 = ca.at(1).cs.at(0).ql.begin() + j2; ca.at(1).cs.at(0).ql.erase(it2); ca.at(1).cs.at(0).ql.erase(it1); // Multiply with -TR/(Nc) Mon_tmp.pow_TR = 1; Mon_tmp.pow_Nc = -1; Mon_tmp.int_part = -1; - ca.at(1).Poly = ca.at(1).Poly * Mon_tmp; + ca.at(1).Poly *= Mon_tmp; // A pair was found, use for stopping found_pair = true; } } if (found_pair) break; // Stop j2 loop } if (found_pair) break; // Stop j1 loop } // If a pair was never found return the info of the original Ql // but with the Polynomial moved to the Col_str if(!found_pair){ Col_str Cs; Cs.Poly=Ql.Poly; Ql.Poly.clear(); Cs.cs.push_back(Ql); ca.push_back(Cs); } // Remove possible 0 and 1-rings remove_1_rings(); remove_0_rings(); return; } void Col_amp::contract_Quark_line_gluons( Col_str & Cs ) { if( !ca.empty() or (Scalar.size()==!1 or Scalar.at(0).int_part!=0 ) ){ std::cerr << "Col_amp::contract_Quark_line_gluons(Cs): This member function " << "stores the result from contracting the Quark_line in the Col_amp itself. " << "It therefore expects an empty initially Col_amp, but it was:" << *this << std::endl; } Cs.remove_1_rings(); Cs.remove_0_rings(); // If the Cs is empty there is no color structure if( Cs.empty() ) { ca.push_back(Cs); return; } // Make sure to get Poly, both from Cs and Ql.at(0) // First the result of the contracted Quakr_line is stored in this Col_amp contract_Quark_line_gluons( Cs.at(0) ); // if the Col_str had a Polynomial we multiply with it here *this=*this*Cs.Poly; // Loop over Quark_lines for( uint i=1; i < Cs.size(); i++ ){ // If the Quark_line is short do nothing with it as no gluons // can not be neighbors or next to neighbors if ( Cs.at(i).size()< 6 ){ - *this=*this*Col_str( Cs.at(i) ); + //*this=*this*Col_str( Cs.at(i) ); + *this=*this*Cs.at(i); + } else{ Col_amp contracted_Ql; contracted_Ql.contract_Quark_line_gluons( Cs.at(i) ); *this*=contracted_Ql; } } simplify(); return; } void Col_amp::contract_Quark_line_gluons( ) { // If all the Col_strs are short nothing can be gained // compared to contract_next_neighboring gluons, do nothing // If there is no remaining color structure, do nothing if ( longest_quark_line() < 6 ) return; // Make a copy of the old ca part (from this Ca) Col_amp Ca_copy; Ca_copy.ca=ca; // Keep the scalar part, but replace the ca part ca.clear(); for ( uint i = 0; i < Ca_copy.size(); i++ ) { Col_amp Ca_from_Cs; Ca_from_Cs.contract_Quark_line_gluons( Ca_copy.at(i) ); *this+=Ca_from_Cs; } } void Col_amp::contract_quarks( const Col_amp & Ca1, const Col_amp & Ca2 ) { if( !ca.empty() or (Scalar.size()==!1 or Scalar.at(0).int_part!=0 ) ){ std::cerr << "Col_amp::contract_quarks(Ca1, Ca2): This member function " << "stores the result from contracting quarks in the Col_amp itself. " << "It therefore expects an empty initially Col_amp, but it was:" << *this << std::endl; } if(Ca1.empty()){ std::cerr << "Col_amp::contract_quarks: Expects non-empty Col_amps, got first argument " << Ca1 << std::endl; assert(0); } if(Ca2.empty()){ std::cerr << "Col_amp::contract_quarks: Expects non-empty Col_amps, got second argument " << Ca2 << std::endl; assert(0); } //Col_amp Ca_res; Col_amp Ca1_copy=Ca1; Col_amp Ca2_copy=Ca2; // Make sure the Col_strs are not empty "[]"=1, as all indices contracted Ca1_copy.remove_empty_Col_strs(); Ca1_copy.remove_empty_Col_strs(); // Loop over Col_strs, and contract quarks between all possible combinations // Loop over Col_strs in Ca1 for(uint m1=0; m1 < Ca1_copy.ca.size(); m1++ ){ // Loop over Col_strs in Ca2 for(uint m2=0; m2 < Ca2_copy.ca.size(); m2++ ){ Col_str Cs_tmp; Cs_tmp.contract_quarks( Ca1_copy.ca.at(m1), Ca2_copy.ca.at(m2)); ca.push_back( Cs_tmp ); } } return; } void Col_amp::contract_a_gluon( Col_str & Cs ) { if( Cs.n_quark()!=0 ){ std::cerr << "Col_amp::contract_a_gluon(Cs): Expects Col_str with gluons only, got Cs" << std::endl; } if( !ca.empty() or (Scalar.size()==!1 or Scalar.at(0).int_part!=0 ) ){ std::cerr << "Col_amp::contract_Quark_line_gluons(Cs): This member function " << "stores the result from contracting the Quark_line in the Col_amp itself. " << "It therefore expects an empty initially Col_amp, but it was:" << *this << std::endl; } //If the Col_str is empty, or the first ql is empty return it as Col_amp if ( Cs.empty() or Cs.cs.at(0).empty() ) { ca.push_back(Cs); return; } // Pick first gluon int the_g = Cs.at(0, 0); std::vector place; place.push_back(0); place.push_back(0); // For storing place of second gluon std::vector place2; // Locate the same gluon for (uint i2 = 0; i2 < Cs.cs.size(); i2++) { for (uint j2 = 0; j2 < Cs.cs.at(i2).ql.size(); j2++) { // Make sure it is a different gluon if ((i2 != 0 or j2 != 0) && Cs.at(i2, j2) == the_g) { place2.push_back(i2); place2.push_back(j2); } } } // If the gluon was not found, something went wrong if (place2.empty()) { std::cerr << "Col_functions::contract_a_gluon: The gluon " << the_g << " was only found once " << Cs; std::cerr.flush(); assert( 0 ); } // If the gluon was found in same Ql, use contract_Ql_gluons if (place.at(0) == place2.at(0)) { contract_Quark_line_gluons( Cs ); return; } // If the removed gluon was part of a two-ring else if( Cs.cs.at(place.at(0)).ql.size()==2 or Cs.cs.at(place2.at(0)).ql.size()==2){ Cs.contract_2_rings( ); ca.push_back(Cs); return; } else { // The result is a sum of two terms, to contain these Col_str Cs1 = Cs; Col_str Cs2; // The Nc suppressed term, obtained by just removing the g // location of first and second g to remove quark_line::iterator it1 = Cs1.cs.at(place.at(0)).ql.begin(); quark_line::iterator it2 = Cs1.cs.at(place2.at(0)).ql.begin() + place2.at(1); // Remove the gluon in both places Cs1.cs.at(place2.at(0)).ql.erase(it2); Cs1.cs.at(place.at(0)).ql.erase(it1); // The non-suppressed term, obtained by joining the ql's (where the g is removed) Cs2 = Cs1; // Construct the contracted ql by // 1, taking the first part of the 2nd ql, by erasing everything after the erased gluon Quark_line ql_first_part= Cs.cs.at(place2.at(0)).before( place2.at(1) ); // 2, the in between part=first ql, the contracted gluon is already erased Quark_line middle_part=Cs2.cs.at(place.at(0)); // 3, Last part equal to second part of 2nd ql Quark_line last_part= Cs.cs.at(place2.at(0)).after( place2.at(1) ); // The new combined Ql, to replace the 2nd involved Qualk_line Quark_line new_Ql=ql_first_part; new_Ql.append( middle_part.ql ); new_Ql.append( last_part.ql ); // Copy Polynomial info, both from first and 2nd involved Ql new_Ql.Poly=Cs.cs.at(place2.at(0)).Poly*Cs.cs.at(place.at(0)).Poly; // Replace the first involved Ql with the constructed Ql Cs2.cs.at(0)=new_Ql; // erase moved ql col_str::iterator it = Cs2.cs.begin() + place2.at(0); Cs2.cs.erase(it); // Multiply with TR for non-suppressed term Monomial Mon_tmp; Mon_tmp.pow_TR = 1; - Cs2.Poly = Cs2.Poly * Mon_tmp; + Cs2.Poly *= Mon_tmp; // Multiply with -TR/Nc for suppressed term Mon_tmp.pow_Nc = -1; Mon_tmp.int_part = -1; - Cs1.Poly = Cs1.Poly * Mon_tmp; + Cs1.Poly *= Mon_tmp; // Look for simple simplifications in Cs1 // This has to be done after Cs1 is used to construct Cs2 // After the removal there may be new next to neighbors in the affected ql's // First affected Ql // look forward at nn Cs1.cs.at(place.at(0)).contract_neighboring_gluons(place.at(0)); // look backward at nn Cs1.cs.at(place.at(0)).contract_neighboring_gluons( place.at(0) - 1); // look forward at next nei Cs1.cs.at(place.at(0)).contract_next_neighboring_gluons( place.at(0) ); // look backward at next nei Cs1.cs.at(place.at(0)).contract_next_neighboring_gluons( place.at(0) - 2); // Second affected ql // look forward at nn Cs1.cs.at(place2.at(0)).contract_neighboring_gluons( place2.at(0)); // look backward at nn Cs1.cs.at(place2.at(0)).contract_neighboring_gluons( place2.at(0) - 1); // look forward at next nei Cs1.cs.at(place2.at(0)).contract_next_neighboring_gluons( place2.at(0) ); // look backward at next nei Cs1.cs.at(place2.at(0)).contract_next_neighboring_gluons( place2.at(0) - 2); ca.push_back(Cs1); ca.push_back(Cs2); // remove rings with just one gluon (=0) remove_1_rings(); // remove rings with no partons (=Nc, if closed) remove_0_rings(); } return; } void Col_amp::contract_a_gluon( ) { // If the Ca has only an empty Col_str or a Col_str with an empty Quark_line, do nothing if( size()==1 && ( at(0).empty() or at(0).cs.at(0).empty())) return ; // Copy ca to copy, not that the ca is in the copy and the Scalar is still in this Ca Col_amp Ca_copy; Ca_copy.ca=ca; ca.clear(); // Make one contraction in each Col_str for ( uint i=0; i< Ca_copy.ca.size(); i++ ) { // Contract one gluon in Col_str Col_amp Ca_part; // The copy has no Scalar, so adding does not double count Scalar Ca_part.contract_a_gluon( Ca_copy.ca.at(i) ); // ... and add resulting Col_amp to Ca_res *this+=Ca_part; } return; } void Col_amp::contract_all_gluons( Col_str & Cs ) { if( !ca.empty() or (Scalar.size()==!1 or Scalar.at(0).int_part!=0 ) ){ std::cerr << "Col_amp::contract_all_gluons(Cs): This member function " << "stores the result from contracting the Quark_line in the Col_amp itself" << "It therefore expects an empty initially Col_amp, but it was:" << *this << std::endl; } ca.push_back( Cs ); contract_all_gluons(); return ; } void Col_amp::contract_all_gluons( ) { //std::cout << "Col_functions::contract_all_gluons: incoming " << Ca << std::endl; // Make sure the Col_strs are not empty "[]"=1, as all indices contracted remove_empty_Col_strs(); remove_empty_Col_strs(); // Check that all quark indices have been removed if ( !gluons_only() ) { std::cerr << "Col_amp::contract_all_gluons(Ca): Error, all quark indices were not contracted in " << *this << std::endl; std::cerr.flush(); assert( 0 ); } // First normal order and look for simple simplification simplify(); Col_amp Ca_old; // For comparison // Contract remaining gluons as long as result keep changing // Start with contractions only giving one term - while (Ca_old != *this) { + while ( Ca_old != *this ) { Ca_old = *this; // Contract gluons from 2-ring as this gives only one term // and may result in new neighbors or next to neighbors contract_2_rings( ); - //std::cout << "Col_functions::contract_all_gluons: after 2 rings " << Ca << std::endl; // Remove neighboring and next to neighboring gluon indices // in each quark_line as this gives only (at most) one term contract_next_neighboring_gluons( ); - //std::cout << "Col_functions::contract_all_gluons: after nn " << Ca << std::endl; + contract_2_rings( ); // Contract gluons within same quark_line // This will give >1 term, but at least rings will be shorter and shorter - contract_Quark_line_gluons( ); - //std::cout << "Col_functions::contract_all_gluons: after Qlg " << Ca << std::endl; - - + contract_Quark_line_gluons( ); + contract_2_rings( ); // To maximize number of CF's contract_next_neighboring_gluons( ); - //std::cout << "Col_functions::contract_all_gluons: after nn " << Ca << std::endl; + contract_2_rings( ); + // Make "arbitrary" gluon contraction when no simple or Ql contraction remains // In each Col_str, contract the first gluon in the first Ql contract_a_gluon( ); + // Look if same Col_str is represented more than once in the Col_amp and simplify simplify(); } //end of while (Ca keeps changing) return; } std::ostream& operator<<(std::ostream& out, const Col_amp & Ca) { int max = Ca.ca.size(); // Write out Scalar part if it is not obviously 0 Polynomial Poly0; // For comparison, the default Polynomial=1 Poly0=Poly0*0; if( ! (Ca.Scalar==Poly0) ) { out << Ca.Scalar << " + "; } // Print color structure if (max == 0) out << "{[]}"; else { for (int i = 0; i < max - 1; i++) { out << Ca.ca.at(i) << " + "; } out << Ca.ca.at(max - 1); } return out; } bool operator==( const Col_amp & Ca1, const Col_amp & Ca2 ) { // The Ca's should have equal length if (Ca1.ca.size() != Ca2.ca.size()) return false; // All Col_strs should be the same for (uint i = 0; i < Ca1.ca.size(); i++) { if (Ca1.ca.at(i) != Ca2.ca.at(i)) return false; } // The scalar part should be the same if (Ca1.Scalar!=Ca2.Scalar) return false; return true; } bool operator!=( const Col_amp & Ca1,const Col_amp & Ca2 ) { if (Ca1 == Ca2) return false; else return true; } Col_amp operator+( const Col_amp & Ca, const Col_str & Cs ){ Col_amp Ca_out(Ca); // Add Col_str Cs to Ca Ca_out.ca.push_back(Cs); return Ca_out; } Col_amp operator+( const Col_str & Cs, const Col_amp & Ca ){ return Ca+Cs; } Col_amp operator+( const Col_amp & Ca1, const Col_amp & Ca2 ){ // To contin the result Col_amp Ca_out; // Add Scalars Ca_out.Scalar = Ca1.Scalar +Ca2.Scalar; // Add col_strs of Ca1 and Ca2 to Ca_out Ca_out.append(Ca1.ca); Ca_out.append(Ca2.ca); return Ca_out; } Col_amp operator+=( Col_amp & Ca1, const Col_amp & Ca2 ){ // Add Scalars Ca1.Scalar+=Ca2.Scalar; // Add col_strs of Ca1 and Ca2 to Ca_out Ca1.append(Ca2.ca); return Ca1; } +Col_amp operator+=( Col_amp & Ca, const Col_str & Cs ){ + + // Add Col_str Cs to Ca + Ca.ca.push_back(Cs); + + return Ca; +} + Col_amp operator-( const Col_amp & Ca1, const Col_amp & Ca2 ) { Col_amp Ca2_out(Ca2); - // Change sign of Ca2 in Polynomial + // Change sign of Ca2 in Scalar Ca2_out.Scalar=Ca2_out.Scalar*(-1); // Loop over Col_strs to change sign for each term for( uint m=0; m < Ca2_out.ca.size(); m++ ) { Ca2_out.ca.at(m).Poly=Ca2_out.ca.at(m).Poly*(-1); } // Now the subtraction is equal to an addition return Ca1+Ca2_out; } +Col_amp operator-( const Col_amp & Ca, const Col_str & Cs ) { + + Col_amp Ca_out(Ca); + + Col_str Cs_tmp=Cs*(-1); + + return Ca_out+Cs_tmp; +} + +Col_amp operator-( const Col_str & Cs, const Col_amp & Ca ) { + + return Ca-Cs; +} + Col_amp operator*( const Col_amp & Ca, const int i ){ Col_amp Ca_res(Ca); // Multiply scalar factor (Polynomial) Ca_res.Scalar=Ca_res.Scalar*i; // Loop over Col_strs and multiply Polynomials for(uint m=0; m< Ca_res.ca.size(); m++){ // Multiply Polynomial of Col_str with the int Ca_res.ca.at(m).Poly=Ca_res.ca.at(m).Poly*i; } return Ca_res; } Col_amp operator*( const int i, const Col_amp & Ca ) { return Ca*i; } Col_amp operator*(const Col_amp & Ca, const cnum c){ Col_amp Ca_res = Ca; // Multiply scalar factor (Polynomial) - Ca_res.Scalar = Ca_res.Scalar * c; + Ca_res.Scalar *= c; // Loop over Col_strs and multiply Polynomials for (uint m = 0; m < Ca_res.ca.size(); m++) { // Multiply Polynomial of Col_str with the complex number - Ca_res.ca.at(m).Poly = Ca_res.ca.at(m).Poly * c; + Ca_res.ca.at(m).Poly *= c; } return Ca_res; } Col_amp operator*( const cnum c, const Col_amp & Ca ){ return Ca*c; } Col_amp operator*( const Col_amp & Ca, const double d ){ Col_amp Ca_res = Ca; // Multiply scalar factor (Polynomial) - Ca_res.Scalar = Ca_res.Scalar * d; + Ca_res.Scalar *= d; // Loop over Col_strs and multiply Polynomials for (uint m = 0; m < Ca_res.ca.size(); m++) { // Multiply Polynomial of Col_str with the complex number - Ca_res.ca.at(m).Poly = Ca_res.ca.at(m).Poly * d; + Ca_res.ca.at(m).Poly *= d; } return Ca_res; } Col_amp operator*( const double d, const Col_amp & Ca ) { return Ca*d; } Col_amp operator*( const Col_amp & Ca, const Monomial & Mon ){ // To contain result Col_amp Ca_out(Ca); // Multiply scalar factor (Polynomial) - Ca_out.Scalar=Ca_out.Scalar*Mon; + Ca_out.Scalar *= Mon; // Loop over Col_strs and multiply Polynomials for(uint m=0; m< Ca_out.ca.size(); m++){ // Multiply Polynomial of Col_str with the complex number - Ca_out.ca.at(m).Poly=Ca_out.ca.at(m).Poly*Mon; + Ca_out.ca.at(m).Poly *= Mon; } return Ca_out; } Col_amp operator*(const Monomial & Mon, const Col_amp & Ca){ return Ca*Mon; } Col_amp operator*(const Col_amp & Ca, const Polynomial & Poly){ Col_amp Ca_out(Ca); // Multiply scalar factor (Polynomial) Ca_out.Scalar=Ca_out.Scalar*Poly; // Loop over Col_strs and multiply Polynomials for(uint m=0; m< Ca_out.ca.size(); m++){ // Multiply Polynomial of Col_str with the Polynomial Ca_out.ca.at(m).Poly=Ca_out.ca.at(m).Poly*Poly; } return Ca_out; } Col_amp operator*( const Polynomial & Poly, const Col_amp & Ca ){ return Ca*Poly; } Col_amp operator*( const Col_amp & Ca, const Col_str & Cs ){ Col_amp Ca_res; // The Scalar part of Ca gives rise to a Col_str=Cs*Scalar when multiplied with Cs // but this should only be kept if the scalar Polynomial is non-zero if(! (Ca.Scalar.size()==1 and Ca.Scalar.at(0).int_part==0) ) { Col_str Cs_tmp= Ca.Scalar*Cs; Ca_res=Ca_res+Cs_tmp; } // Multiply each Col_str in Col_amp with Cs for(uint m=0; m< Ca.ca.size(); m++){ // Multiply Col_str in Col_amp with the Col_str Col_str Cs_tmp= Ca.ca.at(m)*Cs; - Ca_res=Ca_res+Cs_tmp; + //Ca_res=Ca_res+Cs_tmp; + Ca_res+=Cs_tmp; + } return Ca_res; } Col_amp operator*( const Col_str & Cs, const Col_amp & Ca ){ return Ca*Cs; } +Col_amp operator*( const Col_amp & Ca, const Quark_line & Ql ){ + + Col_amp Ca_res; + + // The Scalar part of Ca gives rise to a Col_str=Cs*Scalar when multiplied with Cs + // but this should only be kept if the scalar Polynomial is non-zero + if(! (Ca.Scalar.size()==1 and Ca.Scalar.at(0).int_part==0) ) { + Col_str Cs_tmp= Ca.Scalar*Col_str(Ql); + Ca_res+=Cs_tmp; + } + + // Multiply each Col_str in Col_amp with Cs + for(uint m=0; m< Ca.ca.size(); m++){ + // Multiply Col_str in Col_amp with the Col_str + Col_str Cs_tmp= Ca.ca.at(m)*Ql; + //Ca_res=Ca_res+Cs_tmp; + Ca_res+=Cs_tmp; + } + + return Ca_res; +} +Col_amp operator*( const Quark_line & Ql, const Col_amp & Ca ){ + + return Ca*Ql; +} + + Col_amp operator*(const Col_amp & Ca1, const Col_amp & Ca2){ Col_amp Ca_res; Ca_res=Ca2*Ca1.Scalar; // Multiply each Col_str in Col_amp Ca1 with Ca2 and add to result for( uint m=0; m< Ca1.ca.size(); m++){ // Multiply Col_str in Col_amp with the Col_str Ca_res+=Ca1.ca.at(m)*Ca2; } return Ca_res; } Col_amp operator*=( Col_amp & Ca1, const Col_amp & Ca2){ Col_amp Ca_res; Col_amp Ca1_old=Ca1; Ca1=Ca2*Ca1_old.Scalar; // Multiply each Col_str in Col_amp Ca1 with Ca2 and add to result for( uint m=0; m< Ca1_old.ca.size(); m++){ // Multiply Col_str in Col_amp with the Col_str Ca1+=Ca1_old.ca.at(m)*Ca2; } return Ca1; } }// end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Col_amp.h b/MatrixElement/Matchbox/ColorFull/Col_amp.h --- a/MatrixElement/Matchbox/ColorFull/Col_amp.h +++ b/MatrixElement/Matchbox/ColorFull/Col_amp.h @@ -1,292 +1,310 @@ // -*- C++ -*- /* * Col_amp.h * Contains the declarations of the class Col_amp, related types and operators * Created on: Jul 7, 2010 * Author: Malin Sjodahl */ #ifndef COLORFULL_Col_amp_h #define COLORFULL_Col_amp_h #include "Col_str.h" namespace ColorFull { /// Define a type to contain a linear combination of color structures, /// a col_amp contains the actual color amplitude in a Col_amp. typedef std::vector col_amp; /// The full color amplitude is Scalar + Cs1+Cs2+Cs3... /// Col_amp is a class to contain info on several Col_strs, a color amplitude. class Col_amp { public: - /// Default constructor + /// Default constructor, sets Scalar=0, and leaves ca empty. Col_amp() {Scalar = Scalar * 0;} /// Constructor taking a string as argument. /// The string should be of form /// Polynomial1* col_str1 + Polynomial2*col_str2, /// for example: /// Col_amp Ca("13*Nc*[(1, 3, 4, 2)] +2 TR 5 Nc^(-3) [(1, 4) (3, 2)]"). /// (The Polynomials should multiply the whole col_strs in square brackets, - /// rather than a quark_line inside the [] brackets.) + /// rather than a quark_line inside the []-brackets.) Col_amp( const std::string str ){Col_amp_of_str( str );} /// Constructor converting a Col_str to a Col_amp. Col_amp( Col_str Cs ) { Scalar = Scalar * 0; ca.push_back(Cs); } - /// To actually contain the info of the Col_strs, ca=Cs1+Cs2+Cs3+... . + /// To actually contain the information about the Col_strs, ca=Cs1+Cs2+Cs3+... . /// Technically the ca is a vector of Col_strs, a col_amp. col_amp ca; - /// Scalar is Polynomial for collecting color factors when the color structure - /// has been fully contracted. - /// The full color amplitude is Scalar + Cs1+Cs2+Cs3..., the Polynomial should thus be non-zero - /// only if all indices can be contracted. + /// Scalar is Polynomial for collecting color factors appearing when + /// the color structure has been fully contracted. + /// The full color amplitude is Scalar + Cs1+Cs2+Cs3.... + /// Scalar should thus be non-zero only if all indices can be contracted. Polynomial Scalar; /// Reads in the Col_amp to the member ca from the file filename. /// (This is for reading in an actual color amplitude, /// nothing is read in the the Polynomial member scalar.) void read_in_Col_amp( std::string filename ); /// Function for writing out the Col_amp to a file /// with name filename. void write_out_Col_amp( std::string filename ) const; /// Returns the Col_str at place i. const Col_str & at( int i ) const{ return ca.at(i); } /// Returns the Col_str at place i. Col_str & at( int i ) {return ca.at(i);} /// The size of the col_amp ca. uint size() const {return ca.size();} /// Is the col_amp empty? bool empty() const { return ca.empty(); } - /// Erase information in col_amp. + /// Erases the information in the col_amp. void clear() { ca.clear(); } /// Erases the Col_str at place i. void erase( int i ); + /// Appends a Col_str to the data member ca. + void append( Col_str Cs ) {ca.push_back( Cs );} + /// Appends the Col_strs in ca_in to the col_amp member ca. void append( col_amp ca_in ); + // Functions for probing the Col_amp /// Checks if the Col_amp only contains gluons, i.e., if all Quark_lines are closed. bool gluons_only() const; /// Returns the number of gluons in the Col_amp as the number of gluons in the first Col_str. /// Note that the other Col_strs could have a different number of (contracted) gluons. /// (Intended for tree-level Col_ams with only one Col_str.) int n_gluon() const; /// Returns the number of quarks in the Col_amp as the number of quarks in the first Col_str. /// Note that the other Col_strs could have a different number of (contracted) quarks. /// (Intended for tree-level Col_ams with only one Col_str.) int n_quark() const; /// Returns the number of quarks in the Col_amp after checking that each Col_str /// has the same number of quarks. int n_quark_check() const; /// Returns the number of gluons in the Col_amp after checking that each Col_str /// has the same number of gluons. int n_gluon_check() const; /// Returns the length of the longest Quark_line in any Col_str. int longest_quark_line() const; // Functions for manipulating the Col_amp /// Remove Col_strs with quark_lines with just 1 gluon, they are 0 as Tr[t^a]=0. void remove_1_rings(); - /// Remove quark_lines with no gluons, they are N if closed, and defined to be 1 if open. + /// Remove quark_lines with no gluons, they are Nc if closed, and defined to be 1 if open. void remove_0_rings(); /// Removes empty Col_strs, an empty Col_str means that all indices have been contracted, /// so the Col_str is equal to its Polynomial, which is moved to the scalar part /// of the Col_amp. void remove_empty_Col_strs(); - /// Compares col_strs in a Col_amp, to collect similar col_strs, + /// Compares col_strs in a Col_amp to collect similar col_strs /// and only store once in ca. void collect_col_strs(); /// Normal orders all col_strs in ca. void normal_order_col_strs(); /// Normal orders the individual col_strs and then /// orders the Col_strs using the order defined in - /// the Col_str function smallest. + /// the Col_str member function smallest. void normal_order(); /// Function for simplifying an amplitude, /// removes 0 and 1-rings, /// compares col_strs, /// removes Col_strs multiplying 0 and /// simplifies Polynomials of the individual Col_strs. void simplify(); /// Function for taking the conjugate of the Col_amp /// by conjugating each Col_str in ca and the /// Polynomial member Scalar. void conjugate(); /// Contracts up to next to neighboring gluons in each Quark_line /// in each Col_str in each Col_amp, only intended for closed Quark_lines. void contract_next_neighboring_gluons( ); /// Contract closed Quark_lines with only 2 gluons in /// each Quark_line in each Col_str in the Col_amp. /// This removes the 2-ring, replaces one of the gluon indices and - /// multiplies with a factor tr[t^a t^a]=(1/2) (no sum). + /// multiplies with a factor tr[t^a t^a]=TR (no sum). + /// only intended for fully contractable Col_strs. void contract_2_rings( ); /// Function for contracting gluon indices within the Quark_lines. - /// Checks only for ONE pair in each Quark_line, i.e., - /// if several gluon indices appear, only one pair is contracted - /// in each Quark_line, only intended for closed Quark_lines. + /// Checks only for ONE pair in each Quark_line. void contract_Quark_line_gluons( ); /// Contracts one gluon, the first gluon in first Quark_line (in each Col_str), /// only intended for closed Quark_lines. void contract_a_gluon( ); /// Function for contracting all gluon indices in a Col_amp, /// only intended for closed Quark_lines. void contract_all_gluons( ); /// Function for contracting the (anti-)quarks in Ca1 with those /// in Ca2. The results is saved in this Col_amp. void contract_quarks( const Col_amp & Ca1, const Col_amp & Ca2 ); private: /// Contracts one gluon, the first gluon in first Quark_line, only intended /// for closed Quark_lines. This function is a member of Col_amp as /// the result is contained in this Col_amp. void contract_a_gluon( Col_str & Cs ); /// Function for contracting gluon indices within the same Quark_line. /// Checks only for ONE pair, i.e. if several gluon indices appear - /// within the Quark_line, only one pair is contracted. - /// Only intended for closed Quark_lines. + /// within the Quark_line, only one pair is contracted, + /// only intended for closed Quark_lines. /// This function is a member of Col_amp as the result is a Col_amp. void contract_Quark_line_gluons( Quark_line & Ql ); /// Function for contracting gluon indices within the same Quark_line. /// Checks only for ONE pair in each Quark_line in each Col_str, /// i.e. if several gluon indices appear within the Quark_line, /// only one pair is contracted. /// The function is only intended for closed Quark_lines. /// This function is a member of Col_amp as it saves the result in /// this Col_amp. void contract_Quark_line_gluons( Col_str & Cs ); /// Function for contracting all gluon indices in a Col_str, /// assumes quarks already contracted and is only intended for /// closed Quark_lines. This function is a member of Col_amp as /// the result is contained in this Col_amp. void contract_all_gluons( Col_str & Cs ); /// Converts a text string to a Col_amp, /// used by string constructor, and by read_in_Col_amp. /// The string should be of form /// Polynomial1* col_str1 + Polynomial2 col_str2, /// for example: /// Col_amp Ca("13*Nc*[(1,3,4,2)] +2 TR 5 Nc^(-3) [(1,4) (3,2)]"). void Col_amp_of_str( const std::string str ); }; // Define operators involving Col_amp /// Define the operator << for col_amp std::ostream& operator<<( std::ostream& out, const col_amp & ca ); /// Define the operator << for Col_amp. std::ostream& operator<<( std::ostream& out, const Col_amp & Ca ); /// Define the operator == for two Col_amps. bool operator==( const Col_amp & Ca1, const Col_amp & Ca2 ); /// Define the operator != for two Col_amps. bool operator!=( const Col_amp & Ca1, const Col_amp & Ca2 ); /// Define the operator + for Col_amp and Col_str, adds Col_str Cs to ca. Col_amp operator+( const Col_amp & Ca, const Col_str & Cs ); /// Define the operator + for Col_str and Col_amp, adds Col_str Cs to ca. Col_amp operator+( const Col_str & Cs, const Col_amp & Ca ); /// Define the operator + for two Col_amps. /// Adds Scalars, and adds Col_strs. Col_amp operator+( const Col_amp & Ca1, const Col_amp & Ca2 ); +/// Define the operator += for two Col_amp+=Col_str. +Col_amp operator+=( Col_amp & Ca, const Col_str & Cs ); + /// Define the operator += for two Col_amps. Col_amp operator+=( Col_amp & Ca1, const Col_amp & Ca2 ); /// Define the operator - for two Col_amps. /// Subtract Scalar of Ca2 from Ca1, and subtracts (=appends with minus sign) Col_strs. Col_amp operator-( const Col_amp & Ca1, const Col_amp & Ca2 ); +/// Define the operator - for Col_amp-Col_str. +Col_amp operator-( const Col_amp & Ca, const Col_str & Cs ); + +/// Define the operator - for Col_str-Col_amp. +Col_amp operator-( const Col_amp & Ca, const Col_str & Cs ); + /// Define the operator * for Col_amps and integers. Col_amp operator*( const Col_amp & Ca, const int i ); /// Define the operator * for integers number and Col_amp. Col_amp operator*( const int i, const Col_amp & Ca ); /// Define the operator * for Col_amps and complex number. Col_amp operator*( const Col_amp & Ca, const cnum c ); /// Define the operator * for complex number and Col_amp. Col_amp operator*( const cnum c, const Col_amp & Ca ); /// Define the operator * for Col_amp and double. Col_amp operator*( const Col_amp & Ca, const double d ); /// Define the operator * for double and Col_amp. Col_amp operator*( const double d, const Col_amp & Ca ); /// Define the operator * for Col_amp and Monomial. Col_amp operator*( const Col_amp & Ca, const Monomial & Mon ); /// Define the operator * for Monomial and Col_amp. Col_amp operator*( const Monomial & Mon, const Col_amp & Ca ); /// Define the operator * for Col_amp and Polynomial. Col_amp operator*( const Col_amp & Ca, const Polynomial & Poly ); /// Define the operator * for Monomial and Col_amp. Col_amp operator*( const Polynomial & Poly, const Col_amp & Ca ); -/// Define the operator *= for Col_amp and Col_str. +/// Define the operator * for Col_amp and Quark_line. +Col_amp operator*( const Col_amp & Ca, const Quark_line & Ql ); + +/// Define the operator * for Quark_line and Col_amp. +Col_amp operator*( const Quark_line & Ql, const Col_amp & Ca ); + +/// Define the operator * for Col_amp and Col_str. Col_amp operator*( const Col_amp & Ca, const Col_str & Cs ); -/// Define the operator * for Col_amp and Col_str. +/// Define the operator * for Col_str and Col_amp. Col_amp operator*( const Col_str & Cs, const Col_amp & Ca ); /// Define the operator * for Col_amps. Col_amp operator*( const Col_amp & Ca1, const Col_amp & Ca2 ); /// Define the operator *= for two Col_amps. Col_amp operator*=( Col_amp & Ca1, const Col_amp & Ca2 ); }// end namespace ColorFull #endif /* COLORFULL_Col_amp_h */ diff --git a/MatrixElement/Matchbox/ColorFull/Col_basis.cc b/MatrixElement/Matchbox/ColorFull/Col_basis.cc --- a/MatrixElement/Matchbox/ColorFull/Col_basis.cc +++ b/MatrixElement/Matchbox/ColorFull/Col_basis.cc @@ -1,1036 +1,1065 @@ // -*- C++ -*- /* * Col_basis.cc * Contains definition of the base class Col_basis and associated types and operators. * Created on: Aug 9, 2012 * Author: Malin Sjodahl */ #include "Col_basis.h" #include "parameters.h" #include #include #include #include #include namespace ColorFull { + +void Col_basis::append( col_basis cb_in ) { + for (uint m = 0; m < cb_in.size(); m++) { + cb.push_back( cb_in.at(m) ); + } +} + void Col_basis::scalar_product_matrix_no_mem(){ if(ng+nq>6){ std::cout << "Col_basis::scalar_product_matrix: nq+ng=" << nq+ng << " is large, consider using numerical and/or memory version. " << std::endl; std::cout.flush(); } return scalar_product_matrix( true, true, false ); } void Col_basis::scalar_product_matrix(){ return scalar_product_matrix( true, true, true ); } void Col_basis::scalar_product_matrix_num_no_mem(){ return scalar_product_matrix( false, true, false ); } void Col_basis::scalar_product_matrix_num(){ return scalar_product_matrix( false, true, true ); } void Col_basis::leading_scalar_product_matrix(){ if( cb.size()==0 ) { std::cerr << "Col_basis::leading_scalar_product_matrix: There are no basis vectors in this basis, consider using create_basis or read_in_Col_basis." << std::endl; std::cerr.flush(); return ; } if( P_spm.empty() ) { scalar_product_matrix( true, true, true ); } leading_P_spm = Col_fun.leading( P_spm ); leading_d_spm = Col_fun.double_num( leading_P_spm ); } std::string Col_basis::basis_file_name() const{ // First construct filename std::ostringstream ss; std::string filename; ss << "ColorResults"; ss << '/'; // ColorFull ss << "CF_"; // Basis Type if( trace_basis ) ss << "TB_"; else if ( tree_level_gluon_basis ) ss << "TGB_"; else if ( orthogonal_basis ) ss << "OB_"; else ss << "CB_"; ss << "q_"; ss << nq; ss << "_g_"; ss << ng; // If Nc is not 3, append Nc info if( Col_fun.get_Nc() != 3 ){ ss << "_Nc_"; ss << Col_fun.get_Nc(); } // If TR is not 1/2, append TR info if( Col_fun.get_TR() != 0.5 ){ ss << "_TR_"; ss << Col_fun.get_TR(); } filename=ss.str(); return filename.c_str(); } std::string Col_basis::spm_file_name(const bool leading, const bool poly ) const{ // First construct filename std::ostringstream ss; std::string filename; ss << "ColorResults"; ss << '/'; // CF as in ColorFull ss << "CF_"; // Prefix according to basis type if( trace_basis ) ss << "TB_"; else if ( tree_level_gluon_basis ) ss << "TGB_"; else if ( orthogonal_basis ) ss << "OB_"; else ss << "CB_"; // Polynomial or numerical matrix? if( poly )ss << "P_"; else ss << "d_"; ss << "spm_q"; ss << nq; ss << "_g"; ss << ng; if ( leading ) ss << "_l"; if ( Col_fun.get_full_CF() ) ss << "_cff"; else ss << "_cfl"; if(Col_fun.get_Nc() != 3 ){ ss << "_Nc_"; ss << Col_fun.get_Nc(); } if(Col_fun.get_TR() != 0.5 ){ ss << "_TR_"; ss << Col_fun.get_TR(); } filename=ss.str(); return filename; } void Col_basis::write_out_Col_basis() const{ write_out_Col_basis( basis_file_name() ); } void Col_basis::write_out_Col_basis( std::string filename ) const { if ((cb.size() == 0)) { std::cerr << "Col_basis::write_out_Col_basis(filename): There are no basis vectors in this basis, consider using create_basis or read_in_Col_basis." << std::endl; return; } std::ofstream outfile(filename.c_str()); if ( !outfile ) std::cerr << "Col_basis::write_out_Col_basis: Cannot write out basis as the file \"" << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; outfile << std::setprecision(16); for ( uint m = 0; m < cb.size(); m++ ) { outfile << m << " " << cb.at(m) << std::endl; } outfile.flush(); } - -void Col_basis::write_out_Col_basis_to_cout() const{ +std::ostream& Col_basis::write_out_Col_basis_to_stream( std::ostream& out ) const{ if( (cb.size()==0 ) ) { std::cerr << "Col_basis::write_out_Col_basis(): There are no basis vectors in this basis, consider using create_basis or read_in_Col_basis." << std::endl; std::cerr.flush(); - return ; } for (uint m = 0; m < cb.size(); m++) { - std::cout << m << " "<< cb.at(m) << std::endl; + out << m << " "<< cb.at(m) << std::endl; } + return out; } - void Col_basis::write_out_d_spm( std::string filename ) const{ Col_fun.write_out_dmatr( d_spm, filename ); } void Col_basis::write_out_d_spm( ) const{ std::string filename = spm_file_name( false, false ); write_out_d_spm( filename ); } void Col_basis::write_out_P_spm( std::string filename ) const{ P_spm.write_out_Poly_matr( filename ); } void Col_basis::write_out_P_spm( ) const{ std::string filename = spm_file_name( false, true ); write_out_P_spm( filename ); } void Col_basis::write_out_leading_d_spm( std::string filename ) const{ Col_fun.write_out_dmatr( leading_d_spm, filename ); } void Col_basis::write_out_leading_d_spm( ) const{ std::string filename = spm_file_name( true, false ); write_out_leading_d_spm( filename ); } void Col_basis::write_out_leading_P_spm( std::string filename ) const{ leading_P_spm.write_out_Poly_matr( filename ); } void Col_basis::write_out_leading_P_spm( ) const{ std::string filename = spm_file_name( true, true ); write_out_leading_P_spm( filename ); } void Col_basis::read_in_Col_basis( std::string filename ) { // Read in file std::ifstream fin(filename.c_str()); // Check that file exists if( !fin ){ std::cerr << "Col_basis::read_in_Col_basis: The file " << filename << " could not be opened." << std::endl; assert( 0 ); } // Erase current information cb.clear(); // Copy info from file to string std::string str((std::istreambuf_iterator(fin)), std::istreambuf_iterator()); + // Skip lines starting with # + while( str.at(0)== '#' ){ + while (str.at(0) != '\n'){ + str.erase(str.begin()); + } + // erase endl sign(s) + while(str.at(0)== '\n'){ + str.erase(str.begin()); + } + } + + // Remove endl chars at the end of the file + while( str.at(str.size()-1) == '\n' ) str.erase(str.size()-1); + Col_basis_of_str( str ); } void Col_basis::read_in_d_spm( std::string filename){ d_spm= Col_fun.read_in_dmatr( filename ); } void Col_basis::read_in_d_spm( ){ d_spm=Col_fun.read_in_dmatr( spm_file_name( false, false ).c_str() ); } void Col_basis::read_in_leading_d_spm( std::string filename){ leading_d_spm=Col_fun.read_in_dmatr( filename ); } void Col_basis::read_in_leading_d_spm( ){ leading_d_spm=Col_fun.read_in_dmatr( spm_file_name( true, false ).c_str() ); } void Col_basis::read_in_P_spm( std::string filename ){ P_spm.read_in_Poly_matr( filename ); } void Col_basis::read_in_P_spm( ){ P_spm.read_in_Poly_matr( spm_file_name( false, true) ); } void Col_basis::read_in_leading_P_spm( std::string filename){ leading_P_spm.read_in_Poly_matr( filename ); } void Col_basis::read_in_leading_P_spm( ){ leading_P_spm.read_in_Poly_matr( spm_file_name( true, true) ); } int Col_basis::n_quark_check() const { if (empty()) return 0; int nq=cb.at(0).n_quark_check(); for( uint n=0; n< cb.size(); n++ ) if( cb.at(n).n_quark_check() != nq) { std::cerr << "Col_basis::n_quark_check: The Col_amps in " << cb << " have differently many quarks." << std::endl; } return nq; } int Col_basis::n_gluon_check() const { if ( empty()) return 0; int nq=cb.at(0).n_gluon_check(); for( uint n=0; n< cb.size(); n++ ) if( cb.at(n).n_gluon_check() != nq) { std::cerr << "Col_basis::n_gluon_check: The Col_amps in " << cb << " have differently many gluons." << std::endl; } return nq; } void Col_basis::simplify(){ for( uint i=0; i< cb.size(); i++ ){ cb.at(i).simplify(); } } -Poly_vec Col_basis::decompose( const Col_amp & ){ +Poly_vec Col_basis::decompose( const Col_amp & Ca ){ + + // This line is to avoid compiler warnings + // Ca should be an argument despite that it's not used + (void) Ca; std::cerr << "Col_basis::decompose: This function is not implemented for the Col_basis class. Try using a derived class (such as Trace_basis). " << std::endl; Poly_vec Pv; assert( 0 ); return Pv; } Col_amp Col_basis::exchange_gluon( uint vec, int p1, int p2 ) { if( (cb.size()==0 ) ) { std::cerr << "Col_basis::exchange_gluon: There are no basis vectors in this basis, consider using create_basis or read_in_Col_basis." << std::endl; std::cerr.flush(); assert( 0 ); } // Check that the basis vector exists if ( vec >= cb.size() ) { std::cerr << "Col_basis::exchange_gluon: Basis vector number "<< vec << " does not exist, as the basis only have " << cb.size() << " basis vectors." << std::endl; assert( 0 ); } return Col_fun.exchange_gluon( cb.at(vec), p1, p2); } -Poly_matr Col_basis::Col_gamma( int p1, int p2 ) { +Poly_matr Col_basis::color_gamma( int p1, int p2 ) { if( (cb.size()==0 ) ) { - std::cerr << "Col_basis::Col_gamma: There are no basis vectors in this basis, consider using create_basis or read_in_Col_basis." << std::endl; + std::cerr << "Col_basis::color_gamma: There are no basis vectors in this basis, consider using create_basis or read_in_Col_basis." << std::endl; std::cerr.flush(); assert( 0 ); } // Function not available for Tree_level_gluon basis if( tree_level_gluon_basis ) { - std::cerr << "Col_basis::Col_gamma: This function is not available for Tree_level_gluon_basis " + std::cerr << "Col_basis::color_gamma: This function is not available for Tree_level_gluon_basis " << "as the vector resulting after gluon exchange contains vectors which are not in the basis. " << "Consider using Trace_basis."<< std::endl; std::cerr.flush(); assert( 0 ); } // Function should only be used for Orthogonal_basis and Trace_basis if( ( !trace_basis ) and (! orthogonal_basis) ) { - std::cerr << "Col_basis::Col_gamma: This function is only implemented for Trace_basis and Orthogonal_basis. " + std::cerr << "Col_basis::color_gamma: This function is only implemented for Trace_basis and Orthogonal_basis. " << "If your basis is not orthogonal, the result will not be correct. " << "If your basis is orthogonal, consider using Orthogonal_basis." << std::endl; std::cerr.flush(); } // To contain the resulting matrix Poly_matr gamma_res; // Fill gamma_res with 0s Polynomial Zero; Zero=Zero*0; for( uint i=0; i < cb.size(); i++ ){ Poly_vec rowi; for( uint j=0; j < cb.size(); j++ ){ - rowi.push_back(Zero); + rowi.append(Zero); } - gamma_res.push_back(rowi); + gamma_res.append(rowi); } // To contain the Col_amp after gluon exchange Col_amp Ca_ae; // First exchange a gluon between partons p1 and p2 in the vector v1 for ( uint vi=0; vi < cb.size(); vi++ ){ Ca_ae=exchange_gluon( vi, p1, p2 ); // Then decompose the result into the basis, Poly_vec col_vi = decompose( Ca_ae ); - // This gives the vi:th column in Col_gamma + // This gives the vi:th column in color_gamma // Loop over rows in that column for ( uint vj=0; vj < cb.size(); vj++ ){ gamma_res.at(vj).pv.at(vi) = col_vi.at(vj); } } return gamma_res; } void Col_basis::Col_basis_of_str( std::string str ){ if (str.size() == 0) { std::cerr << "Col_basis::Col_basis_of_str: The basis string is empty. This should not happen." << std::endl; assert( 0 ); } // Skip lines starting with # while(str.at(0)== '#'){ while (str.at(0) != '\n'){ str.erase( str.begin() ); } // erase endl sign(s) while(str.at(0)== '\n'){ str.erase(str.begin()); } } // First char in string should be '0' if (str.at(0) != '0') { std::cerr << "Col_basis::Col_basis_of_str: First char in basis data file after comments should be '0', as in basis vector 0 as in vector number 0, but it was: " << str.at(0) << std::endl; assert( 0 ); } // Read the string, starting from 0th element uint i = 0; while (i < str.size() - 2) { //i += 1; testing to move down 130917 // We may have some white spaces while (i< str.size()-2 &&(str.at(i) == ' ')) i++; // After white spaces there should be a number, the vector number while (i< str.size()-2 &&(str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9') ) i++; // We may have some more white spaces while (i< str.size()-2 &&(str.at(i) == ' ')) i++; // String to make a Col_amp of std::string Ca_str; Ca_str.clear(); // Keep reading the Ca_str while not '\n which marks new vector //while ( i< str.size()-1 && (str.at(i) != '\n') ) while ( i< str.size() && (str.at(i) != '\n') ){ Ca_str.push_back(str.at(i)); i++; } // Make the Col_amp (basis vector) Col_amp Ca( Ca_str ); // Simplify, for example move Polynomials to multiply Col_strs, rather than Quark-lines Ca.simplify(); - push_back(Ca); + append(Ca); i++; } // Check that the basis is not empty if( empty()){ std::cerr << "Col_basis::Col_basis_of_str: The basis is empty. " << std::endl; assert( 0 ); } // Check that first vector is not empty if(cb.at(0).ca.empty()){ std::cerr << "Col_basis::Col_basis_of_str: The first Col_amp (vector) in the basis is empty. " << std::endl; assert( 0 ); } // Check that first col_str in first vector is not empty if(cb.at(0).ca.at(0).cs.empty()){ std::cerr << "Col_basis::Col_basis_of_str: The first Col_str in the first basis vector is empty. " << std::endl; assert( 0 ); } // Check that first quark_line in the first col_str in first vector is not empty if(cb.at(0).ca.at(0).cs.at(0).ql.empty()){ std::cerr << "Col_basis::Col_basis_of_str: The first Quark_line in the first Col_str in the first basis vector is empty. " << std::endl; assert( 0 ); } // Find the number of quarks and gluons nq=n_quark_check(); ng=n_gluon_check(); // Test if it's a trace basis if( trace_basis ){ bool is_trace_basis=true; for ( uint j=0; j< cb.size(); j++ ){ if( cb.at(j).size() !=1 ) is_trace_basis=false; if(! is_trace_basis ){ std::cerr << "Col_basis::Col_basis_of_str: You are trying to read in a non-trace basis to a trace basis object. " <<"The length of the Col_amp " << cb.at(j) << " is not 1." << std::endl; assert( 0 ); } } } } void Col_basis::read_in_Col_basis( ) { read_in_Col_basis( basis_file_name() ); } void Col_basis::scalar_product_matrix( bool save_P_spm, bool save_d_spm, bool use_mem ) { if( (cb.size()==0 ) ) { std::cerr << "Col_basis::scalar_product_matrix: There are no basis vectors in this basis, consider using create_basis or read_in_Col_basis." << std::endl; std::cerr.flush(); return ; } // Simplify the basis vectors simplify(); // If the P_spm and d_spm have already been calculated, erase them if( !P_spm.empty() ) P_spm.clear(); if( !d_spm.empty() ) d_spm.clear(); // Check that all Polynomials are real // (check doesn't cover the case of complex Poly in a Quark_line) // but if an entry is not real this will be discovered as the d_spm is calculated for ( uint cbi=0; cbi < cb.size(); cbi++){ for ( uint j=0; j< cb.at(cbi).size(); j++){ if( imag (Col_fun.cnum_num( cb.at(cbi).at(j).Poly )) > accuracy ){ std::cerr << "Col_basis::scalar_product_matrix: ColorFull expects real Polynomial multiplying the color structure, but the Polynomial\n" << cb.at(cbi).at(j).Poly << std::endl << " appearing in front of the Col_str " << cb.at(cbi).at(j).cs << " in basis vector number " << cbi << " is not real."<< std::endl << std::endl; std::cerr.flush(); assert( 0 ); } } } // For remembering already calculated topologies std::map > mem_map; // Loop over basis vectors in Basis for( uint i=0; i < cb.size(); i++){ // To contain a row of the resulting scalar product matrix Poly_vec rowi; // Loop over basis vectors in Basis, normally over all vectors, // but for orthogonal_basis only for i==j uint minj=0, maxj=cb.size(); for( uint j=minj; j< maxj; j++){ Polynomial ijRes; ijRes=ijRes*0; if(use_mem){ // Loop over Col_strs in first Ca for( uint Ca1i=0; Ca1i< cb.at(i).size(); Ca1i++){ // Loop over Col_strs in second Ca for( uint Ca2i=0; Ca2i< cb.at(j).size(); Ca2i++){ // To contain the contribution to the ij-th entry if memoization is used std::shared_ptr ijEntry_contr; // Rename indices, and make string of new col_strs, to use in map // strip off Polynomial information to make the map minimal Col_str Cs1, Cs2; Cs1.append(cb.at(i).at(Ca1i).cs); Cs2.append(cb.at(j).at(Ca2i).cs); rename_indices( Cs1, Cs2 ); std::ostringstream Cs_string; Cs_string << Cs1 << Cs2; // Calculate element contribution to the ij:th element in scalar product matrix // If this has scalar product has occurred before, reuse old value if (mem_map.count( Cs_string.str() ) > 0) { ijEntry_contr = mem_map[Cs_string.str()]; } // Otherwise, calculate value and save topology else { if( !tree_level_gluon_basis ){ Polynomial p = Col_fun.scalar_product(Cs1, Cs2); - ijEntry_contr = shared_ptr (new Polynomial(p)); + ijEntry_contr = std::shared_ptr (new Polynomial(p)); mem_map[Cs_string.str()] = ijEntry_contr; } else if( tree_level_gluon_basis ){ int sign = (ng % 2 ? -1 : 1); Col_str Cs2_conj=Cs2; Cs2_conj.conjugate(); Polynomial P = 2* Col_fun. scalar_product( Cs1, Cs2 ) + sign*2*Col_fun. scalar_product( Cs1, Cs2_conj ); - ijEntry_contr = shared_ptr (new Polynomial(P)); + ijEntry_contr = std::shared_ptr (new Polynomial(P)); mem_map[Cs_string.str()] = ijEntry_contr; } } // Sum up all the contributions to one Polynomial, recall to multiply with Polynomials Polynomial ijEntry_contr_poly=(*ijEntry_contr)* cb.at(i).at(Ca1i).Poly*cb.at(j).at(Ca2i).Poly; // If Polynomial version is not needed convert all algebraic information // to numerical in the vectors if( !save_P_spm ) { double ijEntry_contr_d= Col_fun.double_num( ijEntry_contr_poly ); Monomial Mon( ijEntry_contr_d ); ijRes+= Mon; } else ijRes += ijEntry_contr_poly; } // end looping over Cs in first Ca } // end looping over Cs in 2nd Ca // If Polynomial result is wanted, simplify if ( save_P_spm ) ijRes.simplify(); // Otherwise convert to numerical else { double num_ijRes= Col_fun.double_num(ijRes); ijRes.clear(); Monomial Mon( num_ijRes ); - ijRes.push_back(Mon); + ijRes.append(Mon); } - rowi.push_back( ijRes ); + rowi.append( ijRes ); } // end if ( use_mem ) else{ // Calculate element ij in scalar product matrix ijRes=ij_entry( i, j ); - rowi.push_back( ijRes ); + rowi.append( ijRes ); // Convert to numerical if not saving Polynomial version if ( ! save_P_spm ) { double num_ijRes= Col_fun.double_num(ijRes); ijRes.clear(); Monomial Mon( num_ijRes ); - ijRes.push_back(Mon); + ijRes.append(Mon); } } //end if (not mem) } - P_spm.push_back(rowi); + P_spm.append(rowi); } // For saving and checking symmetry of numerical version // d_spm has to be calculated even if it's not saved to facilitate // symmetry check d_spm= Col_fun.double_num( P_spm ); // Making consistency checks (needs double version) check_spm(); // Erasing the double spm if( ! save_d_spm ) { d_spm.clear(); } // Erasing the Polynomial spm if( !save_P_spm ) { P_spm.clear(); } return; } Polynomial Col_basis::ij_entry( const int i, const int j ) const{ Polynomial ijEntry; ijEntry=Col_fun.scalar_product(cb.at(i), cb.at(j)); ijEntry.simplify(); return ijEntry; } bool Col_basis::check_symmetry( const dmatr & matr ) const { if( matr.size()==0 ){ std::cout << "Col_basis::check_symmetry( dmatr ): The numerical matrix is empty..." << std::endl; } else{ //std::cout << "Col_basis::check_symmetry( dmatr ): Numerically verifying symmetry of matrix..."; } // Verifying that the matrix is symmetric to accuracy "accuracy", // if not sym is put to false bool sym=true; // Loop over basis vectors in Basis for (uint i = 0; i < d_spm.size(); i++) { Poly_vec rowi; // Loop over basis vectors in Basis for (uint j = 0; j <=i; j++) { if ((fabs(d_spm.at(i).at(j) / d_spm.at(j).at(i) - 1.0) > accuracy) && (d_spm.at(i).at(j) > accuracy) && (d_spm.at(j).at(i) > accuracy)) { sym=false; std::cerr << "Col_basis::check_symmetry( dmatr ): Error, the resulting scalar product matrix is not symmetric. \n " << "Element " << i << "," << j << ": " << d_spm.at(i).at(j) << ", Element " << j << "," << i << ": " << d_spm.at(j).at(i) << std::endl << "This indicates an error in calculation of scalar products. " << std::endl; } } } //if( matr.size() !=0 and sym ) std::cout << " done." << std::endl; return sym; } bool Col_basis::check_diagonal( const dmatr & matr ) const{ if( matr.size()==0 ){ std::cerr << "Col_basis::check_diagonal( dmatr ): The numerical matrix is empty..."; std::cout.flush(); } else{ //std::cout << "Col_basis::check_diagonal( dmatr ): Numerically checking if the matrix is diagonal..."; //std::cout.flush(); } // Verifying that the matrix is diagonal // if not sym is put to false bool diag=true; // Loop over basis vectors in Basis for (uint i = 0; i < matr.size(); i++) { Poly_vec rowi; // Loop over basis vectors in Basis for (uint j = 0; j <=i; j++) { // If non-diagonal elements are sufficiently large write warning if ( std::abs( matr.at(i).at(j) ) > accuracy and i!=j ) { diag=false; if( !(trace_basis or tree_level_gluon_basis) ){ std::cout << "Col_basis::check_diagonal( matr ): Warning, the matrix is not diagonal. \n " << "Element " << i << "," << j << ": " << matr.at(i).at(j) << std::endl; } } } } if (! diag ){ std::cout << "Col_basis::check_diagonal: the matrix is not diagonal." << std::endl; } //else if (diag) std::cout << " done." << std::endl; return diag; } void Col_basis::check_spm() const{ // Verifying that the matrix is symmetric to accuracy bool sym=check_symmetry(d_spm); if( !sym ) { std::cerr <<"Col_basis::check_spm(): scalar product matrix not symmetric. Please report bug." << std::endl; std::cerr.flush(); assert( 0 ); } // Verifying that the leading terms only sit on the diagonal bool diagonal=true; if( !leading_d_spm.empty() ){ diagonal=check_diagonal(leading_d_spm); } if(!diagonal and ( trace_basis or tree_level_gluon_basis ) ) { std::cout <<"Col_basis::check_spm(): Leading terms appear of the diagonal. This should not happen in a trace type basis." << " For numerical bases it can appear to happen as powers of Nc may hide in numerical constants." << std::endl; std::cout.flush(); } } void Col_basis::rename_indices(Col_str & Cs1, Col_str & Cs2) const{ /* This is a two-step process: 1) Compute new indices 2) Replace indices The new indices are stored in a vector named new_indices, such that old_index should be replaced by new_indices[old_index] */ std::vector new_indices; uint n_indices_total = 2*Cs1.n_quark() + Cs1.n_gluon(); new_indices.resize(n_indices_total+1); assert(2*Cs1.n_quark() + Cs1.n_gluon() == 2*Cs1.n_quark() + Cs1.n_gluon()); int new_ind=0; // The new index uint Nql= Cs1.cs.size(); // Loop over Quark_lines in Cs1 for(uint i=0; i< Nql; i++ ){ Quark_line & Ql_i = Cs1.cs.at(i); uint Nind = Ql_i.ql.size(); // Loop over indices in the Quark_line for(uint j=0; j< Nind; j++ ){ new_ind++; // The old index in Cs1, to be replaced also in Cs2 uint old_ind=Ql_i.ql.at(j); // Replace index in Cs1 Ql_i.ql.at(j) = new_ind; // Insert into replacement map // If the assert fails, we have not allocated enough space, and will fail. assert(old_ind <= n_indices_total); new_indices[old_ind] = new_ind; } } // Loop over Quark_lines in Cs2 Nql= Cs2.cs.size(); for(uint i=0; i< Nql; i++ ){ Quark_line & Ql_i = Cs2.cs.at(i); uint Nind = Ql_i.ql.size(); // Loop over indices in the Quark_line for(uint j=0; j< Nind; j++ ){ // Replace index in Cs2 Ql_i.ql.at(j) = new_indices[Ql_i.ql.at(j)]; } } } Polynomial Col_basis::scalar_product( const Col_amp & Ca1, const Col_amp & Ca2 ) { // Check that we have a basis if(cb.size()==0){ std::cerr << "Col_basis::scalar_product: The basis vector cb is empty, consider using create_basis or read in basis." << std::endl; assert( 0 ); } // Check that the Polynomial scalar product matrix is calculated, if not calculate it if( P_spm.size() != cb.size() ) { std::cerr << "Col_basis::scalar_product: This function uses the scalar product matrix which has not yet been calculated." << std::endl; assert( 0 ); } // To contain the resulting Polynomial Polynomial Poly_res; Poly_res=Poly_res*0; // Decompose the Col_amps Poly_vec Polyv1=decompose(Ca1); Polyv1.conjugate(); Poly_vec Polyv2=decompose( Ca2 ); // Then add contributions for ( uint m1=0; m1< cb.size(); m1++ ){ // Diagonal terms - Poly_res=Poly_res+Polyv1.at(m1) *Polyv2.at(m1) *P_spm.at(m1).at(m1); + Poly_res+= Polyv1.at(m1) *Polyv2.at(m1) *P_spm.at(m1).at(m1); for (uint m2=0; m2< m1; m2++){ // Other terms, use symmetry of scalar product matrix - Poly_res=Poly_res+ ( Polyv1.at(m1) *Polyv2.at(m2)+Polyv1.at(m2) *Polyv2.at(m1) ) *P_spm.at(m1).at(m2); + Poly_res+= ( Polyv1.at(m1) *Polyv2.at(m2)+Polyv1.at(m2) *Polyv2.at(m1) ) *P_spm.at(m1).at(m2); } } return Poly_res; } cnum Col_basis::scalar_product_num( const Col_amp & Ca1, const Col_amp & Ca2 ) { // Check that we have a basis if(cb.size()==0){ std::cerr << "Col_basis::scalar_product_num: The basis vector cb is empty consider using create_basis or read in basis." << std::endl; assert( 0 ); } // Check that the Polynomial scalar product matrix is calculated, if not calculate it if( d_spm.size() != cb.size() ) { std::cerr << "Col_basis::scalar_product_num: This function uses the numerical scalar product matrix which has not yet been calculated." << std::endl; assert( 0 ); } // To contain the resulting Polynomial cnum res=0; uint basis_size=cb.size(); // Decompose the Col_amps Poly_vec Polyv1, Polyv2; Polyv1=decompose(Ca1); if( &Ca1==&Ca2 ) Polyv2=Polyv1; else Polyv2=decompose(Ca2); // Conjugate first arg Polyv1.conjugate(); // Make ordinary vectors to speed up multiplication cvec v1, v2; v1.reserve( basis_size ); for (uint i=0; i< basis_size; i++){ v1.push_back( Col_fun.cnum_num( Polyv1.at(i) ) ); } if (false) v2=v1; else{ v2.reserve(basis_size); for (uint i=0; i< basis_size; i++) v2.push_back( Col_fun.cnum_num( Polyv2.at(i) ) ); } // Then add contributions cnum v1m1, v2m1; dvec row; for (uint m1=0; m1< basis_size; m1++){ v1m1=v1.at(m1); v2m1=v2.at(m1); row=d_spm.at( m1 ); // Diagonal terms res=res+ v1m1 *v2.at(m1) *row.at(m1); for (uint m2=0; m2< m1; m2++){ res += (v1m1*v2.at(m2) +v1.at(m2)*v2m1 ) *row.at(m2); } } return res; } cnum Col_basis::scalar_product_num( const cvec & v1, const cvec & v2) { if( v1.size()!= v2.size() ){ std::cerr << "Col_basis::scalar_product_num: Size of first vector " << v1.size() << " does not agree with size of second vector " << v2.size() << std::endl; assert( 0 ); } if( v1.size()!= d_spm.size() ){ std::cerr << "Col_basis::scalar_product_num: Size of vectors " << v1.size() << " does not agree with size of d_spm matrix " << d_spm.size() << std::endl; assert( 0 ); } uint basis_size=v1.size(); // To contain the result cnum res=0; cnum tmp=0, tmp2=0; // Add contributions cnum v1m1, v2m1; for (uint m1=0; m1< basis_size; m1++){ v1m1= conj( v1.at(m1) ); v2m1=v2.at(m1); const dvec &row = d_spm.at(m1); // Diagonal parts res += v1m1 *v2.at(m1) *row.at(m1); tmp = 0; tmp2=0; for (uint m2=0; m2< m1; m2++){ tmp += (v1m1*v2[m2]+v2m1*conj(v1[m2])) *row[m2]; } res+=tmp; } return res; } cnum Col_basis::scalar_product_num_diagonal( const cvec & v1, const cvec & v2 ) { if(v1.size()!= v2.size()){ std::cerr << "Col_basis::scalar_product_num_diagonal: Size of first vector " << v1.size() << " does not agree with size of second vector " << v2.size() << std::endl; assert( 0 ); } if(v1.size()!= d_spm.size()){ std::cerr << "Col_basis::scalar_product_num_diagonal: Size of vectors " << v1.size() << " do not agree with size of d_spm matrix " << d_spm.size() << std::endl; assert( 0 ); } // To contain the result cnum res=0; // Check dimension uint basis_size=v1.size(); // Then add contributions for (uint m1=0; m1< basis_size; m1++){ res=res+ conj( v1.at(m1) ) *v2.at(m1) *d_spm.at(m1).at(m1); } return res; } +std::ostream& operator<<( std::ostream& out, const Col_basis & Cb ) { + + return Cb.write_out_Col_basis_to_stream( out ); +} + + std::ostream& operator<<( std::ostream& out, const col_basis & cb ) { int max = cb.size(); for (int i = 0; i < max; i++) { out <<", " << cb.at(i); } return out; } } diff --git a/MatrixElement/Matchbox/ColorFull/Col_basis.h b/MatrixElement/Matchbox/ColorFull/Col_basis.h --- a/MatrixElement/Matchbox/ColorFull/Col_basis.h +++ b/MatrixElement/Matchbox/ColorFull/Col_basis.h @@ -1,396 +1,405 @@ // -*- C++ -*- /* * Col_basis.h * Contains the declarations of the class Col_basis, related types and operators. * Created on: Aug 9, 2012 * Author: Malin Sjodahl */ #ifndef COLORFULL_Col_basis_h #define COLORFULL_Col_basis_h #include "Col_functions.h" namespace ColorFull { /// Define a type to store all basis vectors. typedef std::vector col_basis; /// To contain a color basis, where each basis vector is a Col_amp. /// Technically the color information is contained /// in the member cb, which is a vector of Col_amps, a col_basis. class Col_basis { public: /// Default constructor Col_basis(){ nq=0; ng=0; trace_basis = false; tree_level_gluon_basis = false; orthogonal_basis = false; } /// Destructor. virtual ~Col_basis(){} - /// The number of quarks, initially set to 0 and (possibly) + /// The number of qqbar-pairs, initially set to 0 and (possibly) /// changed with create_basis, or while reading in the basis. int nq; /// The number of gluons, initially set to 0 and (possibly) /// changed with create_basis, or while reading in the basis. int ng; /// To actually contain the info about the basis vectors cb= vector1, vector2... . /// Technically cb is a col_basis, a vector of Col_amps. col_basis cb; /// To contain the Polynomial version of the scalar product matrix. Poly_matr P_spm; /// To contain the Polynomial version of the leading part of the scalar product matrix. Poly_matr leading_P_spm; /// To contain the double version of the scalar product matrix. dmatr d_spm; /// To contain the double version of the leading part of the scalar product matrix. dmatr leading_d_spm; /// To contain the set of Col_functions used. - /// As a pointer to a Col_functions object is used, - /// several bases may share the same functions for - /// numerical evaluation etc. Col_functions Col_fun; /// Is it a Trace_basis? bool is_Trace_basis() const {return trace_basis;} /// Is it an Orthogonal_basis? bool is_Orthogonal_basis() const {return orthogonal_basis;} /// Is it a Tree_level_gluon_basis? bool is_Tree_level_gluon_basis() const {return tree_level_gluon_basis;} /// Returns the number of basis vectors. uint size() const {return cb.size();} /// Returns the Col_amp (basis vector) at place i. const Col_amp & at( const int i ) const{return cb.at(i);} /// Returns the Col_amp (basis vector) at place i. Col_amp & at( const int i ) {return cb.at(i);} /// Is the col_basis empty? bool empty() const { return cb.empty(); } /// Erase the basis, stored in cb. void clear() { cb.clear(); } /// Appends a Col_amp to the basis, stored in cb. - void push_back( Col_amp Ca ) { cb.push_back( Ca ); } + void append( Col_amp Ca ) { cb.push_back( Ca ); } + + /// Appends the Col_amps in cb_in to the col_basis member cb. + void append( col_basis cb_in ); /******************** Functions for scalar products **********************/ /// Function for calculating the scalar products matrix. /// This function loops over all basis vectors and stores the /// value of the scalar product between basis vector /// i and basis vector j in the i,j -entry in P_spm and d_spm. /// The calculation is done using memoization. /// The symmetry of the scalar product matrix is not used /// for the calculation, instead it is checked that the /// resulting matrix is indeed symmetric. void scalar_product_matrix(); /// This function works as scalar_product_matrix, but does the - /// calculation numerically. It hence only saves (and writes out) d_spm. + /// calculation numerically. It hence only calculates d_spm. void scalar_product_matrix_num(); /// This function works like scalar_product_matrix, but does /// not use memoization. virtual void scalar_product_matrix_no_mem(); /// This function works like scalar_product_matrix_num, but does /// not use memoization. void scalar_product_matrix_num_no_mem(); /// Finds the leading Nc scalar product matrices, /// leading_P_spm and leading_d_spm. /// If the polynomial scalar product matrix, P_spm has /// been calculated, P_spm is used, otherwise P_spm is first calculated /// and the leading Nc limit is then taken of P_spm. void leading_scalar_product_matrix(); /// Function for calculating scalar products algebraically - /// knowing the basis and the scalar product matrix (Poly_matr) in the basis. + /// using the basis and the scalar product matrix (Poly_matr) in the basis. /// (Does add implicit conjugated part for Tree_level_gluon_basis, /// as these terms are contained in the matrix of scalar products.) virtual Polynomial scalar_product( const Col_amp & Ca1, const Col_amp & Ca2 ); /// Function for calculating scalar products numerically, knowing the basis /// and the scalar product matrix in numerical form. /// (Does add implicit conjugated part for Tree_level_gluon_basis, /// as these terms are contained in the matrix of scalar products.) virtual cnum scalar_product_num( const Col_amp & Ca1, const Col_amp & Ca2 ); /// Calculates the scalar product between numerical (complex) amplitudes v1, V2 /// using the numerical scalar product matrix, d_spm. /// The vectors thus needs to be expressed in the basis contained in cb. /// (Does add implicit conjugated part for Tree_level_gluon_basis, /// as these terms are contained in the matrix of scalar products.) virtual cnum scalar_product_num( const cvec & v1, const cvec & v2 ); /// Calculates the scalar product between numerical (complex) amplitudes v1, v2 /// using the numerical scalar product matrix, d_spm. /// Assumes that there are only diagonal contributions. /// This is useful for calculations in leading Nc limit. /// (Does add implicit conjugated part for Tree_level_gluon_basis, /// as these terms are contained in the matrix of scalar products.) cnum scalar_product_num_diagonal( const cvec & v1, const cvec & v2 ); /******************** Functions for reading and writing **********************/ // Functions for naming files /// Returns a standard filename, used for writing out /// the basis to a file. std::string basis_file_name() const; /// Returns a standard filename, used for writing out /// scalar product matrices. /// If leading is true, "_l" is appended to the filename. /// If "poly" is true "P_" is added to the filename, and if it is /// false "d_", as in double, is added to the filename. std::string spm_file_name( const bool leading, const bool poly) const; /// Function for reading in the basis from the file filename. /// The basis should be in human readable format, of form:
/// 0 [{1,3,4,2}]
/// 1 [{1,4,3,2}]
/// 2 [{1,2}(3,4)]
/// i.e. first basis vector number 0,1,2..., then /// the Col_amp corresponding to the basis vector in question. /// The Col_amps may consist of several Col_strs, for example
/// 0 [(1,2,3,4)]+[(1,4,3,2)]
/// 1 [(1,2,4,3)]+[(1,3,4,2)]
/// 2 [(1,3,4,2)]+[(1,2,4,3)]
/// and each Col_str may also contain a Polynomial. /// (The Polynomial should multiply the whole col_str, - /// rather than a quark_line inside the [] brackets.) + /// rather than a quark_line inside the []-brackets.) virtual void read_in_Col_basis( std::string filename ); /// Function for reading in the basis from default filename /// (see basis_file_name). virtual void read_in_Col_basis(); /// Read in a numerical matrix from a file filename /// (see spm_file_name) and save it as a double matrix, dmatr, /// in the member variable d_spm. /// The file should be in the format
/// {{d11,...,d1n},
/// ...,
/// {dn1,....dnn}},
/// and may contain comment lines starting with # at the top. void read_in_d_spm( std::string filename ); /// Read in a numerical matrix from a file with default filename /// (see spm_file_name) and save it as a double matrix, dmatr, /// in the member variable d_spm. /// The file should be in the format /// {{d11,...,d1n},
/// ...,
/// {dn1,....dnn}},
/// and may contain comment lines starting with # at the top. void read_in_d_spm( ); /// Read in a numerical matrix from the file filename and save it as a double matrix, dmatr, /// in the member variable leading_d_spm. /// The file should be in the format
/// {{d11,0,...,0},
/// ...,
/// {0,0,....dnn}},
/// and may contain comment lines starting with # at the top. void read_in_leading_d_spm( std::string filename ); /// Read in a numerical matrix from a file with default filename (see spm_file_name) /// and save it as a double matrix, dmatr, in the member variable leading_d_spm. /// The file should be in the format
/// {{d11,0,...,0},
/// ...,
/// {0,0,....dnn}},
/// and may contain comment lines starting with # at the top. void read_in_leading_d_spm( ); /// Read in a Polynomial matrix from the file filename and save it as a Poly_matr /// in the member variable P_spm. /// The file should be in the format
/// {{Poly11,...,Poly1n},
/// ...,
/// {Polyn1,...,Polynn}},
/// and may contain comment lines starting with # at the top. void read_in_P_spm( std::string filename ); /// Read in a Polynomial matrix from a file with default filename (see spm_file_name) /// and save it as a Poly_matr in the member variable P_spm. /// The file should be in the format
/// {{Poly11,...,Poly1n},
/// ...,
/// {Polyn1,...,Polynn}},
/// and may contain comment lines starting with # at the top. void read_in_P_spm( ); /// Read in a Polynomial matrix from a file with default filename /// (see spm_file_name) and save it as a Poly_matr /// in the member variable leading_P_spm. /// The file should be in the format
/// {{Poly11,0...,0},
/// ...,
/// {0,...,Polynn}},
/// and may contain comment lines starting with # at the top. void read_in_leading_P_spm( std::string filename ); /// Reads in a Polynomial matrix from default filename (see spm_file_name) /// and save it as a Poly_matr in the member variable leading_P_spm. /// The file should be in the format
/// {{Poly11,0...,0},
/// ...,
/// {0,...,Polynn}},
/// and may contain comment lines starting with # at the top. void read_in_leading_P_spm( ); // Functions for writing /// Function for writing out the basis to a file with name filename. virtual void write_out_Col_basis( std::string filename) const; /// Function for writing out the basis to a file /// with default name (see basis_file_name). virtual void write_out_Col_basis() const; - /// Function for writing out the basis in a human readable - /// format to cout. - virtual void write_out_Col_basis_to_cout() const; - - /// Writes out d_spm to file filename. + /// Writes out d_spm to the file filename. void write_out_d_spm( std::string filename ) const; /// Writes out d_spm to the standard filename, see spm_file_name. void write_out_d_spm( ) const; - /// Writes out P_spm to file filename. + /// Writes out P_spm to the file filename. void write_out_P_spm( std::string filename ) const; /// Writes out P_spm to the standard filename, see spm_file_name. void write_out_P_spm( ) const; - /// Writes out leading_d_spm to file filename. + /// Writes out leading_d_spm to the file filename. void write_out_leading_d_spm( std::string filename ) const; /// Writes out leading_d_spm to the standard filename, see spm_file_name. void write_out_leading_d_spm( ) const; /// Writes out leading_P_spm to the file filename. void write_out_leading_P_spm( std::string filename ) const; /// Writes out leading_P_spm to the standard filename, see spm_file_name. void write_out_leading_P_spm( ) const; /******************** Other functions **********************/ - /// Simplify all the basis vectors by using simplify on the + /// Simplifies all the basis vectors by using simplify on the /// individual Col_amps in the basis. void simplify(); /// Each type of color basis has to implement a function for decomposing /// an amplitude in the color basis. - virtual Poly_vec decompose( const Col_amp & Ca ) ; + virtual Poly_vec decompose( const Col_amp & Ca ); /// Function for finding the resulting Col_amp after exchanging /// a gluon between parton p1 and parton p2 in the /// basis vector vec. - Col_amp exchange_gluon( uint vec, int p1, int p2 ) ; + Col_amp exchange_gluon( uint vec, int p1, int p2 ); /// Function for calculating the color structure part of the soft anomalous /// dimension matrix. First calculates the effect of gluon exchange on a /// basis vector and then decomposes the result into the basis. /// For this to work the basis must clearly contain all resulting - /// bases vectors, meaning for example that it can not be + /// basis vectors, meaning for example that it can not be /// used for Tree_level_gluon_basis. /// The function is only available for the Trace_basis /// and the Orthogonal_basis classes. - Poly_matr Col_gamma( int p1, int p2 ); + /// The ij-component of the resulting matrix gives the amplitude + /// for ending up in component i if starting in component j. + Poly_matr color_gamma( int p1, int p2 ); /// Returns the number of quarks in the Col_basis after /// checking that each Col_str in each Col_amp /// has the same number of quarks. int n_quark_check() const; /// Returns the number of gluons in the Col_basis after /// checking that each Col_str in each Col_amp /// has the same number of gluons. int n_gluon_check() const; /// A function to rename the indices in two Col_strs, such that in the first /// they are called 1,2,3..., and in the second the relative order is kept. void rename_indices( Col_str & Cs1, Col_str & Cs2 ) const; /******************** Internal functions **********************/ protected: bool trace_basis; bool tree_level_gluon_basis; bool orthogonal_basis; /// The underlying function for calculation of scalar product /// matrices. Calculation depends on the arguments: /// save_P_spm, save_d_spm and use_mem. /// If save_P_spm is true the Polynomial scalar product /// matrix is saved to P_spm. /// If save_d_spm is true, the numerical scalar product matrix /// is saved to d_spm. /// The leading_d_spm is only calculated and written out if save_P_spm is true. /// If use_mem is true memoization is used /// in order to calculate a color topology only once. virtual void scalar_product_matrix( bool save_P_spm, bool save_d_spm, bool use_mem ); /// Calculates element i,j in scalar product matrix using the scalar product. virtual Polynomial ij_entry( const int i, const int j ) const; /// Checking that a numerical (double) matrix is diagonal. /// Used for the leading version of the scalar product matrix. /// Returns true if the matrix is diagonal and false otherwise. bool check_diagonal( const dmatr & matr ) const; /// Checking that a numerical (double) matrix (the scalar product matrix) /// is symmetric. /// Returns true if the matrix is symmetric and false otherwise. bool check_symmetry( const dmatr & matr ) const; /// Makes consistency checks on the scalar product matrix. void check_spm() const; /// Converts a text string (in a file) to a basis, /// used by the read_in_basis functions. void Col_basis_of_str( std::string str ); + /// Function for writing out the basis in a human readable + /// format to an ostream. + virtual std::ostream& write_out_Col_basis_to_stream( std::ostream& out ) const; + + /// The operator << for Col_basis operator must be able to access the + /// write_out_Col_basis_to_stream operator. + friend std::ostream& operator<<( std::ostream& out, const Col_basis & Cb ); + };// end class Col_basis /// Define the operator << for col_basis. std::ostream& operator<<( std::ostream& out, const col_basis & cb ); +/// Define the operator << for Col_basis. +std::ostream& operator<<( std::ostream& out, const Col_basis & Cb ); + }// end namespace ColorFull #endif /* COLBASIS_H_ */ diff --git a/MatrixElement/Matchbox/ColorFull/Col_functions.cc b/MatrixElement/Matchbox/ColorFull/Col_functions.cc --- a/MatrixElement/Matchbox/ColorFull/Col_functions.cc +++ b/MatrixElement/Matchbox/ColorFull/Col_functions.cc @@ -1,1833 +1,1918 @@ /* * Col_functions.cc * Contains definitions of functions and operators used for treating the color structure * Created on: Jul 8, 2010 * Author: malin */ #include "Col_functions.h" #include "parameters.h" #include #include #include #include namespace ColorFull { int Col_functions::leading_Nc_pow( const Polynomial & Poly ) const{ if( Poly.poly.empty()) { if( double_num(Poly)!=0 ) return 0; else return std::numeric_limits::min(); } int leading_pow=Poly.at(0).pow_Nc + Poly.at(0).pow_CF; // Loop over non-zero Monomials for (uint i=0; i leading_pow && Poly.at(i).int_part!=0 ) leading_pow=Poly.at(i).pow_Nc + Poly.at(i).pow_CF; } return leading_pow; } int Col_functions::leading_Nc_pow( const Poly_vec & Pv ) const{ int leading_pow=leading_Nc_pow( Pv.at(0) ); // Loop over Polynomials for (uint p=0; p leading_pow ) leading_pow=leading_Nc_pow( Pv.at(p) ); } return leading_pow; } +/* int Col_functions::leading_Nc_pow( const std::vector< shared_ptr > & Pvp) const{ int leading_pow=leading_Nc_pow( *(Pvp.at(0)) ); // Loop over Polynomials for (uint p=1; p leading_pow ) leading_pow=leading_Nc_pow( *(Pvp.at(p)) ); } return leading_pow; } +*/ Polynomial Col_functions::leading( const Polynomial & Poly ) const{ // If the Polynomial is empty=1, or it it has only one term, // just return the Poly itself if( Poly.empty() ) return Poly; // Candidate for highest power of Nc+CF int cand_pow = Poly.at(0).pow_Nc + Poly.at(0).pow_CF; // For containing the leading Nc Polynomial Polynomial Leading_pol; - Leading_pol.push_back(Poly.at(0)); + Leading_pol.append(Poly.at(0)); // If only one term, keep that term if( Poly.size()==1 ) Leading_pol=Poly; // If more than one term, look for higher powers // Looks for Monomials with highest Nc power if( Poly.size()>1 ){// found bug for Polynomials with one term 13 07 12 for (int k = 1; k < Poly.size(); k++) { // If power higher if ((Poly.at(k).pow_Nc + Poly.at(k).pow_CF) > cand_pow) { cand_pow = Poly.at(k).pow_Nc + Poly.at(k).pow_CF; Leading_pol.clear(); Leading_pol = Leading_pol * 0; Leading_pol = Leading_pol + Poly.at(k); } // If power equal else if (Poly.at(k).pow_Nc + Poly.at(k).pow_CF == cand_pow) { Leading_pol = Leading_pol + Poly.at(k); } } } // now Leading_pol contains terms with maximal power of CF plus Nc // If the variable full_CF is false (default), CF should be replaced with TR*Nc. // If numerical evaluation is done after this, CF will thus be evaluated to TR Nc. // If full_cf is true, CF will be replaced by CF(Nc). if ( ! full_CF ) { // Loop over terms and replace CF with the Nc -> infinity limit of CF =TR*Nc for (int i = 0; i < Leading_pol.size(); i++) { int pow_CF = Leading_pol.at(i).pow_CF; Leading_pol.at(i).pow_Nc += pow_CF; Leading_pol.at(i).pow_TR += pow_CF; Leading_pol.at(i).pow_CF = 0; } } Leading_pol.simplify(); return Leading_pol; } Poly_vec Col_functions::leading( const Poly_vec & Pv ) const{ // To contain the result Poly_vec Pv_res; // Take the leading part of each Polynomial // after this all Monomials in the SAME Polynomial have the same Nc power for (uint i = 0; i < Pv.size(); i++) { // Take the leading terms in each component - Pv_res.push_back( leading( Pv.at(i) ) ); + Pv_res.append( leading( Pv.at(i) ) ); } // Find the leading power int leading_pow = leading_Nc_pow(Pv_res); // Loop over entries and keep only those with maximal power for (uint p = 0; p < Pv_res.size(); p++) { if (leading_Nc_pow(Pv_res.at(p)) != leading_pow) { - Pv_res.at(p) = 0 * Pv_res.at(p); + Pv_res.at(p) *= 0; Pv_res.at(p).simplify(); } } return Pv_res; } Poly_matr Col_functions::leading( const Poly_matr & Pm ) const{ // To contain the result Poly_matr Pm_res; Pm_res.pm.reserve(Pm.pm.size()); // Loop over Poly_vecs, and take the leading part of each Poly_vec for (uint i = 0; i < Pm.size(); i++) { // Take the leading terms in each Poly_vec Poly_vec Pvl= leading( Pm.at(i) ); - Pm_res.push_back( Pvl ); + Pm_res.append( Pvl ); } int cand_pow = leading_Nc_pow( Pm.at(0).pv ); // Loop over Poly_vecs to locate highest power for (uint k = 0; k < Pm_res.size(); k++) { int pow_at_k = leading_Nc_pow(Pm_res.at(k).pv); if (pow_at_k > cand_pow) cand_pow = pow_at_k; } // In all Polynomials, in all Poly_vec's, // put all elements which doesn't have highest power to 0 for (uint k = 0; k < Pm_res.size(); k++) { // In each Poly_vec, loop over all Polynomials for (uint p = 0; p < Pm_res.at(k).size(); p++) { if (leading_Nc_pow(Pm_res.at(k).at(p)) != cand_pow) { //Pm_res.at(k).pv.at(p) = Pm_res.at(k).pv.at(p) * 0; Pm_res.at( k,p )=Pm_res.at(k).at(p) * 0; Pm_res.at(k,p).simplify(); } } } return Pm_res; } - +/* Poly_vec Col_functions::leading( const std::vector > & Pvp ) const{ Poly_vec Pv_res; // Loop over entries in vector (Poly_vecs), and take the leading part of each Polynomial // after this each Monomial has the same Nc+CF-power for (uint i = 0; i < Pvp.size(); i++) { // Take the leading terms in each component - shared_ptr the_pointer=Pvp.at(i); + std::shared_ptr the_pointer=Pvp.at(i); Pv_res.push_back( leading( (*the_pointer) ) ); } // Find the leading power int leading_pow = leading_Nc_pow(Pv_res); // Loop over entries and keep only those with maximal power for (uint p = 0; p < Pv_res.size(); p++) { if (leading_Nc_pow(Pv_res.at(p)) != leading_pow) { - Pv_res.at(p) = 0 * Pv_res.at(p); + Pv_res.at(p) *= 0; Pv_res.at(p).simplify(); } } return Pv_res; } +*/ - +/* dmatr Col_functions::leading( const std::vector< std::vector< shared_ptr > > & Ppm ) const { // To contain the result as a matrix of double dmatr res; res.reserve( Ppm.size() ); int cand_pow = leading_Nc_pow( Ppm.at(0) ); // Loop over Poly_vecs to locate highest power for (uint k = 0; k < Ppm.size(); k++) { int the_pow=leading_Nc_pow(Ppm.at(k)); if ( the_pow > cand_pow ) cand_pow = the_pow; } // In all Polynomials, in all Poly_vec's, // put all elements which doesn't have highest power to 0 for (uint k = 0; k < Ppm.size(); k++) { dvec dummy; res.push_back(dummy); // Not to run out of range // In each Poly_vec, loop over all Polynomials for (uint p = 0; p < Ppm.at(k).size(); p++) { Polynomial the_poly=*Ppm.at(k).at(p); leading_Nc_pow( the_poly ); if ( leading_Nc_pow( the_poly ) != cand_pow ) {res.at(k).push_back(0);} else res.at(k).push_back( double_num(leading(the_poly)) ); } } return res; } - -std::map< std::string, Polynomial > Col_functions::leading( const std::map< std::string, Polynomial > & mem_map ) const{ - - // To contain (string, leading(Polynomial)) - std::map< std::string, Polynomial > res; - - // Find the highest power of Nc+CF - int pow_cand=leading_Nc_pow( mem_map.begin()->second ); - for( auto iter = mem_map.begin(); iter != mem_map.end(); ++iter ) { - Polynomial Poly= (iter->second); - if( leading_Nc_pow(Poly) > pow_cand ) pow_cand=leading_Nc_pow(Poly); - } - - for( auto iter = mem_map.begin(); iter != mem_map.end(); ++iter ) { - - // Insert pair of string and the leading versions of the Polynomial - Polynomial lead_Poly= leading( (iter->second) ); - if ( leading_Nc_pow(lead_Poly) != pow_cand ) lead_Poly=lead_Poly*0; - - res.insert( make_pair( iter->first, lead_Poly ) ); - } // After this only the leading part of each Polynomial contributes - - return res; -} +*/ cnum Col_functions::cnum_num( const Monomial & Mon ) const { cnum res=Mon.cnum_part*static_cast(Mon.int_part); for ( int ix=0; ixMon.pow_Nc; ix-- ) res /= static_cast(Nc); for ( int ix=0; ix>Mon.pow_CF; ix-- ) res /= static_cast(CF); for ( int ix=0; ix>Mon.pow_TR; ix-- ) res /= static_cast(TR); //cnum res=pow(Nc, Mon.pow_Nc)*pow(CF, Mon.pow_CF)*pow(TR, Mon.pow_TR)*Mon.int_part* Mon.cnum_part; return res; } double Col_functions::double_num( const Monomial & Mon ) const{ double im=imag( cnum_num(Mon) ); double re=real( cnum_num(Mon) ); // Warn if the complex number has significant imaginary parts if( im!=0.0 and re!=0.0 ) { double ratio =im/re; if( ratio > accuracy ) std::cerr << "Col_functions::double_num(Mon): Warning keeping only real part of complex number, the ratio im/re was " << ratio << std::endl; } else if ( im!=0.0 and re==0.0 ){ std::cerr << "Col_functions::double_num(Mon): Warning keeping only real part of complex number, imaginary part was " << im << std::endl; } return re; } cnum Col_functions::cnum_num( const Polynomial & Poly ) const { // An empty Polynomial has numerical value 1 if( Poly.empty() ) return 1.0; cnum res = 0; // Add contributions from Monomials for ( int k = 0; k < Poly.size(); k++ ) { cnum part_k; part_k=cnum_num( Poly.at(k) ); res = res + part_k; } return res; } double Col_functions::double_num( const Polynomial & Poly ) const{ double im=imag( cnum_num(Poly) ); double re=real( cnum_num(Poly) ); // Warn if the complex number has significant imaginary parts if( im!=0.0 and re!=0.0 ) { double ratio =im/re; if( ratio > accuracy ) std::cerr << "Col_functions::double_num: Warning keeping only real part of complex number, the ratio im/re was " << ratio << std::endl; } else if ( im!=0.0 and re==0.0 ){ std::cerr << "Col_functions::double_num: Warning keeping only real part of complex number, imaginary part was " << im << std::endl; } return re; } Col_amp Col_functions::emit_gluon( const Col_str & in_Col_str, int emitter, int g_new ) const{ // Locate the emitter in the Col_str std::pair place=in_Col_str.find_parton(emitter); // Find what kind, q qbar or g, the emitter is std::string kind=in_Col_str.find_kind(emitter); // Defining Col_str to return (two needed in case of g) Col_str out_Col_str1=in_Col_str; Col_str out_Col_str2=in_Col_str; // Defining Col_amp to return Col_amp out_Col_amp; // If the emitter is a quark if( kind =="q" ){ // Add new parton index at second first place in relevant quark_line out_Col_str1.insert( place.first, place.second+1, g_new ); out_Col_amp.ca.push_back( out_Col_str1 ); } // If the emitter is an anti-quark else if( kind =="qbar" ){ // Add new gluon before the qbar out_Col_str1.insert( place.first, place.second, g_new ); // Change sign out_Col_str1.Poly=out_Col_str1.Poly*(-1); // Making a Col_amp to return out_Col_amp.ca.push_back( out_Col_str1 ); } // If the emitter is a g else if( kind =="g" ){ // If the emitter is a gluon a new gluon line should be inserted in two // different places, before and after the emitter // -sign when inserting the gluon before // Change sign Poly for first term Monomial Mon_tmp; Mon_tmp.int_part=-1; - out_Col_str1.Poly=out_Col_str1.Poly*Mon_tmp; + out_Col_str1.Poly *= Mon_tmp; // Inserting the gluon before out_Col_str1.insert( place.first, place.second, g_new ); // Inserting the gluon after out_Col_str2.insert( place.first, place.second+1, g_new ); // Appending result to out_Col_amp out_Col_amp.ca.push_back( out_Col_str1 ); out_Col_amp.ca.push_back( out_Col_str2 ); // Normal order out_Col_amp.normal_order(); } out_Col_amp.simplify(); return out_Col_amp; } Col_amp Col_functions::emit_gluon( const Col_amp & Ca_in, int emitter, int g_new ) const{ Col_amp Ca_out; // Emit from each Col_str, and append to new Col_amp for( uint m=0; m< Ca_in.ca.size(); m++ ){ Col_amp part_m=emit_gluon(Ca_in.ca.at(m), emitter, g_new); Ca_out.append(part_m.ca); } return Ca_out; } +Col_amp Col_functions::split_gluon(const Col_str & in_Col_str, int g_old, + int q_new, int qbar_new) const { + + // Locate the splitting gluon in the Col_str + std::pair place = in_Col_str.find_parton(g_old); + + // Find what kind, q qbar or g, the emitter is + std::string kind = in_Col_str.find_kind(g_old); + if (kind != "g"){ + std::cerr + << "Col_functions::split_gluon: The splitting parton must be a gluon but was a " + << kind << "." << std::endl; + assert( 0 ); + } + + // Quark_line to be treated + Quark_line Ql = in_Col_str.at(place.first); + + // To contain the resulting Col_amp + Col_amp out_Col_amp; + + // If the Ql is open we should get two open Qls + if (Ql.open) { + + // Col_strs to return in Col_amp + Col_str out_Col_str1 = in_Col_str; + Col_str out_Col_str2 = in_Col_str; + + // Define Quark_lines, to contain the two new Quark_lines after splitting + // For leading part + Quark_line Ql1 = in_Col_str.at(place.first); + Quark_line Ql2 = in_Col_str.at(place.first); + // For suppressed part + Quark_line Ql3; + + // Take first part up to place of gluon + Ql1 = Ql.before(place.second); + // and append qbar_new + Ql1.append(qbar_new); + Ql1.open = true; + + // Take 2nd part after place of gluon + Ql2 = Ql.after(place.second); + // and prepend q_new + Ql2.prepend(q_new); + Ql2.open = true; + + // In the Col_str, replace old Quark_line with two new + out_Col_str1.erase(place.first); + out_Col_str1 = out_Col_str1 * Ql1 * Ql2; + + // Multiply with TR + Monomial Mon1; + Mon1.pow_TR=1; + out_Col_str1 = out_Col_str1 * Mon1; + + + // Erase the gluon + out_Col_str2.erase(place); + // Add quark_line with just qqbar pair + Ql3.append(q_new); + Ql3.append(qbar_new); + out_Col_str2= out_Col_str2*Ql3; + // Multiply with -TR/Nc + Monomial Mon2; + Mon2.pow_TR=1; + Mon2.pow_Nc=-1; + Mon2*=-1; + out_Col_str2 = out_Col_str2 * Mon2; + + // Add parts in resulting Col_amp + out_Col_amp.append(out_Col_str1); + out_Col_amp.append(out_Col_str2); + + } + // If the Ql is closed + else { + + Quark_line new_Ql; + Col_str out_Col_str = in_Col_str; + + // Leftmost we should have the new quark + new_Ql.prepend(q_new); + + // After this should follow the segment after the splitting gluon + new_Ql.append(Ql.after(place.second).ql); + + // and after that the first part of the old Ql + new_Ql.append(Ql.before(place.second).ql); + + // Rightmost we should have the new anti-quark + new_Ql.append(qbar_new); + + // We should not forget the polynomial factor of the old Ql + new_Ql.Poly = in_Col_str.at(place.first).Poly; + + // ... and we get a factor TR from the gluon contraction + out_Col_str = out_Col_str*Monomial("TR"); + + // In the Col_str, replace old Quark_line with new + out_Col_str.at(place.first) = new_Ql; + + out_Col_amp.append(out_Col_str); + } + + // Normal order + out_Col_amp.normal_order(); + + out_Col_amp.simplify(); + + return out_Col_amp; +} + + + +Col_amp Col_functions::split_gluon( const Col_amp & in_Col_amp, int g_old, int q_new, int qbar_new ) const{ + + Col_amp Ca_out; + + // Split each Col_str, and append to new Col_amp + for( uint m=0; m< in_Col_amp.ca.size(); m++ ){ + Col_amp Cam = split_gluon( in_Col_amp.ca.at(m), g_old, q_new, qbar_new ); + Ca_out= Ca_out + Cam; + } + + return Ca_out; +} + // See my general color structure paper Col_amp Col_functions::exchange_gluon( const Col_str & Cs, int p1, int p2 ) const{ Col_str Cs_copy=Cs; // Find out kind and location of partons std::string kind1 = Cs_copy.find_kind(p1); std::string kind2 = Cs_copy.find_kind(p2); std::pair place1 = Cs_copy.find_parton(p1); std::pair place2 = Cs_copy.find_parton(p2); // To contain result Col_amp Ca; // Make sure the col_f in multiplying participating Ql's are 0 Polynomial Poly1; // For comparing Poly is 1, all powers are 0 if (!(Poly1 == Cs_copy.cs.at(place1.first).Poly)) { // Move factor of Quark_line to Col_str Cs_copy.Poly = Cs_copy.Poly * Cs_copy.cs.at(place1.first).Poly; Cs_copy.cs.at(place1.first).Poly = Poly1; } if (!(Cs_copy.cs.at(place2.first).Poly == Poly1)) { // Move factor of Quark_line to Col_str Cs_copy.Poly = Cs_copy.Poly * Cs_copy.cs.at(place2.first).Poly; Cs_copy.cs.at(place2.first).Poly = Poly1; } // sign if ( place1.first == place2.first && place1.second == place2.second ) { Monomial mon; // For qq or qbar qbar // If the qs are the same, the result is just CF*old for q and qbar // there are 0 minus signs for quarks and 2 for anti-quarks if (kind1 == "q" or kind1 == "qbar") mon.pow_CF = 1; // for gluons else{ mon.pow_Nc = 1; // There is a factor TR from the contracted gluon, // The sign can be checked by writing out four terms // corresponding to inserting the new gluon // first/first, first/last, last/first, last/last mon.pow_TR+=1; mon.int_part*=2; } - Cs_copy.Poly = Cs_copy.Poly * mon; + Cs_copy.Poly *= mon; Ca.ca.push_back( Cs_copy ); return Ca; } // Exchange between qq or qbar qbar if ((kind1 == "q" && kind2 == "q") or (kind1 == "qbar" && kind2 == "qbar")) { Col_str Cs2 = Cs_copy; // The q's normally sit on different quark_lines // The leading Nc-tems has the q's exchanged Cs_copy.cs.at(place1.first).ql.at(place1.second) = p2; Cs_copy.cs.at(place2.first).ql.at(place2.second) = p1; // Multiply with TR Monomial Mon; Mon.pow_TR = 1; - Cs_copy.Poly = Cs_copy.Poly * Mon; + Cs_copy.Poly *= Mon; // The suppressed term is a copy of the old term, // but multiplied with -TR /Nc Mon.pow_TR = 1; Mon.int_part = -1; Mon.pow_Nc = -1; - Cs2.Poly = Cs2.Poly * Mon; + Cs2.Poly *= Mon; Ca.ca.push_back(Cs_copy); Ca.ca.push_back(Cs2); } // Exchange between q qbar if ((kind1 == "q" && kind2 == "qbar") or (kind1 == "qbar" && kind2 == "q")) { std::pair q_place; std::pair qbar_place; int the_q=-1; int the_qbar=-1; if ((kind1 == "q" && kind2 == "qbar")) { q_place = place1; qbar_place = place2; the_q = p1; the_qbar = p2; } else if ((kind1 == "qbar" && kind2 == "q")) { q_place = place2; qbar_place = place1; the_q = p2; the_qbar = p1; } Col_str Cs2 = Cs_copy; // If q and qbar are not part of same ql if (q_place.first != qbar_place.first) { // The non-suppressed term, obtained by erasing q qbar and joining rest Cs_copy.erase(q_place); Cs_copy.erase(qbar_place); // In the ql of the qbar, append the ql of the q, then erase the ql of the q Cs_copy.cs.at(qbar_place.first).append(Cs_copy.cs.at(q_place.first).ql); // and erase the ql of the q Cs_copy.erase(q_place.first); } // If q and qbar sit on same ql else { // The non-suppressed term, obtained by erasing q qbar and joining rest to a ring Cs_copy.erase(qbar_place); Cs_copy.erase(q_place); Cs_copy.cs.at(q_place.first).open = false; // If only one quark remains the term should be 0 } // q and qbar must be found assert( the_q!=-1); assert( the_qbar!=-1); // Construct Quark_line={q,qbar}, and append it Quark_line Ql; Ql.open = true; - Ql.push_back( the_q ); - Ql.push_back( the_qbar ); - Cs_copy.push_back(Ql); + Ql.append( the_q ); + Ql.append( the_qbar ); + Cs_copy.append(Ql); // Multiply with TR Monomial Mon; Mon.pow_TR = 1; - Cs_copy.Poly = Cs_copy.Poly * Mon; + Cs_copy.Poly *= Mon; // The suppressed term is a copy of the old term, // but multiplied with -TR / Nc Mon.pow_TR=1; Mon.int_part = -1; Mon.pow_Nc = -1; - Cs2.Poly = Cs2.Poly * Mon; + Cs2.Poly *= Mon; // Construct the resulting color_amplitude and return // Make sure there is not only one or 0 gluons in a closed ql // Append Cs to ca if >=2 gluons // if open, append if (Cs_copy.cs.at(q_place.first).open) Ca.ca.push_back(Cs_copy); // if closed but >= 2 gluons, append else if (Cs_copy.cs.at(q_place.first).ql.size() >= 2 && !Cs_copy.cs.at( q_place.first).open) Ca.ca.push_back(Cs_copy); // If 1 gluon, the term is 0 and is not appended // If 0 gluons the empty colsed ql is equivalent to multiplying with Nc, obtained after simplification else if (Cs_copy.cs.at(q_place.first).empty() && !Cs_copy.cs.at(q_place.first).open) { // return 2nd term * (1-Nc^2) Ca.ca.push_back(Cs_copy); Ca.ca.push_back(Cs2); Ca.simplify(); return Ca; } Ca.ca.push_back(Cs2); } // Exchange between q g if ((kind1 == "q" && kind2 == "g") or (kind1 == "g" && kind2 == "q")) { std::pair q_place; std::pair g_place; int the_q=0; int the_g=0; if (kind1 == "q" && kind2 == "g") { q_place = place1; g_place = place2; the_q = p1; the_g = p2; } else if (kind1 == "g" && kind2 == "q") { q_place = place2; g_place = place1; the_q = p2; the_g = p1; } // For containing Quark_lines Quark_line Ql11; Quark_line Ql12; Quark_line Ql21; Quark_line Ql22; Col_str Cs1 = Cs_copy; Col_str Cs2 = Cs_copy; // If the q and the g are not part of same ql if (g_place.first != q_place.first) { // Take split the Ql with the gluon after the gluon Quark_line Qlg_start = Cs_copy.cs.at(g_place.first).before( g_place.second ); Quark_line Qlg_end = Cs_copy.cs.at(g_place.first).after( g_place.second ); Quark_line Qlq_end = Cs_copy.cs.at(q_place.first).after( q_place.second ); // First part, + sign // First part, first Ql Ql11 = Qlg_end; Ql11.prepend(the_g); Ql11.prepend(the_q); // First part, second Ql Ql12 = Qlg_start; Ql12.append(Qlq_end.ql); // If the ql of the gluon was closed account for this by gluing qls together if (!Qlg_start.open) { Ql11.append(Ql12.ql); Ql11.open = true; }; // Second part, first Ql Ql21 = Qlg_end; Ql21.prepend(the_q); // Second part, second Ql Ql22 = Qlg_start; Ql22.append(the_g); Ql22.append(Qlq_end.ql); // If the ql of the gluon was closed account for this by gluing qls together if (!Qlg_start.open) { Ql21.append(Ql22.ql); Ql21.open = true; }; // Replace Ql's in the Col_str Cs1.cs.at(q_place.first) = Ql11; Cs2.cs.at(q_place.first) = Ql21; // There is only one ql if the ql of the gluon was closed if (Qlg_start.open) { Cs1.cs.at(g_place.first) = Ql12; Cs2.cs.at(g_place.first) = Ql22; } else { // Erase 2nd ql Cs1.erase(g_place.first); Cs2.erase(g_place.first); } } // If q and g are part of same ql else { Quark_line Ql1 = Cs_copy.cs.at(g_place.first).before( g_place.second ); Ql1.ql.erase(Ql1.ql.begin()); Quark_line Ql2 = Cs_copy.cs.at(g_place.first).after( g_place.second); // First term, First ql Ql11 = Ql1; Ql11.open = false; // First term, 2nd ql Ql12 = Ql2; Ql12.prepend(the_g); Ql12.prepend(the_q); // 2nd term, 1st ql Ql21 = Ql1; Ql21.append(the_g); Ql21.open = false; // 2nd term 2nd ql Ql22 = Ql2; Ql22.prepend(the_q); // Replace Ql's in the Col_str Cs1.cs.at(q_place.first) = Ql11; Cs1.cs.insert(Cs1.cs.begin() + q_place.first + 1, Ql12); // Replace Ql's in the Col_str Cs2.cs.at(q_place.first) = Ql21; Cs2.cs.insert(Cs2.cs.begin() + q_place.first + 1, Ql22); } // Multiply first part with TR Monomial Mon_tmp; Mon_tmp.pow_TR = 1; - Cs1.Poly = Cs1.Poly * Mon_tmp; + Cs1.Poly *= Mon_tmp; // Multiply second part with -TR Monomial Mon_tmp2; Mon_tmp2.pow_TR = 1; Mon_tmp2.int_part = -1; - Cs2.Poly = Cs2.Poly * Mon_tmp2; + Cs2.Poly *= Mon_tmp2; // Add to result Ca.ca.push_back(Cs1); Ca.ca.push_back(Cs2); }// end exchange between q g // Exchange between qbar g (q and g in my paper) if ((kind1 == "qbar" && kind2 == "g") or (kind1 == "g" && kind2 == "qbar")) { std::pair qbar_place; std::pair g_place; int the_qbar=0; int the_g=0; if (kind1 == "qbar" && kind2 == "g") { qbar_place = place1; g_place = place2; the_qbar = p1; the_g = p2; } else if (kind1 == "g" && kind2 == "qbar") { qbar_place = place2; g_place = place1; the_qbar = p2; the_g = p1; } // For containing Quark_lines Quark_line Ql11, Ql12, Ql21, Ql22; // For containing resulting Col_str's Col_str Cs1 = Cs_copy; Col_str Cs2 = Cs_copy; // If ql and g on different quark_lines if (g_place.first != qbar_place.first) { // Split ql's into parts Quark_line Qlg_start = Cs_copy.cs.at(g_place.first).before( g_place.second) ; Quark_line Qlg_end = Cs_copy.cs.at(g_place.first).after( g_place.second ); Quark_line Qlqbar_start = Cs_copy.cs.at( qbar_place.first ).before(qbar_place.second); // First part, + sign, first Ql Ql11 = Qlqbar_start; Ql11.append(the_g); Ql11.append(Qlg_end.ql); // First part, + sign, second Ql Ql12 = Qlg_start; Ql12.append(the_qbar); // If the ql of the gluon was closed account for this by gluing qls together if (!Qlg_start.open) { Ql11.append(Ql12.ql); Ql11.open = true; }; // Second part, - sign, first Ql Ql21 = Qlqbar_start; Ql21.append(Qlg_end.ql); // Second part, - sign, second Ql Ql22 = Qlg_start; Ql22.append(the_g); Ql22.append(the_qbar); // If the ql of the gluon was closed account for this by gluing qls together if (!Qlg_start.open) { Ql21.append(Ql22.ql); Ql21.open = true; }; // Replace Ql's in the Col_str Cs1 = Cs_copy; Cs2 = Cs_copy; if (Qlg_start.open) { Cs1.cs.at(qbar_place.first) = Ql11; Cs1.cs.at(g_place.first) = Ql12; Cs2.cs.at(qbar_place.first) = Ql21; Cs2.cs.at(g_place.first) = Ql22; } else { // If the ql of the g was closed, there is only one ql, erase 2nd ql Cs1.cs.at(qbar_place.first) = Ql11; Cs1.erase(g_place.first); Cs2.cs.at(qbar_place.first) = Ql21; Cs2.erase(g_place.first); } } // If qbar and g are part of same quark_line else { Quark_line Ql1 = Cs_copy.cs.at(g_place.first).before(g_place.second ); Quark_line Ql2 = Cs_copy.cs.at(g_place.first).after( g_place.second ); Ql2.ql.erase(Ql2.ql.end() - 1); // First term, First ql Ql11 = Ql1; Ql11.append(the_qbar); // First term, 2nd ql Ql12 = Ql2; Ql12.prepend(the_g); Ql12.open = false; // 2nd term, 1st ql Ql21 = Ql1; Ql21.append(the_g); Ql21.append(the_qbar); // 2nd term 2nd ql Ql22 = Ql2; Ql22.open = false; // Replace Ql's in the Col_str Cs1 = Cs_copy; Cs1.cs.at(qbar_place.first) = Ql11; Cs1.cs.insert(Cs1.cs.begin() + qbar_place.first + 1, Ql12); // Replace Ql's in the Col_str Cs2 = Cs_copy; Cs2.cs.at(qbar_place.first) = Ql21; Cs2.cs.insert(Cs2.cs.begin() + qbar_place.first + 1, Ql22); } // Multiply with TR Monomial Mon_tmp; Mon_tmp.pow_TR = 1; - Cs1.Poly = Cs1.Poly * Mon_tmp; + Cs1.Poly *= Mon_tmp; // Multiply with -TR Monomial Mon_tmp2; Mon_tmp2.pow_TR = 1; Mon_tmp2.int_part = -1; - Cs2.Poly = Cs2.Poly * Mon_tmp2; + Cs2.Poly *= Mon_tmp2; Ca.ca.push_back(Cs1); Ca.ca.push_back(Cs2); } // Exchange between g g if (kind1 == "g" && kind2 == "g") { // For containing resulting Col_str's Col_str Cs1 = Cs_copy; Col_str Cs2 = Cs_copy; Col_str Cs3 = Cs_copy; Col_str Cs4 = Cs_copy; // For containing Quark_lines Quark_line Ql11, Ql12, Ql21, Ql22, Ql31, Ql32, Ql41, Ql42; // If g's on different Ql's if (place1.first != place2.first) { // Split both ql's into parts Quark_line Ql1_start = Cs_copy.cs.at(place1.first).before( place1.second ); Quark_line Ql1_end = Cs_copy.cs.at(place1.first).after( place1.second ); Quark_line Ql2_start = Cs_copy.cs.at(place2.first).before( place2.second ); Quark_line Ql2_end = Cs_copy.cs.at(place2.first).after( place2.second ); // First term, both quarks on 2nd ql, First ql Ql11 = Ql1_start; Ql11.append(Ql2_end.ql); // First term, 2nd ql Ql12 = Ql2_start; Ql12.append(p2); Ql12.append(p1); Ql12.append(Ql1_end.ql); // 2nd term, g1 on ql1, g2 on ql2, 1st ql Ql21 = Ql1_start; Ql21.append(p1); Ql21.append(Ql2_end.ql); // 2nd term, 2nd ql Ql22 = Ql2_start; Ql22.append(p2); Ql22.append(Ql1_end.ql); // 3rd term, g2 on ql1, g1 on ql2,1st ql Ql31 = Ql1_start; Ql31.append(p2); Ql31.append(Ql2_end.ql); // 3rd term, 2nd ql Ql32 = Ql2_start; Ql32.append(p1); Ql32.append(Ql1_end.ql); // last term, both quarks on 1st ql,1st ql Ql41 = Ql1_start; Ql41.append(p1); Ql41.append(p2); Ql41.append(Ql2_end.ql); // last term, 2nd ql Ql42 = Ql2_start; Ql42.append(Ql1_end.ql); // Replace Ql's in the Col_str // If both Ql's are open if (Cs_copy.cs.at(place1.first).open && Cs_copy.cs.at(place2.first).open) { Cs1.cs.at(place1.first) = Ql11; Cs1.cs.at(place2.first) = Ql12; Cs2.cs.at(place1.first) = Ql21; Cs2.cs.at(place2.first) = Ql22; Cs3.cs.at(place1.first) = Ql31; Cs3.cs.at(place2.first) = Ql32; Cs4.cs.at(place1.first) = Ql41; Cs4.cs.at(place2.first) = Ql42; } // If string where closed glue together ends // If first ql closed else if (!Cs_copy.cs.at(place1.first).open) { Ql12.append(Ql11.ql); Ql22.append(Ql21.ql); Ql32.append(Ql31.ql); Ql42.append(Ql41.ql); //The result should be open/closed if the ql2 was open/closed Ql12.open = Cs_copy.cs.at(place2.first).open; Ql22.open = Cs_copy.cs.at(place2.first).open; Ql32.open = Cs_copy.cs.at(place2.first).open; Ql42.open = Cs_copy.cs.at(place2.first).open; //The reslut contaied int Qlx2 sould be used for the Color structure // Replace Ql's in the Col_str Cs1.cs.at(place2.first) = Ql12; Cs2.cs.at(place2.first) = Ql22; Cs3.cs.at(place2.first) = Ql32; Cs4.cs.at(place2.first) = Ql42; Cs1.erase(place1.first); Cs2.erase(place1.first); Cs3.erase(place1.first); Cs4.erase(place1.first); } // if only 2nd ql closed if (!Cs_copy.cs.at(place2.first).open && Cs_copy.cs.at(place1.first).open) { Ql11.append(Ql12.ql); Ql21.append(Ql22.ql); Ql31.append(Ql32.ql); Ql41.append(Ql42.ql); // The result should be open Ql11.open = true; Ql21.open = true; Ql21.open = true; Ql21.open = true; //The result contained int Qlx1 should be used for the Color structure // Replace Ql's in the Col_str Cs1.cs.at(place2.first) = Ql11; Cs2.cs.at(place2.first) = Ql21; Cs3.cs.at(place2.first) = Ql31; Cs4.cs.at(place2.first) = Ql41; Cs1.erase(place1.first); Cs2.erase(place1.first); Cs3.erase(place1.first); Cs4.erase(place1.first); } } // If g's on same ql's if (place1.first == place2.first) { Quark_line Ql_start = Cs_copy.cs.at(place1.first).before( std::min(place2.second, place1.second)); // the part between the g's Quark_line Ql_between = Cs_copy.cs.at(place1.first).before(std::max( place2.second, place1.second)); Ql_between = Ql_between.after( std::min(place2.second, place1.second) ); // the end after 2nd g Quark_line Ql_end = Cs_copy.cs.at(place1.first).after( std::max(place2.second, place1.second)); // we have to know what g is first to put them back in right order int first_g, last_g; if( place1.second < place2.second ) { first_g=p1; last_g=p2; } else { first_g=p2; last_g=p1; } // First term, both gluons inbetween Ql11 = Ql_start; Ql11.append(Ql_end.ql); // First term, part between quarks Ql12 = Ql_between; Ql12.append( last_g ); Ql12.append( first_g ); Ql12.open = false; // 2nd term, g1 after first part, g2 after 2nd Ql21 = Ql_start; Ql21.append( first_g ); Ql21.append(Ql_end.ql); // First term, part between quarks Ql22 = Ql_between; Ql22.append( last_g ); Ql22.open = false; // 3rd term, g2 after first part, g1 after 2nd Ql31 = Ql_start; Ql31.append( last_g); Ql31.append(Ql_end.ql); // First term, part between quarks Ql32 = Ql_between; Ql32.append( first_g ); Ql32.open = false; // 4th term, g2 after first part, g1 after 2nd Ql41 = Ql_start; Ql41.append( first_g ); Ql41.append( last_g ); Ql41.append(Ql_end.ql); // First term, part between quarks Ql42 = Ql_between; Ql42.open = false; Cs1.cs.at(place1.first) = Ql11; Cs1.cs.insert(Cs1.cs.begin() + place1.first + 1, Ql12); Cs2.cs.at(place1.first) = Ql21; Cs2.cs.insert(Cs2.cs.begin() + place1.first + 1, Ql22); Cs3.cs.at(place1.first) = Ql31; Cs3.cs.insert(Cs3.cs.begin() + place1.first + 1, Ql32); Cs4.cs.at(place1.first) = Ql41; Cs4.cs.insert(Cs4.cs.begin() + place1.first + 1, Ql42); } // Multiplying with TR where appropriate Monomial Mon_tmp; Mon_tmp.pow_TR = 1; - Cs2.Poly = Cs2.Poly * Mon_tmp; - Cs3.Poly = Cs3.Poly * Mon_tmp; + Cs2.Poly *= Mon_tmp; + Cs3.Poly *= Mon_tmp; // Multiplying with -TR where appropriate Mon_tmp.pow_TR = 1; Mon_tmp.int_part = -1; - Cs1.Poly = Cs1.Poly * Mon_tmp; - Cs4.Poly = Cs4.Poly * Mon_tmp; + Cs1.Poly *= Mon_tmp; + Cs4.Poly *= Mon_tmp; Ca.ca.push_back(Cs1); Ca.ca.push_back(Cs2); Ca.ca.push_back(Cs3); Ca.ca.push_back(Cs4); } // Simplify Col_amp Ca.simplify(); return Ca; } Col_amp Col_functions::exchange_gluon( const Col_amp & Ca, int p1, int p2 ) const{ // Final col_amp to return Col_amp Ca_out; // Exchange in each Col_str, and append to new Col_amp for(uint m=0; m< Ca.ca.size(); m++ ){ // Exchange in Col_str m Col_amp part_m=exchange_gluon( Ca.ca.at(m), p1, p2 ); // Append result Ca_out.append(part_m.ca); } return Ca_out; } /* -Col_str Col_functions::contract_quarks( Col_str Cs1, Col_str Cs2 ) const{ - - std::vector q_place; - std::vector q_place2; - - // The conjugate of Cs1 - Col_str conj_Cs1 = Cs1; - conj_Cs1.conjugate(); - - // The total color structure - Col_str Cs = conj_Cs1*Cs2; - - // Count how many quarks should be contracted - int n_q = Cs.n_quark(); - - // As long as there are quark_lines left to contract - while (n_q > 0) { - // Find first quark in Cs1 by looping over Quark_lines - for (int i = 0; (n_q>0 && i < static_cast(Cs.cs.size()) ); i++) { - // Check if the quark-line is open, in which case it has a q - if (Cs.cs.at(i).open) { - // The first quark is located and has position - q_place.clear(); - q_place.push_back(i); - q_place.push_back(0); - // and number - int q = Cs.at(q_place.at(0), q_place.at(1)); - - // Locate same quark a second time - // Loop over Quark_lines - q_place2.clear(); - int i2 = i + 1; // Quark_line of second occurrence - while (q_place2.empty()) { // As long as quark not found a second time - if (Cs.cs.at(i2).at(Cs.cs.at(i2).ql.size() - 1) == q) {// If quark found, store place - q_place2.push_back(i2); - q_place2.push_back(Cs.cs.at(i2).ql.size() - 1); - } - i2++; - } - if (q_place2.empty()) { - std::cerr << "Col_functions::contract_quarks(Cs1, Cs2): Found q " << q - << " only once in " << Cs << std::endl; - } - - // Prepare new Quark_line - // to be inserted at the place of found open Quark_line - Quark_line new_Quark_line; - Quark_line part2_new_Quark_line; - // The first part of the new Quark_line should be the Quark_line - // containing q in the conjugate - new_Quark_line = Cs.cs.at(q_place2.at(0)); - - // Erasing q in the end - new_Quark_line.ql.erase(--new_Quark_line.ql.end()); - part2_new_Quark_line = Cs.cs.at(q_place.at(0)); - - // Erasing the q in the beginning of second part - part2_new_Quark_line.ql.erase(part2_new_Quark_line.ql.begin()); - - new_Quark_line.append(part2_new_Quark_line.ql); - - // So far we have not included the Polynomial of part2_new_Quark_line - new_Quark_line.Poly=new_Quark_line.Poly*part2_new_Quark_line.Poly; - - // If the first q index and the last qbar index in the new - // Quark_line is the same (and the Quark_line is "open"), the indices - // should be removed and the Quark_line should be closed - if (new_Quark_line.ql.at(0) == new_Quark_line.ql.at( - new_Quark_line.ql.size() - 1) && new_Quark_line.open) { - // The string is closed - new_Quark_line.open = false; - // Remove last and first index - new_Quark_line.ql.erase(--new_Quark_line.ql.end(), - new_Quark_line.ql.end()); - new_Quark_line.ql.erase(new_Quark_line.ql.begin()); - } - - // Inserting new Quark_line in the place of the old - Cs.cs.at(i) = new_Quark_line; - - // Remove quark_line with q in Cs - Cs.cs.erase((Cs.cs.begin() + q_place2.at(0))); - i=-1; // reset to keep looking from the beginning in the new Cs (i will be increased to 0) - }// end of if (open) - - n_q = Cs.n_quark(); - - } // end of for, loop over quark_lines - - } - - return Cs; - -} - - -Col_amp Col_functions::contract_quarks( Col_amp Ca1, Col_amp Ca2 ) const{ - - if(Ca1.empty()){ - std::cerr << "Col_functions::contract_quarks: Expects non-empty Col_amps, got first argument " - << Ca1 << std::endl; - assert(0); - } - if(Ca2.empty()){ - std::cerr << "Col_functions::contract_quarks: Expects non-empty Col_amps, got second argument " - << Ca2 << std::endl; - assert(0); - } - - Col_amp Ca_res; - - // Make sure the Col_strs are not empty "[]"=1, as all indices contracted - Ca1.remove_empty_Col_strs(); - Ca2.remove_empty_Col_strs(); - - // Loop over Col_strs, and contract quarks between all possible combinations - // Loop over Col_strs in Ca1 - for(uint m1=0; m1 < Ca1.ca.size(); m1++ ){ - // Loop over Col_strs in Ca2 - for(uint m2=0; m2 < Ca2.ca.size(); m2++ ){ - Col_str Cs_tmp; - Cs_tmp.contract_quarks( Ca1.ca.at(m1), Ca2.ca.at(m2)); - Ca_res.ca.push_back( Cs_tmp ); - } - } - - return Ca_res; -} -*/ - std::map< std::string, double > Col_functions::double_num( const std::map< std::string, Polynomial > & mem_map ) const{ std::map< std::string, double > res; for( auto iter = mem_map.begin(); iter != mem_map.end(); ++iter) { // Insert pair of string and the leading versions of the Polynomial double_num( (iter->second) ); res.insert(std::make_pair( iter->first, double_num((iter->second)) )); } return res; } - +*/ Polynomial Col_functions::Polynomial_cnum_num( const Polynomial & Poly ) const{ // Store content in numerical part of the Monomial Monomial Mon; Mon.cnum_part = cnum_num(Poly); Polynomial res; - res = res * Mon; + res *= Mon; return res; } cvec Col_functions::cnum_num( const Poly_vec & Pv ) const{ // To contain the numerical result cvec res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint p = 0; p < Pv.size(); p++) { res.push_back(cnum_num(Pv.at(p))); } return res; } dvec Col_functions::double_num( const Poly_vec & Pv ) const{ // To contain the numerical result dvec res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint p = 0; p < Pv.size(); p++) { res.push_back(double_num( Pv.at(p)) ); } return res; } dmatr Col_functions::double_num( const Poly_matr & Pm ) const{ // To contain the numerical result dmatr res; // Loop over Poly_vecs in the vector, and add the numerical value // to the vector to return for (uint pv = 0; pv < Pm.size(); pv++) { res.push_back(double_num( Pm.at(pv).pv ) ); } return res; } +/* dvec Col_functions::double_num( const std::vector > & Pv ) const{ // To contain the numerical result dvec res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint p = 0; p < Pv.size(); p++) { shared_ptr the_pointer=Pv.at(p); // Want double of the polynomial which the pointer points at res.push_back( double_num(*the_pointer) ); } return res; } +*/ Poly_vec Col_functions::Poly_vec_cnum_num( const Poly_vec & Pv) const{ // To contain the result Poly_vec res; // Loop over Polynomials in the vector, put each Polynomial to its numerical value for (uint p = 0; p < Pv.size(); p++) { - res.push_back( Polynomial_cnum_num( Pv.at( p ) ) ); + res.append( Polynomial_cnum_num( Pv.at( p ) ) ); } return res; } Poly_matr Col_functions::Poly_matr_cnum_num( const Poly_matr & Pm ) const { // To contain the result Poly_matr res_matr; // Loop over Polynomials in the matrix // and change each Polynomial to its numerical version for (uint v = 0; v < Pm.size(); v++) { - res_matr.push_back( Poly_vec_cnum_num( Pm.at( v ).pv )); + res_matr.append( Poly_vec_cnum_num( Pm.at( v ).pv )); } return res_matr; } cmatr Col_functions::cnum_num( const Poly_matr & Pm ) const{ // To contain the numerical result cmatr res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint v = 0; v < Pm.size(); v++) { res.push_back(cnum_num( Pm.at(v).pv )); } return res; } - +/* dmatr Col_functions::double_num( const std::vector > > & Pm ) const{ // To contain the numerical result dmatr res; // Loop over Polynomials in the vector, and add the numerical value // to the vector to return for (uint v = 0; v < Pm.size(); v++) { res.push_back(double_num(Pm.at(v))); } return res; } - +*/ Polynomial Col_functions::scalar_product( const Col_amp & Ca1 , const Col_amp & Ca2 ) const{ //std::cout << "Col_functions::scalar_product, incoming Ca1 " << Ca1 <<" and Ca2 " << Ca2 << std::endl; if( !Ca1.Scalar.empty() and ( cnum_num(Ca1.Scalar).real()!=0 or cnum_num(Ca1.Scalar).imag()!=0 ) ){ std::cerr << "Col_functions::scalar_product(Ca1,Ca2): " << "Expects Col_amps with empty Scalar parts, but the Scalar of the first Col_amp was " << Ca1.Scalar << std::endl; assert(0); } if( !Ca2.Scalar.empty() and ( cnum_num(Ca2.Scalar).real()!=0 or cnum_num(Ca2.Scalar).imag()!=0 ) ){ std::cerr << "Col_functions::scalar_product(Ca1,Ca2): " << "Expects Col_amps with empty Scalar parts, but the Scalar of the second Col_amp was " << Ca2.Scalar << std::endl; assert(0); } + // To contain the result Col_amp Ca_res; // Contract the quarks - //Ca_res = contract_quarks(Ca1, Ca2); Ca_res.contract_quarks( Ca1, Ca2 ); - //std::cout << "Col_functions::scalar_product, contracted quarks " << Ca_res << std::endl; // Look for simple simplifications Ca_res.simplify(); - //Ca_res.simplify(); // why twice - - //std::cout << "Col_functions::scalar_product, simplified " << Ca_res << std::endl; // Contract the gluons Ca_res.contract_all_gluons(); if (!Ca_res.empty()) { std::cerr << "Col_functions::scalar_product: terminating due to non-contracted indices." << std::endl; std::cerr << "The Col_amp is " << Ca_res << std::endl; std::cerr.flush(); assert( 0 ); return Ca_res.Scalar; } else return Ca_res.Scalar; } Polynomial Col_functions::scalar_product( const Col_str & Cs1, const Col_str & Cs2 ) const{ Col_str Cs_tmp; // Contract the quarks Cs_tmp.contract_quarks( Cs1, Cs2 ); Col_amp Ca_tmp(Cs_tmp); // Contract the gluons Ca_tmp.contract_all_gluons(); if ( !Ca_tmp.empty() ){ std::cerr << "Col_functions::scalar_product: terminating due to non-contracted quark indices." <=0, argument was " << i << std::endl; std::cerr.flush(); assert( 0 ); } if (i==0) return 1; return factorial(i-1)*i; // Recursive call } +std::map Col_functions::default_parton_numbers( const Col_str & Cs, int g_old, int q_new, int qbar_new ) const{ + + + // First check that all parton numbers except g_old, but including q_new, qbar_new + // can be found in the Col_str + Cs.find_parton( q_new ); + Cs.find_parton( qbar_new ); + int n_partons=2*Cs.n_quark() + Cs.n_gluon(); + // The two highest numbers are missing (instead q_new, qbar_new) + for ( int p = 1; p < n_partons-2; p++ ) { + if(p != g_old) Cs.find_parton(p); + } + + + // To contain the replacements to be done + std::map replacements; + + // Find replacing numbers for q_new and qbar_new and insert in map + int new_q_new = 2*Cs.n_quark() - 1; + int new_qbar_new = 2*Cs.n_quark(); + replacements.insert( std::make_pair( q_new, new_q_new) ); + replacements.insert( std::make_pair(qbar_new, new_qbar_new) ); + + + // Loop over old qs and qbars and make sure numbers don't change + for ( int p = 1; p <= Cs.n_quark()*2-2; p++ ) { + replacements.insert( std::make_pair( p, p ) ); + } + + + // Gluons with numbers smaller than g_old should have indices + // shifted twice (once for q, once for qbar) + for ( int p = Cs.n_quark()*2-1; p < g_old; p++ ) { + replacements.insert( std::make_pair( p, p+2) ); + } + + + // Gluons with numbers larger than g_old should have indices + // shifted once (for qbar) + for ( int p = g_old+1; p <= n_partons-1; p++ ) { + replacements.insert( std::make_pair( p, p+1) ); + } + + return replacements; +} + + +Col_str Col_functions::rename_partons( const Col_str & in_Col_str, const std::map replacements ) const{ + + + Col_str out_Col_str = in_Col_str; + + // Loop over Quark_lines + for( uint i=0; i < in_Col_str.size(); i++ ){ + // Loop over partons in Quark_line + for( uint j=0; j < in_Col_str.at(i).size(); j++ ){ + // Find parton number in map and replace + out_Col_str.at(i).ql.at(j) = replacements.at(in_Col_str.at(i,j));; + } + } + + return out_Col_str; +} + + +Col_amp Col_functions::rename_partons( const Col_amp & in_Col_amp, const std::map replacements ) const{ + + + Col_amp out_Col_amp; + + // Loop over Col_strs + for( uint i=0; i < in_Col_amp.size(); i++ ){ + out_Col_amp.append( rename_partons( in_Col_amp.at(i), replacements) ); + } + + out_Col_amp.normal_order(); + + + return out_Col_amp; +} + + void Col_functions::write_out_dvec( const dvec & dv, std::string filename ) const { std::ofstream outfile(filename.c_str() ); outfile << dv; } dmatr Col_functions::read_in_dmatr( std::string filename ) const { // Read in file std::ifstream fin(filename.c_str()); // Check that file exists if( !fin ){ std::cerr << "Col_functions::read_in_dmatr: The file " << filename << " could not be opened." << std::endl; assert( 0 ); } // Copy info from file to string std::string str((std::istreambuf_iterator(fin)), std::istreambuf_iterator()); // Skip lines starting with # while(str.at(0)== '#'){ while (str.at(0) != '\n'){ str.erase(str.begin()); } // erase endl sign(s) while(str.at(0)== '\n'){ str.erase(str.begin()); } } // First char in file should be '{' if (str.at(0) != '{') { std::cerr << "Col_functions::read_in_dmatr: First char in matrix data file after comments should be '{', it was: " << str.at(0) << std::endl; assert( 0 ); } // Check that only allowed characters uint j = 0; while (j < str.size()) { if (!(str.at(j) == '+' or str.at(j) == '-' or str.at(j) == '.' or str.at(j) == '{' or str.at(j) == '}' or str.at(j) == '\n' or str.at(j) == ',' or str.at(j) == ' ' or str.at(j) == '0' or str.at(j) == '1' or str.at(j) == '2' or str.at(j) == '3' or str.at(j) == '4' or str.at(j) == '5' or str.at(j) == '6' or str.at(j) == '7' or str.at(j) == '8' or str.at(j) == '9')) { std::cerr << "Col_functions::read_in_dmatr: A disallowed characters encountered in string for dmatr: " << str.at(j) << ", in file " << filename << std::endl; std::cerr << "Col_functions::read_in_dmatr expects a numerical matrix." << std::endl; assert( 0 ); } j++; } // Row to contain numbers dvec row; // To contain matrix of scalar products dmatr matr; // Read the string, starting from 0th element uint i = 0; while (i < str.size() - 2) { i += 1; // We may have to skip some chars while (i< str.size()-2 &&(str.at(i) == ',' or str.at(i) == '}' or str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == ' ' or str.at(i) == '{') ) i++; // String to make a number of, and double to contain number std::string num_str; num_str.clear(); double num; // Keep reading the number while not ',' or '}' while ( i< str.size()-2 && (str.at(i) != ',' && str.at(i) != '}') ) { num_str.push_back(str.at(i)); i++; } // num_str contains the string to make a number of std::istringstream parton_str_st( num_str ); parton_str_st >> num; // Add number to vector row.push_back(num); // If we have a new row if( i< str.size()-2 && str.at(i)=='}'){ // Save row in matrix, and empty row matr.push_back(row); row.clear(); // We may have to skip some chars while (i< str.size()-2 &&(str.at(i) == ',' or str.at(i) == '}' or str.at(i) == ' ' or str.at(i) == '\n' ) ) { i++; } } // Otherwise just keep on reading the next number in row } return matr; } dvec Col_functions::read_in_dvec( std::string filename ) const { // Read in file std::ifstream fin(filename.c_str()); // Check that file exists if( !fin ){ std::cerr << "Col_functions::read_in_dvec: The file " << filename << " could not be opened." << std::endl; assert( 0 ); } // Copy info from file to string std::string str((std::istreambuf_iterator(fin)), std::istreambuf_iterator()); // Skip lines starting with # while(str.at(0)== '#'){ while (str.at(0) != '\n'){ str.erase(str.begin()); } // erase endl sign(s) while(str.at(0)== '\n'){ str.erase(str.begin()); } } // First char in file should be '{' if (str.at(0) != '{') { std::cerr << "Col_functions::read_in_dvec: First char in matrix data file after comments should be '{', it was: " << str.at(0) << std::endl; assert( 0 ); } // Check that only allowed characters uint j = 0; while (j < str.size()) { if (!(str.at(j) == '+' or str.at(j) == '-' or str.at(j) == '.' or str.at(j) == '{' or str.at(j) == '}' or str.at(j) == '\n' or str.at(j) == ',' or str.at(j) == ' ' or str.at(j) == '0' or str.at(j) == '1' or str.at(j) == '2' or str.at(j) == '3' or str.at(j) == '4' or str.at(j) == '5' or str.at(j) == '6' or str.at(j) == '7' or str.at(j) == '8' or str.at(j) == '9')) { std::cerr << "Col_functions::read_in_dvec: A disallowed character encountered in string for dmatr: " << str.at(j) << ", in file " << filename << std::endl; std::cerr << "Col_functions::read_in_dvec expects a numerical matrix." << std::endl; assert( 0 ); } j++; } // Row to contain numbers dvec row; // To contain matrix of scalar products dmatr matr; // Read the string, starting from 0th element unsigned int i = 0; while (i < str.size() - 1) { i += 1; // We may have to skip some chars while (i< str.size()-2 &&(str.at(i) == ',' or str.at(i) == '}' or str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == ' ' or str.at(i) == '{') ) i++; // String to make a number of, and double to contain number std::string num_str; num_str.clear(); double num; // Keep reading the number while not ',' or '}' while ( i< str.size()-1 && (str.at(i) != ',' && str.at(i) != '}') ) { num_str.push_back(str.at(i)); i++; } // now, at(i), there is either , or } // num_str contains the string to make a number of std::istringstream num_str_st( num_str ); num_str_st >> num; // Add number to vector row.push_back(num); // Skip signs in end and make sure not to enter loop one extra time while( i::iterator operator+( std::list::iterator x, int n ) { while(n>0){ x++; n--; } while(n<0){ x--; n++; } return x; } std::list::iterator operator-( std::list::iterator x, int n ) { while(n>0){ x--; n--; } while(n<0){ x++; n++; } return x; } std::list< Quark_line >::iterator operator+( std::list < Quark_line >::iterator x, int n ){ while(n>0){ x++; n--; } while(n<0){ x--; n++; } return x; } col_str::iterator operator-( col_str::iterator x, int n ){ while(n>0){ x--; n--; } while(n<0){ x++; n++; } return x; } std::ostream& operator<<( std::ostream& out, const std::vector & vec ){ int max=vec.size(); if(max==0) out <<"{}"; else{ out <<"{"; for (int i=0; i accuracy ){ outstr << std::fixed << matr.at(i).at(j); } // otherwise is should probably be 0 else outstr << std::fixed << 0; // If not last element print "," if (j pair ) { out << "("; out << pair.first; out << ", "; out << pair.second; out << ")"; return out; } } //end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Col_functions.h b/MatrixElement/Matchbox/ColorFull/Col_functions.h --- a/MatrixElement/Matchbox/ColorFull/Col_functions.h +++ b/MatrixElement/Matchbox/ColorFull/Col_functions.h @@ -1,317 +1,357 @@ // -*- C++ -*- /* * Col_functions.h * Contains declarations of the class Col_str and associated types and operators * Author: Malin Sjodahl */ #ifndef COLORFULL_Col_functions_h #define COLORFULL_Col_functions_h #include "Col_amp.h" #include "Poly_matr.h" #include #include #include namespace ColorFull { using std::shared_ptr; /// Library class containing functions for index contraction and /// numerical evaluation. /// This is where the parameters Nc, TR and CF are contained. class Col_functions { private: -/// The number of colors, used in numerical results. Change here for different value. +/// The number of colors, used in numerical results, +/// changed by using set_Nc. double Nc; /// The trace convention Tr( t^a t^a )=TR (no sum). -/// The normalization of the SU(Nc) generators, to be used in numerical evaluation. +/// The normalization of the SU(Nc) generators, to be used in numerical evaluation, +/// changed by using set_TR. /// The value 1/2 corresponds to the Gell-Mann normalization. double TR; -/// The value of CF=TR (Nc^2-1)/(Nc). +/// The value of CF=TR*Nc-TR/Nc, changed by using set_CF. /// Note that CF can be changed independently of Nc. double CF; /// While evaluating leading terms one may want to keep the full value of CF for -/// Nc=3, (TR(Nc^2-1)/Nc), or only keep the leading Nc term =TR*Nc (default). +/// TR(Nc^2-1)/Nc, or only keep the leading Nc term =TR*Nc (default). /// full_CF is used by the Polynomial version of leading /// (and hence also Poly_vec and Poly_matr versions etc). /// The leading functions replaces CF by TR*Nc if full_CF is false (default) /// while evaluating the leading terms. /// If full_CF is true, CF is replaced by TR(Nc^2-1)/Nc. /// Clearly this affects the result of subsequent numerical evaluation. /// In the Col_basis class (and derived) the matrix version of leading /// is used to evaluate scalar product matrices. bool full_CF; public: /// Default constructor. Col_functions() : Nc(3.0), TR(0.5), CF(4.0/3.0), full_CF( false ) {} /// Set the number of colors. /// The value of CF is adjusted accordingly. void set_Nc( double n) { Nc = n; CF = TR*(Nc*Nc-1.)/Nc; } /// Set the normalization of the generators. /// The value of CF is adjusted accordingly. void set_TR( double tr) { CF *= tr/TR; TR = tr; } /// Set the value of CF. /// The value of Nc is NOT adjusted accordingly. void set_CF( double cf) { CF = cf; } /// Switch on/off full_CF. void set_full_CF( bool is_full ) { full_CF = is_full; } -/// Get the number of colors. +/// Returns the number of colors. double get_Nc() const { return Nc; } -// Returns the normalization of the generators, +/// Returns the normalization of the generators, /// tr(t^a t^b)=TR*delta^{a,b}. double get_TR() const { return TR; } -/// Get the number of colors. +/// Returns the value of CF. double get_CF() const { return CF; } -/// Return true, if full CF is used +/// Returns true, if full CF is used. bool get_full_CF() const { return full_CF; } /****************** Functions for leading terms *************************/ // The functions called leading(...) depend on the variable full_CF. // As it would be messy to let each Polynomial carry around // its own full_CF, these functions are kept here. /// Function for finding the leading power of Nc in a Poly_vec, /// i.e., the power of Nc plus the power of CF. int leading_Nc_pow( const Polynomial & Poly ) const; /// Function for finding the leading power of Nc in a Poly_vec. int leading_Nc_pow( const Poly_vec & Pv ) const; +/* /// Function for finding the leading power of Nc in a -/// vector of pointer to Polynomials. +/// vector of pointers to Polynomials. int leading_Nc_pow( const std::vector< shared_ptr > & Pvp) const; +*/ /// Takes the leading Nc terms of a Polynonmial, i.e. Monomials with highest /// power of Nc+CF. If full_CF is false (default), CF is replaced by TR Nc. /// If full_CF is true CF is replaced by TR(Nc^2-1)/Nc. Polynomial leading( const Polynomial & Poly ) const; /// Take the leading part of a Poly_vec. -/// Keeps only Monomials with maximal power of CF + Nc. -/// Uses leading( const Polynomial & Poly). +/// Keeps only Monomials with maximal power of CF plus Nc, +/// uses leading( const Polynomial & Poly). /// If full_CF is false (default), CF is replaced by TR Nc. /// If full_CF is true CF is replaced by TR(Nc^2-1)/Nc. /// Note that taking the leading terms of a Poly_vec is not /// the same as taking the leading terms in each Polynomial. // Used only by Poly_matr version of leading Poly_vec leading( const Poly_vec & Pv ) const; /// Takes the leading part of a matrix of Polynomials, -/// keeping only those with maximal power of CF + Nc. +/// keeping only those with maximal power of CF plus Nc. /// If full_CF is false (default), CF is replaced by TR Nc. /// If full_CF is true CF is replaced by TR(Nc^2-1)/Nc. /// Note that taking the leading terms of a Poly_matr is not /// the same as taking the leading terms in each Poly_vec. // Used only once in Col_basis Poly_matr leading( const Poly_matr & Pm ) const; +/* /// Take the leading part of a Poly_vec, given a vector of pointers to the Polynomials. -/// Keeps only Monomials with maximal power of CF + Nc. +/// Keeps only Monomials with maximal power of CF plus Nc. // Currently never used Poly_vec leading( const std::vector > & Pvp) const; /// Take the leading part of a Poly_matr, given a vector of vector of pointers to the Polynomials. /// Loops over Monomials in all Polynomials -/// and keeps only those with maximal power of CF + Nc. +/// and keeps only those with maximal power of CF plus Nc. // used only by scalar_product_matrix_mem in Col_functions dmatr leading( const std::vector< std::vector< shared_ptr > > & Pm ) const; - -/// To keep only leading terms in a map. -// used only on Col_functions by scalar product_matrix_mem_2 and radiation_amplitude_matrix -std::map< std::string, Polynomial > leading( const std::map< std::string, Polynomial > & mem_map ) const; - +*/ /********************* Functions for numerical evaluation *************************/ // These functions has to be kept in Col_functions class as they need numerical // values for evaluation. Letting each Polynomial carry around its own Nc etc. // would be messy. -/// To take the numerical value of a map. -std::map< std::string, double > double_num( const std::map< std::string, shared_ptr > & mem_map ) const; -/// Numerically evaluates a Monomial using the Nc and CF variables; +/// Numerically evaluates a Monomial using the Nc, TR and CF data members. cnum cnum_num( const Monomial & Mon ) const; -// Numerically evaluates a Monomial to a double. -double double_num( const Monomial & Mon ) const; - -/// Numerically evaluates a Polynomial, using the CF and Nc variables. +/// Numerically evaluates a Polynomial, using the Nc, TR and CF data members. cnum cnum_num( const Polynomial & Poly ) const; -/// Numerically evaluates a Polynomial using the data members Nc, CF and TR. -double double_num( const Polynomial & Poly ) const; - -/// To take the numerical value of a map. -std::map< std::string, double > double_num(const std::map< std::string, Polynomial > & mem_map) const; - -/// Numerically evaluates a Poly_vec (vector of Polynomial) for Nc=3. -dvec double_num( const Poly_vec & Pv ) const; - -/// Returns a double value. The argument is a vector of pointers to Polynomials. -dvec double_num( const std::vector > & Pv ) const; - -/// Returns a double value. The argument is a vector of vector of pointers to Polynomials. -dmatr double_num( const std::vector > > & Pm ) const; - -/// Numerically evaluates a Polynomial for Nc=3, -/// and stores in the format of a Polynomial with only one term with only a numerical part. -Polynomial Polynomial_cnum_num( const Polynomial & Poly ) const; - /// Numerically evaluates a Poly_vec (vector of Polynomial), /// using cnum_num (Polynomial). cvec cnum_num( const Poly_vec & Pv ) const; +/// Numerically evaluates a Poly_matr (vector of Poly_vec), +/// using cnum_num( Poly_vec ) for each Poly_vec. +cmatr cnum_num( const Poly_matr & Pm ) const; + +/// Numerically evaluates a Monomial to a double using the Nc, TR and CF data members. +double double_num( const Monomial & Mon ) const; + +/// Numerically evaluates a Polynomial to a double using the Nc, TR and CF data members. +double double_num( const Polynomial & Poly ) const; + +/// Numerically evaluates a Poly_vec (vector of Polynomial) +/// using the Nc, TR and CF data members. +dvec double_num( const Poly_vec & Pv ) const; + +/// Numerically evaluates a Poly_matr (vector of Poly_vec), +/// using the Nc, TR and CF data members. +dmatr double_num( const Poly_matr & Pm ) const; + +/* +/// Returns a double vector. The argument is a vector of pointers to Polynomials. +dvec double_num( const std::vector > & Pv ) const; + +/// Returns a double matrix. The argument is a vector of vector of pointers to Polynomials. +// (not used 14 06 08) +dmatr double_num( const std::vector > > & Pm ) const; + +/// To take the numerical value of a map. +// (not used 14 06 08) +std::map< std::string, double > double_num( std::map< std::string, std::shared_ptr > mem_map ) const; + +/// To take the numerical value of a map. +// (not used 14 06 08) +std::map< std::string, double > double_num( std::map< std::string, Polynomial > mem_map ) const; +*/ + +/// Numerically evaluates a Polynomial using the value of the data member Nc, +/// and stores in the format of a Polynomial with only one term with only a numerical part. +Polynomial Polynomial_cnum_num( const Polynomial & Poly ) const; + /// Numerically evaluates a Poly_vec (vector of Polynomial) /// and stores in the form of a Poly_vec, uses polynomial_cnum_num( Pv.at( p ) ). /// for each Polynomial. Poly_vec Poly_vec_cnum_num( const Poly_vec & Pv ) const; /// Numerically evaluates a Poly_matr (vector of Poly_vec) /// and stores in the form of a Poly_matr. Poly_matr Poly_matr_cnum_num( const Poly_matr & Pm ) const; -/// Numerically evaluates a Poly_matr (vector of Poly_vec), -/// using cnum_num( Poly_vec ) for each Poly_vec. -cmatr cnum_num( const Poly_matr & Pm ) const; - -/// Numerically evaluates a Poly_matr (vector of Poly_vec). -dmatr double_num( const Poly_matr & Pm ) const; /****************** Functions for scalar products *************************/ -/// Function for calculating scalar products between Col_amps. +/// Function for calculating the scalar products between Col_amps. /// Does not add implicit state in the gluons only case. Polynomial scalar_product( const Col_amp & Ca1, const Col_amp & Ca2 ) const; -/// Function for calculating scalar product between two Col_strs. +/// Function for calculating the scalar product between two Col_strs. /// Does not add implicit state in the gluons only case. Polynomial scalar_product( const Col_str & Cs1, const Col_str & Cs2 ) const; -/****************** Functions for gluon emission and exchange *************/ +/****************** Functions for gluon emission exchange, and splitting *************/ /// Function for emitting a gluon from a Col_str. /// When the gluon is inserted before the emitter in a Quark_line, /// the amplitude comes with a minus sign. Col_amp emit_gluon( const Col_str & in_Col_str, int emitter, int g_new ) const; /// Function for emitting a gluon from a Col_amp. /// When the gluon is inserted before the emitter in a Quark_line, /// the amplitude comes with a minus sign. Col_amp emit_gluon( const Col_amp & Ca_in, int emitter, int g_new ) const; +/// Function for splitting the gluon g_old in a Col_str to a qqbar pair. +Col_amp split_gluon( const Col_str & in_Col_str, int g_old, int q_new, int qbar_new ) const; + +/// Function for splitting the gluon g_old in a Col_amp to a qqbar pair. +Col_amp split_gluon( const Col_amp & in_Col_amp, int g_old, int q_new, int qbar_new ) const; + + /// Function for exchanging a gluon between the partons p1 and p2 in the Col_str Cs. /// When the gluon is inserted before the emitter in a Quark_line, /// the amplitude comes with a minus sign. Col_amp exchange_gluon( const Col_str & Cs, int p1, int p2 ) const; /// Function for exchanging a gluon between two partons p1 and p2 in the Col_amp Ca. /// When the gluon is inserted before the emitter in a Quark_line, /// the amplitude comes with a minus sign. -/// (The incoming amplitude is what it is, there is no special -/// treatment of glons only cases.) +/// (There is no special treatment of the glons only cases.) Col_amp exchange_gluon( const Col_amp & Ca, int p1, int p2 ) const; -/// Calculates < M | T^(i) T^(j) | M >, the "color correlator" +/// Calculates < M | T_i T_j | M >, the "color correlator" /// relevant for coherent gluon emission of gluon g_new from /// parton i and parton j, or gluon exchange between i and j. -/// The Ca should thus be | M >, g_new should be a unique dummy index, +/// The Ca should thus be | M >, /// and i and j are the partons involved in the emission (exchange). /// (The incoming amplitude is what it is, there is no special /// treatment of gluons only cases.) -Polynomial color_correlator( const Col_amp Ca, int i, int j, int g_new ) const; +Polynomial color_correlator( const Col_amp Ca, int i, int j ) const; /********************* Other functions *************************/ // As dvec and dmatr are not classes some read and write functions // are contained here. -/// Read in a numerical matrix from filename and save it as a double matrix, dmatr. -/// The file should be in the format -/// {d11,...,d1n}. +/// Reads in a numerical vector and save it as a double vector, dvec. +/// The file should be of the format +/// {d11,...,d1n}, +/// and may contain comment lines starting with # at the top. dvec read_in_dvec( std::string filename ) const; -/// Read in a numerical matrix from filename and save it as a double matrix, dmatr. -/// The file should be in the format +/// Reads in a numerical matrix and save it as a double matrix, dmatr. +/// The file should be of the format /// {{d11,...,d1n}, /// ..., /// {dn1,...,dnn}}, /// and may contain comment lines starting with # at the top. dmatr read_in_dmatr( std::string filename ) const; /// Function for writing out a numerical vector, /// to the file filename. void write_out_dvec( const dvec & dv, std::string filename ) const; /// Writes out the double version of a (scalar product) matrix /// to the file filename. void write_out_dmatr( const dmatr & matr, std::string filename ) const; /// The factorial of an int, 0! is defined as 1. int factorial( int i ) const; +/// Function that finds the default parton numbers for a Col_str. +/// The default numbers are 1,...,N_parton, where quarks have the first +/// odd numbers, anti-quarks have the first even numbers, and gluons have +/// subsequent numbers. The intended usage is after gluon splitting, where +/// the gluon g_old has split into q_new and qbar_new, and the parton numbers +/// before splitting are assumed to be default. +std::map default_parton_numbers( const Col_str &, int g_old, int q_new, int qbar_new ) const; + + +/// Function that renames the partons in a Col_str using a map where, in each pair, +/// the first number is to be replaced by the second. (The Col_functions member +/// function default_parton_numbers returns a map where the partons are given +/// default numbers.) +Col_str rename_partons( const Col_str &, const std::map replacements ) const; + + +/// Function that renames the partons in a Col_amp using a map where, in each pair, +/// the first number is to be replaced by the second. (The Col_functions member +/// function default_parton_numbers returns a map where the partons are given +/// default numbers.) +Col_amp rename_partons( const Col_amp &, const std::map replacements ) const; + + }; // end class Col_functions /////////////////////// DECLEARING OPERATORS ///////////////////// /// Defining + operator to be able to erase elements at place std::list::iterator operator+( std::list::iterator x, int n ); std::list::iterator operator-( std::list::iterator x, int n ); /// Defining + operator to be able to erase elements at place std::list< Quark_line >::iterator operator+( std::list < Quark_line >::iterator x, int n ); col_str::iterator operator-( col_str::iterator x, int n ); /// Define the operator << for vector of int. std::ostream& operator<<( std::ostream& out, const std::vector & vec ); /// Define the operator << for cvec. std::ostream& operator<<( std::ostream& out, const cvec & cv ); /// Define the operator << for dvec. std::ostream& operator<<( std::ostream& out, const dvec & dv ); /// Define the operator << for cmatr. std::ostream& operator<<( std::ostream& out, const cmatr & cm ); /// Define the operator << for dmatr. std::ostream& operator<<( std::ostream& out, const dmatr & matr ); /// Define the operator << for std::pair. std::ostream& operator<<( std::ostream& out, std::pair pair ); } // end namespace ColorFull #endif /* COLORFULL_Col_functions_h */ diff --git a/MatrixElement/Matchbox/ColorFull/Col_str.cc b/MatrixElement/Matchbox/ColorFull/Col_str.cc --- a/MatrixElement/Matchbox/ColorFull/Col_str.cc +++ b/MatrixElement/Matchbox/ColorFull/Col_str.cc @@ -1,954 +1,976 @@ // -*- C++ -*- /* * Col_str.cc * Contains definition of the class Col_str and associated types and operators. * Created on: Jul 7, 2010 * Author: Malin Sjodahl */ #include "Col_str.h" #include #include #include namespace ColorFull { Col_str::Col_str( const std::string str ) { Col_str_of_str( str ); } void Col_str::Col_str_of_str( const std::string str ) { // First split the string into Col_str and Polynomial part uint j=0; // Check that left and right normal brackets match up j=0; int left_brackets=0,right_brackets=0; while (j < str.size()) { if(str.at(j)=='(') left_brackets++; if(str.at(j)==')') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Col_str::Col_str_of_str: The normal brackets, (), in the Col_str\"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } // Check that left and right curly brackets match up left_brackets=0,right_brackets=0; j=0; while (j < str.size()) { if(str.at(j)=='{') left_brackets++; if(str.at(j)=='}') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Col_str::Col_str_of_str: The curly brackets in the Col_str\"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } // Check that left and right [] brackets match up j=0; left_brackets=0, right_brackets=0; while (j < str.size()) { if(str.at(j)=='[') left_brackets++; if(str.at(j)==']') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Col_str::Col_str_of_str: The square brackets, [], in the string \"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } if( left_brackets != 1){ std::cerr << "Col_str::Col_str_of_str: Found " << left_brackets << " squared, [], left brackets in the string " << str <<" but there should be 1;" << std::endl; assert( 0 ); } // Read in the Polynomial until a [ is found j=0; std::string Poly_string; Poly_string.empty(); while( j(fin)), std::istreambuf_iterator()); + + // Skip lines starting with # + while( str.at(0)== '#' ){ + while (str.at(0) != '\n'){ + str.erase(str.begin()); + } + // erase endl sign(s) + while(str.at(0)== '\n'){ + str.erase(str.begin()); + } + } + + // Remove endl chars at the end of the file + while( str.at(str.size()-1) == '\n' ) str.erase(str.size()-1); Col_str_of_str( str ); } void Col_str::write_out_Col_str( std::string filename ) const { if ((cs.size() == 0)) { std::cout << "Col_str::write_out_Col_str: The Col_str is empty." << std::endl; std::cout.flush(); return; } std::ofstream outfile(filename.c_str()); if ( !outfile ) std::cerr << "Col_str::write_out_Col_str: Cannot write out Col_str as the file \"" << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; outfile << *this; } int Col_str::at( int i, int j ) const { if (i < 0) { std::cout << "Col_str::at: First argument <0\n"; std::cerr.flush(); assert( 0 ); } else if (i >= static_cast(cs.size()) ) { std::cerr << "Col_str::at: First argument > size -1\n"; std::cerr.flush(); assert( 0 ); } if (j < 0) { std::cerr << "Col_str::at: Second argument <0 \n"; std::cerr.flush(); assert( 0 ); } else if (j >= static_cast(cs.at(i).ql.size())) { std::cerr << "Col_str::at: Second argument > size -1\n"; std::cerr.flush(); assert( 0 ); } return cs.at(i).ql.at(j); } void Col_str::erase( int i ) { cs.erase(cs.begin() + i); } void Col_str::erase( int i, int j ) { cs.at(i).ql.erase(cs.at(i).ql.begin() + j); } void Col_str::erase( std::pair place ) { erase(place.first, place.second); } void Col_str::insert( int i, int j, int part_num ) { // Checking that location is within range if (i < 0) { std::cerr << "Col_str::insert: First argument <0\n"; std::cerr.flush(); assert( 0 ); } else if (i >= static_cast (cs.size()) ) { std::cerr << "Col_str::insert: First argument > size -1\n"; std::cerr.flush(); assert( 0 ); } if (j < 0) { std::cerr << "Col_str::insert: Second argument <0\n"; std::cerr.flush(); assert( 0 ); } else if (j > static_cast( cs.at(i).ql.size()) ) { std::cerr << "Col_str::insert: Second argument > size, was " << j << std::endl; std::cerr.flush(); assert( 0 ); } // Inserting element at place given by iterator itj quark_line::iterator itj = cs.at(i).ql.begin() + j; cs.at(i).ql.insert(itj, part_num); } void Col_str::append( col_str cs_in ) { for (uint j = 0; j < cs_in.size(); j++) { cs.push_back(cs_in.at(j)); } } std::pair Col_str::find_parton( int part_num ) const { // Loop over all Quark_lines for (uint i = 0; i < cs.size(); i++) { // Loop over all places in the Quark_lines for (uint j = 0; j < cs.at(i).ql.size(); j++) { if (cs.at(i).ql.at(j) == part_num) { // cout << j<< "\n"; return std::make_pair(i, j); } } } // Assert parton found std::cerr << "Col_str::find_parton: The function find_parton did not find the parton " << part_num << "in \n" << cs; std::cerr.flush(); assert( 0 ); return std::make_pair(-1, -1); } bool Col_str::neighbor(int p1,int p2) const{ // The places std::pair place1 = find_parton(p1); std::pair place2 = find_parton(p2); // First make sure the partons are in the same Quark_line if( place1.first!=place2.first ) return false; if( place2.second==place1.second + 1 or place2.second==place1.second - 1) return true; return false; } bool Col_str::right_neighbor(int p1,int p2) const{ return left_neighbor(p2,p1); } bool Col_str::left_neighbor(int p1,int p2) const{ // The places std::pair place1 = find_parton(p1); std::pair place2 = find_parton(p2); // First make sure the partons are in the same Quark_line if( place1.first!=place2.first ) return false; bool closed = !at(place1.first).open; int length = at(place1.first).size(); if ( !(place1.second-place2.second == 1 || (place1.second-place2.second == 1 - length && closed)) ) return false; return true; } void Col_str::replace(int old_ind, int new_ind) { // First, locate the index to replace std::pair place = find_parton(old_ind); // Then erase the index at that place erase(place.first, place.second); // Then insert the new index insert(place.first, place.second, new_ind); } -std::string Col_str::find_kind( int part_num ) const { +std::string Col_str::find_kind( int p ) const { // Locate the parton in the Col_str - std::pair place = find_parton(part_num); + std::pair place = find_parton(p); // Check if the quark-line is closed // If the parton is in a closed quark line it's a g if (!at(place.first).open) { return "g"; } // If the parton is first in an open quark-line it's a q // (the first relevant place is place 1) else if (place.second == 0) { return "q"; } // If the parton is last in an open quark-line it's a qbar // To find location, subtract the number of characters it takes to write part_num else if (place.second == static_cast (cs.at(place.first).ql.size()) - 1) { return "qbar"; } // add warning if not found? // If the parton wasn't found, it must be a gluon else { return "g"; } } bool Col_str::gluons_only() const { // Loop over Quark_lines for (uint i = 0; i < cs.size(); i++) { if (cs.at(i).open) return false; } // If no Ql was open, the Cs has gluons only return true; } int Col_str::n_gluon() const { int ng = 0; // Loop over Quark_lines for (uint i = 0; i < cs.size(); i++) { // If the ql is closed, all partons are gluons // if it is open, all -2 are gluons ng = ng+at(i).ql.size(); if (at(i).open) ng = ng - 2; } return ng; } int Col_str::n_quark() const { int nq = 0; // Loop over Quark_lines for (uint i = 0; i < cs.size(); i++) { // If the ql is closed, there is no gluon // If it is open, there is one quark (and one anti-quark) if (at(i).open) nq++; } return nq; } void Col_str::normal_order() { // All individual quark_lines should be normal_ordered for (uint i = 0; i < cs.size(); i++) { cs.at(i).normal_order(); } // Loop over the ql's which are candidates to move for (uint i = 1; i < cs.size(); i++) { // How many steps to the left should the ql be moved? int steps_left = 0; while (steps_left <= static_cast(i) - 1 && compare_quark_lines(i, i - steps_left - 1) == static_cast(i)) steps_left++; if (steps_left != 0) { // Insert ql in new place cs.insert(cs.begin() + i - steps_left, cs.at(i)); // Eras in old cs.erase(cs.begin() + i + 1); } } } int Col_str::smallest( const Col_str & Cs1, const Col_str & Cs2 ) const{ // First order Col_strs according to how many Quark_lines they have // The Col_str with fewest Quark_lines is "smallest" if ( Cs1.size() < Cs2.size() ) return 1; else if( Cs2.size() < Cs1.size() ) return 2; // First judge depending on if the Qls are open or not // open ql's are "smaller" for( uint i=0; i< std::min( Cs1.cs.size(), Cs2.cs.size() ); i++ ){ // The "smallest" Ql at place i if (Cs1.cs.at(i).open && !Cs2.cs.at(i).open) return 1; else if (Cs2.cs.at(i).open && !Cs1.cs.at(i).open) return 2; } // Then, judge depending on size of the Ql's // longer qls are "smaller", should stand first for( uint i=0; i< std::min( Cs1.cs.size(), Cs2.cs.size() ); i++ ){ // The "longest" Ql at place i if ( Cs1.cs.at(i).ql.size() > Cs2.cs.at(i).ql.size() ) return 1; else if (Cs2.cs.at(i).ql.size() > Cs1.cs.at(i).ql.size() ) return 2; } // Then, loop over the Quark_lines to see which ql is "smaller", taking index ordering into account // First different index decides, ql with smallest index is smaller for( uint i=0; i< std::min( Cs1.cs.size(), Cs2.cs.size() ); i++ ){ // The "smallest" Ql at place i int OneOrTwo=Cs1.cs.at(i).smallest( Cs1.cs.at(i), Cs2.cs.at(i) ); if ( OneOrTwo==1 ) return 1; else if( OneOrTwo==2 ) return 2; } //If the col_str's are identical, return 0 return 0; } int Col_str::longest_quark_line() const{ int length=0; for ( uint j = 0; j < cs.size(); j++ ) { if( static_cast ( at(j).size() )> length ) length=static_cast ( at(j).size() ); } return length; } void Col_str::remove_1_rings() { // Loop over quarl_lines for (uint j = 0; j < cs.size(); j++) { // If a quark_line contains only one gluon, replace whole Col_str with a 0-monomial if (cs.at(j).ql.size() == 1) { // If the quark_line is closed the Col_str is 0 if (!cs.at(j).open) { // Remove irrelevant Polynomial and col_str // The col_str is now multiplying 0 cs.clear(); Poly.clear(); Monomial Mon0; Mon0.int_part = 0; - Poly.push_back(Mon0); + Poly.append(Mon0); } // If the Ql is open and has only one element, something is wrong else if (cs.at(j).open) { std::cerr << "Col_str::remove_1_rings: An open quark_line cannot have only one parton, but it had in \n" << cs << std::endl; std::cerr.flush(); assert( 0 ); } } } } void Col_str::remove_0_rings() { // Loop over Quark_lines for (int j = 0; j < static_cast(cs.size()); j++) { // If a quark_line contains no gluons and is closed // it is equal to Nc, move factor Nc to the Polynomial of the Col_str if (cs.at(j).ql.size() == 0) { // Move color factor of the Quark_line to the Polynomial of the Col_str Poly = Poly * cs.at(j).Poly; // Multiply with Nc if the quark_line is closed if (!cs.at(j).open) { Monomial Mon_tmp; Mon_tmp.pow_Nc = 1; - Poly = Poly * Mon_tmp; + Poly *= Mon_tmp; } // If the ql is open and has 0 elements, // it is defined as 1 and can be removed // Erase the Ql erase(j); // In order to check all elements, decrease j when Ql removed // j may get to -1, so can not be seen as uint j--; } } } void Col_str::simplify() { remove_1_rings(); remove_0_rings(); // Move factors multiplying the individual Ql's to multiply // the Col_str instead for (uint i = 0; i < cs.size(); i++) { Poly = Poly * cs.at(i).Poly; cs.at(i).Poly.clear(); } // Simplify Polynomial of Col_str Poly.simplify(); // Normal order normal_order(); } void Col_str::conjugate(){ // Conjugating Polynomial Poly.conjugate(); // Take conjugate of cs by conjugating each Quark_line for (uint i=0; i < cs.size(); i++ ){ cs.at(i).conjugate(); } } int Col_str::compare_quark_lines( int i1, int i2 ) const { int OneOrTwo= cs.at(i1).smallest( cs.at(i1), cs.at(i2) ); if ( OneOrTwo==1 ) return i1; else if ( OneOrTwo==2 ) return i2; // If ql's are equal, return i1 else if ( OneOrTwo==0 ) return i1; else{ std::cerr << "Col_str::compare_quark_lines: cannot decide on ordering of quark_lines " << cs.at(i1) << " and " << cs.at(i2); return 0; } } void Col_str::contract_2_rings( ) { // Contract gluons from 2-ring, this gives only one term // For storing place of two-ring std::vector place1; // For storing place of gluon with same index as second gluon in two-ring // i.e. the gluon to be replaced with the first index in the 2-ring std::vector place2; // The second index in two-ring, to be removed int the_g; // Loop over Quark_lines to find a two-ring - for (uint i = 0; i < cs.size(); i++) { + for ( uint i = 0; i < cs.size(); i++ ) { place1.clear(); place2.clear(); // Search for 2-rings // A gluon 2-ring has length 2 and is closed if ( cs.at(i).ql.size() == 2 && !cs.at(i).open ) { // Save place of two-ring place1.push_back(i); // Pick second gluon (for no good reason) // this index should be replaced by first index // in the other place where it occurs, and the 2-ring should be removed place1.push_back(1); the_g = at(place1.at(0), place1.at(1)); // Now, in all Quark_lines, look for same the_g until found for ( uint i2 = 0; i2 < size(); i2++ ) { for (uint j2 = 0; j2 < at(i2).ql.size(); j2++) { // If the_g was found at a DIFFERENT place, (not same g again) if ((i2 != i or j2 != 1) && at(i2, j2) == the_g) { place2.push_back(i2); place2.push_back(j2); } } } // Check that the gluon index was found again if (place2.empty()) { std::cerr << "Col_str:contract_2_rings: Only found the index " << the_g << " once in " << *this << std::endl; assert( 0 ); } // If the_g was found twice in the same ql - if (place1.at(0) == place2.at(0)) { + if ( place1.at(0) == place2.at(0) ) { // Keep the Monomial // Multiply with Nc Mon from the contraction Monomial Mon_tmp; Mon_tmp.pow_Nc = 1; Mon_tmp.pow_CF = 1; // Multiply the Poly of the Col_str with the Poly of the ql // and the color factor from the contraction - Poly = Poly * cs.at(place1.at(0)).Poly * Mon_tmp; + Poly = Poly* cs.at(place1.at(0)).Poly * Mon_tmp; // Erase the ql erase(place1.at(0)); } - else{ + else if ( !place2.empty() ){ // That index should be changed to the index of the first gluon // in the 2-ring cs.at(place2.at(0)).ql.at(place2.at(1)) = at(place1.at(0), 0); // Multiply the Poly of the Col_str with the Poly of the ql // and the color factor from the contraction Monomial Mon_tmp; Mon_tmp.pow_TR = 1; - Poly = Poly * Mon_tmp*cs.at(place1.at(0)).Poly; + Poly = Poly * Mon_tmp*cs.at(place1.at(0)).Poly; // The two ring should be removed cs.erase( cs.begin() + i); } + //else {i++;}; // Compensate for i-- i--; // if we found a 2-ring, we also erased it } // end if we found a 2-ring }// end looping over Quark_lines // One-rings may have been created remove_1_rings(); return; } void Col_str::contract_quarks( const Col_str Cs1, const Col_str Cs2 ) { if( !cs.empty() or Poly.size()!=0 ){ std::cerr << "Col_str::contract_quarks(Cs1,Cs2): This member function " << "stores the result from contracting quarks in the Col_str itself. " << "It therefore expects an empty initially Col_str, but it was:" << *this << std::endl; } std::vector q_place; std::vector q_place2; // The conjugate of Cs1 Col_str conj_Cs1 = Cs1; conj_Cs1.conjugate(); // The total color structure *this = conj_Cs1*Cs2; // Count how many quarks should be contracted int n_q = n_quark(); // As long as there are quark_lines left to contract while (n_q > 0) { // Find first quark in Cs1 by looping over Quark_lines for (int i = 0; (n_q>0 && i < static_cast( size()) ); i++) { // Check if the quark-line is open, in which case it has a q if ( cs.at(i).open ) { // The first quark is located and has position q_place.clear(); q_place.push_back(i); q_place.push_back(0); // and number int q = at(q_place.at(0), q_place.at(1)); // Locate same quark a second time // Loop over Quark_lines q_place2.clear(); int i2 = i + 1; // Quark_line of second occurrence while (q_place2.empty()) { // As long as quark not found a second time if ( cs.at(i2).at( cs.at(i2).ql.size() - 1) == q) {// If quark found, store place q_place2.push_back(i2); q_place2.push_back( cs.at(i2).ql.size() - 1); } i2++; } if (q_place2.empty()) { std::cerr << "Col_functions::contract_quarks(Cs1, Cs2): Found q " << q << " only once in " << *this << std::endl; } // Prepare new Quark_line // to be inserted at the place of found open Quark_line Quark_line new_Quark_line; Quark_line part2_new_Quark_line; // The first part of the new Quark_line should be the Quark_line // containing q in the conjugate new_Quark_line = cs.at(q_place2.at(0)); // Erasing q in the end new_Quark_line.ql.erase(--new_Quark_line.ql.end()); part2_new_Quark_line = cs.at(q_place.at(0)); // Erasing the q in the beginning of second part part2_new_Quark_line.ql.erase(part2_new_Quark_line.ql.begin()); new_Quark_line.append(part2_new_Quark_line.ql); // So far we have not included the Polynomial of part2_new_Quark_line new_Quark_line.Poly=new_Quark_line.Poly*part2_new_Quark_line.Poly; // If the first q index and the last qbar index in the new // Quark_line is the same (and the Quark_line is "open"), the indices // should be removed and the Quark_line should be closed if (new_Quark_line.ql.at(0) == new_Quark_line.ql.at( new_Quark_line.ql.size() - 1) && new_Quark_line.open) { // The string is closed new_Quark_line.open = false; // Remove last and first index new_Quark_line.ql.erase(--new_Quark_line.ql.end(), new_Quark_line.ql.end()); new_Quark_line.ql.erase(new_Quark_line.ql.begin()); } // Inserting new Quark_line in the place of the old cs.at(i) = new_Quark_line; // Remove quark_line with q in Cs cs.erase(( cs.begin() + q_place2.at(0))); i=-1; // reset to keep looking from the beginning in the new Cs (i will be increased to 0) }// end of if (open) n_q = n_quark(); } // end of for, loop over quark_lines } // end while (n_q > 0) return; } void Col_str::contract_next_neighboring_gluons( ) { // Loop over Quark_lines and remove neighboring and next to neighboring // gluon indices for( uint i=0; i < cs.size(); i++ ){ // Create a Quark_line to use as argument Quark_line Ql= at(i); Ql.contract_next_neighboring_gluons( ); // Collect Polynomial in Cs Polynomial Poly=Poly*Ql.Poly; // and put powers in Ql to 0 Ql.Poly.clear(); cs.insert( cs.begin() +i, Ql); // Erase old version of Quark_line (now at place i+1) cs.erase( cs.begin() +i + 1 ); } // Remove 1 and 0-rings, simplify Poly and normal order simplify(); return; } -bool operator==(const col_str & cs1, const col_str & cs2){ +bool operator==( const col_str & cs1, const col_str & cs2 ){ // col_str's must have equal length if( cs1.size() != cs2.size() ) return false; - // Individual ql's be be equal + // Individual Ql's be be equal for ( uint i=0; i< cs1.size(); i++){ - if(cs1.at(i).ql != cs2.at(i).ql ) return false; // Equal color structure - if(cs1.at(i).Poly != cs2.at(i).Poly ) return false; // Equal polynomial structure + if(cs1.at(i) != cs2.at(i) ) return false; } //If all ql's equal the cs are considered equal return true; } -bool operator!=(const col_str & cs1, const col_str & cs2){ +bool operator!=( const col_str & cs1, const col_str & cs2 ){ if(cs1==cs2) return false; else return true; } -std::ostream& operator<<(std::ostream& out, const col_str & cs) { +std::ostream& operator<<( std::ostream& out, const col_str & cs ) { int max = cs.size(); if (max == 0) out << "[]"; else { out << "["; for (int i = 0; i < max - 1; i++) { out << cs.at(i); } out << cs.at(max - 1) << "]"; //<< std::endl; } return out; } Col_str operator*( const Col_str & Cs, const int i){ Col_str Cs_res=Cs; Cs_res.Poly=Cs.Poly*i; return Cs_res; } // Define the operator * for int and Col_str -Col_str operator*( const int i, const Col_str & Cs){ +Col_str operator*( const int i, const Col_str & Cs ){ Col_str Cs_res=Cs; Cs_res.Poly=Cs.Poly*i; return Cs_res; } -Col_str operator*( const Col_str & Cs, const double d){ +Col_str operator*( const Col_str & Cs, const double d ){ // Multiply Polynomial of Col_str with the double Col_str Cs_res=Cs; - Cs_res.Poly=Cs.Poly*d; + Cs_res.Poly *= d; return Cs_res; } -Col_str operator*( const double d, const Col_str & Cs){ +Col_str operator*( const double d, const Col_str & Cs ){ Col_str Cs_res=Cs; Cs_res.Poly=Cs.Poly*d; return Cs_res; } -Col_str operator*(const Col_str & Cs, const cnum c){ +Col_str operator*(const Col_str & Cs, const cnum c ){ Col_str Cs_res=Cs; Cs_res.Poly=Cs.Poly*c; return Cs_res; return Cs; } -Col_str operator*(const cnum c, const Col_str & Cs){ +Col_str operator*( const cnum c, const Col_str & Cs ){ Col_str Cs_res=Cs; Cs_res.Poly=Cs.Poly*c; return Cs_res; } -Col_str operator*(const Col_str & Cs, const Monomial & Mon){ +Col_str operator* (const Col_str & Cs, const Monomial & Mon ){ Col_str Cs_res=Cs; - Cs_res.Poly=Cs.Poly*Mon; + Cs_res.Poly =Cs.Poly*Mon; return Cs_res; } -Col_str operator*( Monomial & Mon, const Col_str & Cs){ +Col_str operator*( Monomial & Mon, const Col_str & Cs ){ Col_str Cs_res=Cs; Cs_res.Poly=Cs.Poly*Mon; return Cs_res; } Col_str operator*( const Col_str & Cs, const Polynomial & Poly ){ Col_str Cs_res=Cs; Cs_res.Poly=Cs.Poly*Poly; return Cs_res; } Col_str operator*( const Polynomial & Poly, const Col_str & Cs ){ Col_str Cs_res=Cs; Cs_res.Poly=Cs.Poly*Poly; return Cs_res; } Col_str operator*( const Col_str & Cs, const Quark_line & Ql){ // Col_str to return Col_str out_Cs=(Cs); // Quark_line copy Quark_line out_Ql(Ql); // Multiplying Polynomials out_Cs.Poly = out_Ql.Poly*Cs.Poly; // Remove Polynomial info from Quark_line (put to 1), // as this info is stored in Polynomial of Col_str instead out_Ql.Poly.clear(); // Append Quark_line (without multiplicative polynomial) to out_Col_str out_Cs.cs.push_back( out_Ql ); return out_Cs; } Col_str operator*( const Quark_line & Ql , const Col_str & Cs){ return Cs*Ql; } Col_str operator*( const Col_str & Cs1, const Col_str & Cs2 ){ // Col_str to return Col_str out_Cs=Cs1; // Multiplying Polynomials out_Cs.Poly = Cs1.Poly*Cs2.Poly; // Looping over Quark_lines in Cs2 to add to Cs1 for (uint i=0; i < Cs2.cs.size(); i++ ){ // Append i:th Quark_line at i:th place in out_Col_str out_Cs.cs.push_back( Cs2.cs.at(i) ); } return out_Cs; } +Col_str operator*( const Quark_line & Ql1, const Quark_line & Ql2 ){ + + // Col_str to return + Col_str out_Cs(Ql1); + out_Cs.simplify(); + + return out_Cs*Ql2; +} std::ostream& operator<<(std::ostream& out, const Col_str & Cs){ // Write out polynomial if it is not 1, or if the cs has no structure Polynomial Poly1; // For comparison, the default Polynomial=1 if( Cs.Poly!=Poly1 or Cs.cs.size()==0 ) out << Cs.Poly; out << Cs.cs; return out; } bool operator==(const Col_str & Cs1, const Col_str & Cs2){ // col_str's be equal if( Cs1.cs != Cs2.cs ) return false; // Poly's must be equal if( Cs1.Poly != Cs2.Poly ) return false; //If all col_strs and all Polynomials are equal the Col_strs are considered equal return true; } bool operator!=( const Col_str & Cs1, const Col_str & Cs2){ if( Cs1==Cs2 ) return false; else return true; } } //end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Col_str.h b/MatrixElement/Matchbox/ColorFull/Col_str.h --- a/MatrixElement/Matchbox/ColorFull/Col_str.h +++ b/MatrixElement/Matchbox/ColorFull/Col_str.h @@ -1,274 +1,277 @@ // -*- C++ -*- /* * Col_str.h * Contains declaration of the class Col_str and associated types and operators. * Created on: Jul 7, 2010 * Author: Malin Sjodahl */ #ifndef COLORFULL_Col_str_h #define COLORFULL_Col_str_h #include "Quark_line.h" namespace ColorFull { /// For containing a vector (or list) of Quark_lines /// the color information part of a Col_str. -/// The col_str is a direct product of Quark_lines, +/// The col_str is a product of Quark_lines, /// contained in a vector of quark-lines. typedef std::vector < Quark_line > col_str; /// A class to contain ONE color structure, a direct product of Quark_lines, /// multiplying a Polynomial, Poly. /// The Quark_lines are stored as components in a vector, a col_str. class Col_str { public: - /// Default constructor, sets nothing. + /// Default constructor, leaves cs empty. Col_str(){}; /// Constructor for setting the color structure using a string. /// Should be used as: /// "Col_str Cs("2*Nc*TR^(3) [{1,2,3,4}(5,6)(7,8)(9,10,11,12)]");", /// i.e. the argument should be a Polynomial * col_str. /// (The Polynomial should multiply the whole col_str, /// rather than a quark_line inside the [] brackets.) Col_str( const std::string str ); /// Make a Col_str of a Quark_line. Col_str( Quark_line Ql ) {cs.push_back(Ql);} - /// For containing the info on color structure, + /// For containing the information about the color structure, /// a direct product of Quark_lines, /// contained in a vector of quark-lines. col_str cs; - /// Polynomial factor multiplying the whole color structure. + /// Polynomial factor multiplying the whole product of quark-lines. Polynomial Poly; /// Returns the Quark_line at place i. const Quark_line & at( int i ) const {return cs.at(i);} /// Returns the Quark_line at place i. Quark_line & at( int i ) {return cs.at(i);} - /// Returns the parton at place j in in Quark_line i. + /// Returns the parton at place j in Quark_line i. int at( int i, int j ) const; /// The size of the col_str uint size() const{ return cs.size(); } /// Is the col_str empty? bool empty() const { return cs.empty(); } /// Erase information in col_str. void clear() { cs.clear(); } /// Erases the Quark_line at place i. void erase( int i ); /// Erases the parton at place i, j. void erase( int i, int j ); /// Erases a parton at location place. void erase(std::pair place); /// Appends a Quark_line to data member cs. - void push_back( Quark_line Ql ) { cs.push_back( Ql ); } + void append( Quark_line Ql ) { cs.push_back( Ql ); } /// To insert the parton part_num in quark_line i /// at place j. void insert( int i, int j, int part_num ); /// Append the content of a col_str to the cs of the Col_str. void append( col_str cs_in ); /// Function for reading in the Col_str from the file filename. void read_in_Col_str( std::string filename ); /// Function for writing out the Col_str to a file /// with name filename. void write_out_Col_str( std::string filename ) const; /// Locates the parton with number part_num in a Col_str. std::pair find_parton( int part_num ) const; /// Function for telling if the partons p1 and p2 are neighbors. bool neighbor( int p1, int p2 ) const; /// Function for telling if parton p2 stands to the right of parton p1. bool right_neighbor( int p1, int p2 ) const; /// Function for telling if parton p2 stands to the left of parton p1. bool left_neighbor( int p1, int p2 ) const; /// Replaces the parton index old_ind with new_ind. void replace( int old_ind, int new_ind ); - /// Finds out if a parton is a q, qbar or g, + /// Finds out if a parton is a quark, anti-quark or a gluon, /// returns "q", "qbar" or "g" respectively. /// This function does NOT loop over all partons, but assumes /// that the parton is a gluon if the Quark_line is closed, - /// or if the Quark_line is open, but the parton cannot be found in the ends. - std::string find_kind( int part_num ) const; + /// or if the Quark_line is open, but p cannot be found in the ends. + std::string find_kind( int p ) const; /// Checks if the amplitude only has gluons, i.e. if all Quark_lines are closed. bool gluons_only() const; /// Counts the number of gluons in a Col_str. - /// Counts all gluon indices, both free and contracted. + /// Counts all gluon indices, both free and contractable. int n_gluon() const; /// Counts the number of quarks (=number of anti-quarks) in a Col_str. /// Counts all quark indices, both free and contracted. int n_quark() const; /// Normal orders the Col_str by first /// normal order individual Quark_lines /// and then normal order different Quark_lines in the cs. /// For the ordering see the member function smallest in this /// class and in the Quark_line class. void normal_order(); /// Finds out the "smallest" Col_str of two Col_strs, i.e. - /// which Col_str should stand first in a normal ordered Col_amp or a basis. + /// which Col_str should stand first in a normal ordered Col_amp or basis. /// Returns 1, if Cs1 should stand before Cs2 /// and 2 if Cs2 should stand before Cs1. /// Both Col_strs have to be normal ordered for the result to be unique. /// The Col_strs are ordered by - /// (1) Number of Quark_lines - /// (2) if the Quark_line at place 0,1,2... is open or not. + /// (1) number of Quark_lines + /// (2) if the Quark_line at place 0,1,2... is open or not /// (3) the size of the Quark_line at place 1,2,3... /// (4) the parton numbers in the Quark_lines at place 1,2,3..., /// i.e. first the first parton in the first Quark_line is checked /// and last the last parton in the last Quark_line. /// The function returns 0 if Cs1=Cs2. int smallest( const Col_str & Cs1, const Col_str & Cs2 ) const; /// Returns the length of the longest Quark_line in the Col_str. int longest_quark_line() const; /// Removes Quark_lines with only one gluon as Tr(t^a)=0. void remove_1_rings(); /// Removes Quark_lines without partons, equal to Nc (closed) or 1 (open). void remove_0_rings(); /// Removes 0 and 1-rings, /// moves factors multiplying the individual Quark_lines to /// multiply the col_str instead (i.e., being stored in Poly) - /// simplifys the Polnomial and normal orders the quark_lines. + /// simplifies the Polynomial and normal orders the quark_lines. void simplify(); /// Function for conjugating the Col_str by conjugating each Quark_line in cs, /// as well as the Polynomial Poly. void conjugate(); /// Contracts neighboring and next to neighboring gluons in each /// Quark_line in the Col_str, starting with contracting neighbors. /// This function should only be used on Col_strs with only closed Quark_lines. void contract_next_neighboring_gluons( ); /// Function for contracting gluon indices in closed Quark_lines with only 2 gluons. - /// This removes the 2-ring, replaces one of the gluon indices and - /// multiplies with a factor tr[t^a t^a]=TR (no sum), - /// only intended for closed Quark_lines. + /// This removes the 2-ring, replaces one of the gluon indices,, + /// and multiplies with a factor tr[t^a t^a]=TR (no sum), + /// only intended for fully contractable Col_strs. void contract_2_rings( ); /// Function for contracting quarks between two color structures Cs1 and Cs2. /// The result is stored in the Col_str itself. void contract_quarks( const Col_str Cs1, const Col_str Cs2 ); - private: /// Function to tell which quark_line should stand first in normal order. /// The quark_lines at place i1 and i2 are compared. /// Returns i1 if i1 should stand first, i2 if i2 should stand first /// and i1 if the quark_lines are equal. int compare_quark_lines( int i1, int i2 ) const; /// Function to allow setting the color structure by using a string. /// Used by string constructor and by void Col_str_of_str( const std::string str ); /// Setting the col_str member cs /// (i.e. the non-Polynomial information) using a string, /// used by Col_str_of_str and read_in_Col_str. void col_str_of_str( std::string ); }; //end class Col_str /// Define the operator == for two col_str's. /// The col_str's must have equal length and /// all Quark_lines must be the same /// (i.e. have same Polynomial and same parton ordering). /// The quark_lines are NOT normal ordered before comparison. bool operator==(const col_str & cs1, const col_str & cs2); /// Define the operator != for two col_str's. -/// Returns fals if cs1==cs2 and false otherwise. +/// Returns false if cs1==cs2 and false otherwise. bool operator!=(const col_str & cs1, const col_str & cs2); /// Define the operator << for col_str -std::ostream& operator<<(std::ostream& out, const col_str & cs); +std::ostream& operator<<( std::ostream& out, const col_str & cs ); /// Define the operator * for Col_str and int. Col_str operator*( const Col_str & Cs, const int i ); /// Define the operator * for int and Col_str. Col_str operator*( const int i, const Col_str & Cs ); /// Define the operator * for Col_str and double. Col_str operator*( const Col_str & Cs, const double d ); /// Define the operator * for double and Col_str. Col_str operator*( const double d, const Col_str & Cs ); /// Define the operator * for Col_str and cnum. Col_str operator*( const Col_str & Cs, const cnum c); /// Define the operator * for cnum and Col_str. Col_str operator*( const cnum c, const Col_str & Cs ); /// Define the operator * for Col_str and Monomial. Col_str operator*( const Col_str & Cs, const Monomial & Mon ); /// Define the operator * for Monomial and Col_str. Col_str operator*( const Monomial & Mon, const Col_str & Cs); /// Define the operator * for Col_str and Polynomial. Col_str operator*( const Col_str & Cs, const Polynomial & Poly ); /// Define the operator * for Polynomial and Col_str. Col_str operator*( const Polynomial & Poly, const Col_str & Cs ); /// Define the operator * for a Col_str and a Quark_line, adding Ql and multiplying Polynomial info. Col_str operator*( const Col_str & Cs, const Quark_line & Ql); /// Define the operator * for a Quark_line and a Col_str, adding Ql and multiplying Polynomial info. Col_str operator*( const Quark_line & Ql, const Col_str & Cs ); /// Define the operator * for two Col_str's, adding Ql and multiplying Polynomial info. Col_str operator*( const Col_str & Cs1, const Col_str & Cs2 ); +/// Define the operator * for two Quark_lines. Clearly the result cannot be contained in +/// a Quark_line, but needs (at least) a Col_str. +Col_str operator*( const Quark_line & Ql1, const Quark_line & Ql2 ); + /// Define the operator << for Col_str. std::ostream& operator<<(std::ostream& out, const Col_str & Cs); /// Define the operator == for two Col_str's /// the col_strs must be equal, and the /// Polynomials must be equal. Note that for Polynomials cf+Nc!=Nc+cf. /// Function does NOT first normal order Col_strs. bool operator==( const Col_str & Cs1, const Col_str & Cs2 ); /// Define the operator != for two Col_str's, the negation of ==. /// Function does NOT first normal order Col_strs. bool operator!=( const Col_str & Cs1, const Col_str & Cs2 ); } #endif /* COLORFULL_Col_str_h */ diff --git a/MatrixElement/Matchbox/ColorFull/Monomial.cc b/MatrixElement/Matchbox/ColorFull/Monomial.cc --- a/MatrixElement/Matchbox/ColorFull/Monomial.cc +++ b/MatrixElement/Matchbox/ColorFull/Monomial.cc @@ -1,522 +1,571 @@ // -*- C++ -*- /* * Monomial.cc * Contains definition of the class Monomial and associated types and operators * Created on: Jul 7, 2010 * Author: Malin Sjodahl */ #include "Monomial.h" #include "parameters.h" #include #include #include namespace ColorFull { Monomial::Monomial( std::string str ){ Monomial_of_str( str ); } void Monomial::Monomial_of_str( std::string str ) { //std::cout << "Monomial::Monomial: got string \"" << str << "\"" << std::endl; // Strings to contain various parts std::string num_str, Nc_pow_str, TR_pow_str, CF_pow_str, int_part_str; // Is it in denominator? bool denominator=0; // Start with setting to default pow_TR = pow_Nc = pow_CF = 0; int_part = 1; cnum_part = 1.0; // Check that left and right brackets match up int left_brackets=0, right_brackets=0; uint j=0; while (j < str.size()) { if(str.at(j)=='(') left_brackets++; if(str.at(j)==')') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Monomial::Monomial_of_str: The brackets in the monomial\"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } // Check that only allowed signs j = 0; while (j < str.size()) { if (!(str.at(j) == '+' or str.at(j) == '-' or str.at(j) == '.' or str.at(j) == '\n' or str.at(j) == '*' or str.at(j) == '/' or str.at(j) == '^' or str.at(j) == 'T' or str.at(j) == 'R' or str.at(j) == 'N' or str.at(j) == 'c' or str.at(j) == 'C' or str.at(j) == 'F' or str.at(j) == '(' or str.at(j) == ')' or str.at(j) == ' ' or str.at(j) == '0' or str.at(j) == '1' or str.at(j) == '2' or str.at(j) == '3' or str.at(j) == '4' or str.at(j) == '5' or str.at(j) == '6' or str.at(j) == '7' or str.at(j) == '8' or str.at(j) == '9')) { std::cerr << "Monomial::Monomial_of_str: A disallowed sign encountered in string for Monomial constructor: " << str.at(j) << std::endl; assert( 0 ); } num_str.push_back( str.at(j) ); j++; } // Read the string, starting from 0th element uint i = 0; // We may have to skip some chars containing spaces while (i < str.size() && (str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == '*' or str.at(i) == '(' or str.at(i) == ')')) i++; // Check plus or minus if (i < str.size() and str.at(i) == '+') { i++; } // Pick up the sign if (i < str.size() and str.at(i) == '-' ) { int_part = int_part * (-1); i++; } while (i < str.size()) { // Clear strings as we may loop around many times num_str.clear(); Nc_pow_str.clear(); TR_pow_str.clear(); - + CF_pow_str.clear(); // We may have to skip some chars containing spaces and * while (i < str.size() && (str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == '*' or str.at(i) == '(' or str.at(i) == ')')) i++; // look for denominator "/" if(i< str.size() and str.at(i) == '/') { if (denominator) { std::cerr << "Monomial::Monomial_of_str: Can only handle one / " << "but got " << str; assert( 0 ); }; denominator=true; i++; } // If a number is encountered which is not *-1 or *(-1) while (i< str.size() and ( str.at(i) == '.' or str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9') ) { num_str.push_back(str.at(i)); i++; } // If the num_str contains something, save if (!num_str.empty()) { double num_fac=1.0; // Make a number of the string std::istringstream num_str_st(num_str); // Set the num member variable num_str_st >> num_fac; if(denominator) cnum_part=cnum_part/num_fac; else { // factor is in numerator, try to put in int-part if it looks like an int int num_fac_int=static_cast(floor(num_fac + 0.50)); if( std::abs(num_fac_int-num_fac) < accuracy ) int_part= int_part*num_fac_int; else // put factor in numeric part cnum_part=cnum_part*num_fac; } } // If TR is encountered (T should always be followed by R) if (i< str.size() and (str.at(i) == 'T' and (i==str.size()-1 or str.at(i + 1) != 'R')) ){ std::cerr << "Monomial::Monomial_of_str: Got a string containing T, but T was not followed by R (as in TR). " << std::endl; assert( 0 ); } if (i< str.size() and (str.at(i) == 'T' and str.at(i + 1) == 'R') ) { i++; i++; // If there is no ^, the power of TR is 1 int pow_cont=1; // If ^, start reading in power of TR if (i < str.size() and str.at(i) == '^') { i++; // If we have something in a bracket if(i < str.size() and str.at(i)=='('){ i++; // keep reading until end-bracket while(i < str.size() and str.at(i)!=')'){ TR_pow_str.push_back(str.at(i)); i++; } // Skip the ) if(i < str.size() and str.at(i)==')') i++; } // otherwise, as long as we have numbers else while (i< str.size() and (str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9' or str.at(i) == '-')) { TR_pow_str.push_back(str.at(i)); i++; } // Make a number of the string std::istringstream TR_pow_str_st(TR_pow_str); // Set the TR_pow member variable TR_pow_str_st >> pow_cont; } if(denominator) pow_cont=-pow_cont; pow_TR=pow_TR + pow_cont; } // If Nc is encountered if (i < str.size() and (str.at(i) == 'N')) { if( i+1< str.size() and str.at(i + 1) == 'c') i++; // get to c else{// allow only Nc - std::cerr << "Monomial::Monomial_of_str: got a string containing N, but N was not followed by c (as in Nc). " << std::endl; + std::cerr << "Monomial::Monomial_of_str: got a string containing N, " << str <<", but N was not followed by c (as in Nc). " << std::endl; assert( 0 ); } i++; // get to next sign int Nc_pow = 1; //if(i < str.size()) std::cout << "Monomial::Monomial: " << str.at(i) << endl; // If ^, start reading in power of Nc if (i< str.size() and str.at(i) == '^') { i++; // compensate for ^ // If we have something in a bracket if(i < str.size() and str.at(i)=='('){ i++; // keep reading until end-bracket while(i < str.size() and str.at(i)!=')'){ Nc_pow_str.push_back(str.at(i)); i++; } // Skip the ) if(i < str.size() and str.at(i)==')') i++; } // as long as we have numbers or - else while (i< str.size() and (str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9' or str.at(i) == '-')){ Nc_pow_str.push_back(str.at(i)); i++; } // Make a number of the string std::istringstream Nc_pow_str_st(Nc_pow_str); Nc_pow_str_st >> Nc_pow ; } if (denominator) pow_Nc=pow_Nc-Nc_pow; else pow_Nc=pow_Nc + Nc_pow; } // If CF is encountered if (i < str.size() and (str.at(i) == 'C')) { std::cout.flush(); if( i+1< str.size() and str.at(i + 1) == 'F') i++; // get to f i++; // get to next sign int CF_pow = 1; std::cout.flush(); // If ^, start reading in power of CF if (i< str.size() and str.at(i) == '^') { i++; // compensate for ^ // If we have something in a bracket if(i < str.size() and str.at(i)=='('){ i++; // Skip the ( // keep reading until end-bracket while(i < str.size() and str.at(i)!=')'){ CF_pow_str.push_back(str.at(i)); i++; } // Here we may need to skip some spaces as well if(i < str.size() and str.at(i)==' ') i++; // Skip the ) if(i < str.size() and str.at(i)==')') i++; } // as long as we have numbers else while (i< str.size() and (str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9' or str.at(i) == '-')){ CF_pow_str.push_back(str.at(i)); i++; } // Make a number of the string std::istringstream CF_pow_str_st(CF_pow_str); CF_pow_str_st >> CF_pow ; } if (denominator) pow_CF=pow_CF-CF_pow; else pow_CF=pow_CF+ CF_pow; } // If *-1 if ((i < str.size() and str.at(i) == '-') and ( (i-1>0) and str.at(i-1)=='*') ) { i++; //skip the - std::cout.flush(); // Keep reading in while numbers while (i< str.size() and (str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9')){ int_part_str.push_back(str.at(i)); i++; } int n_fac=1; std::istringstream n_str(int_part_str); n_str >> n_fac; int_part=int_part * n_fac*(-1); } // If *(-1) if ((i < str.size() and str.at(i) == '-') and ( ( ( (i-2> 0) and str.at(i-1)=='(') and str.at(i-2)=='*') )) { i++; //skip the - i++; //skip the ( std::cout.flush(); // Keep reading in while numbers while (i< str.size() and (str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9')){ int_part_str.push_back(str.at(i)); i++; } int n_fac=1; std::istringstream n_str(int_part_str); n_str >> n_fac; int_part=int_part * n_fac*(-1); // Skip potential white spaces while (i< str.size() and str.at(i) == ' ') i++; // Make sure we have a closing ) if(str.at(i)==')') i++; else { std::cerr << "Monomial::Monomial_of_str: Expects final ) in *(number)" << " got " << str.at(i) << std::endl; assert( 0 ); } } // We may have to skip some spaces while (i < str.size() && str.at(i) == ' ') {i++;} std::cout.flush(); } }// end Monomial_of_str void Monomial::read_in_Monomial( std::string filename ) { // Read in file std::ifstream fin( filename.c_str() ); // Check that file exists if( !fin ){ std::cerr << "Monomial::read_in_Monomial: The file " << filename << " could not be opened." << std::endl; assert( 0 ); } // Copy info from file to string std::string str((std::istreambuf_iterator(fin)), std::istreambuf_iterator()); - str.erase(str.size()-1); + + // Skip lines starting with # + while( str.at(0)== '#' ){ + while (str.at(0) != '\n'){ + str.erase(str.begin()); + } + // erase endl sign(s) + while(str.at(0)== '\n'){ + str.erase(str.begin()); + } + } + + // Remove endl chars at the end of the file + while( str.at(str.size()-1) == '\n' ) str.erase(str.size()-1); Monomial_of_str( str ); } +void Monomial::write_out_Monomial( std::string filename ) const { + + std::ofstream outfile(filename.c_str()); + + if ( !outfile ) + std::cerr << "Monomial::write_out_Monomial: Cannot write out Monomial as the file \"" + << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; + + outfile << *this; +} + std::ostream& operator<<( std::ostream& out, const Monomial & Mon ){ // If multiplied by 0, the whole Monomial is 0 if (Mon.int_part == 0 ) out << "0"; else{ // If real, print only real part if( imag( Mon.cnum_part )==0 ) { // If both int and numeric parts are 1, print 1 if( Mon.int_part==1 && Mon.cnum_part.real() ==1 ) out << "1"; // If int part is 1, print numeric part if >0 (minus signs gives problems) else if( Mon.int_part==1 && Mon.cnum_part.real() > 1 ) out << Mon.cnum_part.real(); // If numeric part is 1, print int part if >0 (minus signs gives problems) else if( Mon.int_part > 1 && Mon.cnum_part.real() ==1 ) out << Mon.int_part; else out << real( Mon.cnum_part ) << "*" << Mon.int_part; } // else print full complex number else out << Mon.cnum_part << "*" << Mon.int_part; if( Mon.pow_TR !=0 ) { if( Mon.pow_TR ==1 ) out << " TR"; else out << " TR^" <<"(" << Mon.pow_TR <<")"; } if( Mon.pow_Nc !=0 ){ if( Mon.pow_Nc ==1 ) out << " Nc"; else out <<" Nc^" << "(" << Mon.pow_Nc <<")"; } if( Mon.pow_CF !=0 ) { if( Mon.pow_CF ==1 ) out << " CF"; else out << " CF^" << "(" << Mon.pow_CF <<")"; } } return out; } Monomial operator*( const Monomial & Mon, const int i ){ Monomial out_Mon(Mon); out_Mon.int_part=out_Mon.int_part*i; return out_Mon; } Monomial operator*( const int i, const Monomial & Mon){ return Mon*i; } +Monomial operator*=( Monomial & Mon, const int i ){ + return Mon.int_part*=i; +} -Monomial operator*(const Monomial & Mon, const cnum c){ + +Monomial operator*( const Monomial & Mon, const cnum c ){ Monomial out_Mon(Mon); out_Mon.cnum_part=out_Mon.cnum_part*c; return out_Mon; } -Monomial operator*(const cnum c, const Monomial & Mon){ +Monomial operator*( const cnum c, const Monomial & Mon ){ return Mon*c; } +Monomial operator*=( Monomial & Mon, const cnum c ){ + Mon.cnum_part*=c; + return Mon; +} Monomial operator*(const Monomial & Mon, const double d){ Monomial out_Mon(Mon); out_Mon.cnum_part=out_Mon.cnum_part*d; return out_Mon; } - Monomial operator*(const double d, const Monomial & Mon){ return Mon*d; } +Monomial operator*=( Monomial & Mon, const double d ){ + Mon.cnum_part*=d; + return Mon; +} Monomial operator*(const Monomial & Mon1, const Monomial & Mon2){ Monomial Mon_out; - // Adding powers to get total power of 2, N and CF + // Adding powers to get total power of TR, Nc and CF Mon_out.pow_TR = Mon1.pow_TR + Mon2.pow_TR; Mon_out.pow_Nc = Mon1.pow_Nc + Mon2.pow_Nc; Mon_out.pow_CF = Mon1.pow_CF + Mon2.pow_CF; // Multiplying factors Mon_out.int_part=Mon1.int_part * Mon2.int_part; Mon_out.cnum_part=Mon1.cnum_part * Mon2.cnum_part; return Mon_out; } +Monomial operator*=( Monomial & Mon1, const Monomial & Mon2){ + + // Adding powers to get total power of TR, Nc and CF + Mon1.pow_TR += Mon2.pow_TR; + Mon1.pow_Nc += Mon2.pow_Nc; + Mon1.pow_CF += Mon2.pow_CF; + + // Multiplying factors + Mon1.int_part *= Mon2.int_part; + Mon1.cnum_part *= Mon2.cnum_part; + return Mon1; +} + + bool operator==(const Monomial & Mon1 , const Monomial & Mon2){ // If both are 0 if(Mon1.int_part == 0 && Mon2.int_part == 0) return true; // all factors should be same in order for Monomial's to be same if(Mon1.pow_TR != Mon2.pow_TR ) return false; if(Mon1.pow_Nc != Mon2.pow_Nc ) return false; if(Mon1.pow_CF != Mon2.pow_CF ) return false; if(Mon1.int_part != Mon2.int_part ) return false; // For comparing numerical part use an accuracy, as numbers may be small, compare ratio const double r1 = real(Mon1.cnum_part); const double r2 = real(Mon2.cnum_part); const double i1 = imag(Mon1.cnum_part); const double i2 = imag(Mon2.cnum_part); if( r2 != 0 and i2 != 0 ) { if( r1/r2 < (1-accuracy) or r1/r2 > (1+accuracy) ) return false; if( i1/i2 < (1-accuracy) or i1/i2 > (1+accuracy) ) return false; } else if( r2 == 0 and i2 != 0 ) { if( r1 != 0 ) return false; if( i1/i2 < (1-accuracy) or i1/i2 > (1+accuracy) ) return false; } else if ( r2 != 0 and i2 == 0 ) { if( r1/r2 < (1-accuracy) or r1/r2 > (1+accuracy) ) return false; if( i1 != 0 ) return false; } else { // both r2,i2 are zero if( r1 != 0 ) return false; if( i1 != 0 ) return false; } return true; } bool operator!=(const Monomial & Mon1 , const Monomial & Mon2){ if( Mon1==Mon2) return false; else return true; } bool operator<(const Monomial & Mon1, const Monomial & Mon2) { // If different Nc+CF power if (Mon1.pow_Nc + Mon1.pow_CF < Mon2.pow_Nc + Mon2.pow_CF) return true; else if (Mon1.pow_Nc + Mon1.pow_CF > Mon2.pow_Nc + Mon2.pow_CF) return false; else { // same total Nc power // if different powers of N if (Mon1.pow_Nc < Mon2.pow_Nc) return true; else if (Mon1.pow_Nc > Mon2.pow_Nc) return false; else { // same pow_Nc+pow_CF and same pow_Nc // order according to cnum_part*int_part if ( (Mon1.int_part)*(abs(Mon1.cnum_part)) < (Mon2.int_part)*(abs(Mon2.cnum_part))) return true; else if ( (Mon1.int_part)*(abs(Mon1.cnum_part)) > (Mon2.int_part)*(abs(Mon2.cnum_part))) return false; else { // same pow_Nc+pow_CF and same pow_Nc, and same numerical cnum_part*int_part // order according to int_part if (Mon1.int_part < Mon2.int_part) return true; else if (Mon1.int_part < Mon2.int_part) return false; else {// same pow_Nc+pow_CF and same pow_Nc, and same numerical cnum_part*int_part, and same int_part // order according to pow_TR if (Mon1.pow_TR < Mon2.pow_TR) return true; else if (Mon1.pow_TR < Mon2.pow_TR) return false; else { // The Monomials are equal return false; } } } } } std::cerr << "Monomial::operator<: Could not decide on Monomial ordering of " << Mon1 << " and " << Mon2 << ". This should not happen, report bug." << std::endl; assert( 0 ); return false; } } // end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Monomial.h b/MatrixElement/Matchbox/ColorFull/Monomial.h --- a/MatrixElement/Matchbox/ColorFull/Monomial.h +++ b/MatrixElement/Matchbox/ColorFull/Monomial.h @@ -1,161 +1,185 @@ // -*- C++ -*- /* * Monomial.h * Contains declaration of the class Monomial and associated types and operators * Created on: Jul 7, 2010 * Author: Malin Sjodahl */ #ifndef COLORFULL_Monomial_h #define COLORFULL_Monomial_h #include "types.h" namespace ColorFull { /// A class to contain the factor of form TR^a*Nc^b*CF^c*int_part*cnum_part, /// where the powers a, b and c may be negative. /// A default Monomial is defined to be 1, and has int_part and cnum_part=1. /// A 0-Monomial has int_part=0. /// A polynomial is a sum of Monomials. class Monomial { public: - /// Default constructor sets int_part and cnum_part=1, and Pow_Nc=pow_TR=pow_CF=0. + /// Default constructor sets int_part=cnum_part=1, and pow_Nc=pow_TR=pow_CF=0. Monomial(){ pow_TR=pow_Nc=pow_CF=0; int_part=1; cnum_part=1.0; } /// Constructor using a double. /// The cnum_part member is set to contain the value. Monomial( double dnum ){ pow_TR=pow_Nc=pow_CF=0; int_part=1; cnum_part.real(dnum); } /// Constructor using an int. /// The int_part member is set to contain the value. Monomial( int num ){ pow_TR=pow_Nc=pow_CF=0; int_part=num; cnum_part=1.0; } /// Constructor taking a string as argument. - /// The argument should be of the form given in form (for example) + /// The argument should be of the form in for example /// -(20*TR^5)/Nc or -20 TR^(5)/Nc or 20 / TR^(-5)Nc^(1) CF^(3). /// NOTE: All spaces and * are ignored, except in "*(-1)" and *-1, which /// is understood as (*-1). /// EVERYTHING standing after / is /// divided with, whereas everything standing before is multiplied with. /// Parentheses are ignored unless they appear in powers, - /// i.e, directly after ^. + /// i.e., directly after ^. /// No spaces are allowed inside the powers. /// If the string contains no info or is empty the Monomial is put to 1, /// pow_TR = pow_Nc = pow_CF = 0, int_part = 1, cnum_part = 1.0. /// (Expanded Mathematica 8 expressions are in this form.) Monomial( std::string str ); /// Power of TR in Monomial. int pow_TR; /// Power of the number of colors. int pow_Nc; - /// Power of CF=TR (Nc^2-1)/(Nc) + /// Power of CF=TR (Nc^2-1)/Nc. int pow_CF; - /// Integer multiplying monomial, can be 0. + /// Integer multiplying the monomial, can be 0. int int_part; - /// Complex number multiplying monomial. + /// Complex number multiplying the monomial. cnum cnum_part; /// Take the complex conjugate. /// Note that this changes the Monomial itself. void conjugate() { cnum_part=conj( cnum_part ); } /// Function for reading in the Monomial from the file filename, /// uses Monomial_of_str. void read_in_Monomial( std::string filename ); + /// Function for writing out the Monomial to a file + /// with name filename. + void write_out_Monomial( std::string filename ) const; + + private: /// Function for makinga a Monomial from a string. /// The argument should be of the form given in form (for example) /// -(20*TR^5)/Nc or -20 TR^(5)/Nc or 20 / TR^(-5)Nc^(1) CF^3. /// NOTE: All spaces and * are ignored, except in "*(-1)" and *-1, which /// is understood as (*-1). /// EVERYTHING standing after / is /// divided with, whereas everything standing before is multiplied with. /// Parentheses are ignored unless they appear in powers, /// i.e, directly after ^. /// No spaces are allowed inside the powers. - /// If the string contains no info or is empty the Monomial is put to 1, + /// If the string contains no information or is empty, the Monomial is set to 1, /// pow_TR = pow_Nc = pow_CF = 0, int_part = 1, cnum_part = 1.0. void Monomial_of_str( std::string str ); }; /// Define the operator << for Monomial std::ostream& operator<<( std::ostream& out, const Monomial & Mon ); /// Operator * for Monomial and int. /// The int_part member is multiplied by i, whereas other /// members are kept constant. Monomial operator*( const Monomial & Mon, const int i ); /// Operator * for int and Monomial. /// Returns Mon*i. Monomial operator*( const int i, const Monomial & Mon ); /// Operator * for Monomial and cnum. The member Mon.cnum_part /// is multiplied by c, whereas other members are kept /// the same. Monomial operator*( const Monomial & Mon, const cnum c ); /// Operator * for cnum and Monomial, returns Mon*c. Monomial operator*( const cnum c, const Monomial & Mon ); /// Operator * for Monomial and double. The member Mon.cnum_part /// is multiplied by c, whereas other members are kept /// the same. Monomial operator*( const Monomial & Mon, const double d ); /// Operator * for double and Monomial, returns Mon*d. Monomial operator*( const double d, const Monomial & Mon ); /// Operator * for Monomials. The powers, pow_TR, pow_Nc /// and pow_CF are added, and the numbers int_part and mon /// are multiplied. Monomial operator*( const Monomial & Mon1, const Monomial & Mon2 ); +/// Operator *= for Monomial and int. +/// The int_part member is multiplied by i, whereas other +/// members are kept constant. +Monomial operator*=( Monomial & Mon, const int i ); + +/// Operator *= for Monomial and cnum. +/// The cnum_part member is multiplied by c, whereas other +/// members are kept constant. +Monomial operator*=( Monomial & Mon, const cnum c ); + +/// Operator *= for Monomial and double. +/// The cnum_part member is multiplied by d, whereas other +/// members are kept constant. +Monomial operator*=( Monomial & Mon, const double d ); + +/// Operator *= for Monomials. Mon1 is changed by being multipled with +/// Mon2. +Monomial operator*=( Monomial & Mon1, const Monomial & Mon2); + /// Operator == for Monomials, all parts must be equal. /// For the numerical part, an accuracy is used for the ratio. bool operator==( const Monomial & Mon1, const Monomial & Mon2 ); /// Define the operator != Monomial. Returns false if Mon1==Mon2, /// and true otherwise. bool operator!=( const Monomial & Mon1, const Monomial & Mon2 ); /// Operator to find the "smallest" of two Monomials. /// The Monomials are ordered first according to pow_Nc+pow_CF, /// then according to pow_Nc (for same pow_Nc+pow_CF) /// then according to int_part*abs(cnum_part), then according to int_part, and finally according to pow_TR. /// NOTE: this ordering does not agree with ordering according to numerical value. /// NOTE: If the Monomials are equal, Mon1 is not smaller so false will be returned. bool operator<( const Monomial & Mon1, const Monomial & Mon2 ); } #endif /* COLORFULL_Monomial_h */ diff --git a/MatrixElement/Matchbox/ColorFull/Orthogonal_basis.cc b/MatrixElement/Matchbox/ColorFull/Orthogonal_basis.cc --- a/MatrixElement/Matchbox/ColorFull/Orthogonal_basis.cc +++ b/MatrixElement/Matchbox/ColorFull/Orthogonal_basis.cc @@ -1,500 +1,500 @@ /* * Orthogonal_basis.cc * Contains the definitions of the class Orthogonal_basis, related types and operators. * Created on: May 25, 2013 * Author: Malin Sjodahl */ #include "Orthogonal_basis.h" #include "parameters.h" #include #include #include namespace ColorFull { void Orthogonal_basis::scalar_product_matrix(){ if( ng+nq>5 ){ std::cout << "Orthogonal_basis::scalar_product_matrix: nq+n_g0=" << nq+ng << " is large, consider using numerical and/or memory version. " << std::endl; std::cout.flush(); } return scalar_product_matrix( true, true, false ); } void Orthogonal_basis::diagonal_scalar_product_matrix( bool save_P_diagonal_spm, bool save_d_diagonal_spm, bool use_mem ){ // If the diagonal_P_spm and diagonal_d_spm have already been calculated, erase them diagonal_P_spm.clear(); diagonal_d_spm.clear(); if( (cb.size()==0 ) ) { std::cout << "Orthogonal_basis::diagonal_scalar_product_matrix: There are no basis vectors in this basis, consider using read_in_basis." << std::endl; std::cout.flush(); return ; } // Check that all Polynomials are real // (check doesn't cover the case of complex Polynomial in a Quark_line) // but if an entry is not real this will be discovered as the d_spm is calculated for ( uint cbi=0; cbi < cb.size(); cbi++){ for ( uint j=0; j< cb.at(cbi).size(); j++){ if( imag (Col_fun.cnum_num( cb.at(cbi).at(j).Poly )) > accuracy ){ std::cerr << "Orthogonal_basis::diagonal_scalar_product_matrix: ColorFull expects real Polynomial multiplying the color structure, but the Polynomial\n" << cb.at(cbi).at(j).Poly << std::endl << " appearing in front of the Col_str " << cb.at(cbi).at(j).cs << " in basis vector number " << cbi << " is not real."<< std::endl << std::endl; std::cerr.flush(); assert( 0 ); } } } // For remembering already calculated topologies std::map > mem_map; // Loop over basis vectors in Basis for( uint i=0; i < cb.size(); i++){ // To contain the result of vector i square Polynomial iiRes; iiRes=iiRes*0; if(use_mem){ // Loop over Col_strs in first Ca for( uint Ca1i=0; Ca1i< cb.at(i).size(); Ca1i++){ // Loop over Col_strs in second Ca for( uint Ca2i=0; Ca2i< cb.at(i).size(); Ca2i++){ // To contain the contribution to the ii-th entry if memoization is used std::shared_ptr iiEntry_contr; // Rename indices, and make string of new col_strs, to use in map // strip off Polynomial information to make the map minimal Col_str Cs1, Cs2; Cs1.append(cb.at(i).at(Ca1i).cs); Cs2.append(cb.at(i).at(Ca2i).cs); rename_indices( Cs1, Cs2 ); std::ostringstream Cs_string; Cs_string << Cs1 << Cs2; // Calculate element contribution to the ijth element in scalar product matrix // If this has scalar product has occurred before, reuse old value if (mem_map.count( Cs_string.str() ) > 0) { iiEntry_contr = mem_map[Cs_string.str()]; } // Otherwise, calculate value and save topology else { Polynomial p = Col_fun.scalar_product(Cs1, Cs2); - iiEntry_contr = shared_ptr (new Polynomial(p)); + iiEntry_contr = std::shared_ptr (new Polynomial(p)); mem_map[Cs_string.str()] = iiEntry_contr; } // Sum up all the contributions to one Polynomial, recall to multiply with Polynomials Polynomial iiEntry_contr_poly=(*iiEntry_contr)* cb.at(i).at(Ca1i).Poly*cb.at(i).at(Ca2i).Poly; if( !save_P_diagonal_spm ) { double iiEntry_contr_d= Col_fun. double_num( iiEntry_contr_poly ); Monomial Mon( iiEntry_contr_d ); iiRes+= Mon; } else iiRes += iiEntry_contr_poly; } // end looping over Cs in first Ca } // end looping over Cs in 2nd Ca // If Polynomial result is wanted, simplify if ( save_P_diagonal_spm ) iiRes.simplify(); // Otherwise convert to numerical else { double num_iiRes= Col_fun. double_num( iiRes ); iiRes.clear(); Monomial Mon( num_iiRes ); - iiRes.push_back(Mon); + iiRes.append(Mon); } - diagonal_P_spm.push_back( iiRes ); + diagonal_P_spm.append( iiRes ); } // end if ( use_mem ) else{ // Calculate element ij in scalar product matrix Polynomial iiRes=Col_fun.scalar_product(cb.at(i), cb.at(i)); iiRes.simplify(); - diagonal_P_spm.push_back( iiRes ); + diagonal_P_spm.append( iiRes ); } //end if (not mem) }// end looping over i if ( save_d_diagonal_spm ){ diagonal_d_spm=Col_fun.double_num(diagonal_P_spm); } if (! save_P_diagonal_spm){ diagonal_P_spm.clear(); } return; } void Orthogonal_basis::scalar_product_matrix( bool save_P_spm, bool save_d_spm, bool use_mem ) { // If the P_spm and d_spm have already been calculated, erase them P_spm.clear(); d_spm.clear(); // First calculate diagonal version diagonal_scalar_product_matrix( save_P_spm, save_d_spm, use_mem ); // Then copy content to matrix if( save_P_spm ){ // First empty P_spm P_spm.clear(); Polynomial Zero; - Zero=0*Zero; + Zero*=0; for (uint i=0; i< diagonal_P_spm.size(); i++ ){ Poly_vec rowi; for (uint j=0; j< diagonal_P_spm.size(); j++ ){ if(i==j){// diagonal entries - rowi.push_back( diagonal_P_spm.at(i)); + rowi.append( diagonal_P_spm.at(i)); } else{// non-diagonal parts - rowi.push_back(Zero); + rowi.append(Zero); } } - P_spm.push_back(rowi); + P_spm.append(rowi); } } if( save_d_spm ){ // First empty P_spm d_spm.clear(); for (uint i=0; i< diagonal_d_spm.size(); i++ ){ dvec rowi; for (uint j=0; j< diagonal_d_spm.size(); j++ ){ if(i==j){// diagonal entries rowi.push_back( diagonal_d_spm.at(i)); } else{// non-diagonal parts rowi.push_back( 0 ); } } d_spm.push_back(rowi); } } } Poly_vec Orthogonal_basis::decompose( const Col_amp & Ca ) { // Check that we have a basis if(cb.size()==0){ std::cerr << "Orthogonal_basis::decompose: The basis vector cb is empty consider reading in basis." << std::endl; assert( 0 ); } // Check that quark and gluon content agree with that in Col_amp else if(Ca.size()>0 ){ if(Ca.at(0).n_quark() != nq) { std::cerr << "Orthogonal_basis::decompose: The number of quarks in the argument Col_amp, " << Ca.at(0).n_quark() << ", does not fit the number of quarks in the basis " << nq << std::endl;} if(Ca.at(0).n_gluon() != ng ) { std::cerr << "Orthogonal_basis::decompose: The number of gluons in the argument Col_amp " << Ca.at(0).n_gluon() << " does not fit the number of gluons in the basis " << nq << std::endl; } } // This version of decompose (as opposed to trace type versions) // need scalar product matrix // If P_spm is not empty, but has wrong size if( P_spm.size() != cb.size() and !P_spm.empty() ){ std::cerr << "Orthogonal_basis::decompose: The size of the scalar product matrix and the basis do not agree." << std::endl; assert( 0 ); } // If diagonal_P_spm is not empty, but has wrong size if( diagonal_P_spm.size() != cb.size() and !diagonal_P_spm.empty() ){ std::cerr << "Orthogonal_basis::decompose: The size of the diagonal scalar product matrix and the basis do not agree." << std::endl; assert( 0 ); } // If both are empty, calculate diagonal version if( P_spm.empty() and diagonal_P_spm.empty() ){ diagonal_scalar_product_matrix(true, true, true); } // Use matrix information if diagonal spm is empty if( diagonal_P_spm.empty() ){ for ( uint i=0; i< P_spm.size(); i++ ){ - diagonal_P_spm.push_back( P_spm.at(i).at(i) ); + diagonal_P_spm.append( P_spm.at(i).at(i) ); } diagonal_d_spm=Col_fun.double_num(diagonal_P_spm); } // To contain the decomposed vector Poly_vec Decv; // Loop over all vectors in cb and calculate projection for (uint i = 0; i < cb.size(); i++) { double inv_norm=1.0/diagonal_d_spm.at(i); //Decv.at(i)= Col_fun.scalar_product( cb.at(i) , Ca )*inv_norm; - Decv.push_back( Col_fun.scalar_product( cb.at(i) , Ca )*inv_norm ); + Decv.append( Col_fun.scalar_product( cb.at(i) , Ca )*inv_norm ); } return Decv; } std::string Orthogonal_basis::diagonal_spm_file_name(const bool leading, const bool poly ) const{ // First construct filename std::ostringstream ss; std::string filename; ss << "ColorResults"; ss << '/'; // CF as in ColorFull ss << "CF_"; // Prefix according to basis type ss << "OB_"; // diagonal version ss << "diagonal_"; // Polynomial or numerical matrix? if( poly )ss << "P_"; else ss << "d_"; ss << "spm_q"; ss << nq; ss << "_g"; ss << ng; // is the result leading, and if so, how? if ( leading ) ss << "_l"; if ( Col_fun.get_full_CF() ) ss << "_cff"; else ss << "_cfl"; if( Col_fun.get_Nc() != 3 ){ ss << "_Nc_"; ss << Col_fun.get_Nc(); } if(Col_fun.get_TR() != 0.5 ){ ss << "_TR_"; ss << Col_fun.get_TR(); } filename=ss.str(); return filename; } void Orthogonal_basis::write_out_diagonal_d_spm( std::string filename ) const{ Col_fun.write_out_dvec( diagonal_d_spm, filename ); } void Orthogonal_basis::write_out_diagonal_d_spm( ) const{ std::string filename = diagonal_spm_file_name( false, false ); write_out_diagonal_d_spm( filename ); } void Orthogonal_basis::write_out_diagonal_P_spm( std::string filename ) const{ diagonal_P_spm.write_out_Poly_vec( filename ); } void Orthogonal_basis::write_out_diagonal_P_spm( ) const{ std::string filename = diagonal_spm_file_name( false, true ); write_out_diagonal_P_spm( filename ); } Polynomial Orthogonal_basis::scalar_product( const Col_amp & Ca1, const Col_amp & Ca2 ) { // Check that we have a basis if(cb.size()==0){ std::cerr << "Orthogonal_basis::scalar_product: The basis vector cb is empty consider using create_basis or read_in_basis." << std::endl; assert( 0 ); } // Check that size of P_spm is consistent (if calculated) if( (P_spm.size() != cb.size()) and P_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product: Size of scalar product matrix P_spm and color basis cb do not agree." << std::endl; assert( 0 ); } // Check that size of diagonal_P_spm is consistent (if calculated) if( (diagonal_P_spm.size() != cb.size()) and diagonal_P_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product: Size of diagonal_P_spm and color basis cb do not agree." << std::endl; assert( 0 ); } // Check if at least one of P_spm and diagonal_P_spm exist, // If non exist, calculate diagonal if( P_spm.empty() and diagonal_P_spm.empty() ){ diagonal_scalar_product_matrix(true, true, true); } // If matrix form, but not diagonal exist, copy diagonal entries else if(diagonal_P_spm.empty() ){ for( uint i=0; i< P_spm.size(); i++ ){ - diagonal_P_spm.push_back(P_spm.at(i).at(i)); + diagonal_P_spm.append(P_spm.at(i).at(i)); } } // To contain the resulting Polynomial Polynomial Poly_res; Poly_res=Poly_res*0; // Decompose the Col_amps Poly_vec Polyv1=decompose(Ca1); Polyv1.conjugate(); Poly_vec Polyv2=decompose( Ca2 ); // Then add contributions for (uint m1=0; m1< cb.size(); m1++){ // Diagonal terms - Poly_res=Poly_res+Polyv1.at(m1) *Polyv2.at(m1) *diagonal_P_spm.at(m1); + Poly_res+=Polyv1.at(m1) *Polyv2.at(m1) *diagonal_P_spm.at(m1); } return Poly_res; } cnum Orthogonal_basis::scalar_product_num( const Col_amp & Ca1, const Col_amp & Ca2 ) { // Check that we have a basis if(cb.size()==0){ std::cerr << "Orthogonal_basis::scalar_product_num: The basis vector cb is empty consider using create_basis or read_in_basis." << std::endl; assert( 0 ); } // Check that size of d_spm is consistent (if calculated) if( (d_spm.size() != cb.size()) and d_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product_num: Size of scalar product matrix d_spm and color basis cb do not agree." << std::endl; assert( 0 ); } // Check that size of diagonal_d_spm is consistent (if calculated) if( (diagonal_d_spm.size() != cb.size()) and diagonal_d_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product_num: Size of diagonal_d_spm and color basis cb do not agree." << std::endl; assert( 0 ); } // Check if at least one of d_spm and diagonal_d_spm exist, // If non exist, calculate diagonal version if( d_spm.empty() and diagonal_d_spm.empty() ){ diagonal_scalar_product_matrix(false, true, true); } // If matrix form, but not diagonal exist, copy diagonal entries else if(diagonal_d_spm.empty() ){ for( uint i=0; i< d_spm.size(); i++ ){ diagonal_d_spm.push_back(d_spm.at(i).at(i)); } } // To contain the result double res=0; // Decompose the Col_amps Poly_vec Polyv1=decompose(Ca1); Polyv1.conjugate(); Poly_vec Polyv2=decompose( Ca2 ); dvec v1=Col_fun.double_num( Polyv1 ); dvec v2=Col_fun.double_num( Polyv2 ); // Then add contributions for (uint m1=0; m1< cb.size(); m1++){ // Diagonal terms res=res+v1.at(m1) *v2.at(m1) *diagonal_d_spm.at(m1); } return res; } cnum Orthogonal_basis::scalar_product_num( const cvec & v1, const cvec & v2) { if(v1.size()!= v2.size()){ std::cerr << "Orthogonal_basis::scalar_product_num: Size of first vector " << v1.size() << " does not agree with size of second vector " << v2.size() << std::endl; assert( 0 ); } // Check that size of d_spm is consistent (if calculated) if( ( d_spm.size() != cb.size()) and d_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product_num: Size of scalar product matrix d_spm and color basis cb do not agree." << std::endl; assert( 0 ); } // Check that size of diagonal_d_spm is consistent (if calculated) if( ( diagonal_d_spm.size() != cb.size()) and diagonal_d_spm.size() !=0 ) { std::cerr << "Orthogonal_basis::scalar_product_num: Size of diagonal_d_spm and color basis cb do not agree." << std::endl; assert( 0 ); } // Check if at least one of d_spm and diagonal_d_spm exist, // If non exist, calculate diagonal version if( d_spm.empty() and diagonal_d_spm.empty() ){ diagonal_scalar_product_matrix(false, true, true); } // If matrix form, but not diagonal exist, copy diagonal entries else if(diagonal_d_spm.empty() ){ for( uint i=0; i< d_spm.size(); i++ ){ diagonal_d_spm.push_back(d_spm.at(i).at(i)); } } uint basis_size=v1.size(); // To contain the result cnum res=0; // Add contributions, diagonal parts only for (uint m1=0; m1< basis_size; m1++){ res += conj( v1.at(m1) ) *v2.at(m1) *diagonal_d_spm.at(m1); } return res; } void Orthogonal_basis::write_out_diagonal_spm( const dvec & dv, const bool leading ) const{ std::string filename = diagonal_spm_file_name( leading, false ); std::ofstream outfile(filename.c_str()); if ( !outfile ) std::cerr << "Orthogonal_basis::write_out_diagonal_spm: Cannot write out diagonal scalar products as the file \"" << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; outfile << dv; } void Orthogonal_basis::write_out_diagonal_spm( const Poly_vec & Pv, const bool leading ) const{ std::string filename = diagonal_spm_file_name( leading, true ); std::ofstream outfile(filename.c_str()); outfile << Pv; } } //end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Orthogonal_basis.h b/MatrixElement/Matchbox/ColorFull/Orthogonal_basis.h --- a/MatrixElement/Matchbox/ColorFull/Orthogonal_basis.h +++ b/MatrixElement/Matchbox/ColorFull/Orthogonal_basis.h @@ -1,127 +1,128 @@ /* * Orthogonal_basis.h * Contains the declarations of the class Orthogonal_basis, related types and operators. * Created on: May 25, 2013 * Author: Malin Sjodahl */ #ifndef COLORFULL_Orthogonal_basis_h #define COLORFULL_Orthogonal_basis_h #include "Col_basis.h" namespace ColorFull { /// This class is for containing orthogonal bases, /// i.e. bases where the scalar product matrix is diagonal. /// ColorFull has (currently) no functionality for creating /// orthogonal bases, but orthogonal bases /// can be read in using read_in _Col_basis. class Orthogonal_basis:public Col_basis { + public: - - /// Default constructor. + /// Default constructor, puts private variable orthogonal_basis=true + /// and calls the constructor of Col_basis. Orthogonal_basis():Col_basis(){ orthogonal_basis = true; } /// To contain information about scalar products as a dvec, - /// entry i is the square of vector i. + /// i.e., entry i is the square of vector i. dvec diagonal_d_spm; /// To contain information about scalar products as a Poly_vec, /// i.e., entry i is the square of vector i. Poly_vec diagonal_P_spm; /******************** Functions for scalar products **********************/ /// Calculates the scalar product matrix assuming the basis to be orthogonal. /// Calculates both the double (d_spm) and the Polynomial (P_spm) matrices and /// saves to default file names. void scalar_product_matrix(); /// Calculates the diagonal entries in the scalar product matrix, /// and (depending on arguments), saves them to the member variables /// diagonal_P_spm and diagonal_d_spm. /// This function is used by the Orthogonal_basis version of /// scalar_product_matrix. void diagonal_scalar_product_matrix( bool save_P_diagonal_spm, bool save_d_diagonal_spm, bool use_mem ); /// The decomposition of a Col_amp in an orthogonal basis is done /// by calculating scalar products and dividing out the norm. /// The norm is evaluated numerically. Poly_vec decompose( const Col_amp & Ca ); /// Function for calculating scalar products /// given the information about the basis and the scalar product matrix in the basis. /// The Col_amps are first decomposed using decompose, /// and then squared using the scalar product matrix P_spm. /// An orthogonal scalar product matrix is assumed. Polynomial scalar_product( const Col_amp & Ca1, const Col_amp & Ca2 ); /// Function for calculating scalar products /// given the information about the basis /// and the scalar product matrix in numerical form. /// The Col_amps are first decomposed using decompose, /// and then squared using diagonal_d_spm. /// For Orthogonal_basis, an orthogonal scalar product matrix is assumed. cnum scalar_product_num( const Col_amp & Ca1, const Col_amp & Ca2 ); /// Calculates the scalar product between decomposed amplitudes v1, V2 /// using the diagonal_d_spm diagonal numerical scalar product matrix. /// The vectors needs to be expressed in the basis contained in cb, /// i.e., the decomposition has to be known. cnum scalar_product_num( const cvec & v1, const cvec & v2 ); /******************** Functions for reading and writing **********************/ /// Creates a default filename for writing out diagonal scalar products. /// The boolean variable leading should be true if the name is for a leading /// Nc variable. The filename is then modified accordingly. std::string diagonal_spm_file_name( const bool leading, const bool poly ) const; - /// Writes out diagonal_d_spm to file filename. + /// Writes out diagonal_d_spm to the file filename. void write_out_diagonal_d_spm( std::string filename ) const; /// Writes out diagonal_d_spm to the standard filename, see diagonal_spm_file_name. void write_out_diagonal_d_spm( ) const; - /// Writes out diagonal_P_spm to file filename. + /// Writes out diagonal_P_spm to the file filename. void write_out_diagonal_P_spm( std::string filename ) const; /// Writes out diagonal_P_spm to the standard filename, see diagonal_spm_file_name. void write_out_diagonal_P_spm( ) const; /// Function for writing out a dvec (the diagonal scalar products, diagonal_d_spm) /// to a file with standard filename given by diagonal_spm_file_name. /// The boolean variable leading should be true if dv only has leading /// Nc contributions. The filename is then modified accordingly. void write_out_diagonal_spm( const dvec & dv, const bool leading ) const; /// Function for writing out a Poly_vec (the diagonal scalar products, diagonal_d_spm) /// to a file with standard filename given by diagonal_spm_file_name. /// The boolean variable leading should be true if Poly_vec only has leading /// Nc contributions. The filename is then modified accordingly. void write_out_diagonal_spm( const Poly_vec & pv, const bool leading ) const; private: /// Function for calculating the scalar products matrix. /// This function uses diagonal_scalar_product_matrix /// and saved the value of the diagonal scalar products between basis vector /// i and basis vector j in the i,j -entry in /// P_spm (if save P_spm is true) and d_spm (if save_d_spm is true). /// If use_mem is true, memoization is used. void scalar_product_matrix( bool save_P_spm, bool save_d_spm, bool use_mem ); };// end class Orthogonal_basis }// end namespace ColorFull #endif /* COLORFULL_Orthogonal_basis_h */ diff --git a/MatrixElement/Matchbox/ColorFull/Poly_matr.cc b/MatrixElement/Matchbox/ColorFull/Poly_matr.cc --- a/MatrixElement/Matchbox/ColorFull/Poly_matr.cc +++ b/MatrixElement/Matchbox/ColorFull/Poly_matr.cc @@ -1,173 +1,192 @@ // -*- C++ -*- /* * Poly_matr.cc * Contains the definition of the class Poly_matr and related operators. * Created on: Aug 5, 2013 * Author: Malin Sjodahl */ #include "Poly_matr.h" #include #include #include namespace ColorFull{ std::ostream& operator<<(std::ostream& out, const poly_matr & pm){ out <<"{" <(fin)), std::istreambuf_iterator()); // Skip lines starting with # while(str.at(0)== '#'){ while (str.at(0) != '\n'){ str.erase(str.begin()); } // erase endl sign(s) while(str.at(0)== '\n'){ str.erase(str.begin()); } } // First char in file should be '{' if (str.at(0) != '{') { std::cerr << "Poly_matr::read_in_Poly_matr: First char in matrix data after comments file should be '{', it was: " << str.at(0) << std::endl; assert( 0 ); } // Row to contain numbers Poly_vec row; // Read the string, starting from 0th element uint i = 0; while (i < str.size() - 2) { // To contain the Polynomial string std::string Poly_str; Poly_str.clear(); // We may have to skip some spaces, end-lines and { while (i < str.size() - 2 && (str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == '{')) i++; // Read next Polynomial, until , or { // Keep reading the number while not ',' or '}' while (i < str.size() - 2 && (str.at(i) != ',' && str.at(i) != '}')) { Poly_str.push_back(str.at(i)); i++; } Polynomial Poly( Poly_str ); - row.push_back( Poly ); + row.append( Poly ); // If we have a new row if (i < str.size() - 2 && str.at(i) == '}') { // Save row in matrix, and empty row pm.push_back(row); row.clear(); // We may have to skip some chars while (i< str.size()-2 &&(str.at(i) == ',' or str.at(i) == '}' or str.at(i) == ' ' or str.at(i) == '\n' ) ) i++; } i++; } } void Poly_matr::write_out_Poly_matr( std::string filename ) const { std::ofstream outfile( filename.c_str() ); if ( !outfile ) std::cerr << "Poly_matr::write_out_Poly_matr: Cannot write out Polynomial matrix as the file \"" << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; outfile << pm; } }// end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Poly_matr.h b/MatrixElement/Matchbox/ColorFull/Poly_matr.h --- a/MatrixElement/Matchbox/ColorFull/Poly_matr.h +++ b/MatrixElement/Matchbox/ColorFull/Poly_matr.h @@ -1,102 +1,106 @@ // -*- C++ -*- /* * Poly_matr.h * Contains the declaration of the class Poly_matr and related types and operators. * Created on: Aug 5, 2013 * Author: Malin Sjodahl */ #ifndef COLORFULL_Poly_matr_h #define COLORFULL_Poly_matr_h #include "Poly_vec.h" namespace ColorFull { /// To contain a matrix of Polynomials, a vector of Poly_vec. typedef std::vector< Poly_vec > poly_matr; /// Class for containing a Polynomial matrix, and functions /// for Polynomial matrices. class Poly_matr { public: - /// Default constructor, sets nothing. + /// Default constructor, leaves pm empty. Poly_matr() {} - /// To actually contain the polynomial information. + /// To actually contain the matrix of Polynomials. poly_matr pm; /// Returns the Poly_vec at place i. const Poly_vec& at( int i ) const {return pm.at(i);} /// Returns the Poly_vec at place i. Poly_vec& at( int i ) {return pm.at(i);} /// Returns the matrix element at i, j. Polynomial& at( int i, int j ) { return pm.at(i).pv.at(j);} /// Returns the matrix element at i, j. const Polynomial& at( int i, int j ) const { return pm.at(i).pv.at(j);} /// Is the matrix, stored in pm, empty? bool empty( ) const {return pm.empty();} /// Returns the size of the matrix, the number of Poly_vec's /// in the member pm. uint size( ) const {return pm.size();} /// Erases the matrix information. void clear() {pm.clear();} - /// Appends a Poly_vec constructed from a poly_vec to the data member pm. - void push_back( poly_vec pv ) {pm.push_back( Poly_vec(pv) );} - /// Appends a Poly_vec to data member pm. - void push_back( Poly_vec Pv ) {pm.push_back( Pv );} + void append( Poly_vec Pv ) {pm.push_back( Pv );} /// Remove CF in the poly_matr member pm, i.e., replace CF by /// TR (Nc^2-1)/Nc. void remove_CF(); - /// Normal orders all polynomials in the poly_matr member pm. - /// (Uses Polynomial.normal_order.) + /// Normal orders all polynomials in the poly_matr member pm, + /// (uses Polynomial.normal_order.) void normal_order(); - /// Simplifies all polynomials in the poly_matr member pm. - /// (Uses Polynomial.simplify.) + /// Simplifies all polynomials in the poly_matr member pm, + /// (uses Polynomial.simplify.) void simplify(); /// Conjugates the matrix. void conjugate(); /// Reads in the matrix from the file filename. - /// The file should be in the format + /// The file should be of the format /// {{Poly11,...,Poly1n}, /// ..., /// {Polyn1,...,Polynn}}, /// and may contain comment lines starting with # at the top. void read_in_Poly_matr( std::string filename ); /// Writes out the matrix to the file filename. void write_out_Poly_matr( std::string filename ) const; }; /// Operator << for poly_matr. std::ostream& operator<<( std::ostream& out, const poly_matr & pm ); /// Operator << for poly_vec. std::ostream& operator<<( std::ostream& out, const Poly_matr & Pm ); +/// Define the operator == for Poly_matr, +/// each Poly_vec has to be identical. +bool operator==( const Poly_matr & Pm1, const Poly_matr & Pm2 ); + +/// Define the operator == for Poly_matr, +/// each Poly_vec has to be identical. +bool operator!=( const Poly_matr & Pm1, const Poly_matr & Pm2 ); } #endif /* COLORFULL_Poly_matr_h */ diff --git a/MatrixElement/Matchbox/ColorFull/Poly_vec.cc b/MatrixElement/Matchbox/ColorFull/Poly_vec.cc --- a/MatrixElement/Matchbox/ColorFull/Poly_vec.cc +++ b/MatrixElement/Matchbox/ColorFull/Poly_vec.cc @@ -1,157 +1,174 @@ // -*- C++ -*- /* * Poly_vec.cc * Contains the definition of the class Poly_vec related operators. * Created on: Aug 5, 2013 * Author: Malin Sjodahl */ #include "Poly_vec.h" #include #include #include namespace ColorFull{ std::ostream& operator<<(std::ostream& out, const poly_vec & poly_v ){ out <<"{"; // Loop over entries for( uint i=0; i< poly_v.size(); i++ ){ out << poly_v.at(i); // If not last element print "," if (i(fin)), std::istreambuf_iterator()); // Skip lines starting with # while( str.at(0)== '#' ){ while (str.at(0) != '\n'){ str.erase(str.begin()); } // erase endl sign(s) while(str.at(0)== '\n'){ str.erase(str.begin()); } } // First char in file should be '{' if ( str.at(0) != '{' ) { std::cerr << "Poly_vec::read_in_Poly_vec: First char in vector data after comments should be '{', it was: " << str.at(0) << std::endl; assert(0); } // Row to contain numbers Poly_vec row; // Read the string, starting from 0th element unsigned int i = 0; - while ( i < str.size() - 2 ) { + while ( i < str.size() - 1 ) { // To contain the Polynomial string std::string Poly_str; Poly_str.clear(); // We may have to skip some spaces, end-lines and { - while (i < str.size() - 2 && (str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == '{')) + while (i < str.size() - 1 && (str.at(i) == ' ' or str.at(i) == '\n' or str.at(i) == '{')) i++; // Keep reading the number while not ',' or '}' - while (i < str.size() - 2 && (str.at(i) != ',' && str.at(i) != '}')) { + // Last char }, is at place size -1 + while (i < str.size() - 1 && (str.at(i) != ',' && str.at(i) != '}')) { Poly_str.push_back(str.at(i)); i++; } Polynomial Poly( Poly_str ); pv.push_back( Poly ); // If we have a new row - if (i < str.size() - 2 && str.at(i) == '}') { i++;} + if (i < str.size() - 1 && str.at(i) == '}') { i++;} i++; } } void Poly_vec::write_out_Poly_vec( std::string filename ) const { std::ofstream outfile( filename.c_str() ); if ( !outfile ) std::cerr << "Poly_vec::write_out_Poly_vec: Cannot write out vector of Polynomials as the file \"" << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; outfile << pv; } } // end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Poly_vec.h b/MatrixElement/Matchbox/ColorFull/Poly_vec.h --- a/MatrixElement/Matchbox/ColorFull/Poly_vec.h +++ b/MatrixElement/Matchbox/ColorFull/Poly_vec.h @@ -1,94 +1,100 @@ // -*- C++ -*- /* * Poly_vec.h * Contains the declaration of the class Poly_vec related types and operators. * Created on: Aug 5, 2013 * Author: Malin Sjodahl */ #ifndef COLORFULL_Poly_vec_h #define COLORFULL_Poly_vec_h #include "Polynomial.h" namespace ColorFull { /// To contain a vector of Polynomials. typedef std::vector< Polynomial> poly_vec; /// Class for containing vector of Polynomials, and functions /// for Polynomial vectors. class Poly_vec { public: - /// Default constructor, sets nothing. + /// Default constructor, leaves pv empty. Poly_vec() {} - /// Make a Poly_vec of a poly_vec. + /// Makes a Poly_vec of a poly_vec. Poly_vec( poly_vec poly_v ){pv=poly_v;} /// To actually contain the polynomial information. poly_vec pv; - /// Returning Polynomial at place i. + /// Returns the Polynomial at place i. const Polynomial& at( int i ) const { return pv.at(i); } - /// Returning Polynomial at place i. + /// Returns the Polynomial at place i. Polynomial& at( int i ) { return pv.at(i); } /// Return the number of Polynomials in the vector, /// i.e., the size of the member pv. uint size() const { return pv.size(); } - /// Erase information in vector. + /// Erases the information in vector. void clear() { pv.clear(); } /// Appends a Polynomial to data member pv. - void push_back( Polynomial Poly ) { pv.push_back( Poly ); } + void append( Polynomial Poly ) { pv.push_back( Poly ); } /// Is the vector empty? bool empty() const { return pv.empty(); } /// Remove CF in the poly_vec member pv, i.e., replace CF by - /// TR (Nc^2-1)/(2 Nc). + /// TR*Nc -TR/Nc. void remove_CF(); - /// Normal order all Polynomials in the poly_vec member pv. - /// (Uses the Polynomial.normal_order function.) + /// Normal order all Polynomials in the poly_vec member pv + /// (uses the Polynomial.normal_order function.) void normal_order(); - /// Simplifies all polynomials in the poly_vec member pv. - /// (Uses the simplify member function in Polynomial). + /// Simplifies all polynomials in the poly_vec member pv + /// (uses the simplify member function in Polynomial). void simplify(); /// Conjugates the Poly_vec. void conjugate(); /// Reads in a Polynomial vector of form {Poly1, Poly2,...} /// to the member pv from the file filename. /// Comments starting with # are allowed /// at the top of the file. void read_in_Poly_vec( std::string filename ); /// Writes out the vector to the file filename. void write_out_Poly_vec( std::string filename ) const; - }; /// Operator << for poly_vec. std::ostream& operator<<( std::ostream& out, const poly_vec & poly_v ); /// Operator << for Poly_vec. std::ostream& operator<<( std::ostream& out, const Poly_vec & Pv ); +/// Define the operator == for Poly_vec, +/// each Polynomial has to be identical. +bool operator==( const Poly_vec & Pv1, const Poly_vec & Pv2 ); + +/// Define the operator != for Poly_vec, +/// each Polynomial has to be identical. +bool operator!=( const Poly_vec & Pv1, const Poly_vec & Pv2 ); } #endif /* COLORFULL_Poly_vec_h */ diff --git a/MatrixElement/Matchbox/ColorFull/Polynomial.cc b/MatrixElement/Matchbox/ColorFull/Polynomial.cc --- a/MatrixElement/Matchbox/ColorFull/Polynomial.cc +++ b/MatrixElement/Matchbox/ColorFull/Polynomial.cc @@ -1,781 +1,878 @@ // -*- C++ -*- /* Polynomial.cc * Contains definition of the class Polynomial and associated types and operators * Created on: Jul 7, 2010 * Author: Malin Sjodahl */ #include "Polynomial.h" #include "parameters.h" #include #include #include #include namespace ColorFull { Polynomial::Polynomial( double dnum ) { Monomial Mon; Mon.cnum_part.real(dnum); poly.push_back(Mon); } Polynomial::Polynomial( int num ) { Monomial Mon; Mon.int_part= num ; poly.push_back(Mon); } Polynomial::Polynomial( const std::string str ) { Polynomial_of_str( str ); } void Polynomial::Polynomial_of_str( const std::string str ) { // Special case of empty string if( str.empty() ) { polynomial empty; poly=empty; return; } uint i = 0, j=0; std::string Mon_str; int left_brackets=0, right_brackets=0; // Check that left and right brackets match up while (j < str.size()) { if(str.at(j)=='(') left_brackets++; if(str.at(j)==')') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Polynomial::Polynomial_of_str: The brackets in the polynomial\"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } // Skip potential spaces while (i < str.size() && (str.at(i) == ' ')) i++; // Skip initial left bracket(s) surrounding polynomial // there are left brackets-right brackets such brackets, // we many have ((TR+Nc)) int found_left_brackets=0; // if we have a surplus of left_brackets before we have a + or - // then (with some exceptions) it's an overall bracket int right_before_plus=0, left_before_plus=0; j=0; bool new_term=false; while ( j0 ){ // if (- continue, (this is not a new term) if(str.at(j-1)=='('){} // if ^- continue, (this is not a new term) else if(str.at(j-1)=='^'){} // if *- continue, (this is not a new term) else if(str.at(j-1)=='*'){} else new_term=true; } else new_term=true; } // a plus sign could mean that it's a new term if(str.at(j)=='+'){ // we can allow - inside powers, i.e. after ^(, ^(- or ^- if( j>0 ){ // if (+ continue, (this is not a new term) if(str.at(j-1)=='('){} // if ^+ continue, (this is not a new term) else if(str.at(j-1)=='^'){} // if *+ continue, (this is not a new term) else if(str.at(j-1)=='*'){} else new_term=true; } else new_term=true; } if(str.at(j)== '(') left_before_plus++; if(str.at(j)== ')') right_before_plus++; j++; } int extra_left_brackets = left_before_plus-right_before_plus; while (i < str.size() and ( str.at(i)=='(' or str.at(i)==' ') and found_left_brackets < extra_left_brackets ) { if(str.at(i)== '(') found_left_brackets++; i++; } int Moncount=0; // Look for Monomials until the end of the string while (i < str.size()) { Mon_str.clear(); Moncount++; // Check for one + or - for each Monomial while (i < str.size() && (str.at(i) == '+' or str.at(i) == '-' )){ Mon_str.push_back(str.at(i)); i++; } // Look for more factors in same Monomial while (i < str.size()) { // There should be no surplus of ) in the Monomial left_brackets=right_brackets=0; // Check for Monomials // Everything but + and - should go to Monomail // Read until another + or - while making sure we don't get final )'s if ( i < str.size() ){ if( str.at(i) != '+' and str.at(i) != '-' ){ Mon_str.push_back(str.at(i)); i++; } } // we can allow - after (, "(-"for example inside powers if ( i < str.size() and str.at(i) == '-'){ // removed i-1>= 0 as always true, no check that read inside if(( str.at(i-1)=='(')){ Mon_str.push_back(str.at(i)); i++; } } // We can also allow for - after*, for example 0.5*-1 if (i < str.size() and str.at(i) == '-' and ( (i>= 2) and str.at(i-1)=='*')) { // read in sign Mon_str.push_back(str.at(i)); i++; // skip sign // Keep reading in while numbers while (i< str.size() and (str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9')){ Mon_str.push_back(str.at(i)); i++; } } + // We can also allow for - after^, for example TR^-1 + // added 14 11 26 + if (i < str.size() and str.at(i) == '-' and ( (i>= 1) and str.at(i-1)=='^')) { + // read in sign + Mon_str.push_back(str.at(i)); + i++; // skip sign + // Keep reading in while numbers + while (i< str.size() and (str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' + or str.at(i) == '3' or str.at(i) == '4' + or str.at(i) == '5' or str.at(i) == '6' + or str.at(i) == '7' or str.at(i) == '8' + or str.at(i) == '9')){ + Mon_str.push_back(str.at(i)); + i++; + } + } + // We can also allow for - after ( as in (-1) if (i < str.size() and str.at(i) == '-' and (i>= 3) and str.at(i-1)=='(') { // read in sign Mon_str.push_back(str.at(i)); i++; // skip sign // Keep reading in while numbers while (i< str.size() and (str.at(i) == '0' or str.at(i) == '1' or str.at(i) == '2' or str.at(i) == '3' or str.at(i) == '4' or str.at(i) == '5' or str.at(i) == '6' or str.at(i) == '7' or str.at(i) == '8' or str.at(i) == '9')){ Mon_str.push_back(str.at(i)); i++; } // Skip spaces while ( i< str.size() and str.at(i) == ' ') i++; // Look for closing ) if(str.at(i)==')') { // read in ) Mon_str.push_back(str.at(i)); i++;} else { std::cerr << "Polynomial::Polynomial_of_str: Expects final ) in *(number)" << " got " << str.at(i) << std::endl; assert( 0 ); } }// Stop looking for (- // Break if we have + or - and not a special case if ( i == str.size() ) break; if ( str.at(i)== '+' ) break; if (str.at(i) == '-' and !( (i < str.size() and str.at(i) == '-' and ( ( i-3 > 0 and str.at(i-1)=='(' and str.at(i-2)=='^') or ( i-2> 0 and str.at(i-1)=='^' ) )) // We can also allow for - after*, for example 0.5*-1 or (i < str.size() and str.at(i) == '-' and ( (i-2> 0) and str.at(i-1)=='*')) // We can also allow for - after 0.5*(-1) or (i < str.size() and str.at(i) == '-' and (i-3> 0) and str.at(i-1)=='(' and str.at(i-2)=='*')) ) break; } // end of while (i < str.size()), i.e. now keep looking for factors in same Monomial // Then make a Monomial out of the str // Check that left and right brackets match up left_brackets=right_brackets=0; for ( uint j=0; j< Mon_str.size(); j++ ){ if(Mon_str.at(j)=='(') left_brackets++; if(Mon_str.at(j)==')') right_brackets++; } int brack_diff=right_brackets-left_brackets; // Remove (-brackets in the end until they match ), i.e. brack_diff==0. uint j=1; while( ( Mon_str.at( Mon_str.size()-j )==')' or Mon_str.at( Mon_str.size()-j )==' ' or Mon_str.at( Mon_str.size()-j )=='*') and brack_diff > 0 ){ if( Mon_str.at( Mon_str.size()-j )==')' ) { brack_diff--; Mon_str.at( Mon_str.size()-j )= ' '; } j++; } poly.push_back(Monomial(Mon_str)); } // end of while (i < str.size()) } void Polynomial::read_in_Polynomial( std::string filename) { // Read in file std::ifstream fin(filename.c_str()); // Check that file exists if( !fin ){ std::cerr << "Polynomial::read_in_Polynomial: The file " << filename << " could not be opened." << std::endl; assert( 0 ); } // Erase current information poly.clear(); // Copy info from file to string std::string str((std::istreambuf_iterator(fin)), std::istreambuf_iterator()); //str.erase(str.size()-1); + // Remove endl chars at the end of the file + while( str.at( str.size()-1 ) == '\n' ) str.erase(str.size()-1); + + // Skip lines starting with # + while( str.at(0)== '#' ){ + while (str.at(0) != '\n'){ + str.erase(str.begin()); + } + // erase endl sign(s) + while(str.at(0)== '\n'){ + str.erase(str.begin()); + } + } + Polynomial_of_str( str ); } void Polynomial::write_out_Polynomial( std::string filename ) const { std::ofstream outfile(filename.c_str()); if ( !outfile ) - std::cerr << "Polynomial::write_out_Polynomial: Cannot write out diagonal scalar products as the file \"" + std::cerr << "Polynomial::write_out_Polynomial: Cannot write out Polynomial as the file \"" << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; outfile << *this; } void Polynomial::conjugate(){ // Loop over Monomials and complex conjugate them for(uint i=0; i< poly.size(); i++) poly.at(i).conjugate(); } void Polynomial::simplify() { // If empty Polynomial or only one Monomial, do nothing if (poly.size() <= 1) return; // Collect different Monomials, keep 0th Monomial polynomial terms; terms.push_back(poly.at(0)); poly.erase(poly.begin()); // Move terms from Polynomial to unique set of terms // as long as terms remain in Polynomial while (poly.size() > 0) { bool was_found = false; // Check if term already there for (uint i = 0; (i < terms.size() && !was_found); i++) { // If same powers of TR, Nc and CF if (poly.at(0).pow_TR == terms.at(i).pow_TR && poly.at(0).pow_Nc == terms.at(i).pow_Nc && poly.at(0).pow_CF == terms.at(i).pow_CF) { was_found = true; // Change the int_part (if no numerical factor) // or the num factor of term to be added // If already stored term, or part to be added, has a numeric part, // store info in numeric part if ( abs(poly.at(0).cnum_part-1.0) > accuracy or abs( terms.at(i).cnum_part -1.0 ) > accuracy) { // Float to add to num, put numerical factor, and int_part, // and sign here cnum c_to_add; c_to_add = poly.at(0).cnum_part; c_to_add *= poly.at(0).int_part; // Move int-part of existing to numeric part terms.at(i).cnum_part *= terms.at(i).int_part; terms.at(i).int_part = 1; // Add cnum from the term terms.at(i).cnum_part += c_to_add; } else { //if no numeric part change the int-part instead terms.at(i).int_part += poly.at(0).int_part; } } // If, after this the i:th term is 0, remove it, // to avoid carrying around 0's if( ( terms.at(i).int_part==0 or terms.at(i).cnum_part==0.0 ) and terms.size()>1 ){ terms.erase(terms.begin()+i); } } // If term not already there, add term if ( !was_found and poly.at(0).int_part!=0 ) terms.push_back(poly.at(0)); // Erase the term in poly poly.erase(poly.begin()); } for (uint i = 0; i < terms.size() ; i++) { // If term not already there, add term if ((terms.at(i).int_part == 0 or terms.at(i).cnum_part == 0.0) and terms.size() > 1) { terms.erase(terms.begin() + i); } } poly = terms; } void Polynomial::remove_CF() { // If empty Polynomial, do nothing if (poly.size() == 0) return; else{//non-empty polynomial polynomial poly_res; // to contain the result // Loop over terms in Polynomial and replace CF for (uint i = 0; i < poly.size(); i++) { // If the term contains no CF, simply keep in terms if( poly.at(i).pow_CF == 0 ) poly_res.push_back( poly.at(i) ); // replace CF if raced to positive power // (unless the int part is 0, in which case nothing should be added) else if( poly.at(i).pow_CF > 0 and poly.at(i).int_part != 0){ // Factors given by binomial theorem // one term for each power (Nc)^power_Nc (-1/Nc)^(pow_CF-power_Nc) // i.e. TR ( (Nc)^power_Nc (-1/Nc)^(pow_CF-power_Nc) ) // =TR Nc^(power_Nc-(pow_CF-power_Nc))==TR Nc^(2*power_Nc-pow_CF) Monomial const_fact=poly.at(i); // To contain the factor that was multiplying CF const_fact.pow_CF=0; // replace CF const_fact.pow_TR+=poly.at(i).pow_CF; // we always get a factor TR for ( int power_Nc = 0; power_Nc <= poly.at(i).pow_CF; power_Nc++) { Monomial term; // one term in (a+b)^n // The corresponding binomial coefficient int bin_coef=factorial( poly.at(i).pow_CF) /factorial( poly.at(i).pow_CF - power_Nc )/factorial( power_Nc ); term.pow_Nc=2*power_Nc-poly.at(i).pow_CF; term=bin_coef*term*int(pow( -1.0, int( poly.at(i).pow_CF-power_Nc) ));// fix sign // if the term is not 0, add it if( term.int_part!=0 && const_fact.int_part!=0 ) poly_res.push_back( term*const_fact ); }// end looping over binomial factors }// end if pow_CF>0 else if ( poly.at(i).pow_CF < 0 ) { std::cerr << "Polynomial::remove_CF(): Warning: cannot replace negative powers of CF. Leaving Monomial term << " << poly.at(i) << " as it is." << std::endl; poly_res.push_back( poly.at(i) ); } }// end looping over Monomials in poly // If, after this, poly_res is still empty, this is because nothing was added // and the polynomial should be 0 (can for example happen for 0*CF) if ( poly_res.empty() ) poly_res.push_back(Monomial("0")); poly=poly_res; } } void Polynomial::normal_order() { // To contain the Polynomial in order polynomial poly_ordered; // Order the different Monomials in the Polynomial. // Do this by moving the Monomials one by one to poly_ordered while (poly.size() > 0) { // Next monomial to put in place (the last Monomial in poly) Monomial Mon_next = poly.at( poly.size() - 1 ); // Then insert the Mon_next among the ordered Monomials // Count how many steps left Mon_next should be moved in poly_ordered uint steps_left = 0; while ( (steps_left < (poly_ordered.size())) && (!(Mon_next < poly_ordered.at(poly_ordered.size()-1-steps_left ))) ==1) { steps_left++; } // Insert the Monomial in the right place among the ordered Col_strs polynomial::iterator it=poly_ordered.end()-steps_left; poly_ordered.insert( it, Mon_next); // Erase the Monomial from poly poly.erase( poly.end()-1 ) ; } poly=poly_ordered; } int Polynomial::factorial( int i ) const{ if(i<0) { std::cerr << "Polynomial::factorial: intended for int >=0, was " << i << std::endl; std::cerr.flush(); assert( 0 ); } if (i==0) return 1; return factorial(i-1)*i; // Recursive call } std::ostream& operator<<( std::ostream& out, const Polynomial & Poly ) { if (Poly.size() == 0) out << "1"; else if (Poly.size() == 1) out << Poly.at(0); else { if (Poly.size() > 1) out << "("; for (int i = 0; i < Poly.size(); i++) { out << Poly.at(i); if (i != Poly.size() - 1) out << " + "; } if (Poly.size() > 1) out << ")"; } return out; } std::ostream& operator<<(std::ostream& out, const polynomial & poly) { if (poly.size() == 0) out << "1"; else if (poly.size() == 1) out << poly.at(0); else { if (poly.size() > 1) out << "("; for ( uint i = 0; i < poly.size(); i++ ) { out << poly.at(i); if (i != poly.size() - 1) out << " + "; } if (poly.size() > 1) out << ")"; } return out; } bool operator==( const Polynomial & Poly1, const Polynomial & Poly2){ if( Poly1.size() != Poly2.size() ) return false; // All terms should be the same in order for Polynomials to be the same for (int i=0; i < Poly1.size(); i++ ){ if( Poly1.at(i) != Poly2.at(i ) ) return false; } return true; } bool operator!=( const Polynomial & Poly1, const Polynomial & Poly2){ if( Poly1==Poly2) return false; else return true; } Polynomial operator+(const Polynomial & Poly, const Monomial & Mon){ Polynomial out_Poly(Poly); - // If original Polynomial was empty=1, push_back dummy Monomial=1 + // If original Polynomial was empty=1, append dummy Monomial=1 // (such that after the addition the Polynomial has indeed two terms) if(out_Poly.empty()){ Monomial dummy_Mon; - out_Poly.push_back(dummy_Mon); + out_Poly.append(dummy_Mon); } // Add term unless it's 0 - if(Mon.int_part != 0) out_Poly.push_back( Mon ); + if(Mon.int_part != 0) out_Poly.append( Mon ); return out_Poly; } Polynomial operator-( const Polynomial & Poly, const Monomial & Mon){ Monomial out_Mon(Mon); // Change sing of Monomial out_Mon.int_part*=(-1); // Add term unless it's 0 return Poly+out_Mon; } Polynomial operator-(const Monomial & Mon, const Polynomial & Poly){ Polynomial out_Poly(Poly); // If Poly is empty=1, add a default Monomial=1, which can change sign if (out_Poly.empty()) { Monomial dummy_Mon; - out_Poly.push_back(dummy_Mon); + out_Poly.append(dummy_Mon); } // Change sing of Polynomial, by looping over terms for (uint m=0; m < out_Poly.poly.size(); m++) out_Poly.poly.at(m).int_part*=(-1); // Add Polynomial and Monomial return out_Poly+Mon; } Polynomial operator+(const Monomial & Mon, const Polynomial & Poly ){ return Poly + Mon; } Polynomial operator+=( Polynomial & Poly, const Monomial & Mon ) { - // If original Poly was empty=1, push_back dummy Monomial=1 + // If original Poly was empty=1, append dummy Monomial=1 // to make the Poly 1. if ( Poly.empty() ) { Monomial dummy_Mon; - Poly.push_back( dummy_Mon ); + Poly.append( dummy_Mon ); } - if(Mon.int_part != 0) Poly.push_back( Mon ); + if(Mon.int_part != 0) Poly.append( Mon ); return Poly; } Polynomial operator+=( Polynomial & Poly1, const Polynomial & Poly2 ) { if (&Poly1 == &Poly2){ std::cerr << "operator+=: Polynomials need to have different address, both arguments " << Poly1 << "."<< std::endl; assert(0); } - // If original Poly2 was empty=1, push_back dummy Monomial=1 + // If original Poly2 was empty=1, append dummy Monomial=1 // add a dummy Monomial to Poly1 if ( Poly2.empty() ) { Monomial dummy_Mon; - Poly1.push_back( dummy_Mon ); + Poly1.append( dummy_Mon ); } // otherwise add Monomials from Poly2 to Poly 1 else for (int i = 0; i < Poly2.size(); i++) { - Poly1.push_back( Poly2.at(i) ); + Poly1.append( Poly2.at(i) ); } return Poly1; } Polynomial operator+( const Polynomial & Poly1, const Polynomial & Poly2) { Polynomial out_Poly1(Poly1); Polynomial out_Poly2(Poly2); - // If original Poly1 was empty=1, push_back dummy Monomial=1 + // If original Poly1 was empty=1, append dummy Monomial=1 // after adding empty Monomial the Polynomial is still 1 if (out_Poly1.empty()) { Monomial dummy_Mon; - out_Poly1.push_back(dummy_Mon); + out_Poly1.append(dummy_Mon); } - // If original Poly2 was empty=1, push_back dummy Monomial=1 + // If original Poly2 was empty=1, append dummy Monomial=1 // after adding empty Monomial the Polynomial is still 1 if (out_Poly2.empty()) { Monomial dummy_Mon; - out_Poly2.push_back(dummy_Mon); + out_Poly2.append(dummy_Mon); } // Add terms from Poly2 to Poly1 for (int i = 0; i < out_Poly2.size(); i++) { out_Poly1 = out_Poly1 + out_Poly2.at(i); } return out_Poly1; } Polynomial operator-( const Polynomial & Poly1, const Polynomial & Poly2 ) { Polynomial out_Poly2(Poly2); // If Poly2 is empty=1 make it contain a Monomial, // as a Monomial by construction is 1 if(out_Poly2.empty()) { Monomial dummy_Mon; - out_Poly2.push_back(dummy_Mon); + out_Poly2.append(dummy_Mon); } // Change sing of Poly2, by looping over terms for (uint m=0; m < out_Poly2.poly.size(); m++) out_Poly2.poly.at(m).int_part*=(-1); // Now the subtraction is equal to an addition // if Poly1 is empty this is taken care of in + operator return Poly1+out_Poly2; } Polynomial operator*( const Polynomial & Poly, const int in ){ Polynomial out_Poly(Poly); - // If initially empty Polynomial=1, append empty Monomial=1, to have something to multiply with + // If initially empty Polynomial=1, append default Monomial=1, to have something to multiply with Monomial dummy_Mon; if (out_Poly.empty()) - out_Poly.push_back(dummy_Mon); + out_Poly.append(dummy_Mon); // Loop over terms in Polynomial for (int i=0; i< out_Poly.size(); i++){ out_Poly.at(i).int_part=out_Poly.at(i).int_part*in; } return out_Poly; } Polynomial operator*( const int i, const Polynomial & Poly ) { return Poly * i; } Polynomial operator*( const Polynomial & Poly, const cnum c ) { Polynomial out_Poly(Poly); // If initially empty Polynomial=1, append empty Monomial=1, to have something to multiply with Monomial dummy_Mon; if (out_Poly.empty()) - out_Poly.push_back(dummy_Mon); + out_Poly.append(dummy_Mon); // Loop over terms in Polynomial for (int i = 0; i < out_Poly.size(); i++) { out_Poly.poly.at(i).cnum_part = out_Poly.at(i).cnum_part * c; } return out_Poly; } Polynomial operator*( const cnum c, const Polynomial & Poly ) { return Poly * c; } Polynomial operator*( const Polynomial & Poly, const double d ) { Polynomial out_Poly(Poly); // If initially empty Polynomial=1, append empty Monomial=1, to have something to multiply with Monomial dummy_Mon; if (out_Poly.empty()) - out_Poly.push_back(dummy_Mon); + out_Poly.append(dummy_Mon); // Loop over terms in Polynomial for (int i = 0; i < out_Poly.size(); i++) { out_Poly.poly.at(i).cnum_part = out_Poly.at(i).cnum_part * d; } return out_Poly; } Polynomial operator*(const double d, const Polynomial & Poly) { return Poly * d; } Polynomial operator*( const Polynomial & Poly, const Monomial & Mon ){ // To contain the result Polynomial Poly_res; // Special case that the Polynomial is empty=1 if( Poly.empty() ) { - Poly_res.push_back(Mon); + Poly_res.append(Mon); return Poly_res; } else{ for (int i1=0; i1< Poly.size(); i1++){ - Poly_res.push_back( Poly.at(i1)*Mon ); + Poly_res.append( Poly.at(i1)*Mon ); } } return Poly_res; } Polynomial operator*( const Monomial & Mon, const Polynomial & Poly ){ return Poly*Mon; } Polynomial operator*( const Polynomial & Poly1, const Polynomial & Poly2 ){ // To contain the result Polynomial Poly_res; // Special case that the vectors are empty=1, needed for special treatment of sum of 0-monomials // Poly_res is also empty, and thus =1, 1*1=1 if(Poly1.empty() && Poly2.empty()){ return Poly_res; } // If one Poly is empty, =1, return other if(!Poly1.empty() && Poly2.empty() ) return Poly1; else if( Poly1.empty() && !Poly2.empty()) return Poly2; // If both vectors contain info else { for (int i1=0; i1< Poly1.size(); i1++){ for (int i2=0; i2< Poly2.size(); i2++){ // Add terms if non of them is 0, else just don't add if( Poly1.at(i1).int_part!=0 && Poly2.at(i2).int_part!=0 ) - Poly_res.push_back(Poly1.at(i1)*Poly2.at(i2)); + Poly_res.append(Poly1.at(i1)*Poly2.at(i2)); } } // If, by now, the Poly_res is empty that's because all terms were 0 if( Poly_res.empty() ){ Monomial Mon_tmp; Mon_tmp.int_part=0; - Poly_res.push_back( Mon_tmp ); + Poly_res.append( Mon_tmp ); } } return Poly_res; } + +Polynomial operator*=( Polynomial & Poly, const int in ){ + + // If initially empty Polynomial=1, append default Monomial=1, to have something to multiply with + Monomial dummy_Mon; + if ( Poly.empty()) + Poly.append(dummy_Mon); + + // Loop over terms in Polynomial + for (int i=0; i< Poly.size(); i++){ + Poly.at(i).int_part*= in; + } + return Poly; +} + + +Polynomial operator*=( Polynomial & Poly, const double d ){ + + // If initially empty Polynomial=1, append default Monomial=1, to have something to multiply with + Monomial dummy_Mon; + if ( Poly.empty()) + Poly.append(dummy_Mon); + + // Loop over terms in Polynomial + for (int i=0; i< Poly.size(); i++){ + Poly.at(i).cnum_part*= d; + } + return Poly; +} + +Polynomial operator*=( Polynomial & Poly, const cnum c ){ + + // If initially empty Polynomial=1, append default Monomial=1, to have something to multiply with + Monomial dummy_Mon; + if ( Poly.empty()) + Poly.append(dummy_Mon); + + // Loop over terms in Polynomial + for (int i=0; i< Poly.size(); i++){ + Poly.at(i).cnum_part *= c; + } + return Poly; +} + + +Polynomial operator*=( Polynomial & Poly, const Monomial & Mon ){ + + // Special case that the Polynomial is empty=1 + if( Poly.empty() ) { + Poly.append(Mon); + return Poly; + } + else{ + for( int i1=0; i1< Poly.size(); i1++ ){ + Poly.at(i1)*=Mon; + } + } + return Poly; +} + +Polynomial operator*=( Polynomial & Poly1, const Polynomial & Poly2 ){ + + Poly1=Poly1*Poly2; + return Poly1; +} + }// end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Polynomial.h b/MatrixElement/Matchbox/ColorFull/Polynomial.h --- a/MatrixElement/Matchbox/ColorFull/Polynomial.h +++ b/MatrixElement/Matchbox/ColorFull/Polynomial.h @@ -1,205 +1,224 @@ // -*- C++ -*- /* * Polynomial.h * Contains declaration of the class Polynomial and associated types and operators. * Created on: Jul 7, 2010 * Author: Malin Sjodahl */ #ifndef COLORFULL_Polynomial_h #define COLORFULL_Polynomial_h #include "Monomial.h" namespace ColorFull { /// For containing the info (as opposed to the functions) of a Polynomial. /// The Polynomial is essentially a sum of Monomials, contained in a /// vector of Monomials. /// Note that a Monomial "is" TR^a Nc^b CF^c* int_part*cnum_part where int_part is an /// integer factor and cnum_part a complex numerical factor. /// An empty polynomial is defined as 1. typedef std::vector < Monomial > polynomial; /// For containing a Polynomial (in Nc, CF and TR), as a sum of Monomials. /// Note that a Monomial "is" TR^a*Nc^b*CF^c*int_part*cnum_part where int_part is an /// integer factor, cnum_part a complex numerical factor and a,b, and c integers /// (not necessarily positive). /// An empty Polynomial is defined as 1. class Polynomial { public: - /// Default constructor. + /// Default constructor, leaves polynomial empty=1. Polynomial(){}; /// Constructor allow setting the polynomial by using a string. /// Should be used as for example "Polynomial Poly("(-20*TR^(5))/Nc + 28*Nc*TR^(5) - 10*Nc^3*TR^(5)")". /// The Momomials should be separated by + or -, see also the /// Monomial string constructor. Polynomial( const std::string str ); - /// Constructor allow setting the Polynomial using a double, + /// Constructor allowing setting the Polynomial using a double. /// The Polynomial gets one Monomial where the real part of /// cnum_part gets the value of dnum. Polynomial( double dnum ); - /// Constructor allow setting the Polynomial using an int, + /// Constructor allowing setting the Polynomial using an int. /// The Polynomial gets one Monomial where int_part /// has the value num. Polynomial( int num ); /// Contains the polynomial, a sum of Monomials, as an std::vector /// of Monomials. /// An empty Polynomial is defined as 1, to get 0, multiply with 0. polynomial poly; /// Function for reading in the Polynomial from the file filename, /// uses Polynomial_of_str. void read_in_Polynomial( std::string filename ); - /// Function for writing out the Quark_linePolynomial to a file + /// Function for writing out the Polynomial to a file /// with name filename. void write_out_Polynomial( std::string filename ) const; - /// Returning Monomial at place i. + /// Returns Monomial at place i. const Monomial& at( int i ) const {return poly.at(i);} - /// Returning Monomial at place i. + /// Returns Monomial at place i. Monomial& at( int i ) {return poly.at(i);} - /// Returning number of terms in Polynomial. + /// Returns the number of terms in the Polynomial. int size() const {return poly.size();} - /// Adding Monomial term. - void push_back( const Monomial Mon ) {poly.push_back(Mon);} + /// Adding a Monomial term. + void append( const Monomial Mon ) {poly.push_back(Mon);} - /// Erase the Monomial at place i. + /// Erases the Monomial at place i. void erase( int i ) {poly.erase(poly.begin() + i);} /// Is the polynomial empty? bool empty() const {return poly.empty();} - /// Erase info in polynomial. + /// Erases info in polynomial. void clear() {poly.clear();} /// Take complex conjugate of the polynomial. /// Note that this changes the Polynomial itself. void conjugate(); /// Collects terms with same power of TR and Nc and Cf. void simplify(); - /// Replaces CF as TR*Nc-TR/Nc. + /// Replaces CF with TR*Nc-TR/Nc. void remove_CF(); - /// Order terms in Polynomial in a unique form, + /// Orders terms in Polynomial in a unique form, /// first according to pow_Nc+pow_CF, then according to pow_Nc (for same pow_Nc+pow_CF) /// then according to int_part*num, then according to int_part, and finally according to pow_TR. void normal_order(); private: /// The factorial of an int. int factorial (int i) const; /// Function for setting the Polynomial using a string, /// used by string constructor. /// The Momomials should be separated by + or -, see also the /// Monomial string constructor. void Polynomial_of_str( const std::string str ); }; /// Define the operator << for Polynomial. std::ostream& operator<<(std::ostream& out, const Polynomial & Poly); /// Define the operator << for polynomial. std::ostream& operator<<(std::ostream& out, const polynomial & poly); /// Define the operator == for Polynomial /// By definition, each Monomial has to be identical, /// as order matters 1+2 is not 2+1. bool operator==( const Polynomial & Poly1, const Polynomial & Poly2); - /// Operator != for Polynomial. Returns false if Poly1==Poly2, and /// true otherwise. bool operator!=( const Polynomial & Poly1, const Polynomial & Poly2 ); /// Operator + for Polynomial and a single Monomial. Polynomial operator+(const Polynomial & Poly, const Monomial & Mon ); /// Operator + for a single Monomial and a Polynomial, /// returns Poly+Mon. Polynomial operator+(const Monomial & Mon, const Polynomial & Poly ); /// Define the operator += for Polynomials. /// The Monomial is appended unless Mon.int_part=0. Polynomial operator+=( Polynomial & Poly, const Monomial & Mon ); /// Define the operator += for Polynomials. /// This operator appends the Monomials of Poly2 to Poly1. Polynomial operator+=( Polynomial & Poly1, const Polynomial & Poly2 ); /// Operator - for Polynomial and a single Monomial. /// Changes sign of Monomial by changing int_part and returns Poly+(-Mon). Polynomial operator-(const Polynomial & Poly, const Monomial & Mon); /// Operator - for Polynomial and a single Monomial /// changes the sign of Poly by changing int_part, /// and then adds Mon to (-Poly). Polynomial operator-(const Monomial & Mon, const Polynomial & Poly); /// Operator + for Polynomials, appends Momomails from Poly2 to Poly1. /// If one of the Polynomials is empty=1, this is compensated for by first /// appending a Monomial=1 to the empty Polynomial. Polynomial operator+( const Polynomial & Poly1, const Polynomial & Poly2 ); /// Operator - for Polynomials, /// changes sign of Poly2, and then returns Poly1 + (-Poly2). Polynomial operator-(const Polynomial & Poly1, const Polynomial & Poly2); /// Operator * for Polynomial and int, /// loops over Monomials and multiplies int_part with i. /// If Poly is empty=1, a default Monomial=1 is first /// appended to Poly. -Polynomial operator*(const Polynomial & Poly, int i); +Polynomial operator*( const Polynomial & Poly, int i ); /// Operator * for int and Polynomial. /// Returns Poly*i. -Polynomial operator*(int i, const Polynomial & Poly); +Polynomial operator*( int i, const Polynomial & Poly ); /// Operator * for Polynomial and cnum, /// loops over Monomials and multiplies cnum_partwith c. /// If Poly is empty=1, a default Monomial=1 is first /// appended to poly. -Polynomial operator*( const Polynomial & Poly, const cnum c); +Polynomial operator*( const Polynomial & Poly, const cnum c ); /// Operator * for cnum and Polynomial, returns Poly*c. -Polynomial operator*( const cnum c, const Polynomial & Poly); +Polynomial operator*( const cnum c, const Polynomial & Poly ); /// Operator * for Polynomial and double, /// loops over Monomials and multiplies cnum_partwith d. /// If Poly is empty=1, a default Monomial=1 is first /// appended to poly. Polynomial operator*( const Polynomial & Poly, const double d ); /// Operator * for Polynomial and double, returns Poly*d. Polynomial operator*( const double d, const Polynomial & Poly ); /// Operator * for Polynomial and Monomial, loops over Monomials in /// Poly, and multiplies each with Mon. If Poly is empty=1, /// a Polynomial with one Monomial=Mon is returned. Polynomial operator*( const Polynomial & Poly, const Monomial & Mon ); /// Operator * for Monomial and Polynomial, /// returns Poly*Mon. Polynomial operator*( const Monomial & Mon, const Polynomial & Poly ); /// Operator * for Polynomials. (For details, see code.) Polynomial operator*( const Polynomial & Poly1, const Polynomial & Poly2 ); +/// Operator *= for Polynomial and int. +/// Multiplies Poly with i. +Polynomial operator*=( Polynomial & Poly, const int i ); + +/// Operator *= for Polynomial and double. +/// Multiplies Poly with d. +Polynomial operator*=( Polynomial & Poly, const double d ); + +/// Operator *= for Polynomial and cnum. +/// Multiplies Poly with c. +Polynomial operator*=( Polynomial & Poly, const cnum c ); + +/// Operator *= for Polynomial and Monomial. +/// Multiplies Poly with Mon. +Polynomial operator*=( Polynomial & Poly, const Monomial & Mon ); + +/// Operator *= for Polynomial and Polynomial. +/// Multiplies Poly1 with Poly2. +Polynomial operator*=( Polynomial & Poly1, const Polynomial & Poly2 ); + } #endif /* COLORFULL_Polynomial_h */ diff --git a/MatrixElement/Matchbox/ColorFull/Quark_line.cc b/MatrixElement/Matchbox/ColorFull/Quark_line.cc --- a/MatrixElement/Matchbox/ColorFull/Quark_line.cc +++ b/MatrixElement/Matchbox/ColorFull/Quark_line.cc @@ -1,768 +1,799 @@ // -*- C++ -*- /* * Quark_line.cc * Contains definitions of the class Quark_line and associated types and operators. * Created on: Jul 7, 2010 * Author: Malin Sjodahl */ #include "Quark_line.h" #include #include #include #include namespace ColorFull { Quark_line::Quark_line( const std::string str ) { Quark_line_of_str( str ); } Quark_line::Quark_line() { // member set just to avoid complaints open=true; } int Quark_line::at( int j ) const{ int size = ql.size(); if (0 <= j and j < size) return ql.at(j); else if ( !open and (size <= j && j < 2 * size) ) return ql.at(j - ql.size()); else if ( !open and (-size < j and j < 0) ) return ql.at(j + size); std::cerr << "Quark_line::at(j): j=" << j << " is out of range" << std::endl; std::cerr.flush(); assert(0); return 0; } void Quark_line::Quark_line_of_str( const std::string str ){ int j=0; // Check that the string contains a comma bool contains_comma=false; while (j < (int)str.size()) { if(str.at(j)==',') contains_comma=true; j++; } if( !contains_comma ){ std::cerr << "Quark_line::Quark_line_of_str: The string " << str << " contains no comma. Each quark_line must contain a comma since (g1)=0" << " and for each quark, there is an anti-quark."; assert(0); } // Check that left and right brackets match up j=0; int left_brackets=0,right_brackets=0; while (j < (int)str.size()) { if(str.at(j)=='(') left_brackets++; if(str.at(j)==')') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Quark_line::Quark_line_of_str: The brackets in the Quark_line\"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } // Check that left and right curly brackets match up left_brackets=0,right_brackets=0; j=0; while (j < (int)str.size()) { if(str.at(j)=='{') left_brackets++; if(str.at(j)=='}') right_brackets++; j++; } if(left_brackets != right_brackets){ std::cerr << "Quark_line::Quark_line_of_str: The curly brackets in the Quark_line\"" << str <<"\" do not seem to match up. There were " << left_brackets <<" left bracket(s) and "<< right_brackets << " right bracket(s)." << std::endl; assert( 0 ); } if( left_brackets > 1){ std::cerr << "Quark_line::Quark_line_of_str: Found " << left_brackets << " curly left brackets in the string " << str <<" but there should be at most 1;" << std::endl; assert( 0 ); } // The string should be split into a part containing the quark_line int i=0; std::string Poly_str, ql_str; Poly_str.clear(); ql_str.clear(); // If there were curly brackets just read until one is found if( left_brackets>0 ) { while( i< (int)str.size() and str.at(i)!='{'){ Poly_str.push_back(str.at(i)); i++; } } // If no curly brackets, look for , else { // See if the Col_str contains ",", // then we know that we should look backwards // until the ( is found int first_comma=0; j=0; while ( j < (int)str.size() and first_comma==0 ) { if(str.at(j)==',') first_comma=j; j++; } if (first_comma>0){ j=first_comma; while (j>=0 and str.at(j)!= '(') { j--; } } // We should have found a ( if (j==0 and str.at(j)!= '('){ std::cerr << "Quark_line::Quark_line_of_str: Found no starting bracket in string" << str << std::endl; assert( 0 ); } // Now, the quark_line starting ( is at place j i=0; while(i(fin)), std::istreambuf_iterator()); + + // Skip lines starting with # + while( str.at(0)== '#' ){ + while (str.at(0) != '\n'){ + str.erase(str.begin()); + } + // erase endl sign(s) + while(str.at(0)== '\n'){ + str.erase(str.begin()); + } + } + + // Remove endl chars at the end of the file + while( str.at(str.size()-1) == '\n' ) str.erase(str.size()-1); Quark_line_of_str( str ); } //uint Quark_line::size() const{ return ql.size();} void Quark_line::quark_line_of_str(std::string str) { //std::cout << "quark_line_of_str, got \"" << str.c_str() << "\""<< endl; // First char tells if the string is open or not if (str.at(0) == '{') open = true; else if (str.at(0) == '(') open = false; else { std::cerr << "Quark_line::quark_line_of_str: First and last char in Quark_line should be '{}' for open or '()' as in closed. It was: " << str.at(0) << std::endl; std::cerr.flush(); assert(0); } // Next parton to be added (as string) std::string parton_str; // Next parton to be added (as int) int parton_num; // To keep track of the place uint i = 1; // Loop over characters in string, last char is '}' or ')' while (i < str.size() - 1) { // When a ',' is found, write content in parton_str to the // quark_line and if (str.at(i) == ',') { std::istringstream parton_str_st(parton_str); parton_str_st >> parton_num; ql.push_back(parton_num); i++; // Empty parton string to prepare for new content parton_str.clear(); } while (i < str.size() - 1 && str.at(i) != ',') { parton_str.push_back(str.at(i)); i++; } } // Adding last term std::istringstream parton_str_st(parton_str); parton_str_st >> parton_num; ql.push_back(parton_num); if (str.at(str.size() - 1) != ')' && str.at(str.size() - 1) != '}') { std::cerr << "Quark_line::quark_line_of_str: Last char in Quark_line should match '{' for open or '(' as for closed. It was: " << str.at(str.size() - 1) << std::endl; std::cerr.flush(); assert(0); } } void Quark_line::write_out_Quark_line( std::string filename ) const { if ((ql.size() == 0)) { std::cout << "Quark_line::write_out_Quark_line: The Quark_line is empty." << std::endl; std::cout.flush(); return; } std::ofstream outfile(filename.c_str()); if ( !outfile ) std::cerr << "Quark_line::write_out_Quark_line: Cannot write out Quark_line as the file \"" << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; outfile << *this; } void Quark_line::normal_order() { // Do nothing if the quark_line is empty if (ql.empty()) return; if (!open) { // For storing smallest gluon number (should be first in normal ordering) int smallest = at(0); int place = 0; for (uint j = 1; j < ql.size(); j++) { if (at(j) < smallest) { smallest = at(j); place = j; } // If indices are equal, check next else if (at(j) == smallest) { // Keep checking next until a difference is found // or all elements are compared int k = 1; while (at(j + k) == at(place + k) && k < static_cast(ql.size())) k++; // Now, at place j+k, the indices are different // Change place if smaler if (at(j + k) < at(place + k)) { place = j; } } } // Not the place of the lowest index is located and the quark_lins should // start with that index // Move elements before place of lowest number to the end for (int j = 0; j < place; j++) { ql.push_back(at(0)); ql.erase(ql.begin()); } } } void Quark_line::conjugate( ) { Poly.conjugate(); // To contain the conjugated quark_line quark_line ql_new; // Take conjugate by reversing order of all partons in the Quark_line for (int i=ql.size()-1; i>=0;){ ql_new.push_back( ql.at(i) ); i--; } ql=ql_new; } // Returns a Quark_line with the the elements before j in a Quark_line Quark_line Quark_line::before( int j ) const{ //cout << "before( Quark_line Ql, int j ): the size was " << static_cast(Ql.ql.size()); //cout << "for the Quark_line " << Ql << endl; Quark_line Ql_copy=*this; if( j<0 or j> static_cast( ql.size()-1) ){ std::cerr << "Quark_line::before( int j ): the size was " << static_cast( ql.size() ); std::cerr << "for the quark_line " << *this << std::endl; std::cerr.flush(); std::cerr <<"before: the argument must be >=0 and inside vector, was " << j << std::endl; std::cerr.flush(); assert(0); } // In first part erase everything after and including j quark_line::iterator it1=Ql_copy.ql.begin()+j; quark_line::iterator it2=Ql_copy.ql.end(); Ql_copy.ql.erase(it1, it2); return Ql_copy; } Quark_line Quark_line::after( int j ) const{ Quark_line Ql_copy=*this; if(j<0 or j> static_cast( ql.size()-1)){ std::cerr <<"Quark_line::after: the argument must be >=0 and inside vector, was " << j << std::endl; std::cerr.flush(); assert(0); } - // In first part erase everything after and including j + // In first part, erase everything after and including j quark_line::iterator it1=Ql_copy.ql.begin(); quark_line::iterator it2=Ql_copy.ql.begin()+j+1; Ql_copy.ql.erase(it1, it2); return Ql_copy; } void Quark_line::erase( int i ){ ql.erase( ql.begin()+i ); } -void Quark_line::append( int p ){ - ql.insert( ql.end(), p ); -} - - void Quark_line::append( const std::vector & in_ql ){ for (uint j=0; j in_ql ){ +void Quark_line::prepend(std::vector in_ql){ for (uint j=0; j ( ql.size() ) and j>=0 ) { quark_line::iterator itj = ql.begin() + j; ql.insert(itj, p); } else if( j>= static_cast ( ql.size() )) { std::cerr << "Quark_line::insert: The size of ql is " << ql.size() << ", so no parton can be inserted at place " << j << " in " << *this << std::endl; assert(0); } else if( j<0 ) { std::cerr << "Quark_line::insert: Can not insert at place " << j << " < 0 in " << *this << std::endl; assert(0); } } std::pair< Quark_line, Quark_line > Quark_line::split_Quark_line( int j1, int j2 ) const{ if(open){ std::cerr <<"Quark_line::split_Quark_line: expects a closed quark_line" << std::endl; std::cerr.flush(); assert(0); } Quark_line Ql1=*this; Quark_line Ql2=*this; // In first part erase everything between j1 and j2 quark_line::iterator it1=Ql1.ql.begin()+j1; quark_line::iterator it2=Ql1.ql.begin()+j2; Ql1.ql.erase(it1, it2+1); // In second part, erase from j2... it1=Ql2.ql.begin()+j1; it2=Ql2.ql.begin()+j2; Ql2.ql.erase( it2, Ql2.ql.end() ); // ... and before j1 Ql2.ql.erase( Ql2.ql.begin(), it1+1 ); return std::make_pair(Ql1,Ql2); } int Quark_line::smallest(const Quark_line & Ql1, const Quark_line & Ql2) const { // If only one is open, that Ql should stand first if (Ql1.open && !Ql2.open) return 1; else if (Ql2.open && !Ql1.open) return 2; // If both are open or both are closed, the longest should stand first else if (Ql1.ql.size() > Ql2.ql.size()) return 1; else if (Ql2.ql.size() > Ql1.ql.size()) return 2; // If the size is the same, the Ql with smallest starting number should stand first // If the first number is the same, check the 2nd number, then the 3rd... else { // Loop over places in the Ql's for (uint j = 0; j < Ql1.ql.size(); j++) { if (Ql1.ql.at(j) < Ql2.ql.at(j)) return 1; if (Ql2.ql.at(j) < Ql1.ql.at(j)) return 2; } // If the Ql's are equal, return 0 return 0; } } void Quark_line::contract_neighboring_gluons(int j) { if (ql.empty()) return; if (open) { std::cerr << "Quark_line::contract_neighboring_gluons(j): Expects a closed Quark_line, got " << *this << std::endl; assert(0); } // If asked for -1st element, check last if (j == -1) j = size() - 1; // If asked for last+1 element, check first if (j == static_cast(ql.size() - 1)) j = 0; // Keep contracting gluons as long as: // there are at least two gluons to contract // neighbors are equal // and j is not the last parton in the ql while (j < static_cast(size() - 1) && (size()) >= 2 && (at(j)) == at(j + 1)) { quark_line::iterator it1 = ql.begin() + j; quark_line::iterator it2 = ql.begin() + j + 2; // Removing neighboring gluons ql.erase(it1, it2); // This should increase the power of CF Monomial Mon_tmp; Mon_tmp.pow_CF = 1; - Poly = Poly * Mon_tmp; + Poly *= Mon_tmp; if (j > 2) j = j - 2; } // If j is the last parton and the Quark_line is closed and first=last // and there are at least 2 gluons while (static_cast(size()) >= 2 && j == static_cast(size() - 1) && !open && at(0) == at(size() - 1)) { quark_line::iterator it1 = ql.begin(); quark_line::iterator it2 = ql.end() - 1; ql.erase(it2); ql.erase(it1); // This should increase the power of CF Monomial Mon_tmp; Mon_tmp.pow_CF = 1; - Poly = Poly * Mon_tmp; + Poly *= Mon_tmp; // If now, all gluons are removed and there are no quarks, // increase Nc and open the ql (if not already open) // An open quark-line with no partons is defined as 1 if (empty() && !open) { Monomial Mon_tmp; Mon_tmp.pow_Nc = 1; - Poly = Poly * Mon_tmp; + Poly *= Mon_tmp; open = true; } j = j - 2; } } void Quark_line::contract_neighboring_gluons( ){ if( empty() ) return; if( open ){ std::cerr << "uark_line::contract_neighboring_gluons( ): Expects a closed Quark_line, got " << *this << std::endl; assert(0); } // Counting powers of CF, // Should be increases once for every gluon that is contracted // initially put to dummy value, minimal int int CF_pow_old=std::numeric_limits::min(); int CF_pow_new=0; // Keep looping over elements as long as CF_pow changes while( CF_pow_old!= CF_pow_new ){ CF_pow_old=CF_pow_new; // Loop over elements in the Quark_line, starting at first parton // and ending with last for (uint j=0; j < size(); j++){ contract_neighboring_gluons( j ); } // To see if CF_pow changed, arbitrarily compare 0th element in Polynomial (if existing) // (all pow_CF changes) if( !Poly.empty() ) CF_pow_new=Poly.at(0).pow_CF; // put CF_pow_new to current pow } return ; } void Quark_line::contract_next_neighboring_gluons( int j ) { if( ql.empty()) return; if( open ){ std::cerr << "Quark_line::contract_next_neighboring_gluons( j): Expects a closed Quark_line, got " << *this << std::endl; assert(0); } // If asked for -1st element, check last if(j==-1) j=ql.size()-1; // If asked for -2nd element, check second last if(j==-2) j=ql.size()-2; // Keep track of changes in number of gluons // add dummy number '1' to run at least once int old_size=ql.size()+1; // Loop over index in quark_line as long as the size keep changing, // or at least once while( old_size!= static_cast(ql.size()) && (ql.size())>=4){ old_size= (ql.size()); // There are three cases: // the normal case, and, if the Quark_line is closed, // 2nd last =first (0th) and last =2nd (1st) bool normal_case=(ql.size()>=4 && static_cast(j)< ql.size()-2 && ql.at(j)==ql.at(j+2)); bool second_last_case=( ql.size()>=4 && !open && static_cast(j)==ql.size()-2 && ql.at(j)==ql.at(0) ); bool last_case=(ql.size()>=4 && !open && static_cast(j)==ql.size()-1 && ql.at(j)==ql.at(1)); // See if next to neighbors are equal // The point of having a while is to check again if one pair was found while(normal_case or second_last_case or last_case) { // Erase the gluons (at places depending on size) quark_line::iterator it1; quark_line::iterator it2; if(normal_case){ it1=ql.begin()+j; it2=ql.begin()+j+2; } else if(second_last_case){ it1=ql.begin(); it2=ql.end()-2; } else if(last_case){ it1=ql.begin()+1; it2=ql.end()-1; } // Erase right most gluon first ql.erase(it2); ql.erase(it1); // The surviving term comes with -TR/(Nc) Monomial Mon_tmp; Mon_tmp.pow_TR = 1; Mon_tmp.int_part=-1; Mon_tmp.pow_Nc=-1; - Poly=Poly*Mon_tmp; + Poly *= Mon_tmp; // Check if new neighbors are equal uint old_size2=ql.size(); // Check if inbetween gluon is now neighbor with itself to the right contract_neighboring_gluons( j ); // Check if inbetween gluon is now neighbor with itself to the left contract_neighboring_gluons( j-1 ); // if a neighboring pair was removed if( (ql.size()) != old_size2 ) j=j-2; // Two gluons have been erased so to keep looking forward in the // Quark_line j must not be increased j--; normal_case=( ql.size()>=4 && static_cast(j)< ql.size()-2 && ql.at(j)==ql.at(j+2)); second_last_case=( ql.size()>=4 && !open && j== static_cast(ql.size())-2 && ql.at(j)==ql.at(0)); last_case=( ql.size()>=4 && !open && j== static_cast(ql.size())-1 && ql.at(j)==ql.at(1)); } } return ; } void Quark_line::contract_next_neighboring_gluons( ) { if( empty() ) return ; if( open ){ std::cerr << "Quark_line::contract_next_neighboring_gluons: Expects a closed Quark_line, got " << *this << std::endl; assert(0); } // Start with looking for neighbors contract_neighboring_gluons(); // Keep track of changes in number of gluons // add dummy number '1' to run at least once int old_size= size()+1; // Loop over index in quark-line as long as the size keep changing, // or at least once while( old_size!= static_cast( size()) && ( size())>=4){ // For seeing if size has changed old_size= size(); for (int j=0; j< static_cast( size()); j++){ // Contract next to neighbors starting at place j contract_next_neighboring_gluons( j ); } } return ; } Quark_line operator*( const Quark_line & Ql, const int i){ Quark_line Ql_out(Ql); - Ql_out.Poly=Ql_out.Poly*i; + Ql_out.Poly*=i; return Ql_out; } Quark_line operator*(const int i, const Quark_line & Ql){ return Ql*i; } Quark_line operator*(const Quark_line & Ql, const cnum c){ Quark_line Ql_out(Ql); - Ql_out.Poly=Ql_out.Poly*c; + Ql_out.Poly*= c; return Ql_out; } Quark_line operator*(const cnum c, const Quark_line & Ql){ return Ql*c; } Quark_line operator*(const Quark_line & Ql, const double d){ Quark_line Ql_out(Ql); - Ql_out.Poly=Ql_out.Poly*d; + Ql_out.Poly*=d; return Ql_out; } Quark_line operator*(const double d, const Quark_line & Ql){ return Ql*d; } Quark_line operator*(const Quark_line & Ql, const Monomial & Mon){ Quark_line Ql_out(Ql); - Ql_out.Poly=Ql_out.Poly*Mon; + Ql_out.Poly*=Mon; return Ql_out; } Quark_line operator*(const Monomial & Mon, const Quark_line & Ql){ return Ql*Mon; } Quark_line operator*(const Quark_line & Ql, const Polynomial & Poly){ Quark_line Ql_out(Ql); Ql_out.Poly=Ql_out.Poly*Poly; return Ql_out; } Quark_line operator*(const Polynomial & Poly, const Quark_line & Ql){ return Ql*Poly; } +bool operator==( const Quark_line & Ql1, const Quark_line & Ql2 ){ + + // Quark_lines's must have equal length + if( Ql1.size() != Ql2.size() ) return false; + + // Both must be closed, or both must be open + if(! (Ql1.open==Ql2.open ) ) return false; + + // The quark_lines must be equal + if( Ql1.ql != Ql2.ql ) return false; + + // The Polynomials must be equal + if( Ql1.Poly != Ql2.Poly ) return false; + + return true; +} + +bool operator!=( const Quark_line & Ql1, const Quark_line & Ql2 ){ + if( Ql1==Ql2 ) return false; + else return true; +} + std::ostream& operator<<(std::ostream& out, const Quark_line & Ql) { int max = Ql.ql.size(); // Write out Polynomial if it is not 1, of if the ql has no structure Polynomial Poly1; // For comparison, the default Polynomial=1 if (Ql.Poly != Poly1 or Ql.ql.size() == 0) out << Ql.Poly; if (max == 0 && Ql.open) out << "{}"; else if (max == 0 && !Ql.open) out << "()"; else if (max > 0 && Ql.open) out << "{"; else if (max > 0 && !Ql.open) out << "("; for (int i = 0; i < max - 1; i++) { out << Ql.ql.at(i) << ","; } if (max > 0) out << Ql.ql.at(max - 1); if (max > 0 && Ql.open) out << "}"; else if (max > 0 && !Ql.open) out << ")"; return out; } } // end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Quark_line.h b/MatrixElement/Matchbox/ColorFull/Quark_line.h --- a/MatrixElement/Matchbox/ColorFull/Quark_line.h +++ b/MatrixElement/Matchbox/ColorFull/Quark_line.h @@ -1,218 +1,225 @@ // -*- C++ -*- /* * Quark_line.h * Contains declaration of the class Quark_line and associated types and operators. * Created on: Jul 7, 2010 * Author: Malin Sjodahl */ #ifndef COLORFULL_Quark_line_h #define COLORFULL_Quark_line_h #include "Polynomial.h" namespace ColorFull { /// Define a type to contain a quark-line with gluons attached, /// the actual color information about how quarks are ordered in a /// Quark_line. typedef std::vector quark_line; /// A class to contain one quark-line with gluons attached /// multiplying a Polynomial, Poly. /// The partons are ordered as {q,g1,g2...gn,qbar} for an open quark-line /// containing a q and a qbar, or (g1,g2...gn) for a closed /// quark-line with only gluons. /// The parton numbers are stored as components in a vector, a quark_line, /// whereas the member open contains information about it the quark-line is /// closed as a trace (true) or open, false. class Quark_line { public: /// Constructor used to set the color structure using a string. /// The string should be of form Polynomial*quark_line, /// used as "Quark_line Ql("5*TR*Nc^2 {1,6,7,2}");" /// for an open Quark_line with a quark with /// number 1, two gluons with number 6 and 7, and a qbar with number 2. /// For a closed Quark_line with 3 gluons the syntax is /// "Quark_line Ql("(1,2,3)");". /// The integers should be positive. - /// The Polnomial should be in such a shape that it is readable by the + /// The Polynomial should be in such a shape that it is readable by the /// Polynomial( std::string ) constructor. Quark_line( const std::string str ); /// Default constructor. Quark_line(); /// To actually contain the color information, /// in order {q, g1, ... gn, qbar} or (g1, ..., gn). quark_line ql; /// Polynomial factor, multiplying the quark_line. Polynomial Poly; /// Is the string open, with a q in the beginning and a qbar in the end, or not? bool open; /// Function for reading in the Quark_line from the file filename, /// uses Quark_line_of_str. void read_in_Quark_line( std::string filename ); /// Function for writing out the Quark_line to a file /// with name filename. void write_out_Quark_line( std::string filename ) const; - /// The parton at place j. + /// Returns the parton at place j. /// For closed quark_lines j may be between -size and 2*size. int at( int j ) const; /// The size of the quark_line. uint size() const{ return ql.size();} - /// Erase information in quark_line. + /// Erase information in quark_line ql. void clear() { ql.clear(); } - /// Appends a parton to data member ql. - void push_back( int p ) { ql.push_back( p ); } - /// Is the quark_line empty? bool empty() const { return ql.empty(); } /// If the quark_line is open, there is nothing to do, /// else order with smallest gluon index first /// (use that the trace is cyclic). void normal_order(); /// To erase the parton at place i. void erase( int i ); - /// Conjugate the Quark_line by reversing the quark_line ql + /// Conjugates the Quark_line by reversing the quark_line ql /// and conjugating the Polynomial Poly. void conjugate(); - /// Function for adding parton p to the quark_line. - void append( int p ); + /// Appends parton p to the Quark_line. + void append( int p ) { ql.push_back( p ); } - /// Function for adding a whole quark_line to the quark_line. + /// Appends a whole quark_line to the Quark_line. void append( const std::vector & in_ql ); - /// Function for prepending parton p to the quark_line. + /// Prepends parton p to the Quark_line. void prepend( int p ); - /// Function for prepending a whole quark_line to the quark_line. + /// Prepends a whole quark_line to the Quark_line. void prepend( std::vector in_ql ); /// Inserting parton p at place j. void insert( int j, int p ); - // Returns a Quark_line with the elements before j in a Quark_line. + /// Returns a Quark_line where the ql member is changed to contain + /// only partons before place j. Quark_line before( int j ) const; - // Returns the elements after j in a Quark_line + /// Returns a Quark_line where the ql member is changed to contain + /// only partons after place j. Quark_line after( int j ) const; - /// Function for splitting a closed Quark_line into two Quark_lines, - /// j1 & j2 are the locations of the gluons to be removed in the split. + /// Function for splitting a closed Quark_line into two Quark_lines. + /// The gluons at j1 and j2 are removed in the split. /// May create 1-rings and 0-rings. std::pair split_Quark_line( int j1, int j2 ) const; /// Function for finding the "smallest" Quark_line of Ql1 and Ql2, /// used for deciding which Quark_line should stand first while normal ordering. - /// Does NOT first normal order quark_lines. + /// Does NOT first normal order the Quark_lines. /// If only one is open, that Quark_line should stand first. /// If both are open or both are closed, the longest Quark_line should stand first. /// If the size is the same, the Quark_line with smallest starting number should stand first. /// If the first number is the same, check the 2nd number, then the 3rd... /// 1 is returned if Ql1 should stand first, and 2 if Ql2 should stand first. /// If Ql1==Ql2, 0 is returned. - /// If the Ql's are equal, 0 is returned. int smallest( const Quark_line & Ql1 , const Quark_line & Ql2 ) const; - /// Function to contract neighboring gluons in the Quark_line starting at j, + /// Contracts neighboring gluons in the Quark_line starting at j, /// only intended for closed Quark_lines. void contract_neighboring_gluons( int j ); - /// Function to contract neighboring gluons in a Quark_line starting at place 0, - /// and looking everywhere, only intended for closed Quark_lines. + /// Contracts neighboring gluons in a Quark_line starting at place 0, + /// and checking all neighbors, only intended for closed Quark_lines. void contract_neighboring_gluons( ); - /// Function to contract next to neighboring gluons in the Quark_line, - /// starting at place j (so gluon j and j+2). + /// Contracts neighboring and next to neighboring gluons in the Quark_line, + /// starting at place j (i.e. checking gluon j and j+2). /// Also looks for new neighbors, only intended for closed Quark_lines. void contract_next_neighboring_gluons( int j ); /// Contracts neighboring and next to neighboring gluons in the Quark_line, /// starting with contracting neighbors, only intended for closed Quark_lines. void contract_next_neighboring_gluons( ); private: /// Function used to set the color structure using a string. /// The string should be of form Polynomial*quark_line, /// used as "Quark_line Ql("Polynomial {5,6,7}");" /// for an open Quark_line with a quark with /// number 5, a gluon with number 6 and a qbar with number 7, and as /// "Quark_line Ql("(5,6,7)");" for 3 gluons attached to a quark line. - /// The Polnomial should be in such a shape that it's readable by the + /// The Polynomial should be in such a shape that it's readable by the /// Polynomial( std::string ) constructor. - void Quark_line_of_str(const std::string str); + void Quark_line_of_str( const std::string str ); /// To make it easy to define a Quark_line using a string, /// used by Quark_line_of_str(std::string str) /// to set the quark_line member ql. void quark_line_of_str( std::string str ); }; /// Define the operator * for Quark_line and int. /// The Polynomial of the Quark_line is multiplied with i. Quark_line operator*( const Quark_line & Ql, const int i ); /// Define the operator * for Quark_line and int, /// returns Ql*i. Quark_line operator*( const int i, const Quark_line & Ql ); /// Define the operator * for Quark_line and cnum. /// The Polynomial of the Quark_line is multiplied with c. Quark_line operator*( const Quark_line & Ql, const cnum c ); /// Define the operator * for Quark_line and cnum, /// returns Ql*c. Quark_line operator*( const cnum c, const Quark_line & Ql ); /// Define the operator * for Quark_line and double. /// The Polynomial of the Quark_line is multiplied with d. Quark_line operator*( const Quark_line & Ql, const double d ); /// Define the operator * for Quark_line and double, /// returns Ql*d. Quark_line operator*( const double d, const Quark_line & Ql ); /// Define the operator * for Quark_line and Monomial. /// The polynomial of the Quark_line is multipled with Mon. Quark_line operator*( const Quark_line & Ql, const Monomial & Mon ); /// Define the operator * for Quark_line and Monomial. /// returns Ql*Mon. Quark_line operator*( const Monomial & Mon, const Quark_line & Ql ); /// Define the operator * for Quark_line and Polynomial. /// The Polynomial of the Quark_line is multiplied with Poly. Quark_line operator*( const Quark_line & Ql, const Polynomial & Poly ); + /// Define the operator * for Quark_line and Polynomial - /// returns Ql*Poly. Quark_line operator*( const Polynomial & Poly, const Quark_line & Ql ); +/// Define the operator == for two Quark_lines +/// the quark_lines must be equal, the member variable open and the +/// Polynomials must be equal. Note that for Polynomials cf+Nc!=Nc+cf. +bool operator==( const Quark_line & Ql1, const Quark_line & Ql2 ); + +/// The negation of the Quark_line operator ==. +bool operator!=( const Quark_line & Ql1, const Quark_line & Ql2 ); + + /// Define the operator << for Quark_line. std::ostream& operator<<( std::ostream& out, const Quark_line & Ql ); }// end namespace ColorFull #endif /* COLORFULL_Quark_line_h */ diff --git a/MatrixElement/Matchbox/ColorFull/TraceBasis.cc b/MatrixElement/Matchbox/ColorFull/TraceBasis.cc --- a/MatrixElement/Matchbox/ColorFull/TraceBasis.cc +++ b/MatrixElement/Matchbox/ColorFull/TraceBasis.cc @@ -1,238 +1,420 @@ // -*- C++ -*- // // TraceBasis.cc is a part of ColorFull // Copyright (C) 2010-2011 Simon Platzer & Malin Sjodahl // // ColorFull 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 TraceBasis class. // #include "TraceBasis.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/EventRecord/Particle.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace ColorFull; -TraceBasis::TraceBasis() {} +TraceBasis::TraceBasis() { } TraceBasis::~TraceBasis() {} IBPtr TraceBasis::clone() const { return new_ptr(*this); } IBPtr TraceBasis::fullclone() const { return new_ptr(*this); } void TraceBasis::clear() { ColourBasis::clear(); theBasisMap.clear(); theScalarProducts.clear(); } map > > TraceBasis::basisList(const vector& basisId) const { map,Trace_basis>::const_iterator bit = theBasisMap.find(basisId); map > > res; const col_basis& cb = bit->second.cb; for ( size_t i = 0; i < cb.size(); ++i ) { vector > cstr; const Col_str& cs = cb.at(i).at(0); for ( size_t j = 0; j < cs.size(); ++j ) { const Quark_line& ql = cs.at(j); vector qline; for ( size_t k = 0; k < ql.size(); ++k ) qline.push_back(ql.at(k)-1); cstr.push_back(qline); } res[i] = cstr; } return res; } size_t TraceBasis::prepareBasis(const vector& sub) { useMe(); vector mySub = normalOrder(sub); if ( theBasisMap.find(mySub) == theBasisMap.end() ) { int ng = count_if(mySub.begin(),mySub.end(),ColourBasis::matchRep(PDT::Colour8)); int nq = (mySub.size() - ng)/2; Trace_basis basis; basis.create_basis(nq,ng); theBasisMap[mySub] = basis; } return theBasisMap[mySub].size(); } void TraceBasis::readBasisDetails(const vector& sub) { prepareBasis(sub); } double TraceBasis::scalarProduct(size_t i, size_t j, const vector& abBasis) const { if ( largeN() && i != j ) return 0.; map,Trace_basis>::const_iterator bit = theBasisMap.find(abBasis); assert(bit != theBasisMap.end()); const Trace_basis& Basis = bit->second; Col_str csi = Basis.cb.at(i).at(0); Col_str csj = Basis.cb.at(j).at(0); + // Rename indices, and make string of new Col_strs, to use in map //pair Css_new = Basis.rename_indices(csi,csj); Basis.rename_indices(csi,csj); Col_str Cs1 = csi; Col_str Cs2 = csj; ostringstream Cs_string; Cs_string << Cs1 << Cs2; map::iterator pit = theScalarProducts.find(Cs_string.str()); // Add result to map if ( pit == theScalarProducts.end() ) { Polynomial p = colorFunctions.scalar_product(Cs1, Cs2); theScalarProducts.insert(make_pair(Cs_string.str(), p)); pit = theScalarProducts.find(Cs_string.str()); } return largeN() ? colorFunctions.double_num(colorFunctions.leading(pit->second)) : colorFunctions.double_num(pit->second); } + +// m is emitting parton +// i is new vector number in new large aBasis +// j is old vector number in old small bBasis +// k is the new number of the emitter +// l is the number of the new gluon +// dict contains the map from old to new numbers of the partons not participating double TraceBasis::tMatrixElement(size_t m, size_t i, size_t j, - const vector& aBasis, - const vector& bBasis) const { + const vector& aBasis, + const vector& bBasis, + size_t k, size_t l, + const map& dict) const { - ++m; + // Call sMatrixElement if it's a gluon splitting + if ( bBasis[m] == PDT::Colour8 && aBasis[k] != PDT::Colour8 ) + return sMatrixElement(m,i,j,aBasis,bBasis,k,l,dict); - map,Trace_basis>::iterator ait = - theBasisMap.find(aBasis); + assert( dict.size()+1 == bBasis.size() ); + map,Trace_basis>::const_iterator ait = + theBasisMap.find(aBasis); + map,Trace_basis>::const_iterator bit = + theBasisMap.find(bBasis); - map,Trace_basis>::const_iterator bit = - theBasisMap.find(bBasis); + assert(bit != theBasisMap.end()); + assert(ait != theBasisMap.end()); - assert(bit != theBasisMap.end()); - assert(ait != theBasisMap.end()); + const Trace_basis& ABasis = ait->second; + const Trace_basis& BBasis = bit->second; - Trace_basis& ABasis = ait->second; - const Trace_basis& BBasis = bit->second; - pair newNumbers = ABasis.new_vector_numbers(BBasis.cb.at(j).at(0),m); + // Initial Col_amp, before emission + Col_amp Ca1 = BBasis.at(j); - if ( (size_t) newNumbers.first == i ) - return 1.; + int g_new, p_old; + p_old = m+1;// ColorFull starts with parton number 1 (normally q or g) + int g_old = aBasis.size();//Give the new g an index that isn't used - if ( (size_t) newNumbers.second == i ) - return -1.; + // If the emitted parton is not a gluon, this is because + // we have backward evolution with a qqbar-> g + if( aBasis[l] != PDT::Colour::Colour8 ){ + // The number of the new gluon is k+1 + g_new = k+1; + } else{ // Standard case, l (+1) is the number of the new gluon + g_new = l+1; + } - return 0.; + // Color structure after gluon split + Col_amp Ca2 = colorFunctions.emit_gluon( Ca1, p_old, g_old); + + // The map should be the dict (containing the map of old non-involved partons) + // + g_new mapped to itself and p_old mapped + std::map map; + for ( size_t ii = 0; ii < bBasis.size(); ii++ ){ + if ( ii != m ) {// exclude splitting gluon + int parton = static_cast( dict.at(ii) ); + map[ii+1] = parton+1;// Parton numbers one unit higher in ColorFull + } + } + // New parton number mapping + map[ g_old ] = g_new; + // Old emitter should also be mapped + if( aBasis[l] != PDT::Colour::Colour8 ){// Exceptional case + map[ p_old ] = l+1; + } + else{ // Standard case + map[ p_old ] = k+1; + } + // Check the size of the map + assert( map.size() == aBasis.size() ); + + // Color structure when partons have names in map + Col_amp Ca3= colorFunctions.rename_partons( Ca2, map ); + + // Check if the new color structure has a component for basis vector i in ABasis + // Loop over Col_strs in Col_amp after split + for ( uint Csi=0; Csi < Ca3.size(); Csi++ ){ + if( Ca3.at(Csi).cs == ABasis.at(i).at(0).cs ){// If col_strs are the same + return colorFunctions.double_num( Ca3.at(Csi).Poly ); // Return Polynomial coefficient in front of Col_str + }; + } + + // If no component corresponding to vector i is found + return 0.; + +} + +// m is splitting gluon +// i is new vector number new large aBasis +// j is old vector number in old small bBasis +// k is the number of the new emitter after (q or qbar) +// l is the number of the "emission" (q or qbar) +// dict contains the map from old to new numbers of the partons not participating +double TraceBasis::sMatrixElement(size_t m, size_t i, size_t j, + const vector& aBasis, + const vector& bBasis, + size_t k, size_t l, + const map& dict) const { + + // Check that dict has the right size (splitting gluon missing, hence +1) + assert( dict.size()+1 == bBasis.size() ); + + map,Trace_basis>::iterator ait = + theBasisMap.find(aBasis); + map,Trace_basis>::const_iterator bit = + theBasisMap.find(bBasis); + + assert(bit != theBasisMap.end()); + assert(ait != theBasisMap.end()); + + + Trace_basis& ABasis = ait->second; // New basis + const Trace_basis& BBasis = bit->second; // Old basis + + + // Initial Col_amp, before split + Col_amp Ca1 = BBasis.at(j); + + + int g_old = m+1;// ColorFull starts with parton number 1 (normally q or g) + int q_old, q_new, qbar_old, qbar_new; + q_old = aBasis.size();// Give the q an index that isn't used + qbar_old = aBasis.size()+1;// Give the qbar an index that isn't used + if ( aBasis[k] == PDT::Colour::Colour3 && aBasis[l] == PDT::Colour::Colour3bar ) { + q_new = k+1; + qbar_new = l+1; + } else if ( aBasis[l] == PDT::Colour::Colour3 && aBasis[k] == PDT::Colour::Colour3bar ) { + q_new = l+1; + qbar_new = k+1; + } else { + assert(false); + } + + // Color structure after gluon split + // split_gluon also simplifies, so no polynomial factor in Qls + Col_amp Ca2= colorFunctions.split_gluon( Ca1, g_old, q_old, qbar_old ); + + // The map should be the dict (containing the map of old non-involved partons) + std::map map; + for ( size_t ii = 0; ii < bBasis.size(); ii++ ){ + if ( ii != m ) {// exclude splitting gluon + int parton = static_cast( dict.at(ii) ); + map[ii+1] = parton+1;// Parton numbers one unit higher in ColorFull + } + } + + map[q_old] = q_new; + map[qbar_old] = qbar_new; + + assert( map.size() == aBasis.size() ); + + // Color structure when partons have ColorFull default names + Col_amp Ca3= colorFunctions.rename_partons( Ca2, map ); // does normal ordering as well + + // Check if the new color structure has a component for basis vector i in ABasis + // Loop over Col_strs in Col_amp after split + for ( uint Csi=0; Csi < Ca3.size(); Csi++ ){ + if( Ca3.at(Csi).cs == ABasis.at(i).at(0).cs ){// If col_strs are the same, note that they must be normal ordered + return colorFunctions.double_num( Ca3.at(Csi).Poly ); // Return Polynomial coefficient in front of Col_str + }; + } + + // If no component corresponding to vector i is found + return 0.; } bool TraceBasis::colourConnected(const cPDVector& sub, const vector& basisId, const pair& first, const pair& second, size_t tensor) const { // get the basis map,Trace_basis>::const_iterator bit = theBasisMap.find(basisId); assert(bit != theBasisMap.end()); const Trace_basis& basis = bit->second; // translate process to basis ids map >::const_iterator trans = indexMap().find(sub); assert(trans != indexMap().end()); int idColoured = first.second ? second.first : first.first; idColoured = trans->second.find(idColoured)->second; ++idColoured; int idAntiColoured = first.second ? first.first : second.first; idAntiColoured = trans->second.find(idAntiColoured)->second; ++idAntiColoured; const Col_str& cs = basis.cb.at(tensor).at(0); return cs.left_neighbor(idAntiColoured,idColoured); } +map TraceBasis::indexChange(const vector& basis, + const size_t dim, + const map& indPerm) const { + // Change the map to indices starting at 1 (colorfull numbering of legs) from + // starting at 0 (Herwig numbering of legs). + map iPerm; + for ( map::const_iterator it = indPerm.begin(); + it != indPerm.end(); it++ ) { + iPerm[(it->first) + 1] = (it->second) + 1; + } + + // Get the basis + map,Trace_basis>::const_iterator bit = + theBasisMap.find(basis); + + assert(bit != theBasisMap.end()); + + const Trace_basis& Basis = bit->second; + + + // Naive way: loop over every basis vector and rename the partons, + // then loop over the basis vectors to see which basis vector it became. + // This is sufficient for the current application, as it will + // only be used on the hard subprocess, which will never have + // a very large colour basis. + map indexMap; + Col_amp Cai, Cai_re; + Col_amp Caj; + for ( size_t i = 0; i < dim; i++ ) { + // Get the basis vector and change the leg numbering + Cai = Basis.at(i); + Cai_re = colorFunctions.rename_partons( Cai, iPerm ); + for ( size_t j = 0; j < dim; j++ ) { + Caj = Basis.at(j); + if ( Cai_re.at(0).cs == Caj.at(0).cs ) + indexMap[i] = j; + } + } + // Check that the map was filled (every vector should have been + // changed into a new vector) + assert( indexMap.size() == dim ); + + return indexMap; +} + // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void TraceBasis::persistentOutput(PersistentOStream &) const {} void TraceBasis::persistentInput(PersistentIStream & , int) {} // *** Attention *** The following static variable is needed for the type // description system in ThePEG. Please check that the template arguments // are correct (the class and its base class), and that the constructor // arguments are correct (the class name and the name of the dynamically // loadable library where the class implementation can be found). DescribeClass describeTraceBasis("ColorFull::TraceBasis", "HwColorFull.so"); void TraceBasis::Init() { static ClassDocumentation documentation ("TraceBasis implements the trace colour basis.", "The colour algebra has been performed using ColorFull \\cite{Sjodahl:2014opa}", "%\\cite{Sjodahl:2014opa}\n" "\\bibitem{Sjodahl:2014opa}\n" "M.~Sjodahl,\n" "``ColorFull -- a C++ library for calculations in SU(Nc)color space,''\n" "arXiv:1412.3967 [hep-ph].\n" "%%CITATION = ARXIV:1412.3967;%%"); } diff --git a/MatrixElement/Matchbox/ColorFull/TraceBasis.h b/MatrixElement/Matchbox/ColorFull/TraceBasis.h --- a/MatrixElement/Matchbox/ColorFull/TraceBasis.h +++ b/MatrixElement/Matchbox/ColorFull/TraceBasis.h @@ -1,179 +1,215 @@ // -*- C++ -*- // // TraceBasis.h is a part of ColorFull // Copyright (C) 2010-2011 Simon Platzer & Malin Sjodahl // // ColorFull is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // #ifndef COLORFULL_TraceBasis_H #define COLORFULL_TraceBasis_H // // This is the declaration of the TraceBasis class. // #include "Herwig/MatrixElement/Matchbox/Utility/ColourBasis.h" #include "Trace_basis.h" namespace ColorFull { using namespace ThePEG; using namespace Herwig; /** * TraceBasis implements the trace colour basis. * * @see \ref TraceBasisInterfaces "The interfaces" * defined for TraceBasis. */ class TraceBasis: public Herwig::ColourBasis { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ TraceBasis(); /** * The destructor. */ virtual ~TraceBasis(); //@} public: /** * Clear this colour basis */ virtual void clear(); /** * Return a map of basis tensor indices to vectors identifying a * certain ordering corresponding to the given colour structure. May * not be supported by all colour basis implementations. */ virtual map > > basisList(const vector&) const; /** * Prepare the basis for the normal ordered legs and return the * dimensionality of the basis. */ virtual size_t prepareBasis(const vector&); /** * Gather any implementation dependend details when reading a basis */ virtual void readBasisDetails(const vector&); /** * Return the scalar product of basis tensors labelled a and b in * the basis used for the given normal ordered legs. */ virtual double scalarProduct(size_t a, size_t b, const vector& abBasis) const; /** + * Return true, if this colour basis supports gluon splittings. + */ + virtual bool canSplitGluons() const { + return false; + } + + /** * Return the matrix element of a colour charge * between basis tensors a and b, with * respect to aBasis and bBasis */ virtual double tMatrixElement(size_t i, size_t a, size_t b, const vector& aBasis, - const vector& bBasis) const; + const vector& bBasis, + size_t k, size_t l, + const map& dict) const; + + /** + * Return the matrix element of a quark splitting matrix + * between basis tensors a and b, with + * respect to aBasis and bBasis. Here + * m is splitting gluon, + * i is the new vector number in the new large aBasis, + * j is the old vector number in the old small bBasis, + * k is the number of the new q, + * l is the number of the new qbar, + * and dict contains the map from old to new numbers, of the partons not participating. + * All parton numbers are given in Herwig conventions. + */ + virtual double sMatrixElement(size_t i, size_t a, size_t b, + const vector& aBasis, + const vector& bBasis, + size_t k, size_t l, + const map& dict) const; /** * Return true, if the colour basis is capable of assigning colour * flows. */ virtual bool haveColourFlows() const { return true; } /** * Return true, if a large-N colour connection exists for the * given external legs and basis tensor. */ virtual bool colourConnected(const cPDVector&, const vector&, const pair&, const pair&, size_t) const; + /** + * Returns a map of how the order of the vectors of a colour basis are + * changed when the indices are changed. + */ + virtual map indexChange(const vector& basis, + const size_t dim, + const map& legPerm) const; + + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The color functions object to be used */ mutable Col_functions colorFunctions; /** * Map legs to known basis vectors. */ mutable map,Trace_basis> theBasisMap; /** * Memorize scalar product intermediate results. */ mutable map theScalarProducts; private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ TraceBasis & operator=(const TraceBasis &) = delete; }; } #endif /* COLORFULL_TraceBasis_H */ diff --git a/MatrixElement/Matchbox/ColorFull/Trace_basis.cc b/MatrixElement/Matchbox/ColorFull/Trace_basis.cc --- a/MatrixElement/Matchbox/ColorFull/Trace_basis.cc +++ b/MatrixElement/Matchbox/ColorFull/Trace_basis.cc @@ -1,255 +1,257 @@ // -*- C++ -*- /* * Trace_basis.cc * Contains the definitions of the class Trace_basis, related types and operators * Created on: Aug 9, 2012 * Author: Malin Sjodahl */ #include "Trace_basis.h" #include #include namespace ColorFull { void Trace_basis::create_basis(int n_quark, int n_gluon){ // Create the basis with an upper bound for n_loop to // keep all basis vectors // Maximal number of "loops" int n_l=n_gluon/2; // Integer division create_basis( n_quark, n_gluon, n_l); } void Trace_basis::create_basis(int n_quark, int n_gluon, int n_loop){ // Setting basis variables nq=n_quark; ng=n_gluon; // If only to finite "loop" the maximal number of qls change max_ql=std::max(nq,1)+n_loop; // Remove a potentially already calculated basis cb.clear(); // The Col_amp containing the basis Col_amp Ca_basis; // Create the basis using the old function for a maximal number of loops Ca_basis=create_trace_basis(nq, ng, n_loop); // Sort the resulting Col_amp into the Col_basis cb for ( uint i=0; i < Ca_basis.ca.size(); i++ ){ // A Col_amp to contain a basis vector Col_amp Ca_vec; Ca_vec.ca.push_back(Ca_basis.ca.at(i)); cb.push_back(Ca_vec); } } Col_amp Trace_basis::create_trace_basis( int n_quark, int n_g, int n_loop ) const { // To contain the resulting basis Col_amp Basis; // First connect all q and qbars in all n_quark! ways // This gives a basis for the case of n_quark quarks and n_quarkbar anti-quarks if( n_quark != 0) Basis= connect_quarks( n_quark ); // If there were no quarks if( n_quark==0 ){ // There has to be at least two gluons if( n_g <= 1 ) { std::cerr << "Trace_basis::create_trace_basis: For 0 quarks there is no basis with " << n_g << " gluons" << std::endl; assert( 0 ); } // If 2 or more gluons, build from the 2-gluon basis else if( n_g>=2 ){ Col_str Cs_OnlyState("[(1,2)]"); Col_amp Ca_tmp; Ca_tmp.ca.push_back(Cs_OnlyState); Basis= Ca_tmp; // For 2 gluons, the work is done if( n_g==2 ) return Basis; //cout << Basis; } } // Then, add the gluons one at the time // If there are only gluons the generation should start from gluon 3, // otherwise from 2*n_quark+1; for( int g_new=std::max(3, 2*n_quark+1); g_new <= 2*n_quark+ n_g; g_new++){ // If only gluons start from the 2-gluon state, so add gluon 3 - Basis=add_one_gluon(Basis, n_quark ,n_g, g_new, n_loop); + Basis=add_one_gluon(Basis, n_quark, g_new, n_loop); } // Remove basis vectors with too many Quark_lines uint max_Ql=std::max(1, n_quark)+n_loop; for(int i=Basis.size()-1; i>-1; i--){ if (Basis.ca.at(i).size() > max_Ql ) Basis.erase(i); } // Normal order the Col_str's Basis.normal_order(); return Basis; } Col_amp Trace_basis::connect_quarks( int n_quark ) const{ Col_amp Basis; // If n_quark=1, there is only one state if( n_quark==1 ) { Col_str Cs_tmp("[{1,2}]"); - Basis=Basis+Cs_tmp; + Basis+=Cs_tmp; } else{ // For more than one q qbar pair, build up states iteratively // Start from the state with one less q qbar state Col_amp Old_bas=connect_quarks( n_quark-1 ); // Loop over old basis vectors for (uint old_v = 0; old_v < Old_bas.ca.size(); old_v++) { // Some, 1*(n_quark-1)!, new states are obtained by just adding q_new qbar_new to the old states // Construct the new Ql with i.e. {q_new, qbar_new} Quark_line Ql_new; Ql_new.open = true; Ql_new.ql.push_back(2 * n_quark - 1); Ql_new.ql.push_back(2 * n_quark); Col_str New_state; New_state.cs.push_back(Ql_new); New_state.append( Old_bas.ca.at(old_v).cs ); - Basis=Basis+New_state; + Basis+= New_state; } // The other (n_quark-1)*(n_quark-1)! states are obtained by combining the new qbar with any old quark // Loop over old basis states for (uint old_v = 0; old_v < Old_bas.ca.size(); old_v++) { // Loop over old qbar indices for (int qbar_old = 2; qbar_old < 2 * n_quark; qbar_old += 2) { // To contain the new basis vector Col_str New_state = Old_bas.ca.at(old_v); // Replace the index qbar_old with 2*nq New_state.replace(qbar_old, 2 * n_quark); // Create the Quark_line {q_new, qbar_old} Quark_line Ql_new; Ql_new.open = true; Ql_new.ql.push_back(2 * n_quark - 1); Ql_new.ql.push_back(qbar_old); // Add the Col_str from {q_new, qbar_old} to the new basis vector New_state.cs.push_back(Ql_new); // Add the new basis tensor to the basis - Basis = Basis + New_state; + Basis += New_state; } } } return Basis; } -Col_amp Trace_basis::add_one_gluon( const Col_str & Cs, int g_new, int ) const { +Col_amp Trace_basis::add_one_gluon( const Col_str & Cs, int g_new ) const { + + //(void) n_loop; // For storing the new basis Col_amp New_tensors; // Add the new gluon in all possible ways to the old Color structure // Loop over the Quark_lines for (uint ql = 0; ql < Cs.cs.size(); ql++) { // The old Quark_line, before insertion of the new gluon index Quark_line Old_Ql = Cs.cs.at(ql); Col_str New_tensor = Cs; // Loop over (potential) insertion places in the Quark_lines, starting from the end for (int j = Old_Ql.ql.size(); j > 0; j--) { // Special treatment of last place, insert here only if the ring is open // (the gluon index cannot take the place of the a quark index) if (Old_Ql.open && j == static_cast(Old_Ql.ql.size()) ) j--; Quark_line New_Ql = Old_Ql; quark_line::iterator it = New_Ql.ql.begin() + j; New_Ql.ql.insert(it, g_new); // Replace the old Col_str with the new and add to the new basis states New_tensor.cs.at(ql) = New_Ql; - New_tensors = New_tensors + New_tensor; + New_tensors += New_tensor; } } return New_tensors; } -Col_amp Trace_basis::add_one_gluon(const Col_amp & Old_basis, int n_q, int, int g_new, int n_loop) const{ +Col_amp Trace_basis::add_one_gluon(const Col_amp & Old_basis, int n_q, int g_new, int n_loop) const{ // For storing the new basis Col_amp New_bas; // Add the new gluon to each of the previous color tensors for (uint t = 0; t < Old_basis.ca.size(); t++) { - New_bas = New_bas + add_one_gluon(Old_basis.ca.at(t), g_new, n_loop); + New_bas = New_bas + add_one_gluon(Old_basis.ca.at(t), g_new); } // Create new states with 2-rings formed from taking the new gluon and any old gluon // these states can be created by taking the ng-2 basis but use the indices // 1...i_ex-1, i_ex+1 ng-1 // for the gluons, that is exclude one index (to be combined with the index ng in a 2-ring) // First, create the ng-2 basis here, must have at least 4 gluons // Do this only is not tree-level mode, i.e. n_loop>0 if ( n_loop>0 && (( n_q>0 && g_new-2*n_q>=2 ) or (n_q==0 && g_new >= 4) )) { Col_amp Bas_n_g_minus_2 = create_trace_basis(n_q, g_new-n_q*2-2, n_loop); // Loop over indices to group with the new gluon index for (int g_ex = 2 * n_q + 1; (g_ex <= g_new - 1 ); g_ex++) { // Create the 2-ring with g_ex and g_new Quark_line Ql2_ring; Ql2_ring.open = false; Ql2_ring.append(g_ex); Ql2_ring.append(g_new); // Loop over Col_str's=basis vectors for (uint bv = 0; bv < Bas_n_g_minus_2.ca.size(); bv++) { Col_str Cs_new; // To contain the new basis vector Cs_new = Bas_n_g_minus_2.ca.at(bv); // Loop over the Quark_lines in the basis vectors for (uint ql = 0; ql < Bas_n_g_minus_2.ca.at(bv).cs.size(); ql++) { // Loop over positions in the quark_line for (uint pos = 0; pos < Bas_n_g_minus_2.ca.at(bv).cs.at(ql).ql.size(); pos++) // Jump over index g_ex by increasing the index g_ex, and all indices above with 1 if (Bas_n_g_minus_2.ca.at(bv).cs.at(ql).ql.at(pos) >= g_ex) { // Change the index if >= g_ex Cs_new.cs.at(ql).ql.at(pos)++; } } Cs_new.cs.push_back(Ql2_ring); - New_bas = New_bas + Cs_new; + New_bas += Cs_new; } } } return New_bas; } }// end namespace ColorFull { diff --git a/MatrixElement/Matchbox/ColorFull/Trace_basis.h b/MatrixElement/Matchbox/ColorFull/Trace_basis.h --- a/MatrixElement/Matchbox/ColorFull/Trace_basis.h +++ b/MatrixElement/Matchbox/ColorFull/Trace_basis.h @@ -1,92 +1,91 @@ // -*- C++ -*- /* * Trace_basis.h * Contains the declarations of the class Trace_basis, related types and operators * Created on: Aug 9, 2012 * Author: Malin Sjodahl */ #ifndef COLORFULL_Trace_basis_h #define COLORFULL_Trace_basis_h #include "Trace_type_basis.h" namespace ColorFull { /// In a trace basis the basis vectors are closed or open quark-lines /// or products of close and open quark-lines. class Trace_basis:public Trace_type_basis { public: /// Default constructor. Trace_basis():Trace_type_basis(){ initialize(); } /// Constructor for creating a trace basis for n_quark qqbar-pairs /// and n_gluon gluons. - /// (Note: For electroweak interactions more color structures may - /// be needed.) Trace_basis( int n_quark, int n_gluon ){ initialize(); create_basis( n_quark, n_gluon ); } /// Constructor for creating a trace basis for n_quark qqbar-pairs - /// and ng gluons, keeping only those color structures that - /// can appear to order n_loop in QCD. + /// and n_gluon gluons, keeping only those color structures that + /// can appear to order n_loop in pure QCD. /// (Note: For electroweak interactions more color structures may /// be needed.) Trace_basis( int n_quark, int n_gluon, int n_loop ){ initialize(); create_basis( n_quark, n_gluon, n_loop ); } - /// Little helper function, called by all constructors. - void initialize(){ - nq=0; - ng=0; - tree_level_gluon_basis = false; - orthogonal_basis = false; - trace_basis = true; - } /******************** Functions for basis creation **********************/ /// Creates a trace basis with basis vectors saved in the cb member. /// Keeps all possible basis vectors, i.e., the basis is valid to any order /// in perturbation theory. void create_basis( int n_q, int n_g ); /// Creates a trace basis with basis vectors saved in the cb member. /// Keeps only basis vectors needed up to n_loop in pure QCD. void create_basis( int n_q, int n_g, int n_loop ); private: /******************** Internal function for basis creation **********************/ + /// Little helper function, called by all constructors. + void initialize(){ + nq=0; + ng=0; + tree_level_gluon_basis = false; + orthogonal_basis = false; + trace_basis = true; + } + /// Function for creating a basis with n_q=n_qbar /// quarks and ng gluons to order n_loop, /// i.e. each Col_str is a product of at most /// max(1+nq)+n_loop Quark_lines. Col_amp create_trace_basis( int n_q, int n_g, int n_loop ) const; /// Connect the n_q quarks in all n_q! ways. /// This function is used when creating a basis. Col_amp connect_quarks( int n_quark ) const; /// Compute the new basis states coming from /// one old vector when one gluon, g_new, is added. - Col_amp add_one_gluon( const Col_str & Cs, int g_new, int n_loop ) const; + Col_amp add_one_gluon( const Col_str & Cs, int g_new ) const; /// Compute the basis if one gluon is added to the old basis Old_basis. /// If n_loop==0, only tree level states are constructed. - Col_amp add_one_gluon( const Col_amp & Old_basis, int n_q, int n_g, int g_new, int n_loop ) const; + Col_amp add_one_gluon( const Col_amp & Old_basis, int n_q, int g_new, int n_loop ) const; }; } /* namespace ColorFull */ #endif /* COLORFULL_Trace_basis_h */ diff --git a/MatrixElement/Matchbox/ColorFull/Trace_type_basis.cc b/MatrixElement/Matchbox/ColorFull/Trace_type_basis.cc --- a/MatrixElement/Matchbox/ColorFull/Trace_type_basis.cc +++ b/MatrixElement/Matchbox/ColorFull/Trace_type_basis.cc @@ -1,692 +1,692 @@ // -*- C++ -*- /* * Trace_type_basis.cc * Contains definition of the class Trace_basis and associated types and operators. * Created on: Aug 9, 2012 * Author: Malin Sjödahl */ #include "Trace_type_basis.h" #include #include namespace ColorFull { Poly_vec Trace_type_basis::decompose( const Col_amp & Ca ) { Col_amp Ca_copy=Ca; Ca_copy.simplify(); // To contain the decomposed vector Poly_vec Decv; Polynomial Zero; - Zero=Zero*0; + Zero*=0; if(cb.size()==0){ std::cerr << "Trace_type_basis::decompose: The basis vector cb is empty consider using create_basis or read in basis." << std::endl; assert( 0 ); } else if(Ca_copy.size()>0 ){ if(Ca_copy.at(0).n_quark() != nq) { std::cerr << "Trace_type_basis::decompose: The number of quarks in the argument Col_amp, " << Ca << ", does not fit the number of quarks in the basis " << nq << "."<< std::endl;} if(Ca_copy.at(0).n_gluon() != ng ) { std::cerr << "Trace_type_basis::decompose: The number of gluons in the argument Col_amp, " << Ca << ", does not fit the number of gluons in the basis " << ng << "."<< std::endl; } } // Initially set all components of the vectors to 0 for (uint m2 = 0; m2 < cb.size(); m2++){ - Decv.push_back(Zero); + Decv.append(Zero); } // Loop over Cs in Ca and check which basis vector they equal for (uint m1 = 0; m1 < Ca_copy.ca.size(); m1++) { for ( uint m2 = 0; m2 < cb.size(); m2++ ) { bool found=false; // For a trace type basis it is enough to compare to see if the basis vector // is equal to the Col_str in the Ca. if (Ca_copy.ca.at(m1).cs == cb.at(m2).at(0).cs) { found=true; - Decv.at(m2)=Decv.at(m2)+Ca_copy.ca.at(m1).Poly; + Decv.at(m2) += Ca_copy.ca.at(m1).Poly; Decv.at(m2).simplify(); } if(found) break; // The tensor is ONE of the tensors in the Basis } } return Decv; } std::pair Trace_type_basis::new_vector_numbers( const Col_str & Cs, int emitter ) { // Number of new gluon int new_g=Cs.n_gluon()+2*Cs.n_quark()+1; // First check that basis is not empty if( cb.empty() ){ std::cerr << "Trace_type_basis::new_vector_numbers: The basis has no vectors, " << "consider using create_basis or read_in_basis." << std::endl; assert( 0 ); } // Then check that number of quarks is the same as for the Cs if( nq != Cs.n_quark() ){ std::cerr << "Trace_type_basis::new_vector_numbers: The number of quarks in the (new) basis, " << nq << " is not the same " << "as the number of quarks in Cs, " << Cs.n_quark() << ", in Cs."<< std::endl; assert( 0 ); } // Then check that number of gluons is one more than for the Cs if( ng != Cs.n_gluon()+1 ){ std::cerr << "Trace_type_basis::new_vector_numbers: The number of gluons in the (new) basis, " << ng << ", is not one plus the number of gluons in Cs << " << Cs.n_gluon()<< std::endl; assert( 0 ); } // To contain the numbers of the new non-zero vectors int plus_comp=-1; int minus_comp=-1; // The vector after emission Col_amp Ca=Col_fun.emit_gluon(Cs, emitter, new_g); Poly_vec vec=decompose( Ca ); // Loop over vector entries to identify the non-zero ones for ( uint veci= 0; veci < cb.size(); veci++) { if( Col_fun. double_num(vec.at(veci))!=0 ){ if(Col_fun.double_num(vec.at(veci)) > 0){plus_comp=veci;} if(Col_fun.double_num(vec.at(veci)) < 0){minus_comp=veci;} } } std::pair res= std::make_pair(plus_comp,minus_comp); return res; } std::pair Trace_type_basis::new_vector_numbers( int old_num, int emitter, int n_loop ) const{ int n_g_old=ng-1; if( !(nq==0 or nq==1 or nq==2) or n_loop!=0 ){ std::cerr << "Trace_type_basis:new_vector_numbers(int, int, int): Function only intended for special case of 0-2 q qbar pair at tree level. For the general case use the general version." << std::endl; assert( 0 ); } // Find the place of the parton std::pair parton_place=find_parton( emitter, old_num, nq, n_g_old, n_loop); // If the q or qbar is radiating, there is only one term if(parton_place.second==0 && nq>=1)// If the q is radiating return std::make_pair(new_vector_number(old_num, std::make_pair(parton_place.first,parton_place.second+1), n_loop),-1); else if(parton_place.second==n_g_old+1 && nq==1) return std::make_pair(-1,new_vector_number(old_num, parton_place, n_loop)); // If the emitter is a g there are two different terms std::pair first_insertion_place, second_insertion_place; if( nq==0 && parton_place.second!=0) { first_insertion_place=std::make_pair(parton_place.first, parton_place.second + 1); second_insertion_place=std::make_pair(parton_place.first, parton_place.second ); } // Special case that the emitter is standing first else if(nq==0 && parton_place.second==0){ first_insertion_place=std::make_pair(0, 1); second_insertion_place=std::make_pair(0, n_g_old ); } else if(nq==1){ // special cases already excluded first_insertion_place=std::make_pair(parton_place.first, parton_place.second+1); second_insertion_place=std::make_pair(parton_place.first, parton_place.second); } else if(nq==2){ // special case of quark already excluded // If the emitter is an anti-quark if(emitter==2 or emitter==4){ return std::make_pair(-1,new_vector_number(old_num, parton_place, n_loop)); } // Emitter is a gluon, and is thus not standing in the ends first_insertion_place=std::make_pair(parton_place.first, parton_place.second+1); second_insertion_place=std::make_pair(parton_place.first, parton_place.second); } int first_tens = new_vector_number(old_num, first_insertion_place, n_loop); int second_tens = new_vector_number(old_num, second_insertion_place, n_loop); return std::make_pair( first_tens, second_tens ); } int Trace_type_basis::new_vector_number( int old_num, std::pair place, int n_loop) const{ int n_g_old=ng-1; std::cout.flush(); if( !( nq==0 or nq==1 or nq==2 ) ){ - std::cerr << "Trace_type_basis::new_vector_number: Function only intended for special case of 1 quark line or 2 open quark lines." << std::endl; + std::cerr << "Trace_type_basis::new_vector_number: Function only intended for special case of 1 or 2 open quark-lines." << std::endl; std::cerr.flush(); assert( 0 ); } if( nq==0 and !tree_level_gluon_basis ){ std::cerr << "Trace_type_basis:new_vector_number: For 0 qqbar-pairs this function is only available for Tree_level_gluon_basis." << std::endl; assert( 0 ); } if( nq>0 and !trace_basis ){ std::cerr << "Trace_type_basis:new_vector_number: The basis type should be Trace_basis for processes with quarks, not Tree_level_gluon_basis." << std::endl; assert( 0 ); } if(place.second == 0 && nq>0 ){ std::cerr << "Trace_type_basis::new_vector_number: Cannot insert gluon at place 0, reserved for quark." << std::endl; std::cerr.flush(); assert( 0 ); } else if(place.second==n_g_old+2 && nq==1){ std::cerr << "Trace_type_basis::new_vector_number: Cannot insert gluon at place n_g_old+2, reserved for qbar." << std::endl; std::cerr.flush(); assert( 0 );} else if(nq==2 && (0 > place.second or place.second > n_g_old+1+1) ){ std::cerr << "Trace_type_basis::new_vector_number: Cannot insert gluon outside range 1...n_g_old." << std::endl; std::cerr.flush(); assert( 0 ); } else if( 0 > place.second or place.second > n_g_old+1+nq){ std::cerr << "Trace_type_basis::new_vector_number: Cannot insert gluon outside range 1...n_g_old ." << std::endl; std::cerr.flush(); assert( 0 ); } // If asked to insert a gluon at place 0, it should be inserted beyond the last place instead if( nq==0 && place.second==0 ){ place.second=n_g_old; } // In the gluons only case we need to keep track of the next position relative to gluon 2 int place2=0; int fnext=old_num; // To contain the remaining part of tensor number // If there are 2 quarks, there is an effective scalar place in {1,g1...gm,2}{3,gm+1...4} // obtained by crossing 2}{3 out. This place is given by // (place in the first ql) if it is in the first ql // (number of gluons in first ql) + place in second if it's in the second ql // If nq=2 the color structure is of type {q1, g1....gm,q2}{q3,gm+1...gn,q4} // This is almost as in the case of only one Ql {q1, g1....gm,gm+1...gn,q4} // with a breakpoint "q2}{q3" inserted or q4}{q3. // To find the tensor number, first locate the breakpoint. // This can be done by noting that there are 2*2*n_g_old! options for each breakpoints, // where the factor 2*2 comes from the options for the quarks // we thus have int break_after=0; int fsplit=0; // Contribution from where the ql is split int fq1=0; // Contribution from first quark if (nq == 2) { // To contain the number of gluon in the first ql // First the number of gluons in 2nd ql // For each gluon in 2nd ql there was // 2 (from nq)*2 (from n_qbar)*factorial(n_g_old) (from the gluons) // tensors with lower numbers from other breaks // Note that for all other splitting the factor from the first q is relevant break_after = old_num /( 2*2*Col_fun.factorial(n_g_old) ) ; fnext= old_num % (2*2*Col_fun.factorial(n_g_old)); // The factor from the split fsplit=old_num-fnext; break_after=n_g_old-break_after; // Check if the first quark is 1 or 3 // The factor from the first quark fq1=(fnext/( 2*Col_fun.factorial(n_g_old) ))*2*Col_fun.factorial(n_g_old); // Special case of 2 quark lines of equal length, then there is no option for q1 (it is 1) if( break_after==n_g_old-break_after ) fq1=0; // 1 is first, no factor from first quark fnext= fnext % (2*Col_fun.factorial(n_g_old)); // First treat the special case when the ql's switch place // This can happen when // 1) The 2nd ql gets longer than the first such as // emission from 6 in {1,5,2}{3,6,4} -> {3,6,7,4}{1,5,2} // 2) The 2nd is one shorter than the first but has the q=1 standing first // {3,5,6,2}{1,7,4} ->{1,8,7,4}{3,5,6,2} if( ((break_after==n_g_old-break_after) && place.first==1) or ((break_after==n_g_old+1-break_after) && fq1>0 && place.first==1)) { // Locate all partons in old ql std::vector< std::pair > > parton_and_place; for( int p=1; p<=n_g_old+2*nq; p++ ){ std::pair p_place=find_parton(p, old_num, nq, n_g_old, n_loop); make_pair(p,p_place); // note that parton p stands at place p-1 parton_and_place.push_back( make_pair(p,p_place) ) ; } // To contain new tensor number int new_num=0; // First calculate the number from the split // After the split the number of gluons in the qls are // break_after in the 2nd ql // This is true for both special cases of form // {1,5,2}{3,6,4} -> {3,6,7,4}{1,5,2} // {3,5,6,2}{1,7,4} ->{1,8,7,4}{3,5,6,2} new_num=(break_after)*2*2*Col_fun.factorial(n_g_old+1); // Then calculate the number from the first quark // There is a factor 2 from the anti-quark and a factor (n_g_old+1)! from the gluons // if the first factor is 3, which it is in swapings of form // {1,5,2}{3,6,4} -> {3,6,7,4}{1,5,2} if( (break_after==n_g_old-break_after) && place.first==1 ) new_num=new_num+2*Col_fun.factorial(n_g_old+1); // In cases of form {3,5,6,2}{1,7,4} ->{1,8,7,4}{3,5,6,2} // the factor from the first quark is 0, and // For the anti-quark qbar=2, check if it stands in first or 2nd ql // If it stands in first ql, there is no contribution to the tensor numbers // otherwise there is a contribution from all orders of gluons in 2nd ql if( parton_and_place.at(2-1).second.first==0) new_num+=Col_fun.factorial(break_after); // The 2nd anti-quark and 2nd quark gives no contribution // Then calculate the contribution to the new tensor number from the gluons // Loop over gluons for( int p=5; p<=n_g_old+2*nq; p++ ){ // The NEW scalar place of the gluon int p_place=parton_and_place.at(p-1).second.second; // contribution from pos in ql // If a gluon was standing in the first ql it is afterwards standing in the 2nd // All gluons in the new first (old 2nd ql) are therefore standing in front // they are n_g_old-break_after +1 (the +1 is added below) if(parton_and_place.at(p-1).second.first==0) p_place+=(n_g_old-break_after); // If the new gluon was inserted before the parton, then the p_place should be increased with one // This happens when the parton stands in the new 2nd ql= old first ql (always) // or when the gluon is in the new first ql, but after the emitter if( parton_and_place.at(p-1).second.first==0 or place.second <=parton_and_place.at(p-1).second.second ) p_place++;// ?? is this right // See how many smaller stand to the right int smaller_right_of_p=0; // Check all gluons with smaller number than p to see how many stand to the right for( int ps=5; psp_place ) smaller_right_of_p++; } // Increase the new tensor number for partons to the right if( smaller_right_of_p>0 ){ // If the gluon stands in the first ql (former 2nd), there is also a factor of 2 from the // choice of anti-quarks // All smaller_right_of_p ways of replacing p with a smaller number contributes a factor // Col_fun.factorial(gluons to the right), and there are smaller_right_of_p options if(parton_and_place.at(p-1).second.first==1) new_num+=2*smaller_right_of_p*Col_fun.factorial(n_g_old+1-p_place); else new_num+=smaller_right_of_p*Col_fun.factorial(n_g_old+1-p_place); } } // End of loop over gluons // Add contribution from the new gluon. The gluon number is larger than every other gluon number, // so all gluons standing to the right are smaller // In the new first ql the new gluon is at place place.second // and there are in total n_g_old-break_after+1 gluons, so the total number of gluons to the right is // break_after+(n_g_old-break_after+1) -place.second int n_right_new=(n_g_old+1)-place.second; // The overall contribution also has a factor 2 from the qbar choice // The factor 2 is from the qbar, Col_fun.factorial(n_right_new) from ordering gluons new_num+=2*n_right_new*Col_fun.factorial(n_right_new); return new_num; } // End of hard case } if ( nq==0 ) place2=find_parton(2, old_num, nq, n_g_old, n_loop).second; // Initially we had q=1, g1,....gn, qbar=2 // After the insertion at place place we have q=1, g1, ...g(place-1), g_new, g(place), ...gn, qbar=2 // The number of the new vector can be seen as a sum of 3 factors: // f1 = the contribution from q=1, g1, ...g(place-1) // fnew= the contribution from the newly inserted gluon // f2= the contribution from g(place), ...gn, qbar=2 (this contribution is the same as before the insertion) // Of these contributions fnew is easiest to calculate it is simply: int fnew=0; // If one ql if( nq==1 ) fnew=(n_g_old - place.second + 1)*Col_fun.factorial(n_g_old - place.second + 1); // Special case of place after last gluon else if( nq==0 && place.second == n_g_old+1 ) fnew=0; // If one closed ql and the parton is to the right of 2 else if( nq==0 && place.second > place2 && place.second < n_g_old+1) fnew=(n_g_old - place.second )*Col_fun.factorial(n_g_old - place.second ); // If the parton is before 2 else if( nq==0 && place2 >= place.second ) fnew=(n_g_old - place.second )*(Col_fun.factorial(n_g_old - place.second )/2); //Rel order 2 3 fixed // The factor f1 is trickier to obtain. Its initial value (before emission) is // sum_{i=1}^(place-1) n_sri (n_p-i)! // where n_sri denotes the number of smaller partons standing to the rigt at place i // these factors are not known, but can be calculated. // Once these factors are known the final value of f1 can also be obtained from // sum_{i=1}^(place-1) n_sri (n_p-i+1)! // where the extra 1 in the factorial is present as after the emission there is // one more parton standing to the right. int f1new=0; // To contain the new f1 int f1old=0; // To contain the old f1 int scalar_place=0; // The scalar place if( place.first!=0 ) scalar_place=break_after; scalar_place+=place.second; if( nq==2 ) { fnew=(n_g_old + 1- scalar_place )*Col_fun.factorial(n_g_old + 1- scalar_place ); // If g inserted in first ql, there is an extra factor of 2 from interchanging quarks if( place.first==0 ) fnew*=2; } // end of if( nq==2 ) // The last relevant parton for calculating the contribution to f1 int last_rel=scalar_place-1; for (int i=1; i<=last_rel; i++){ //cout << "i " << i << endl; // number of partons to the right int n_r=n_g_old-i; if( nq==0 ) n_r--; // nsri is the number of smaller partons to the right int nsri=0; if (nq==1 ) { // Note integer division, how many times do we find the factorial nsri=fnext/(Col_fun.factorial(n_r)); // The contribution to the old f1 f1old+=nsri*Col_fun.factorial(n_r); // The next effective f, to use for finding next nsri fnext=fnext%(Col_fun.factorial(n_r)); // The contribution to the new f1 f1new+=nsri*(Col_fun.factorial(n_r+1)); } else if (nq==2) { int q_part=1; // Keeps track of a possible multiplicative factor from first qbar option if ( i <= break_after) q_part=nq; // Note integer division, how many times do we find the factorial nsri=fnext/(q_part*Col_fun.factorial(n_r)); // The contribution to the old f1 f1old+=nsri*q_part*Col_fun.factorial(n_r); // The next effective f, to use for finding next nsri fnext=fnext%(q_part*Col_fun.factorial(n_r)); // The contribution to the new f1 f1new+=nsri*(q_part*Col_fun.factorial(n_r+1)); // Take care of factor from first qbar "when we reach it" // if( i == break_after && place.second!=break_after+1 && break_after=n_g_old-break_after) { if( i == break_after ) { // Note integer division, how many times do we find the factorial nsri=fnext/(Col_fun.factorial(n_r)); int f1old_before_qbar=f1old; // The contribution to the old f1 f1old+=nsri*Col_fun.factorial(n_r); // The next effective f, to use for finding next nsri fnext=fnext%(Col_fun.factorial(n_r)); // if the gluon is inserted just before the qbar the factor from // the qbar should be taken care of in the f2 instead // (f1old still has to be recalculated as it is used for finding f2 which is unchanged) // If the g is NOT inserted just before the qbar // i.e. if it is either inserted in 2nd ql or at other place than break_after+1 if ( !(scalar_place == break_after+1) or place.first ==1){ // The contribution to the new f1 from the anti-quark //f1new+=nsri*(factorial(n_r+1)); // There are (n_r+1)! options to have 2 before 4 // and we should only have a contribution if 4 is before 2 f1new+=nsri*(Col_fun.factorial(n_r+1)); } else { // If the gluon is inserted just before the qbar // The factor should be accounted for in fnew instead // If the second qbar was 4, this should be compensated for in fnew if( f1old_before_qbar!=f1old && place.first ==0){ // The factor is the number of ways of ordering the gluons in 2nd ql // The q and qbar are fixed and do not contribute // There is no factor if no gluons to the right ?? // There are n_r gluons in the second ql (also after emission) fnew=fnew+Col_fun.factorial(n_r); } } } } // end if (nq==2) // If i is after the place of 2 we have the standard case else if(nq==0 && i >= place2) { nsri=fnext/(Col_fun.factorial(n_r)); f1old+=nsri*Col_fun.factorial(n_r); fnext=fnext%(Col_fun.factorial(n_r)); f1new+=nsri*(Col_fun.factorial(n_r+1)); } // If i is before the place of 2 ordering of 2 and 3 irrelevant else if(nq==0 && i < place2) { nsri=fnext/(Col_fun.factorial(n_r)/2); f1old+=nsri*(Col_fun.factorial(n_r)/2); fnext=fnext%(Col_fun.factorial(n_r)/2); f1new+=nsri*(Col_fun.factorial(n_r+1)/2); } } // Once f1 is known then f2 is simply, int f2=0; if( nq==2 ) { // For 2 qqbar pairs we have to take care of factor from split f2=old_num-f1old-fsplit-fq1; } else f2=old_num-f1old; // and the contribution from f2 is the same as before (irrespectively of position relative 2) // In the case of 2 quarks, if there was a contribution to the tensor number from the split // this has to be added back // The contribution was (n_g_old-break_after)*(2*factorial(n_g_old)) // The special case of ql swapping should already have been taken care of if( nq==2 ) { // Calculating new factor from the split int fsplitnew=0; // New factor from split after insertion in first ql if(place.first==0) { // The length of 2nd ql is then still (n_g_old-break_after) // and for each break there are 2*factorial(n_g_old+1) options // The lengths of first and 2nd ql are never equal as 1st was at least as long as 2nd // -> factor 2 from option of first q fsplitnew=(n_g_old-break_after)*2*2*Col_fun.factorial(n_g_old+1); } // If the insertion was in the second ql (of new length n_g_old-break_after+1) if(place.first==1) { // The length of the 2nd ql is now (n_g_old+1-break_after) // all gluon orders matter, and the qbar matters // Also the factor fomr the split, coming from all "lower splits" // has a factor 2 as the q matters for the lower splits fsplitnew=(n_g_old+1-break_after)*2*2*Col_fun.factorial(n_g_old+1); // 1*2*2! } int fq1new=fq1*(n_g_old+1); // If after emission the 2nd ql has the same length as the first if(place.first==1 && break_after==n_g_old+1-break_after){ fq1new=fq1new/2; // How can this be right?? There should be no factor from first quark then } f1new=fsplitnew+fq1new+f1new; } // End of nq==2 return f1new+fnew+f2; } std::pair Trace_type_basis::find_parton( int parton, int vec_num, int n_quark, int n_gluon, int n_loop ) const{ if( !(n_quark==1 or n_quark==0 or n_quark==2) or n_loop >0 ){ std::cerr << "Trace_type_basis:find_parton: Function only intended for special case of 0-2 qqbar pairs, and a tree level bases." << std::endl; assert( 0 ); } if( n_quark==0 and !tree_level_gluon_basis ){ std::cerr << "Trace_type_basis:find_parton: For 0 qqbar-pairs this function is only available for Tree_level_gluon_basis." << std::endl; assert( 0 ); } if( n_quark>0 and !trace_basis ){ std::cerr << "Trace_type_basis:find_parton: The basis type should be Trace_basis for processes with quarks, not Tree_level_gluon_basis." << std::endl; assert( 0 ); } if( parton==1 && n_quark == 1) return std::make_pair( 0, 0 ); // q=1 or g=1 has position 0 if one ql if( parton==1 && n_quark == 0) return std::make_pair( 0, 0 ); // q=1 or g=1 has position 0 if one ql if( parton==2 && n_quark==1 ) return std::make_pair( 0, n_gluon+1 ); // qbar has last pos // If n_quark=2 the color structure is of type {q1, g1....gm,q2}{q3,gm+1...gn,q4} // This is almost as in the case of only one Ql {q1, g1....gm,gm+1...gn,q4} // with a breakpoint "q2}{q3" inserted or q4}{q3. // To find the tensor number, first locate the breakpoint. // This can be done by noting that there are 2*n_gluon! options for each breakpoints, // where the factor 2 comes from the 2 options for the first anti-quark // we thus have int break_after=n_gluon; if (n_quark == 2) { // There are n_quark!n_quark!n_gluon! assignments for each break break_after = break_after-(vec_num) / (2*2*Col_fun.factorial(n_gluon)); vec_num= vec_num -(n_gluon-break_after)*(2*2*Col_fun.factorial(n_gluon)); // Check if first parton is 1 or 3, there are 2 options for each from the qbar being 2 or 4 bool first3=false; if ( ( vec_num / ( 2*Col_fun.factorial(n_gluon)) ) ) first3=true; vec_num= vec_num % (2*Col_fun.factorial(n_gluon)); // Special cases of asked for quark if(parton == 1){ if(!first3) return std::make_pair(0,0); else return std::make_pair(1,0); } if(parton == 3){ if(first3) return std::make_pair(0,0); else return std::make_pair(1,0); } } // The number of partons gluons with lower parton number than the parton // (and possibly standing to the right, hence -1) int lower=parton-2-n_quark; if ( n_quark==2 ) lower=parton -2*n_quark-1; // quarks don't count // To contain the number of gluons standing to the right of the parton under consideration // and being smaller than 2, noting to the right is smaller than 2, hence 0 // needed for special case of gluons only int lower2=0; // Number of partons to the right, when checking the various positions int n_r=0; // For quark lines(s) all gluons except the one under consideration matter if ( n_quark>0 ) n_r=n_gluon-1; // For a gluon line we have to subtract 1 extra as we start read at place 1 anyway else if ( n_quark==0 ) n_r=n_gluon-2; // Was the gluon 2 found or not, needed for special case of gluons only bool found_2=false; // The resulting quark_line number (can change only in case 2 q qbar pairs) int ql_num=0; // Loop over possible places of the gluon, start with checking place 1 // n_l_r contains the number of partons lower than the parton at the position under consideration int n_l_r=0; // To make compiler not warn int looped_over=0; while(true){ if( n_quark==1 ) { n_l_r=vec_num/Col_fun.factorial(n_r); // The result of integer division vec_num=vec_num%Col_fun.factorial(n_r); // The rest after integer division } if( n_quark==2 ) { // If both qbars to right extra factor 2! if( break_after > looped_over) { n_l_r=vec_num/(Col_fun.factorial(n_r)*2); // The result of integer division vec_num=vec_num%(Col_fun.factorial(n_r)*2); // The rest after integer division } else{ // Check if it is time to compensate for first qbar and second q if ( break_after==looped_over ){ // If we have already looped over all gluons if(n_r==-1) n_r=0; // Special cases if asked for first qbar = 2 or 4 if( parton==2){ // All gluons which are not looped over are to right if (vec_num/(Col_fun.factorial(n_r+1))==0) return std::make_pair(0,break_after+1); else return std::make_pair(1,n_gluon-break_after+1); } if( parton==4){ if (vec_num/(Col_fun.factorial(n_r+1))==1) return std::make_pair(0,break_after+1); else return std::make_pair(1,n_gluon-break_after+1); } vec_num=vec_num%(Col_fun.factorial(n_r+1)); // compensated for first qbar // After looking at first qbar the ql_num should be 1 ql_num=1; } // Otherwise the situation is as for gluons only n_l_r=vec_num/(Col_fun.factorial(n_r)); // The result of integer division vec_num=vec_num%Col_fun.factorial(n_r); // The rest after integer division } } else if (n_quark==0){ if( found_2 ){ n_l_r=vec_num/Col_fun.factorial(n_r); vec_num=vec_num%Col_fun.factorial(n_r); } else{ // First check if 2 was at the position n_l_r=vec_num/(Col_fun.factorial(n_r)); // If 2 was at the place, then all orders matter // If 2 was found if( n_l_r==lower2 ) { found_2=true; vec_num=vec_num%(Col_fun.factorial(n_r)); } else{ // Recalculate knowing that we did not find 2 n_l_r=vec_num/(Col_fun.factorial(n_r)/2); // There is only one relative order of gluon 2 and 3 vec_num=vec_num%(Col_fun.factorial(n_r)/2); // The rest after integer division } } } if( n_l_r==lower ) break; // Number smaller to right = number smaller than parton to right if( n_l_r < lower ) lower--; n_r--; looped_over++; // needed to compare with break in 2q case } // and then the number in the quark_line int pos_num=n_gluon-n_r; // n_quark==1 case if( n_quark==0) pos_num=n_gluon-n_r-1; else if( n_quark==2 && ql_num==0) pos_num=n_gluon-n_r; else if(n_quark==2 && ql_num==1) pos_num=n_gluon-break_after-n_r; return std::make_pair( ql_num, pos_num ); } } // end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Trace_type_basis.h b/MatrixElement/Matchbox/ColorFull/Trace_type_basis.h --- a/MatrixElement/Matchbox/ColorFull/Trace_type_basis.h +++ b/MatrixElement/Matchbox/ColorFull/Trace_type_basis.h @@ -1,104 +1,103 @@ // -*- C++ -*- /* * Trace_type_basis.h * Contains the declarations of the class Trace_type_basis, related types and operators. * Created on: Aug 9, 2012 * Author: Malin Sjödahl */ #ifndef COLORFULL_Trace_type_basis_h #define COLORFULL_Trace_type_basis_h #include "Col_basis.h" namespace ColorFull { /// Trace_type_basis is used for the common features of /// Trace_basis and Tree_level_gluon_basis which both inherit /// from Trace_type_basis. class Trace_type_basis:public Col_basis{ public: /// Default constructor. Trace_type_basis():Col_basis(){ // The maximal number of quark_lines is // nq+n_g/2 for a Trace_basis and 1 for a Tree_level_gluon basis, // but at this time nq and n_g are not known. max_ql=0; } /// A function for decomposing the color amplitude ca in the basis, /// returning the result as a Polynomial. Poly_vec decompose( const Col_amp & Ca ); /// Function for finding the new vector numbers in the new basis /// (this trace basis) /// after radiating a new gluon from the parton emitter. /// The old color structure is Cs, and after emission a linear combination /// of new basis vectors is obtained. - /// For emission from a quark or anti-quark there is only one resulting color + /// For emission from a quark or an anti-quark there is only one resulting color /// structure, and -1 is returned in the place of the absent color structure. /// The second vector, where the new gluon is inserted before the emitter, /// comes with a minus sign in the new total amplitude. std::pair new_vector_numbers( const Col_str & Cs, int emitter ) ; - /// This function is intended for tree level processes with at most 2 qqbar-pairs. + /// This function is intended for tree-level processes with at most 2 qqbar-pairs. /// It finds the new vector numbers in the basis for n_p+1 partons - /// after radiating a new parton from the parton emitter. - /// This function doesn't actually use the cb, but only calculates the + /// after radiating a new gluon from the parton emitter. + /// This function does not actually use the cb, but only calculates /// the basis vector number, which makes it much quicker than the general version. /// The old vector has number old_num, and there were, before emission, /// nq quarks (+ nq anti-quarks) and n_g-1 gluons, i.e. n_p=2 nq+ n_g-1. /// For emission from q or qbar there is only one resulting color structure, /// and -1 is returned in the place of the absent color structure. /// The second vector, where the new gluon is inserted before the emitter /// comes with a minus sign in the new total amplitude. /// The function has been explicitly tested against its sister function /// for initial states with 2 qqbar-pairs and up to 5 gluons, /// 1qqbar-pair and up to 7 gluons and 0 qqbar-pairs and up to 8 gluons. std::pair new_vector_numbers( int old_num, int emitter, int n_loop ) const; - protected: /// The maximal number of quark-lines allowed in the basis. /// This is used for constructing bases that only are valid /// up to a certain order in QCD, such that unused information /// need not be carried around. int max_ql; /// Function for finding the new vector number in the basis with n_p+1 partons (this basis) /// after inserting a new gluon with larger parton number at the place place. /// This function is only intended for the special cases of /// 0 qqbar-pairs and Tree_level_gluon_basis /// or Trace_basis and 1-2 qqbar-pairs at tree level, i.e. n_loop must be 0. /// This function doesn't actually use the cb, but only calculates the /// the basis vector number using find_parton. /// The old vector has number old_num, and there were, before emission /// nq quarks (+ nq anti-quarks) and n_g-1 gluons, i.e. n_p=2 nq + n_g-1. /// The function has been explicitly tested for initial states with 2qqbar-pairs and up to 5 gluons, /// 1qqbar-pair and up to 7 gluons, 0 qqbar-pairs and up to 8 gluons. int new_vector_number( int old_num, std::pair place, int n_loop ) const; /// This function is only intended for special case of: /// 0 qqbar-pairs and Tree_level_gluon_basis /// or Trace_basis and 1-2 qqbar-pairs at tree level, i.e. n_loop must be 0. /// It locates the parton parton in the normal ordered basis, /// given the number of the vector vec_num, and the number of quarks and gluons in the basis. /// The function has been explicitly tested for Trace_basis with 1 qqbar-pair and up to 8 gluons /// and 2 qqbar-pairs and up to 7 gluons, and for Tree_level_gluon_bases with up to 9 gluons. /// The arguments n_quark and n_gluon has to be provided as it may be desirable to /// use the function with a different number of quarks and gluons than in the basis itself. std::pair find_parton( int parton, int vec_num, int n_quark, int n_gluon, int n_loop ) const; }; } #endif /* COLBASIS_H_ */ diff --git a/MatrixElement/Matchbox/ColorFull/Tree_level_gluon_basis.cc b/MatrixElement/Matchbox/ColorFull/Tree_level_gluon_basis.cc --- a/MatrixElement/Matchbox/ColorFull/Tree_level_gluon_basis.cc +++ b/MatrixElement/Matchbox/ColorFull/Tree_level_gluon_basis.cc @@ -1,285 +1,285 @@ // -*- C++ -*- /* * Tree_level_gluon_basis.h * Contains definition of the class Tree_level_gluon_basis and associated types and operators. * Created on: Aug 9, 2012 * Author: Malin Sjodahl */ #include "Tree_level_gluon_basis.h" #include "parameters.h" #include #include #include namespace ColorFull { void Tree_level_gluon_basis::create_basis( int n_gluon ) { // Setting basis variable ng=n_gluon; // Remove a potentially already calculated basis cb.clear(); // The Col_amp containing the basis Col_amp Ca_basis; // Create the basis using the old function for a maximal number of loops Ca_basis = create_trace_basis(ng); // Sort the resulting Col_amp into the Col_basis cb for ( uint i = 0; i < Ca_basis.ca.size(); i++ ) { // A Col_amp to contain a basis vector Col_amp Ca_vec; Ca_vec.ca.push_back(Ca_basis.ca.at(i)); cb.push_back(Ca_vec); } } void Tree_level_gluon_basis::read_in_Col_basis( std::string filename ){ // First read in basis as normally // Read in file std::ifstream fin(filename.c_str()); // Check that file exists if( !fin ){ std::cerr << "Tree_level_gluon_basis::read_in_Col_basis: The file " << filename << " could not be opened." << std::endl; assert( 0 ); } // Erase current information cb.clear(); // Copy info from file to string std::string str((std::istreambuf_iterator(fin)), std::istreambuf_iterator()); Col_basis_of_str( str ); // Then check that it's really a tree level gluon basis // Check that length of each Col_amp is 2 for( uint bv=0; bv < cb.size(); bv++ ){ if( cb.at(bv).size()!=2 ){ std::cerr << "Tree_level_gluon_basis::read_in_Col_basis: The basis read in from file " << filename << " has basis vectors with length > 2, and is thus not a tree level gluon basis." << std::endl; assert( 0 ); } // Check that the first term is the conjugate of the second // Check col_str exactly Col_str Cs_conj=cb.at(bv).at(1) ; Cs_conj.conjugate(); Cs_conj.simplify(); if( cb.at(bv).at(0).cs != Cs_conj.cs ){ std::cerr << "Tree_level_gluon_basis::read_in_Col_basis: The basis read in from file " << filename << " has basis vector " << bv << " with non self-conjugate color structure " << cb.at(bv) <<". The col_str " << cb.at(bv).at(0).cs << " is not the same as " << Cs_conj.cs << std::endl; assert( 0 ); } // Check Polynomial numerically if( std::abs( Col_fun. cnum_num(cb.at(bv).at(0).Poly - (pow(-1.0, ng))* Cs_conj.Poly) ) > accuracy ){ std::cerr << "Tree_level_gluon_basis::read_in_Col_basis: The basis read in from file " << filename << " has basis vector " << bv << " which is not real due to multiplying Polynomial." << std::endl; assert( 0 ); } // Then, remove implicit part cb.at(bv).erase(1); } } void Tree_level_gluon_basis::write_out_Col_basis( ) const{ write_out_Col_basis( basis_file_name() ); } void Tree_level_gluon_basis::read_in_Col_basis( ) { read_in_Col_basis( basis_file_name() ); } void Tree_level_gluon_basis::write_out_Col_basis( std::string filename ) const{ if( (cb.size()==0 ) ) { std::cout << "Tree_level_gluon_basis::write_out_Col_basis(string): There are no basis vectors in this basis, consider using create_basis or read_in_basis." << std::endl; std::cout.flush(); return ; } std::ofstream outfile(filename.c_str()); if ( !outfile ) std::cerr << "Tree_level_gluon_basis::write_out_Col_basis: Cannot write out basis as the file \"" << filename.c_str() << "\" could not be opened. (Does the directory exist? Consider creating the directory.)" << std::endl; int sign=pow(-1,cb.at(0).n_gluon()); for (uint m = 0; m < cb.size(); m++) { outfile << m << " "<< cb.at(m); if(sign==1 ) outfile << " + "; else outfile << " - "; Col_amp Ca_conj=cb.at(m); Ca_conj.conjugate(); Ca_conj.normal_order(); outfile << Ca_conj << std::endl; } outfile.flush(); } -void Tree_level_gluon_basis::write_out_Col_basis_to_cout() const{ +std::ostream& Tree_level_gluon_basis::write_out_Col_basis_to_stream( std::ostream& out ) const{ if( (cb.size()==0 ) ) { std::cout << "Tree_level_gluon_basis::write_out_Col_basis(): There are no basis vectors in this basis, consider using create_basis." << std::endl; std::cout.flush(); - return ; } int sign=pow(-1,cb.at(0).n_gluon()); for (uint m = 0; m < cb.size(); m++) { std::cout << m << " "<< cb.at(m); if(sign==1 ) std::cout << " + "; else std::cout << " - "; Col_amp Ca_conj=cb.at(m); Ca_conj.conjugate(); Ca_conj.normal_order(); - std::cout << Ca_conj << std::endl; + out << Ca_conj << std::endl; } + return out; } Polynomial Tree_level_gluon_basis::ij_entry( const int i, const int j ) const{ // Loop over basis vectors in Basis Polynomial ijEntry; uint Ng=cb.at(i).n_gluon(); // The sign of the interference, (-1)^Ng int sign=(Ng % 2 ? -1:1); Col_amp Cbi_conj=cb.at(i); Cbi_conj.conjugate(); ijEntry=2*Col_fun.scalar_product( cb.at(i), cb.at(j) ) +sign*2*Col_fun.scalar_product( cb.at(j), Cbi_conj ); ijEntry.simplify(); return ijEntry; } Col_amp Tree_level_gluon_basis::create_trace_basis( int n_g ) const { // To contain the resulting basis Col_amp Basis; // There has to be at least two gluons if (n_g <= 1) { std::cerr << "Tree_level_gluon_basis::create_trace_basis: For 0 quarks there is no basis with only " << n_g << " gluons" << std::endl; assert( 0 ); } // If 2 or more gluons, build from the 2-gluon basis else if (n_g >= 2) { Col_str Cs_OnlyState("[(1,2)]"); Col_amp Ca_tmp; Ca_tmp.ca.push_back(Cs_OnlyState); Basis = Ca_tmp; // For 2 gluons, the work is done if (n_g == 2) return Basis; } // Then, add the gluons one at the time // As there are only gluons the generation should start from gluon 3, // otherwise from 2*n_q+1; for (int g_new = 3; g_new <= n_g; g_new++) { // If only gluons start from the 2-gluon state, so add gluon 3 - Basis = add_one_gluon(Basis, n_g, g_new); + Basis = add_one_gluon(Basis, g_new); } // Normal order the Col_str's Basis.normal_order(); return Basis; } Col_amp Tree_level_gluon_basis::add_one_gluon( const Col_str & Cs, int g_new ) const { // For storing the new basis Col_amp New_tensors; // Add the new gluon in all possible ways to the old Color structure // Loop over the Quark_lines for (uint ql = 0; ql < Cs.cs.size(); ql++) { // The old Quark_line, before insertion of the new gluon index Quark_line Old_Ql = Cs.cs.at(ql); Col_str New_tensor = Cs; // Special case of insertion of a gluon in a 2-ring in a gluons only basis // in this case only "half" the basis states for rings with >= 3 gluons are // generated. The rest are obtained by taking the indices in anti-cyclic order // i.e. by complex conjugating if ((Old_Ql.ql.size() == 2 && Cs.gluons_only())) { // Insert the new gluon after the existing gluons Quark_line New_Ql = Old_Ql; New_Ql.ql.push_back(g_new); // Replace the old Col_str with the new and add to the new basis states New_tensor.cs.at(ql) = New_Ql; New_tensors = New_tensors + New_tensor; } else { // ordinary case // Loop over (potential) insertion places in the Quark_lines, starting from the end for (int j = Old_Ql.ql.size(); j > 0; j--) { // Special treatment of last place, insert here only if the ring is open // (the gluon index cannot take the place of the a quark index) if (Old_Ql.open && j == static_cast(Old_Ql.ql.size())) j--; Quark_line New_Ql = Old_Ql; quark_line::iterator it = New_Ql.ql.begin() + j; New_Ql.ql.insert(it, g_new); // Replace the old Col_str with the new and add to the new basis states New_tensor.cs.at(ql) = New_Ql; New_tensors = New_tensors + New_tensor; } } } return New_tensors; } -Col_amp Tree_level_gluon_basis::add_one_gluon(const Col_amp & Old_basis, int, int g_new) const { +Col_amp Tree_level_gluon_basis::add_one_gluon(const Col_amp & Old_basis, int g_new) const { // For storing the new basis Col_amp New_bas; // Add the new gluon to each of the previous color tensors for (uint t = 0; t < Old_basis.ca.size(); t++) { New_bas = New_bas + add_one_gluon(Old_basis.ca.at(t), g_new); } return New_bas; } } // end namespace ColorFull diff --git a/MatrixElement/Matchbox/ColorFull/Tree_level_gluon_basis.h b/MatrixElement/Matchbox/ColorFull/Tree_level_gluon_basis.h --- a/MatrixElement/Matchbox/ColorFull/Tree_level_gluon_basis.h +++ b/MatrixElement/Matchbox/ColorFull/Tree_level_gluon_basis.h @@ -1,116 +1,116 @@ // -*- C++ -*- /* * Trace_basis.h * Contains the declarations of the class Tree_level_gluon_basis, related types and operators * Created on: Aug 9, 2012 * Author: Malin Sjodahl */ #ifndef COLORFULL_Tree_level_gluon_basis_h #define COLORFULL_Tree_level_gluon_basis_h #include "Trace_type_basis.h" namespace ColorFull { /// This class is for containing tree level gluon bases, /// i.e. bases of form Tr(t^a t^b....t^z) +/- Tr(t^z .... t^b t^a), /// where the sign is given by (-1)^n_g. /// Technically only one of the quark-lines are carried /// around in the cb member, the other is implicit. /// This speeds up calculations. class Tree_level_gluon_basis:public Trace_type_basis { public: /// Default constructor. Tree_level_gluon_basis():Trace_type_basis(){ initialize(); } - /// Constructor for creating a tree level gluon basis for n_g gluons. + /// Constructor for creating a tree level gluon basis with n_g gluons. Tree_level_gluon_basis( int n_g ):Trace_type_basis(){ initialize(); create_basis( n_g ); } - /// Little helper function, called by constructors. - void initialize(){ - tree_level_gluon_basis = true; - max_ql=1; - } - /******************** Basis creation **********************/ // Special functions are needed as half the color structure is implicit /// Creates a basis with basis vectors saved in the cb member. /// Each basis vector is a sum of two traces, /// of form Tr(t^a t^b....t^z) +/- Tr(t^z .... t^b t^a). /// The charge conjugated trace /// is implicit, and only one trace is actually /// carried around. void create_basis( int n_g ); /******************** Basis reading and writing **********************/ /// Function for reading in the basis from a file. /// The file should contain the whole basis, /// including the charge conjugated part. void read_in_Col_basis( std::string filename ); /// Function reading in the basis from default name /// (see basis_file_name in the Col_basis class). /// The full basis, including the charge conjugated - /// part is written out. (This is to simplify + /// part should be contained in the file. (This is to simplify /// comparison with other programs, such as ColorMath.) void read_in_Col_basis( ); /// Function for writing out the basis to default name, /// (see basis_file_name in the Col_basis class). /// The full basis, including the charge conjugated /// part is written out. (This is to simplify /// comparison with other programs, such as ColorMath.) void write_out_Col_basis( ) const; /// Function for writing out the basis to filename, /// (see basis_file_name in the Col_basis class). /// The full basis, including the charge conjugated /// part is written out. (This is to simplify /// comparison with other programs, such as ColorMath.) void write_out_Col_basis( std::string filename ) const; - /// Function for writing out the basis to cout. - /// As the charge conjugated quark_line is implicit, - /// a special function is needed for the Tree_level_gluon_basis case. - void write_out_Col_basis_to_cout() const; protected: + /// Function for writing out the basis in a human readable + /// format to an ostream. + virtual std::ostream& write_out_Col_basis_to_stream( std::ostream& out ) const; + private: + /// Little helper function, called by constructors. + void initialize(){ + tree_level_gluon_basis = true; + max_ql=1; + } + /// Calculate element ij in scalar product matrix /// using the implicit presence of a charge conjugated Col_str. Polynomial ij_entry( const int i, const int j ) const; /******************** Basis creation **********************/ /// Function for creating a basis with n_g gluons. Col_amp create_trace_basis( int n_g ) const; /// Computes the new basis states coming from one old Col_str /// when one gluon, g_new, is added. /// g_new is the number of the new gluon. Col_amp add_one_gluon( const Col_str & Cs, int g_new ) const; /// Compute the basis if one gluon is added to the old basis Old_basis. /// (For a Trace_type_basis, the basis is can be contained in a /// Col_amp, and this is the case for Old_basis). /// g_new is the number of the new gluon. /// Used by create_basis. - Col_amp add_one_gluon( const Col_amp & Old_basis, int n_g, int g_new ) const; + Col_amp add_one_gluon( const Col_amp & Old_basis, int g_new ) const; }; } /* namespace ColorFull */ #endif /* COLORFULL_Tree_level_gluon_basis_h */ diff --git a/MatrixElement/Matchbox/Phasespace/FFMassiveInvertedTildeKinematics.cc b/MatrixElement/Matchbox/Phasespace/FFMassiveInvertedTildeKinematics.cc --- a/MatrixElement/Matchbox/Phasespace/FFMassiveInvertedTildeKinematics.cc +++ b/MatrixElement/Matchbox/Phasespace/FFMassiveInvertedTildeKinematics.cc @@ -1,392 +1,325 @@ // -*- C++ -*- // // FFMassiveInvertedTildeKinematics.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 FFMassiveInvertedTildeKinematics class. // #include "FFMassiveInvertedTildeKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "Herwig/MatrixElement/Matchbox/Phasespace/RandomHelpers.h" using namespace Herwig; -FFMassiveInvertedTildeKinematics::FFMassiveInvertedTildeKinematics() - : theFullJacobian(true) {} +FFMassiveInvertedTildeKinematics::FFMassiveInvertedTildeKinematics() {} FFMassiveInvertedTildeKinematics::~FFMassiveInvertedTildeKinematics() {} IBPtr FFMassiveInvertedTildeKinematics::clone() const { return new_ptr(*this); } IBPtr FFMassiveInvertedTildeKinematics::fullclone() const { return new_ptr(*this); } +// Matches Stephen Webster's thesis bool FFMassiveInvertedTildeKinematics::doMap(const double * r) { - // todo - SW: Sort out all of the notation in the matchbox - // kinematics to match the manual, before manual release. if ( ptMax() < ptCut() ) { jacobian(0.0); return false; } Lorentz5Momentum emitter = bornEmitterMomentum(); Lorentz5Momentum spectator = bornSpectatorMomentum(); double mapping = 1.0; vector values(6); pair ptz = generatePtZ(mapping, r, &values); if ( mapping == 0.0 ) { jacobian(0.0); return false; } - // pt and zPrime = qi.nk / (qi+qj).nk are the generated variables Energy pt = ptz.first; Energy2 pt2 = sqr(pt); - double zPrime = ptz.second; - + double z = ptz.second; + + // masses + double mui2 = sqr( realEmitterData()->hardProcessMass() / lastScale() ); + double muj2 = sqr( realEmissionData()->hardProcessMass() / lastScale() ); + double muk2 = sqr( realSpectatorData()->hardProcessMass() / lastScale() ); + double Muij2 = sqr( bornEmitterData()->hardProcessMass() / lastScale() ); + double Muk2 = sqr( bornSpectatorData()->hardProcessMass() / lastScale() ); // Define the scale Energy2 Qijk = sqr(lastScale()); // Most of the required values have been calculated in ptzAllowed double y = values[0]; - double z = values[1]; - double A = values[2]; - double xk = values[3]; - double xij = values[4]; - double suijk = values[5]; - double suijk2 = sqr(suijk); - - // masses - double mui2 = sqr( realEmitterData()->hardProcessMass() / lastScale() ); - double mu2 = sqr( realEmissionData()->hardProcessMass() / lastScale() ); - //double muj2 = sqr( realSpectatorData()->hardProcessMass() / lastScale() ); - double Mui2 = sqr( bornEmitterData()->hardProcessMass() / lastScale() ); - double Muj2 = sqr( bornSpectatorData()->hardProcessMass() / lastScale() ); + double zi = values[1]; + double xk = values[2]; + double xij = values[3]; + double QijN2 = values[4]; + double sijkN = values[5]; + double sijkN2 = sqr(sijkN); + // Construct reference momenta nk, nij + Lorentz5Momentum nij = ( sijkN2 / (sijkN2-Muij2*Muk2) ) + * (emitter - (Muij2/sijkN)*spectator); + Lorentz5Momentum nk = ( sijkN2 / (sijkN2-Muij2*Muk2) ) + * (spectator - (Muk2/sijkN)*emitter); - // Construct reference momenta nk, nij, nt - Lorentz5Momentum nij = ( suijk2 / (suijk2-Mui2*Muj2) ) - * (emitter - (Mui2/suijk)*spectator); - Lorentz5Momentum nk = ( suijk2 / (suijk2-Mui2*Muj2) ) - * (spectator - (Muj2/suijk)*emitter); - - // Following notation in notes, qt = sqrt(wt)*nt + // Construct qt double phi = 2.*Constants::pi*r[2]; - Lorentz5Momentum qt = getKt(nij,nk,pt,phi); + Lorentz5Momentum qt = getKt(emitter,spectator,pt,phi); // Construct qij, qk, qi and qj - Lorentz5Momentum qij = xij*nij + (Mui2/(xij*suijk))*nk; - Lorentz5Momentum spe = xk*nk + (Muj2/(xk*suijk))*nij; + Lorentz5Momentum qij = xij*nij + (Muij2/(xij*sijkN))*nk; + Lorentz5Momentum spe = xk*nk + (Muk2/(xk*sijkN))*nij; - Lorentz5Momentum em = zPrime*qij - + ((pt2/Qijk + mui2 - zPrime*zPrime*Mui2)/(xij*suijk*zPrime))*nk + qt; - Lorentz5Momentum emm = (1.-zPrime)*qij - + ((pt2/Qijk + mu2 - sqr(1.-zPrime)*Mui2)/(xij*suijk*(1.-zPrime)))*nk - qt; + Lorentz5Momentum em = z*qij + + ((pt2/Qijk + mui2 - z*z*Muij2)/(xij*sijkN*z))*nk + qt; + Lorentz5Momentum emm = (1.-z)*qij + + ((pt2/Qijk + muj2 - sqr(1.-z)*Muij2)/(xij*sijkN*(1.-z)))*nk - qt; em.setMass(realEmitterData()->hardProcessMass()); em.rescaleEnergy(); emm.setMass(realEmissionData()->hardProcessMass()); emm.rescaleEnergy(); spe.setMass(realSpectatorData()->hardProcessMass()); spe.rescaleEnergy(); // book realEmitterMomentum() = em; realEmissionMomentum() = emm; realSpectatorMomentum() = spe; + + // Calculate the jacobian + double bar = 1.-mui2-muj2-muk2; - // Compute and store the jacobian - // The jacobian here corresponds to dpt2 / sqr(lastscale) NOT dpt2 / pt2. - // This jacobian is the one-particle phase space - - // jac s.t.: dy dz = jac* dpt2/sqr(lastScale()) dz - double jac = 0.0; - - // SW - Change in notation here that needs to be fixed (j<->nothing<->k) - Energy2 mi2 = sqr(realEmitterData()->hardProcessMass()); - Energy2 mj2 = sqr(realEmissionData()->hardProcessMass()); - Energy2 mk2 = sqr(realSpectatorData()->hardProcessMass()); - Energy2 mij2 = sqr(bornEmitterData()->hardProcessMass()); - Energy2 sbar = Qijk - mi2 - mj2 -mk2; - - if ( theFullJacobian && bornSpectatorData()->hardProcessMass() != ZERO ) { - - Energy2 sijk = Qijk*suijk; - - double lambdaK = 1. + (mk2/sijk); - double lambdaIJ = 1. + (mij2/sijk); - - // Compute dy/dzPrime and pt2* dy/dpt2 - double dyBydzPrime = (1./sbar) * - ( -pt2*(1.-2.*zPrime)/sqr(zPrime*(1.-zPrime)) - - mi2/sqr(zPrime) + mj2/sqr(1.-zPrime) ); - InvEnergy2 dyBydpt2 = 1./(sbar*zPrime*(1.-zPrime)); - - // Compute dA/dzPrime and dA/dpt2 - double dABydzPrime = (sbar/sijk) * dyBydzPrime; - InvEnergy2 dABydpt2 = (sbar/sijk) * dyBydpt2; - - // Compute dxk/dzPrime, dxk/dpt2, dxij/dzPrime and dxij/dpt2 - double factor = (0.5/lambdaK) * - (-1. - - (1./sqrt( sqr(lambdaK + (mk2/sijk)*lambdaIJ - A) - - 4.*lambdaK*lambdaIJ*mk2/sijk)) - * (lambdaK + (mk2/sijk)*lambdaIJ - A) ); - - double dxkBydzPrime = factor * dABydzPrime; - InvEnergy2 dxkBydpt2 = factor * dABydpt2; - - double dxijBydzPrime = (mk2/sijk) * (1./sqr(xk)) * dxkBydzPrime; - InvEnergy2 dxijBydpt2 = (mk2/sijk) * (1./sqr(xk)) * dxkBydpt2; - - Energy2 dqiDotqkBydzPrime = xij*xk*0.5*sijk - + zPrime*dxijBydzPrime*xk*0.5*sijk + zPrime*xij*dxkBydzPrime*0.5*sijk - + 0.5*(mk2/sijk)*(pt2 + mi2) - * (-1./(xk*xij*sqr(zPrime)) - dxkBydzPrime/(zPrime*xij*sqr(xk)) - - dxijBydzPrime/(zPrime*xk*sqr(xij))); - - double dqiDotqkBydpt2 = dxijBydpt2*zPrime*xk*0.5*sijk - + zPrime*xij*dxkBydpt2*0.5*sijk - + (0.5*mk2/sijk) * (1./(zPrime*xk*xij)) - * (1. + (pt2+mi2)*(-dxkBydpt2/xk - dxijBydpt2/xij) ); - - - // Compute dzBydzPrime and dzBydpt2 - Energy2 qiDotqk = (zPrime*xij*xk*sijk*0.5) - + (mk2/ ( 2.*xk*xij*sijk*zPrime))*(pt2 + mi2); - - double dzBydzPrime = (1./sbar) - * ( 2.*qiDotqk*dyBydzPrime/sqr(1.-y) + (1./(1.-y))*2.*dqiDotqkBydzPrime ); - InvEnergy2 dzBydpt2 = (1./sbar) - * ( 2.*qiDotqk*dyBydpt2/sqr(1.-y) + (1./(1.-y))*2.*dqiDotqkBydpt2 ); - - // Compute the jacobian - jac = Qijk * abs(dzBydpt2*dyBydzPrime - dzBydzPrime*dyBydpt2); - - } - - // This is correct in the massless spectator case. - else { - jac = Qijk / (sbar*zPrime*(1.-zPrime)); - } + // mapFactor defined as dy dz = mapFactor * dpt2/sqr(lastScale()) dz + double mapFactor = 0.0; + mapFactor = y*(sqr(lastScale()) / (pt2 + sqr(1.-z)*mui2*Qijk + sqr(z)*muj2*Qijk)) + * abs(1. - 2.*Muk2*QijN2 / (bar*(1.-y)*xij*xk*sijkN)); // Mapping includes only the variable changes/jacobians - mapping *= jac; - jacobian( (sqr(sbar)/rootOfKallen(Qijk,mij2,mk2)) * mapping*(1.-y)/(16.*sqr(Constants::pi)) / sHat() ); + mapping *= mapFactor; + jacobian( (Qijk*sqr(bar)/rootOfKallen(1.,Muij2,Muk2)) * mapping + * (1.-y)/(16.*sqr(Constants::pi)) / sHat() ); // Store the parameters subtractionParameters().resize(3); subtractionParameters()[0] = y; - subtractionParameters()[1] = z; - subtractionParameters()[2] = zPrime; - + subtractionParameters()[1] = zi; + subtractionParameters()[2] = z; + return true; - } Energy FFMassiveInvertedTildeKinematics::lastPt() const { Energy scale = (bornEmitterMomentum()+bornSpectatorMomentum()).m(); // masses double mui2 = sqr( realEmitterData()->hardProcessMass() / scale ); - double mu2 = sqr( realEmissionData()->hardProcessMass() / scale ); - double muj2 = sqr( realSpectatorData()->hardProcessMass() / scale ); + double muj2 = sqr( realEmissionData()->hardProcessMass() / scale ); + double muk2 = sqr( realSpectatorData()->hardProcessMass() / scale ); double y = subtractionParameters()[0]; - double zPrime = subtractionParameters()[2]; + double z = subtractionParameters()[2]; - Energy ret = scale * sqrt( y * (1.-mui2-mu2-muj2) * zPrime*(1.-zPrime) - sqr(1.-zPrime)*mui2 - sqr(zPrime)*mu2 ); + Energy ret = scale * sqrt( y * (1.-mui2-muj2-muk2) * z*(1.-z) - sqr(1.-z)*mui2 - sqr(z)*muj2 ); return ret; } double FFMassiveInvertedTildeKinematics::lastZ() const { return subtractionParameters()[2]; } Energy FFMassiveInvertedTildeKinematics::ptMax() const { Energy scale = (bornEmitterMomentum()+bornSpectatorMomentum()).m(); // masses double mui2 = sqr( realEmitterData()->hardProcessMass() / scale ); - double mu2 = sqr( realEmissionData()->hardProcessMass() / scale ); - double muj2 = sqr( realSpectatorData()->hardProcessMass() / scale ); + double muj2 = sqr( realEmissionData()->hardProcessMass() / scale ); + double muk2 = sqr( realSpectatorData()->hardProcessMass() / scale ); - Energy ptmax = rootOfKallen( mui2, mu2, sqr(1.-sqrt(muj2)) ) / - ( 2.-2.*sqrt(muj2) ) * scale; + Energy ptmax = rootOfKallen( mui2, muj2, sqr(1.-sqrt(muk2)) ) / + ( 2.-2.*sqrt(muk2) ) * scale; return ptmax > 0.*GeV ? ptmax : 0.*GeV; } // NOTE: bounds calculated at this step may be too loose -// These apply to zPrime, which is stored in lastZ() pair FFMassiveInvertedTildeKinematics::zBounds(Energy pt, Energy hardPt) const { hardPt = hardPt == ZERO ? ptMax() : min(hardPt,ptMax()); if(pt>hardPt) return make_pair(0.5,0.5); Energy scale = (bornEmitterMomentum()+bornSpectatorMomentum()).m(); // masses double mui2 = sqr( realEmitterData()->hardProcessMass() / scale ); - double mu2 = sqr( realEmissionData()->hardProcessMass() / scale ); - double muj2 = sqr( realSpectatorData()->hardProcessMass() / scale ); + double muj2 = sqr( realEmissionData()->hardProcessMass() / scale ); + double muk2 = sqr( realSpectatorData()->hardProcessMass() / scale ); - double zp = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) + - rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * + double zp = ( 1.+mui2-muj2+muk2-2.*sqrt(muk2) + + rootOfKallen(mui2,muj2,sqr(1.-sqrt(muk2))) * sqrt( 1.-sqr(pt/hardPt) ) ) / - ( 2.*sqr(1.-sqrt(muj2)) ); - double zm = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) - - rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * + ( 2.*sqr(1.-sqrt(muk2)) ); + double zm = ( 1.+mui2-muj2+muk2-2.*sqrt(muk2) - + rootOfKallen(mui2,muj2,sqr(1.-sqrt(muk2))) * sqrt( 1.-sqr(pt/hardPt) ) ) / - ( 2.*sqr(1.-sqrt(muj2)) ); + ( 2.*sqr(1.-sqrt(muk2)) ); return make_pair(zm,zp); } +// Matches Stephen Webster's thesis bool FFMassiveInvertedTildeKinematics::ptzAllowed(pair ptz, vector* values) const { Energy pt = ptz.first; Energy2 pt2 = sqr(pt); - double zPrime = ptz.second; + double z = ptz.second; // masses double mui2 = sqr( realEmitterData()->hardProcessMass() / lastScale() ); - double mu2 = sqr( realEmissionData()->hardProcessMass() / lastScale() ); - double muj2 = sqr( realSpectatorData()->hardProcessMass() / lastScale() ); - double Mui2 = sqr( bornEmitterData()->hardProcessMass() / lastScale() ); - double Muj2 = sqr( bornSpectatorData()->hardProcessMass() / lastScale() ); + double muj2 = sqr( realEmissionData()->hardProcessMass() / lastScale() ); + double muk2 = sqr( realSpectatorData()->hardProcessMass() / lastScale() ); + double Muij2 = sqr( bornEmitterData()->hardProcessMass() / lastScale() ); + double Muk2 = sqr( bornSpectatorData()->hardProcessMass() / lastScale() ); // Calculate the scales that we need Energy2 Qijk = sqr(lastScale()); - double suijk = 0.5*( 1. - Mui2 - Muj2 + sqrt( sqr(1.-Mui2-Muj2) - 4.*Mui2*Muj2 ) ); - double bar = 1.-mui2-mu2-muj2; + double QijN2 = (pt2/Qijk + (1.-z)*mui2 + z*muj2) / z / (1.-z); + double sijkN = 0.5*( 1. - Muij2 - Muk2 + sqrt( sqr(1.-Muij2-Muk2) - 4.*Muij2*Muk2 ) ); + double bar = 1.-mui2-muj2-muk2; - double y = ( pt2/Qijk + sqr(1.-zPrime)*mui2 + zPrime*zPrime*mu2 ) / - (zPrime*(1.-zPrime)*bar); - - // Calculate A:=xij*w - double A = (1./(suijk*zPrime*(1.-zPrime))) * ( pt2/Qijk + zPrime*mu2 + (1.-zPrime)*mui2 - zPrime*(1.-zPrime)*Mui2 ); + double y = ( pt2/Qijk + sqr(1.-z)*mui2 + z*z*muj2 ) / + (z*(1.-z)*bar); // Calculate the scaling factors, xk and xij - double lambdaK = 1. + (Muj2/suijk); - double lambdaIJ = 1. + (Mui2/suijk); - double xk = (1./(2.*lambdaK)) * ( (lambdaK + (Muj2/suijk)*lambdaIJ - A) + sqrt( sqr(lambdaK + (Muj2/suijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*Muj2/suijk) ); - double xij = 1. - ( (Muj2/suijk) * (1.-xk) / xk ); + double lambdaK = 1. + (Muk2/sijkN); + double lambdaIJ = 1. + (Muij2/sijkN); + double fac1 = lambdaIJ*lambdaK + (muk2 - QijN2)/sijkN; + double xk = + ( fac1 + sqrt( sqr(fac1) - 4.*lambdaIJ*lambdaK*muk2/sijkN ) ) + / 2. / lambdaK ; + double xij = 1. - muk2*(1.-xk) / xk / sijkN; - // Calculate z - double z = ( (zPrime*xij*xk*suijk/2.) + (Muj2/ ( 2.*xk*xij*suijk*zPrime))*(pt2/Qijk + mui2) ) / - ( (xij*xk*suijk/2.) + (Muj2/(2.*xk*xij))*(Mui2/suijk + A) ); + // Calculate zi + double zi = + ( z*xij*xk*sijkN + muk2*(pt2/Qijk + mui2) / (z*xij*xk*sijkN) ) + / (1.-y) / bar; + + // Limits on zi + double facA = (2.*mui2+bar*y)/2./(mui2 + muj2 + bar*y); + double facB = + sqrt( (sqr(2.*muk2 + bar*(1.-y)) - 4.*muk2) * + (sqr(bar)*sqr(y) - 4.*mui2*muj2)) + / bar / (1.-y) / (bar*y + 2.*mui2); + double zim = facA * (1. - facB); + double zip = facA * (1. + facB); // check (y,z) phase space boundary - // TODO: is y boundary necessary? - double ym = 2.*sqrt(mui2)*sqrt(mu2)/bar; - double yp = 1. - 2.*sqrt(muj2)*(1.-sqrt(muj2))/bar; - // These limits apply to z, not zPrime - double zm = ( (2.*mui2+bar*y)*(1.-y) - sqrt(y*y-ym*ym)*sqrt(sqr(2.*muj2+bar-bar*y)-4.*muj2) ) / - ( 2.*(1.-y)*(mui2+mu2+bar*y) ); - double zp = ( (2.*mui2+bar*y)*(1.-y) + sqrt(y*y-ym*ym)*sqrt(sqr(2.*muj2+bar-bar*y)-4.*muj2) ) / - ( 2.*(1.-y)*(mui2+mu2+bar*y) ); - - if ( yyp || zzp ) return false; + double ym = 2.*sqrt(mui2)*sqrt(muj2)/bar; + double yp = 1. - 2.*sqrt(muk2)*(1.-sqrt(muk2))/bar; + + if ( yyp || zizip ) return false; assert( (*values).size() == 6); (*values)[0] = y; - (*values)[1] = z; - (*values)[2] = A; - (*values)[3] = xk; - (*values)[4] = xij; - (*values)[5] = suijk; - + (*values)[1] = zi; + (*values)[2] = xk; + (*values)[3] = xij; + (*values)[4] = QijN2; + (*values)[5] = sijkN; + return true; } -// This is used to generate pt and zPrime +// This is used to generate pt and z pair FFMassiveInvertedTildeKinematics::generatePtZ(double& jac, const double * r, vector * values) const { double kappaMin = ptCut() != ZERO ? sqr(ptCut()/ptMax()) : sqr(0.1*GeV/GeV); double kappa; using namespace RandomHelpers; if ( ptCut() > ZERO ) { pair kw = generate(inverse(0.,kappaMin,1.),r[0]); kappa = kw.first; jac *= kw.second; } else { pair kw = generate((piecewise(), flat(1e-4,kappaMin), match(inverse(0.,kappaMin,1.))),r[0]); kappa = kw.first; jac *= kw.second; } Energy pt = sqrt(kappa)*ptMax(); pair zLims = zBounds(pt); pair zw = generate(inverse(0.,zLims.first,zLims.second)+ inverse(1.,zLims.first,zLims.second),r[1]); double z = zw.first; jac *= zw.second; jac *= sqr(ptMax()/lastScale()); if( !ptzAllowed(make_pair(pt,z), values )) jac = 0.; return make_pair(pt,z); } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FFMassiveInvertedTildeKinematics::persistentOutput(PersistentOStream &) const { } void FFMassiveInvertedTildeKinematics::persistentInput(PersistentIStream &, int) { } void FFMassiveInvertedTildeKinematics::Init() { static ClassDocumentation documentation ("FFMassiveInvertedTildeKinematics inverts the final-final tilde " "kinematics involving a massive particle."); } // *** Attention *** The following static variable is needed for the type // description system in ThePEG. Please check that the template arguments // are correct (the class and its base class), and that the constructor // arguments are correct (the class name and the name of the dynamically // loadable library where the class implementation can be found). DescribeClass describeHerwigFFMassiveInvertedTildeKinematics("Herwig::FFMassiveInvertedTildeKinematics", "Herwig.so"); diff --git a/MatrixElement/Matchbox/Phasespace/FFMassiveTildeKinematics.cc b/MatrixElement/Matchbox/Phasespace/FFMassiveTildeKinematics.cc --- a/MatrixElement/Matchbox/Phasespace/FFMassiveTildeKinematics.cc +++ b/MatrixElement/Matchbox/Phasespace/FFMassiveTildeKinematics.cc @@ -1,229 +1,232 @@ // -*- C++ -*- // // FFMassiveTildeKinematics.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 FFMassiveTildeKinematics class. // #include "FFMassiveTildeKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FFMassiveTildeKinematics::FFMassiveTildeKinematics() {} FFMassiveTildeKinematics::~FFMassiveTildeKinematics() {} IBPtr FFMassiveTildeKinematics::clone() const { return new_ptr(*this); } IBPtr FFMassiveTildeKinematics::fullclone() const { return new_ptr(*this); } +// Matches Stephen Webster's thesis bool FFMassiveTildeKinematics::doMap() { Lorentz5Momentum emitter = realEmitterMomentum(); Lorentz5Momentum emission = realEmissionMomentum(); Lorentz5Momentum spectator = realSpectatorMomentum(); // Compute y double y = emission*emitter / (emission*emitter + emission*spectator + emitter*spectator); // Calculate the scale Lorentz5Momentum pTot = emitter+emission+spectator; Energy scale = pTot.m(); // masses double mui2 = sqr( realEmitterData()->hardProcessMass() / scale ); - double mu2 = sqr( realEmissionData()->hardProcessMass() / scale ); - double muj2 = sqr( realSpectatorData()->hardProcessMass() / scale ); - double Mui2 = sqr( bornEmitterData()->hardProcessMass() / scale ); - double Muj2 = sqr( bornSpectatorData()->hardProcessMass() / scale ); + double muj2 = sqr( realEmissionData()->hardProcessMass() / scale ); + double muk2 = sqr( realSpectatorData()->hardProcessMass() / scale ); + double Muij2 = sqr( bornEmitterData()->hardProcessMass() / scale ); + double Muk2 = sqr( bornSpectatorData()->hardProcessMass() / scale ); // Calculate the invariants Energy2 Qijk = sqr(scale); - double suijk = 0.5*( 1. - Mui2 - Muj2 + sqrt( sqr(1.-Mui2-Muj2) - 4.*Mui2*Muj2 ) ); - double bar = 1. - mui2 - mu2 - muj2; + double sijkN = 0.5*( 1. - Muij2 - Muk2 + sqrt( sqr(1.-Muij2-Muk2) - 4.*Muij2*Muk2 ) ); + double bar = 1. - mui2 - muj2 - muk2; + + // Calculate Qij2 + double QijN2 = sqr(emitter + emission)/Qijk; + + // Calculate the scale factors, xk and xij + double lambdaK = 1. + (Muk2/sijkN); + double lambdaIJ = 1. + (Muij2/sijkN); + double fac1 = lambdaIJ*lambdaK + (muk2 - QijN2)/sijkN; + double xk = + ( fac1 + sqrt( sqr(fac1) - 4.*lambdaIJ*lambdaK*muk2/sijkN ) ) + / 2. / lambdaK ; + double xij = 1. - muk2*(1.-xk) / xk / sijkN; + + // Calculate z = qi.nk / (qi+qj).nk from qi.qk and y + double l = Muk2 / (2.*xk*xij*sijkN); + double a = (xij*xk*sijkN/2.) - l*(bar*y + mui2 + muj2); + double b = (-emitter*spectator)/Qijk + l*(bar*y + 2.*mui2); + double z = -b/a; - // Calculate A (as in notes) - double A = (y*bar - Mui2 + mui2 + mu2)/suijk; + // Calculate zi + double zi = emitter*spectator / ((emitter + emission)*spectator); - // Calculate the scale factors, xk and xij - double lambdaK = 1. + (Muj2/suijk); - double lambdaIJ = 1. + (Mui2/suijk); - double xk = (1./(2.*lambdaK)) * ( (lambdaK + (Muj2/suijk)*lambdaIJ - A) + sqrt( sqr(lambdaK + (Muj2/suijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*Muj2/suijk) ); - double xij = 1. - ( (Muj2/suijk) * (1.-xk) / xk ); - - // Calculate zPrime = qi.nk / (qi+qj).nk from qi.qk and y - double l = Muj2 / (2.*xk*xij*suijk); - double a = (xij*xk*suijk/2.) - l*(bar*y + mui2 + mu2); - double b = (-emitter*spectator)/Qijk + l*(bar*y + 2.*mui2); - double zPrime = -b/a; - - - // Store z as well - double z = emitter*spectator / ((emitter + emission)*spectator); - + // Store the variables subtractionParameters().resize(3); subtractionParameters()[0] = y; - subtractionParameters()[1] = z; - subtractionParameters()[2] = zPrime; + subtractionParameters()[1] = zi; + subtractionParameters()[2] = z; // Calculate nij and nk from qi, q, qj - double B = (Mui2/(xij*suijk) + A/xij) / xk; - double den = (xij/B - Muj2/(xk*suijk)); - - Lorentz5Momentum nij = (1./den)*( (emitter+emission)/B - spectator); - Lorentz5Momentum nk = (1./xk)*(spectator - Muj2*nij/(xk*suijk)); - + double L = QijN2/xij/xk/sijkN; + Lorentz5Momentum nij = (emitter + emission - L*spectator)/(xij - L*Muk2/xk/sijkN); + Lorentz5Momentum nk = (1./xk)*(spectator - Muk2*nij/(xk*sijkN)); + // Calculate the born momenta from nij and nk - bornSpectatorMomentum() = nk + (Muj2/suijk)*nij; + bornSpectatorMomentum() = nk + (Muk2/sijkN)*nij; bornEmitterMomentum() = pTot - bornSpectatorMomentum(); bornEmitterMomentum().setMass( bornEmitterData()->hardProcessMass() ); bornEmitterMomentum().rescaleEnergy(); bornSpectatorMomentum().setMass( bornSpectatorData()->hardProcessMass() ); bornSpectatorMomentum().rescaleEnergy(); return true; } Energy FFMassiveTildeKinematics::lastPt() const { Energy scale = (bornEmitterMomentum()+bornSpectatorMomentum()).m(); double y = subtractionParameters()[0]; - double zPrime = subtractionParameters()[2]; + double z = subtractionParameters()[2]; // masses double mui2 = sqr( realEmitterData()->hardProcessMass() / scale ); - double mu2 = sqr( realEmissionData()->hardProcessMass() / scale ); - double muj2 = sqr( realSpectatorData()->hardProcessMass() / scale ); + double muj2 = sqr( realEmissionData()->hardProcessMass() / scale ); + double muk2 = sqr( realSpectatorData()->hardProcessMass() / scale ); - Energy ret = scale * sqrt( y * (1.-mui2-mu2-muj2) * zPrime*(1.-zPrime) - sqr(1.-zPrime)*mui2 - sqr(zPrime)*mu2 ); - + Energy ret = scale * sqrt( y * (1.-mui2-muj2-muk2) * z*(1.-z) - sqr(1.-z)*mui2 - sqr(z)*muj2 ); + return ret; + } - +// Matches Stephen Webster's thesis Energy FFMassiveTildeKinematics::lastPt(Lorentz5Momentum emitter, Lorentz5Momentum emission, Lorentz5Momentum spectator)const { // Compute y double y = emission*emitter / (emission*emitter + emission*spectator + emitter*spectator); // Calculate the scale Lorentz5Momentum pTot = emitter+emission+spectator; Energy scale = pTot.m(); // masses double mui2 = sqr( emitter.mass() / scale ); - double mu2 = sqr( emission.mass() / scale ); - double muj2 = sqr( spectator.mass() / scale ); + double muj2 = sqr( emission.mass() / scale ); + double muk2 = sqr( spectator.mass() / scale ); // TODO: here we assume a gluon bool isgluon= emitter.mass()==emission.mass(); - double Mui2 = sqr(( isgluon?ZERO:max(emission.mass(),emitter.mass()) )/ scale ); - double Muj2 = sqr( spectator.mass() / scale ); + double Muij2 = sqr(( isgluon?ZERO:max(emission.mass(),emitter.mass()) )/ scale ); + double Muk2 = sqr( spectator.mass() / scale ); // Calculate the invariants Energy2 Qijk = sqr(scale); - double suijk = 0.5*( 1. - Mui2 - Muj2 + sqrt( sqr(1.-Mui2-Muj2) - 4.*Mui2*Muj2 ) ); - double bar = 1. - mui2 - mu2 - muj2; + double sijkN = 0.5*( 1. - Muij2 - Muk2 + sqrt( sqr(1.-Muij2-Muk2) - 4.*Muij2*Muk2 ) ); + double bar = 1. - mui2 - muj2 - muk2; + + // Calculate Qij2 + double QijN2 = sqr(emitter + emission)/Qijk; + + // Calculate the scale factors, xk and xij + double lambdaK = 1. + (Muk2/sijkN); + double lambdaIJ = 1. + (Muij2/sijkN); + double fac1 = lambdaIJ*lambdaK + (muk2 - QijN2)/sijkN; + double xk = + ( fac1 + sqrt( sqr(fac1) - 4.*lambdaIJ*lambdaK*muk2/sijkN ) ) + / 2. / lambdaK ; + double xij = 1. - muk2*(1.-xk) / xk / sijkN; + + // Calculate z = qi.nk / (qi+qj).nk from qi.qk and y + double l = Muk2 / (2.*xk*xij*sijkN); + double a = (xij*xk*sijkN/2.) - l*(bar*y + mui2 + muj2); + double b = (-emitter*spectator)/Qijk + l*(bar*y + 2.*mui2); + double z = -b/a; - // Calculate A (as in notes) - double A = (y*bar - Mui2 + mui2 + mu2)/suijk; - - // Calculate the scale factors, xk and xij - double lambdaK = 1. + (Muj2/suijk); - double lambdaIJ = 1. + (Mui2/suijk); - double xk = (1./(2.*lambdaK)) * ( (lambdaK + (Muj2/suijk)*lambdaIJ - A) + sqrt( sqr(lambdaK + (Muj2/suijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*Muj2/suijk) ); - double xij = 1. - ( (Muj2/suijk) * (1.-xk) / xk ); - - // Calculate zPrime = qi.nk / (qi+qj).nk from qi.qk and y - double l = Muj2 / (2.*xk*xij*suijk); - double a = (xij*xk*suijk/2.) - l*(bar*y + mui2 + mu2); - double b = (-emitter*spectator)/Qijk + l*(bar*y + 2.*mui2); - double zPrime = -b/a; - - - - Energy ret = scale * sqrt( y * (1.-mui2-mu2-muj2) * zPrime*(1.-zPrime) - sqr(1.-zPrime)*mui2 - sqr(zPrime)*mu2 ); - + Energy ret = scale * sqrt( z*(1.-z)*QijN2 - (1.-z)*mui2 - z*muj2 ); + return ret; - - + } // NOTE: bounds calculated at this step may be too loose pair FFMassiveTildeKinematics::zBounds(Energy pt, Energy hardPt) const { if(pt>hardPt) return make_pair(0.5,0.5); Energy scale = (bornEmitterMomentum()+bornSpectatorMomentum()).m(); // masses double mui2 = sqr( realEmitterData()->hardProcessMass() / scale ); - double mu2 = sqr( realEmissionData()->hardProcessMass() / scale ); - double muj2 = sqr( realSpectatorData()->hardProcessMass() / scale ); + double muj2 = sqr( realEmissionData()->hardProcessMass() / scale ); + double muk2 = sqr( realSpectatorData()->hardProcessMass() / scale ); - double zp = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) + - rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * + double zp = ( 1.+mui2-muj2+muk2-2.*sqrt(muk2) + + rootOfKallen(mui2,muj2,sqr(1.-sqrt(muk2))) * sqrt( 1.-sqr(pt/hardPt) ) ) / - ( 2.*sqr(1.-sqrt(muj2)) ); - double zm = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) - - rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * + ( 2.*sqr(1.-sqrt(muk2)) ); + double zm = ( 1.+mui2-muj2+muk2-2.*sqrt(muk2) - + rootOfKallen(mui2,muj2,sqr(1.-sqrt(muk2))) * sqrt( 1.-sqr(pt/hardPt) ) ) / - ( 2.*sqr(1.-sqrt(muj2)) ); + ( 2.*sqr(1.-sqrt(muk2)) ); return make_pair(zm,zp); } double FFMassiveTildeKinematics::lastZ() const { return subtractionParameters()[2]; } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FFMassiveTildeKinematics::persistentOutput(PersistentOStream &) const { } void FFMassiveTildeKinematics::persistentInput(PersistentIStream &, int) { } void FFMassiveTildeKinematics::Init() { static ClassDocumentation documentation ("FFMassiveTildeKinematics implements the 'tilde' kinematics for " "a final-final subtraction dipole involving a massive particle."); } // *** Attention *** The following static variable is needed for the type // description system in ThePEG. Please check that the template arguments // are correct (the class and its base class), and that the constructor // arguments are correct (the class name and the name of the dynamically // loadable library where the class implementation can be found). DescribeClass describeHerwigFFMassiveTildeKinematics("Herwig::FFMassiveTildeKinematics", "Herwig.so"); diff --git a/MatrixElement/Matchbox/Phasespace/IFMassiveInvertedTildeKinematics.cc b/MatrixElement/Matchbox/Phasespace/IFMassiveInvertedTildeKinematics.cc --- a/MatrixElement/Matchbox/Phasespace/IFMassiveInvertedTildeKinematics.cc +++ b/MatrixElement/Matchbox/Phasespace/IFMassiveInvertedTildeKinematics.cc @@ -1,166 +1,165 @@ // -*- C++ -*- // // IFMassiveInvertedTildeKinematics.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 IFMassiveInvertedTildeKinematics class. // #include "IFMassiveInvertedTildeKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IFMassiveInvertedTildeKinematics::IFMassiveInvertedTildeKinematics() {} IFMassiveInvertedTildeKinematics::~IFMassiveInvertedTildeKinematics() {} IBPtr IFMassiveInvertedTildeKinematics::clone() const { return new_ptr(*this); } IBPtr IFMassiveInvertedTildeKinematics::fullclone() const { return new_ptr(*this); } bool IFMassiveInvertedTildeKinematics::doMap(const double * r) { if ( ptMax() < ptCut() ) { jacobian(0.0); return false; } // Compute dipole scale Lorentz5Momentum emitter = bornEmitterMomentum(); Lorentz5Momentum spectator = bornSpectatorMomentum(); Energy2 scale = 2.*(spectator*emitter); // Generate pt and z double mapping = 1.0; pair ptz = generatePtZ(mapping,r); if ( mapping == 0.0 ){ jacobian(0.0); return false; } Energy pt = ptz.first; double z = ptz.second; // Compute x and u double ratio = sqr(pt)/scale; double muk2 = sqr(bornSpectatorData()->hardProcessMass())/scale; double rho = 1. - 4.*ratio*(1.-muk2)*z*(1.-z)/sqr(1.-z+ratio); double x = 0.5*((1.-z+ratio)/(ratio*(1.-muk2))) * (1. - sqrt(rho)); double u = x*ratio / (1.-z); // Following Catani-Seymour paper double muk2CS = x*muk2; double up = (1.-x) / ( 1.-x + muk2CS ); if ( x < emitterX() || x > 1. || u < 0. || u > up ) { jacobian(0.0); return false; } // Store x and u subtractionParameters().resize(2); subtractionParameters()[0] = x; subtractionParameters()[1] = u; - // The jacobian here is the single particle phasespace - // saj*(1./x^2)*dx*du + // jac = sajk*(1./x^2)*dx*du // Note - lastScale() is not equal to scale!!!!!!! - double jac = abs( (1.+x*(muk2-1.))*(-u*(1.-u)/sqr(x)) - (1.+u*(muk2-1.))*((1.-2.*u)*(1.-x)/x - 2.*u*muk2) ); - mapping /= x*x*jac; + double jac = u/x/(u + x - 2.*u*x*(1.-muk2))*scale/sqr(pt); + mapping *= jac; jacobian( mapping*(sqr(lastScale())/sHat()) / (16.*sqr(Constants::pi)) ); // Compute the new momenta double phi = 2.*Constants::pi*r[2]; Lorentz5Momentum kt = getKt(emitter,spectator,pt,phi,true); realEmitterMomentum() = (1./x)*emitter; realEmissionMomentum() = ((1.-x)*(1.-u)/x - 2.*u*muk2)*emitter + u*spectator + kt; realSpectatorMomentum() = ((1.-x)*u/x + 2.*u*muk2)*emitter + (1.-u)*spectator - kt; realEmitterMomentum().setMass(ZERO); realEmitterMomentum().rescaleEnergy(); realEmissionMomentum().setMass(ZERO); realEmissionMomentum().rescaleEnergy(); realSpectatorMomentum().setMass(bornSpectatorData()->hardProcessMass()); realSpectatorMomentum().rescaleEnergy(); return true; } Energy IFMassiveInvertedTildeKinematics::lastPt() const { Energy2 scale = 2.*(bornEmitterMomentum()*bornSpectatorMomentum()); double muk2 = sqr(bornSpectatorData()->hardProcessMass())/scale; double x = subtractionParameters()[0]; double u = subtractionParameters()[1]; return sqrt(scale * ( u*(1.-u)*(1.-x)/x - u*u*muk2 )); } double IFMassiveInvertedTildeKinematics::lastZ() const { Energy2 scale = 2.*(bornEmitterMomentum()*bornSpectatorMomentum()); double muk2 = sqr(bornSpectatorData()->hardProcessMass())/scale; double x = subtractionParameters()[0]; double u = subtractionParameters()[1]; return u + x + u*x*(muk2-1.); } Energy IFMassiveInvertedTildeKinematics::ptMax() const { double xe = emitterX(); Energy2 scale = 2.*(bornEmitterMomentum()*bornSpectatorMomentum()); Energy2 A = scale*(1.-xe)/xe; Energy2 mk2 = sqr(bornSpectatorData()->hardProcessMass()); return 0.5*A/sqrt(mk2+A); } pair IFMassiveInvertedTildeKinematics::zBounds(Energy pt, Energy hardPt) const { hardPt = hardPt == ZERO ? ptMax() : min(hardPt,ptMax()); if(pt>hardPt) return make_pair(0.5,0.5); double s = sqrt(1.-sqr(pt/hardPt)); double xe = emitterX(); return make_pair(0.5*(1.+xe-(1.-xe)*s),0.5*(1.+xe+(1.-xe)*s)); } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFMassiveInvertedTildeKinematics::persistentOutput(PersistentOStream &) const { } void IFMassiveInvertedTildeKinematics::persistentInput(PersistentIStream &, int) { } void IFMassiveInvertedTildeKinematics::Init() { static ClassDocumentation documentation ("IFMassiveInvertedTildeKinematics inverts the initial-final tilde " "kinematics involving a massive particle."); } // *** Attention *** The following static variable is needed for the type // description system in ThePEG. Please check that the template arguments // are correct (the class and its base class), and that the constructor // arguments are correct (the class name and the name of the dynamically // loadable library where the class implementation can be found). DescribeClass describeHerwigIFMassiveInvertedTildeKinematics("Herwig::IFMassiveInvertedTildeKinematics", "Herwig.so"); diff --git a/MatrixElement/Matchbox/Utility/ColourBasis.cc b/MatrixElement/Matchbox/Utility/ColourBasis.cc --- a/MatrixElement/Matchbox/Utility/ColourBasis.cc +++ b/MatrixElement/Matchbox/Utility/ColourBasis.cc @@ -1,1311 +1,1338 @@ // -*- C++ -*- // // ColourBasis.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 ColourBasis class. // #include "ColourBasis.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/EventRecord/Particle.h" #include "ThePEG/Handlers/SamplerBase.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Utilities/DescribeClass.h" #include "Herwig/MatrixElement/Matchbox/MatchboxFactory.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include #include #include using std::ostream_iterator; #include "DiagramDrawer.h" using namespace Herwig; using boost::numeric::ublas::trans; // default gcc on SLC6 confuses this with std::conj, // use explicit namespacing in the code instead // // using boost::numeric::ublas::conj; using boost::numeric::ublas::row; using boost::numeric::ublas::column; using boost::numeric::ublas::prod; Ptr::tptr ColourBasis::factory() const { return MatchboxFactory::currentFactory(); } ColourBasis::ColourBasis() : theLargeN(false), didRead(false), didWrite(false), theSearchPath("") {} ColourBasis::~ColourBasis() { for ( map::tcptr,vector >::iterator cl = theColourLineMap.begin(); cl != theColourLineMap.end(); ++cl ) { for ( vector::iterator c = cl->second.begin(); c != cl->second.end(); ++c ) { if ( *c ) delete *c; } } theColourLineMap.clear(); } void ColourBasis::clear() { theLargeN = false; theNormalOrderedLegs.clear(); theIndexMap.clear(); + theEmissionMaps.clear(); + theCharges.clear(); theScalarProducts.clear(); - theCharges.clear(); - theChargeNonZeros.clear(); theCorrelators.clear(); theFlowMap.clear(); theColourLineMap.clear(); theOrderingStringIdentifiers.clear(); theOrderingIdentifiers.clear(); didRead = false; didWrite = false; tmp.clear(); } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). bool ColourBasis::colourConnected(const cPDVector& sub, const vector& basis, const pair& i, const pair& j, size_t a) const { // translate process to basis ids map >::const_iterator trans = indexMap().find(sub); assert(trans != indexMap().end()); int idColoured = i.second ? j.first : i.first; idColoured = trans->second.find(idColoured)->second; int idAntiColoured = i.second ? i.first : j.first; idAntiColoured = trans->second.find(idAntiColoured)->second; return colourConnected(basis,idColoured,idAntiColoured,a); } const string& ColourBasis::orderingString(const cPDVector& sub, const map& colourToAmplitude, size_t tensorId) { map& tensors = theOrderingStringIdentifiers[sub]; if ( !tensors.empty() ) { assert(tensors.find(tensorId) != tensors.end()); return tensors[tensorId]; } const set >& xordering = ordering(sub,colourToAmplitude,tensorId); ostringstream os; os << "["; for ( set >::const_iterator t = xordering.begin(); t != xordering.end(); ++t ) { os << "["; for ( vector::const_iterator s = t->begin(); s != t->end(); ++s ) { os << *s << (s != --t->end() ? "," : ""); } os << "]" << (t != --xordering.end() ? "," : ""); } os << "]"; tensors[tensorId] = os.str(); return tensors[tensorId]; } const set >& ColourBasis::ordering(const cPDVector& sub, const map& colourToAmplitude, size_t tensorId, size_t shift) { map > >& tensors = theOrderingIdentifiers[sub]; if ( !tensors.empty() ) { assert(tensors.find(tensorId) != tensors.end()); return tensors[tensorId]; } const vector& basisId = normalOrderedLegs(sub); map > > labels = basisList(basisId); for ( map > >::const_iterator t = labels.begin(); t != labels.end(); ++t ) { set > xordering; for ( vector >::const_iterator s = t->second.begin(); s != t->second.end(); ++s ) { vector crossed; for ( vector::const_iterator l = s->begin(); l != s->end(); ++l ) { map::const_iterator trans = colourToAmplitude.find(*l); assert(trans != colourToAmplitude.end()); crossed.push_back(trans->second + shift); } xordering.insert(crossed); } tensors[t->first] = xordering; } assert(tensors.find(tensorId) != tensors.end()); return tensors[tensorId]; } vector ColourBasis::normalOrderMap(const cPDVector& sub) { vector allLegs = projectColour(sub); vector legs = normalOrder(allLegs); // if no coloured legs. if(legs.empty())return legs; if ( allLegs[0] == PDT::Colour3 ) allLegs[0] = PDT::Colour3bar; else if ( allLegs[0] == PDT::Colour3bar ) allLegs[0] = PDT::Colour3; if ( allLegs[1] == PDT::Colour3 ) allLegs[1] = PDT::Colour3bar; else if ( allLegs[1] == PDT::Colour3bar ) allLegs[1] = PDT::Colour3; if ( theIndexMap.find(sub) == theIndexMap.end() ) { map trans; vector checkLegs = legs; size_t n = checkLegs.size(); for ( size_t i = 0; i < allLegs.size(); ++i ) { size_t j = 0; while ( checkLegs[j] != allLegs[i] ) { ++j; if ( j == n ) break; } if ( j == n ) continue; trans[i] = j; checkLegs[j] = PDT::ColourUndefined; } theIndexMap[sub] = trans; } return legs; } const vector& ColourBasis::normalOrderedLegs(const cPDVector& sub) const { static vector empty; map >::const_iterator n = theNormalOrderedLegs.find(sub); if ( n != theNormalOrderedLegs.end() ) return n->second; return empty; } +const std::tuple,vector, + size_t,size_t,size_t,map >& +ColourBasis::normalOrderEmissionMap(const cPDVector& subFrom, + const cPDVector& subTo, + size_t ij, size_t i, size_t j, + const map& emissionMap) { + + auto key = std::make_tuple(subFrom,subTo,ij,i,j,emissionMap); + + auto em = theEmissionMaps.find(key); + if ( em != theEmissionMaps.end() ) + return em->second; + + auto transFrom = indexMap().find(subFrom); + auto transTo = indexMap().find(subTo); + const vector& lFrom = normalOrderedLegs(subFrom); + const vector& lTo = normalOrderedLegs(subTo); + + assert(transFrom != theIndexMap.end() && + transTo != theIndexMap.end()); + + map res; + + size_t cij, ci, cj; + auto tFromIt = transFrom->second.find(ij); + assert(tFromIt != transFrom->second.end()); + cij = tFromIt->second; + + auto tToIt = transTo->second.find(i); + assert(tToIt != transTo->second.end()); + ci = tToIt->second; + tToIt = transTo->second.find(j); + assert(tToIt != transTo->second.end()); + cj = tToIt->second; + + for ( auto fromBasisIt = transFrom->second.begin(); + fromBasisIt != transFrom->second.end(); ++fromBasisIt ) { + if ( fromBasisIt->first == ij ) + continue; + auto toProcessIndexIt = emissionMap.find(fromBasisIt->first); + assert(toProcessIndexIt != emissionMap.end()); + auto toBasisIndexIt = transTo->second.find(toProcessIndexIt->second); + assert(toBasisIndexIt != transTo->second.end()); + res[fromBasisIt->second] = toBasisIndexIt->second; + } + + return theEmissionMaps[key] = std::make_tuple(lFrom,lTo,cij,ci,cj,res); + +} + +const pair,vector > >& +ColourBasis::charge(const cPDVector& subFrom, + const cPDVector& subTo, + size_t ij, size_t i, size_t j, + const map& emissionMap) { + + auto bKey = normalOrderEmissionMap(subFrom,subTo,ij,i,j,emissionMap); + + auto it = theCharges.find(bKey); + + if ( it != theCharges.end() ) + return it->second; + + size_t dimFrom = prepare(subFrom,false); + size_t dimTo = prepare(subTo,false); + + compressed_matrix tm(dimTo,dimFrom); + vector > nonZero; + + const vector& basisFrom = std::get<0>(bKey); + const vector& basisTo = std::get<1>(bKey); + size_t cij = std::get<2>(bKey); + size_t ci = std::get<3>(bKey); + size_t cj = std::get<4>(bKey); + const map& dict = std::get<5>(bKey); + + for ( size_t bFrom = 0; bFrom < dimFrom; ++bFrom ) + for ( size_t aTo = 0; aTo < dimTo; ++aTo ) { + tm(aTo,bFrom) = + tMatrixElement(cij,aTo,bFrom,basisTo,basisFrom,ci,cj,dict); + if ( tm(aTo,bFrom) != 0. ) + nonZero.push_back(make_pair(aTo,bFrom)); + } + + return theCharges[bKey] = make_pair(tm,nonZero); + +} + size_t ColourBasis::prepare(const cPDVector& sub, bool noCorrelations) { vector legs = normalOrderMap(sub); bool doPrepare = false; if ( theNormalOrderedLegs.find(sub) == theNormalOrderedLegs.end() ) theNormalOrderedLegs[sub] = legs; //if no coloured legs. if(legs.empty()){ auto sp = symmetric_matrix(1,1); sp(0,0)=1.; theScalarProducts.insert({ legs , sp }); return 0; } if ( theScalarProducts.find(legs) == theScalarProducts.end() ) doPrepare = true; if ( doPrepare ) doPrepare = !readBasis(legs); size_t dim = doPrepare ? prepareBasis(legs) : theScalarProducts[legs].size1(); - if ( theCharges.find(legs) != theCharges.end() ) + if ( theCorrelators.find(legs) != theCorrelators.end() ) return dim; if ( !doPrepare && noCorrelations ) return dim; symmetric_matrix& sp = theScalarProducts.insert(make_pair(legs,symmetric_matrix(dim,dim))).first->second; for ( size_t a = 0; a < dim; ++a ) for ( size_t b = a; b < dim; ++b ) sp(a,b) = scalarProduct(a,b,legs); if ( noCorrelations ) return dim; vector legsPlus = legs; legsPlus.push_back(PDT::Colour8); legsPlus = normalOrder(legsPlus); bool doPreparePlus = theScalarProducts.find(legsPlus) == theScalarProducts.end(); - size_t dimPlus = doPreparePlus ? prepareBasis(legsPlus) : theScalarProducts[legsPlus].size1(); symmetric_matrix& spPlus = doPreparePlus ? theScalarProducts.insert(make_pair(legsPlus,symmetric_matrix(dimPlus,dimPlus))).first->second : theScalarProducts[legsPlus]; if ( doPreparePlus ) { for ( size_t a = 0; a < dimPlus; ++a ) for ( size_t b = a; b < dimPlus; ++b ) spPlus(a,b) = scalarProduct(a,b,legsPlus); } - typedef map > cMap; - cMap& cm = theCharges.insert(make_pair(legs,cMap())).first->second; - - typedef map > > ccMap; - ccMap& ccm = theChargeNonZeros.insert(make_pair(legs,ccMap())).first->second; + map > cm; + map > > ccm; tmp.resize(dimPlus,dim); for ( size_t i = 0; i < legs.size(); ++i ) { size_t nonZero = 0; vector > nonZeros; + map standardMap; + for ( size_t j = 0; j < legs.size(); ++j ) + if ( j != i ) + standardMap[j] = j; for ( size_t a = 0; a < dimPlus; ++a ) for ( size_t b = 0; b < dim; ++b ) { - tmp(a,b) = tMatrixElement(i,a,b,legsPlus,legs); + tmp(a,b) = tMatrixElement(i,a,b,legsPlus,legs,i,legs.size(),standardMap); if ( tmp(a,b) != 0. ) { ++nonZero; nonZeros.push_back(make_pair(a,b)); } } ccm.insert(make_pair(i,nonZeros)); compressed_matrix& tm = cm.insert(make_pair(i,compressed_matrix(dimPlus,dim,nonZero))).first->second; for ( size_t a = 0; a < dimPlus; ++a ) for ( size_t b = 0; b < dim; ++b ) { if ( tmp(a,b) != 0. ) tm(a,b) = tmp(a,b); } } map,symmetric_matrix >& xm = theCorrelators[legs]; for ( size_t i = 0; i < legs.size(); ++i ) for ( size_t j = i+1; j < legs.size(); ++j ) { symmetric_matrix& mm = xm.insert(make_pair(make_pair(i,j),symmetric_matrix(dim,dim))).first->second; chargeProduct(cm[i],ccm[i],spPlus,cm[j],ccm[j],mm); } return dim; } void ColourBasis::chargeProduct(const compressed_matrix& ti, const vector >& tiNonZero, const symmetric_matrix& X, const compressed_matrix& tj, const vector >& tjNonZero, symmetric_matrix& result) const { for ( size_t i = 0; i < result.size1(); ++i ) for ( size_t j = i; j < result.size1(); ++j ) result(i,j) = 0.; for ( vector >::const_iterator i = tiNonZero.begin(); i != tiNonZero.end(); ++i ) for ( vector >::const_iterator j = tjNonZero.begin(); j != tjNonZero.end(); ++j ) { if ( j->second < i->second ) continue; result(i->second,j->second) += ti(i->first,i->second)*tj(j->first,j->second)*X(i->first,j->first); } } void ColourBasis::chargeProductAdd(const compressed_matrix& ti, const vector >& tiNonZero, const matrix& X, const compressed_matrix& tj, const vector >& tjNonZero, matrix& result, double factor) const { for ( vector >::const_iterator i = tiNonZero.begin(); i != tiNonZero.end(); ++i ) for ( vector >::const_iterator j = tjNonZero.begin(); j != tjNonZero.end(); ++j ) { result(i->first,j->first) += factor* ti(i->first,i->second)*tj(j->first,j->second)*X(i->second,j->second); } } string ColourBasis::cfstring(const list > >& flow) { ostringstream out(""); for ( list > >::const_iterator line = flow.begin(); line != flow.end(); ++line ) { for ( list >::const_iterator node = line->begin(); node != line->end(); ++node ) { out << (node->second ? "-" : "") << (node->first+1) << " "; } if ( line != --(flow.end()) ) out << ", "; } return out.str(); } vector ColourBasis::makeFlows(Ptr::tcptr diag, size_t dim) const { vector res(dim); //if no coloured legs. if(dim==0)return res; list > > > fdata = colourFlows(diag); cPDVector ext; tcPDVector dext = diag->external(); copy(dext.begin(),dext.end(),back_inserter(ext)); vector colouredLegs = normalOrder(projectColour(ext)); for ( list > > >::const_iterator flow = fdata.begin(); flow != fdata.end(); ++flow ) { for ( size_t i = 0; i < dim; ++i ) { bool matches = true; for ( list > >::const_iterator line = flow->begin(); line != flow->end(); ++line ) { pair front(diag->externalId(line->front().first),line->front().second); if ( front.first < 2 ) front.second = !front.second; pair back(diag->externalId(line->back().first),line->back().second); if ( back.first < 2 ) back.second = !back.second; if ( !colourConnected(ext,colouredLegs,front,back,i) ) { matches = false; break; } } if ( matches ) { assert(res[i] == "" && "only support colour bases with unique mapping to large-N colour flows"); res[i] = cfstring(*flow); } } } bool gotone = false; for ( vector::const_iterator f = res.begin(); f != res.end(); ++f ) { if ( *f != "" ) { gotone = true; break; } } if ( !gotone ) { generator()->log() << "warning no color flow found for diagram\n"; DiagramDrawer::drawDiag(generator()->log(),*diag); } return res; } size_t ColourBasis::prepare(const MEBase::DiagramVector& diags, bool noCorrelations) { size_t dim = 0; for ( MEBase::DiagramVector::const_iterator d = diags.begin(); d != diags.end(); ++d ) { Ptr::tcptr dd = dynamic_ptr_cast::ptr>(*d); assert(dd); dim = prepare(dd->partons(),noCorrelations); if ( !haveColourFlows() || theFlowMap.find(dd) != theFlowMap.end() ) continue; theFlowMap[dd] = makeFlows(dd,dim); } return dim; } bool matchEnd(int a, pair b, Ptr::tcptr diag) { if ( a != b.first ) return false; if ( b.first != diag->nSpace()-1 ) { return !b.second ? diag->allPartons()[b.first]->hasColour() : diag->allPartons()[b.first]->hasAntiColour(); } else { return !b.second ? diag->allPartons()[b.first]->hasAntiColour() : diag->allPartons()[b.first]->hasColour(); } return false; } bool findPath(pair a, pair b, Ptr::tcptr diag, list >& path, bool backward) { assert(a.first==0 ? !backward : true); if ( path.empty() ) path.push_back(a); if ( !backward ) { if ( diag->children(a.first).first == -1 ) return matchEnd(a.first,b,diag); pair children = diag->children(a.first); bool cc = (children.first == diag->nSpace()-1); if ( diag->allPartons()[children.first]->coloured() ) if ( !cc ? (!a.second ? diag->allPartons()[children.first]->hasColour() : diag->allPartons()[children.first]->hasAntiColour()) : (!a.second ? diag->allPartons()[children.first]->hasAntiColour() : diag->allPartons()[children.first]->hasColour()) ) { pair next(children.first,a.second); path.push_back(next); if ( !findPath(next,b,diag,path,false) ) { path.pop_back(); } else return true; } cc = (children.second == diag->nSpace()-1); if ( diag->allPartons()[children.second]->coloured() ) if ( !cc ? (!a.second ? diag->allPartons()[children.second]->hasColour() : diag->allPartons()[children.second]->hasAntiColour()) : (!a.second ? diag->allPartons()[children.second]->hasAntiColour() : diag->allPartons()[children.second]->hasColour()) ) { pair next(children.second,a.second); path.push_back(next); if ( !findPath(next,b,diag,path,false) ) { path.pop_back(); } else return true; } if ( path.size() == 1 ) path.pop_back(); return false; } else { int parent = diag->parent(a.first); pair neighbours = diag->children(parent); int neighbour = a.first == neighbours.first ? neighbours.second : neighbours.first; if ( matchEnd(parent,b,diag) ) { path.push_back(b); return true; } if ( matchEnd(neighbour,b,diag) ) { path.push_back(b); return true; } if ( diag->allPartons()[neighbour]->coloured() ) if ( a.second ? diag->allPartons()[neighbour]->hasColour() : diag->allPartons()[neighbour]->hasAntiColour() ) { pair next(neighbour,!a.second); path.push_back(next); if ( !findPath(next,b,diag,path,false) ) { path.pop_back(); } else return true; } if ( parent == 0 ) { if ( path.size() == 1 ) path.pop_back(); return false; } if ( diag->allPartons()[parent]->coloured() ) if ( !a.second ? diag->allPartons()[parent]->hasColour() : diag->allPartons()[parent]->hasAntiColour() ) { pair next(parent,a.second); path.push_back(next); if ( !findPath(next,b,diag,path,true) ) { path.pop_back(); } else return true; } if ( path.size() == 1 ) path.pop_back(); return false; } return false; } list > ColourBasis::colouredPath(pair a, pair b, Ptr::tcptr diag) { list > res; if ( a.first == b.first ) return res; bool aIn = (a.first < 2); bool bIn = (b.first < 2); if ( (aIn && bIn) || (!aIn && !bIn) ) if ( (a.second && b.second) || (!a.second && !b.second) ) return res; if ( (aIn && !bIn) || (!aIn && bIn) ) if ( (!a.second && b.second) || (a.second && !b.second) ) return res; if ( a.first > b.first ) swap(a,b); a.first = diag->diagramId(a.first); b.first = diag->diagramId(b.first); if ( a.first == diag->nSpace()-1 ) a.second = !a.second; if ( b.first == diag->nSpace()-1 ) b.second = !b.second; if ( !findPath(a,b,diag,res,a.first != 0) ) return res; if ( b.first == diag->nSpace()-1 ) { res.back().second = !res.back().second; } if ( a.first == diag->nSpace()-1 ) { res.front().second = !res.front().second; } return res; } list > > > ColourBasis::colourFlows(Ptr::tcptr diag) { vector > connectSource; vector > connectSink; for ( size_t i = 0; i != diag->partons().size(); ++i ) { if ( i < 2 && diag->partons()[i]->hasAntiColour() ) connectSource.push_back(make_pair(i,true)); if ( i < 2 && diag->partons()[i]->hasColour() ) connectSink.push_back(make_pair(i,false)); if ( i > 1 && diag->partons()[i]->hasColour() ) connectSource.push_back(make_pair(i,false)); if ( i > 1 && diag->partons()[i]->hasAntiColour() ) connectSink.push_back(make_pair(i,true)); } assert(connectSource.size() == connectSink.size()); list > > > ret; do { vector >::iterator source = connectSource.begin(); vector >::iterator sink = connectSink.begin(); list > > res; for ( ; source != connectSource.end(); ++source, ++sink ) { if ( source->first == sink->first ) { res.clear(); break; } list > line = colouredPath(*source,*sink,diag); if ( line.empty() ) { res.clear(); break; } res.push_back(line); } if ( !res.empty() ) { // check, if all dressed properly vector > dressed((*diag).allPartons().size(),make_pair(0,0)); for ( size_t p = 0; p < diag->allPartons().size(); ++p ) { if ( diag->allPartons()[p]->hasColour() && !diag->allPartons()[p]->hasAntiColour() ) dressed[p].first = 1; if ( diag->allPartons()[p]->hasAntiColour() && !diag->allPartons()[p]->hasColour() ) dressed[p].second = 1; if ( diag->allPartons()[p]->hasAntiColour() && diag->allPartons()[p]->hasColour() ) { dressed[p].first = 1; dressed[p].second = 1; } } for ( list > >::const_iterator l = res.begin(); l != res.end(); ++l ) { for ( list >::const_iterator n = l->begin(); n != l->end(); ++n ) { if ( !(n->second) ) dressed[n->first].first -= 1; else dressed[n->first].second -= 1; } } for ( vector >::const_iterator d = dressed.begin(); d != dressed.end(); ++d ) { if ( d->first != 0 || d->second != 0 ) { res.clear(); break; } } if ( !res.empty() ) ret.push_back(res); } } while ( std::next_permutation(connectSink.begin(),connectSink.end()) ); return ret; } void ColourBasis::updateColourLines(Ptr::tcptr dd) { map::tcptr,vector >::const_iterator cl = theFlowMap.find(dd); assert(cl != theFlowMap.end()); vector clines(cl->second.size()); for ( size_t k = 0; k < cl->second.size(); ++k ) { if ( cl->second[k] == "" ) { clines[k] = 0; continue; } clines[k] = new ColourLines(cl->second[k]); } theColourLineMap[cl->first] = clines; } map::tcptr,vector >& ColourBasis::colourLineMap() { if ( !theColourLineMap.empty() ) return theColourLineMap; for ( map::tcptr,vector >::const_iterator cl = theFlowMap.begin(); cl != theFlowMap.end(); ++cl ) { vector clines(cl->second.size()); for ( size_t k = 0; k < cl->second.size(); ++k ) { if ( cl->second[k] == "" ) { clines[k] = 0; continue; } clines[k] = new ColourLines(cl->second[k]); } theColourLineMap[cl->first] = clines; } return theColourLineMap; } Selector ColourBasis::colourGeometries(tcDiagPtr diag, const map,CVector>& amps) { Ptr::tcptr dd = dynamic_ptr_cast::tcptr>(diag); assert(dd && theFlowMap.find(dd) != theFlowMap.end()); map::tcptr,vector >::const_iterator colit = colourLineMap().find(dd); if ( colit == colourLineMap().end() ) { updateColourLines(dd); colit = colourLineMap().find(dd); } const vector& cl = colit->second; Selector sel; size_t dim = amps.begin()->second.size(); // special treatment if no coloured legs. // as in e.g. MEee2gZ2ll.cc if (cl.empty()){ static ColourLines ctST(" "); sel.insert(1.,&ctST); return sel; } assert(dim == cl.size()); double w = 0.; for ( size_t i = 0; i < dim; ++i ) { if ( !cl[i] ) continue; w = 0.; for ( map,CVector>::const_iterator a = amps.begin(); a != amps.end(); ++a ) w += real(conj((a->second)(i))*((a->second)(i))); if ( w > 0. ) sel.insert(w,cl[i]); } assert(!sel.empty()); return sel; } size_t ColourBasis::tensorIdFromFlow(tcDiagPtr diag, const ColourLines * flow) { Ptr::tcptr dd = dynamic_ptr_cast::tcptr>(diag); assert(dd && theFlowMap.find(dd) != theFlowMap.end()); map::tcptr,vector >::const_iterator colit = colourLineMap().find(dd); if ( colit == colourLineMap().end() ) { updateColourLines(dd); colit = colourLineMap().find(dd); } const vector& cl = colit->second; size_t res = 0; for ( ; res < cl.size(); ++res ) { if ( flow == cl[res] ) break; } assert(res < cl.size()); return res; } const symmetric_matrix& ColourBasis::scalarProducts(const cPDVector& sub) const { map >::const_iterator lit = theNormalOrderedLegs.find(sub); assert(lit != theNormalOrderedLegs.end()); ScalarProductMap::const_iterator spit = theScalarProducts.find(lit->second); assert(spit != theScalarProducts.end()); return spit->second; } -const compressed_matrix& ColourBasis::charge(const cPDVector& sub, size_t iIn) const { - - map >::const_iterator lit = - theNormalOrderedLegs.find(sub); - assert(lit != theNormalOrderedLegs.end()); - - ChargeMap::const_iterator ct = - theCharges.find(lit->second); - assert(ct != theCharges.end()); - - map >::const_iterator trans - = theIndexMap.find(sub); - assert(trans != theIndexMap.end()); - size_t i = trans->second.find(iIn)->second; - - map >::const_iterator cit - = ct->second.find(i); - assert(cit != ct->second.end()); - - return cit->second; - -} - -const vector >& ColourBasis::chargeNonZero(const cPDVector& sub, size_t iIn) const { - - map >::const_iterator lit = - theNormalOrderedLegs.find(sub); - assert(lit != theNormalOrderedLegs.end()); - - ChargeNonZeroMap::const_iterator ct = - theChargeNonZeros.find(lit->second); - assert(ct != theChargeNonZeros.end()); - - map >::const_iterator trans - = theIndexMap.find(sub); - assert(trans != theIndexMap.end()); - size_t i = trans->second.find(iIn)->second; - - map > >::const_iterator cit - = ct->second.find(i); - assert(cit != ct->second.end()); - - return cit->second; - -} - const symmetric_matrix& ColourBasis::correlator(const cPDVector& sub, const pair& ijIn) const { map >::const_iterator lit = theNormalOrderedLegs.find(sub); assert(lit != theNormalOrderedLegs.end()); CorrelatorMap::const_iterator cit = theCorrelators.find(lit->second); assert(cit != theCorrelators.end()); map >::const_iterator trans = theIndexMap.find(sub); assert(trans != theIndexMap.end()); pair ij(trans->second.find(ijIn.first)->second, trans->second.find(ijIn.second)->second); if ( ij.first > ij.second ) swap(ij.first,ij.second); map,symmetric_matrix >::const_iterator cijit = cit->second.find(ij); assert(cijit != cit->second.end()); return cijit->second; } double ColourBasis::me2(const cPDVector& sub, const map,CVector>& amps) const { const symmetric_matrix& sp = scalarProducts(sub); double res = 0.; for ( map,CVector>::const_iterator a = amps.begin(); a != amps.end(); ++a ) { res += real(inner_prod(boost::numeric::ublas::conj(a->second),prod(sp,a->second))); } return res; } double ColourBasis::interference(const cPDVector& sub, const map,CVector>& amps1, const map,CVector>& amps2) const { const symmetric_matrix& sp = scalarProducts(sub); double res = 0.; map,CVector>::const_iterator a = amps1.begin(); map,CVector>::const_iterator b = amps2.begin(); for ( ; a != amps1.end(); ++a, ++b ) { assert(a->first == b->first); res += 2.*real(inner_prod(boost::numeric::ublas::conj(a->second),prod(sp,b->second))); } assert(!std::isnan(res)); return res; } double ColourBasis::colourCorrelatedME2(const pair& ij, const cPDVector& sub, const map,CVector>& amps) const { const symmetric_matrix& cij = correlator(sub,ij); double res = 0.; for ( map,CVector>::const_iterator a = amps.begin(); a != amps.end(); ++a ) { res += real(inner_prod(boost::numeric::ublas::conj(a->second),prod(cij,a->second))); } return res; } Complex ColourBasis::interference(const cPDVector& sub, const CVector& left, const CVector& right) const { const symmetric_matrix& sp = scalarProducts(sub); return inner_prod(boost::numeric::ublas::conj(left),prod(sp,right)); } Complex ColourBasis::colourCorrelatedInterference(const pair& ij, const cPDVector& sub, const CVector& left, const CVector& right) const { const symmetric_matrix& cij = correlator(sub,ij); return inner_prod(boost::numeric::ublas::conj(left),prod(cij,right)); } double ColourBasis::me2(const cPDVector& sub, const matrix& amp) const { const symmetric_matrix& sp = scalarProducts(sub); double tr = 0; size_t n = amp.size1(); for ( size_t i = 0; i < n; ++i ) { tr += real(inner_prod(row(sp,i),column(amp,i))); } return tr; } double ColourBasis::colourCorrelatedME2(const pair& ij, const cPDVector& sub, const matrix& amp) const { const symmetric_matrix& cij = correlator(sub,ij); double tr = 0; size_t n = amp.size1(); for ( size_t i = 0; i < n; ++i ) { tr += real(inner_prod(row(cij,i),column(amp,i))); } return tr; } struct pickColour { PDT::Colour operator()(tcPDPtr p) const { return p->iColour(); } }; vector ColourBasis::projectColour(const cPDVector& sub) const { vector res(sub.size()); transform(sub.begin(),sub.end(),res.begin(),pickColour()); return res; } vector ColourBasis::normalOrder(const vector& legs) const { //if no coloured legs. if (legs.empty())return legs; vector crosslegs = legs; if ( crosslegs[0] == PDT::Colour3 ) crosslegs[0] = PDT::Colour3bar; else if ( crosslegs[0] == PDT::Colour3bar ) crosslegs[0] = PDT::Colour3; if ( crosslegs[1] == PDT::Colour3 ) crosslegs[1] = PDT::Colour3bar; else if ( crosslegs[1] == PDT::Colour3bar ) crosslegs[1] = PDT::Colour3; int n3 = count_if(crosslegs.begin(),crosslegs.end(),matchRep(PDT::Colour3)); int n8 = count_if(crosslegs.begin(),crosslegs.end(),matchRep(PDT::Colour8)); vector ordered(2*n3+n8,PDT::Colour8); int i = 0; while ( i < 2*n3 ) { ordered[i] = PDT::Colour3; ordered[i+1] = PDT::Colour3bar; i+=2; } return ordered; } string ColourBasis::file(const vector& sub) const { string res = name() + "-"; for ( vector::const_iterator lit = sub.begin(); lit != sub.end(); ++lit ) { if ( *lit == PDT::Colour3 ) res += "3"; if ( *lit == PDT::Colour3bar ) res += "3bar"; if ( *lit == PDT::Colour8 ) res += "8"; } if ( largeN() ) res += "largeN"; return res; } void ColourBasis::writeBasis(const string& prefix) const { if ( didWrite ) return; set > legs; for ( map >::const_iterator lit = theNormalOrderedLegs.begin(); lit != theNormalOrderedLegs.end(); ++lit ) { legs.insert(lit->second); } string searchPath = theSearchPath; if ( searchPath != "" ) if ( *(--searchPath.end()) != '/' ) searchPath += "/"; for ( set >::const_iterator known = legs.begin(); known != legs.end(); ++known ) { string fname = searchPath + prefix + file(*known) + ".cdat"; if ( !( SamplerBase::runLevel() == SamplerBase::ReadMode || SamplerBase::runLevel() == SamplerBase::BuildMode ) ) { ifstream check(fname.c_str()); if ( check ) continue; } ofstream out(fname.c_str()); if ( !out ) throw Exception() << "ColourBasis: Failed to open " << fname << " for storing colour basis information." << Exception::runerror; out << setprecision(18); const symmetric_matrix& sp = theScalarProducts.find(*known)->second; write(sp,out); - if ( theCharges.find(*known) != theCharges.end() ) { - out << "#charges\n"; - const map >& tm = - theCharges.find(*known)->second; - const map > >& tc = - theChargeNonZeros.find(*known)->second; - map > >::const_iterator kc = - tc.begin(); - for ( map >::const_iterator k = tm.begin(); - k != tm.end(); ++k, ++kc ) { - out << k->first << "\n"; - write(k->second,out,kc->second); - } + if ( theCorrelators.find(*known) != theCorrelators.end() ) { + out << "#correlators\n"; const map,symmetric_matrix >& cm = theCorrelators.find(*known)->second; for ( map,symmetric_matrix >::const_iterator k = cm.begin(); k != cm.end(); ++k ) { out << k->first.first << "\n" << k->first.second << "\n"; write(k->second,out); } } else { - out << "#nocharges\n"; + out << "#nocorrelators\n"; } out << flush; } didWrite = true; } bool ColourBasis::readBasis(const vector& legs) { string searchPath = theSearchPath; if ( searchPath != "" ) if ( *(--searchPath.end()) != '/' ) searchPath += "/"; string fname = searchPath + file(legs) + ".cdat"; ifstream in(fname.c_str()); if ( !in ) return false; read(theScalarProducts[legs],in); string tag; in >> tag; - if ( tag != "#nocharges" ) { - for ( size_t k = 0; k < legs.size(); ++k ) { - size_t i; in >> i; - read(theCharges[legs][i],in,theChargeNonZeros[legs][i]); - } + if ( tag == "#correlators" ) { for ( size_t k = 0; k < legs.size()*(legs.size()-1)/2; ++k ) { size_t i,j; in >> i >> j; read(theCorrelators[legs][make_pair(i,j)],in); } } readBasisDetails(legs); return true; } void ColourBasis::readBasis() { if ( didRead ) return; string searchPath = theSearchPath; if ( searchPath != "" ) if ( *(--searchPath.end()) != '/' ) searchPath += "/"; set > legs; for ( map >::const_iterator lit = theNormalOrderedLegs.begin(); lit != theNormalOrderedLegs.end(); ++lit ) legs.insert(lit->second); for ( set >::const_iterator known = legs.begin(); known != legs.end(); ++known ) { if ( theScalarProducts.find(*known) != theScalarProducts.end() ) continue; //if no coloured legs. if(known->empty()){ auto sp = symmetric_matrix(1,1); sp(0,0)=1.; theScalarProducts.insert({ *known , sp }); continue; } string fname = searchPath + file(*known) + ".cdat"; if ( !readBasis(*known) ) throw Exception() << "ColourBasis: Failed to open " << fname << " for reading colour basis information." << Exception::runerror; } didRead = true; } void ColourBasis::write(const symmetric_matrix& m, ostream& os) const { os << m.size1() << "\n"; for ( size_t i = 0; i < m.size1(); ++i ) for ( size_t j = i; j < m.size1(); ++j ) os << m(i,j) << "\n"; os << flush; } void ColourBasis::read(symmetric_matrix& m, istream& is) { size_t s; is >> s; m.resize(s); for ( size_t i = 0; i < m.size1(); ++i ) for ( size_t j = i; j < m.size1(); ++j ) is >> m(i,j); } void ColourBasis::write(const compressed_matrix& m, ostream& os, const vector >& nonZeros) const { os << nonZeros.size() << "\n" << m.size1() << "\n" << m.size2() << "\n"; for ( vector >::const_iterator nz = nonZeros.begin(); nz != nonZeros.end(); ++nz ) os << nz->first << "\n" << nz->second << "\n" << m(nz->first,nz->second) << "\n"; os << flush; } void ColourBasis::read(compressed_matrix& m, istream& is, vector >& nonZeros) { size_t nonZero, size1, size2; is >> nonZero >> size1 >> size2; nonZeros.resize(nonZero); m = compressed_matrix(size1,size2,nonZero); for ( size_t k = 0; k < nonZero; ++k ) { size_t i,j; double val; is >> i >> j >> val; nonZeros[k] = make_pair(i,j); m(i,j) = val; } } void ColourBasis::doinit() { HandlerBase::doinit(); if ( theSearchPath.empty() && factory() ) theSearchPath = factory()->runStorage(); readBasis(); } void ColourBasis::dofinish() { HandlerBase::dofinish(); writeBasis(); } void ColourBasis::doinitrun() { HandlerBase::doinitrun(); if ( theSearchPath.empty() && factory() ) theSearchPath = factory()->runStorage(); readBasis(); } void ColourBasis::persistentOutput(PersistentOStream & os) const { os << theLargeN << theNormalOrderedLegs << theIndexMap << theFlowMap << theOrderingStringIdentifiers << theOrderingIdentifiers << theSearchPath; writeBasis(); } void ColourBasis::persistentInput(PersistentIStream & is, int) { is >> theLargeN >> theNormalOrderedLegs >> theIndexMap >> theFlowMap >> theOrderingStringIdentifiers >> theOrderingIdentifiers >> theSearchPath; } // *** Attention *** The following static variable is needed for the type // description system in ThePEG. Please check that the template arguments // are correct (the class and its base class), and that the constructor // arguments are correct (the class name and the name of the dynamically // loadable library where the class implementation can be found). DescribeAbstractClass describeColourBasis("Herwig::ColourBasis", "Herwig.so"); void ColourBasis::Init() { static ClassDocumentation documentation ("ColourBasis is an interface to a colour basis " "implementation."); static Switch interfaceLargeN ("LargeN", "Switch on or off large-N evaluation.", &ColourBasis::theLargeN, false, false, false); static SwitchOption interfaceLargeNYes (interfaceLargeN, "Yes", "Work in N=infinity", true); static SwitchOption interfaceLargeNNo (interfaceLargeN, "No", "Work in N=3", false); } diff --git a/MatrixElement/Matchbox/Utility/ColourBasis.h b/MatrixElement/Matchbox/Utility/ColourBasis.h --- a/MatrixElement/Matchbox/Utility/ColourBasis.h +++ b/MatrixElement/Matchbox/Utility/ColourBasis.h @@ -1,566 +1,602 @@ // -*- C++ -*- // // ColourBasis.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_ColourBasis_H #define HERWIG_ColourBasis_H // // This is the declaration of the ColourBasis class. // #include "ThePEG/Handlers/HandlerBase.h" #include "ThePEG/MatrixElement/Tree2toNDiagram.h" #include "ThePEG/MatrixElement/MEBase.h" #include "Herwig/MatrixElement/Matchbox/Utility/MatchboxXComb.h" #include "Herwig/MatrixElement/Matchbox/MatchboxFactory.fh" #include +#include namespace Herwig { using std::iterator_traits; using std::distance; using namespace ThePEG; using boost::numeric::ublas::matrix; using boost::numeric::ublas::symmetric_matrix; using boost::numeric::ublas::compressed_matrix; using boost::numeric::ublas::upper; /** * \ingroup Matchbox * \author Simon Platzer * * \brief ColourBasis is an interface to a colour basis * implementation. * */ class ColourBasis: public HandlerBase { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ ColourBasis(); /** * The destructor. */ virtual ~ColourBasis(); //@} public: /** * Return the factory which produced this matrix element */ Ptr::tptr factory() const; /** * Clone this colour basis. */ Ptr::ptr cloneMe() const { return dynamic_ptr_cast::ptr>(clone()); } /** * Clear this colour basis */ virtual void clear(); /** * Prepare for the given sub process and return the basis * dimensionality. */ size_t prepare(const cPDVector&, bool); /** * Prepare for the given diagrams. */ size_t prepare(const MEBase::DiagramVector&, bool); /** * Return the index map. */ const map >& indexMap() const { return theIndexMap; } /** * Return a map of basis tensor indices to vectors identifying a * certain ordering corresponding to the given colour structure. May * not be supported by all colour basis implementations. */ virtual map > > basisList(const vector&) const { return map > >(); } /** * Given a physical subprocess, a colour to amplitude label map and * a basis tensor index, return an identifier of the ordering * coresponding to the given colour structure. This will only return * sensible results for colour bases which implement the basisList * query. */ const string& orderingString(const cPDVector& sub, const map& colourToAmplitude, size_t tensorId); /** * Given a physical subprocess, a colour to amplitude label map and * a basis tensor index, return an identifier of the ordering * coresponding to the given colour structure. This will only return * sensible results for colour bases which implement the basisList * query. */ const set >& ordering(const cPDVector& sub, const map& colourToAmplitude, size_t tensorId, size_t shift = 0); /** * For the given subprocess and amplitude vectors * calculate the amplitude squared. */ double me2(const cPDVector&, const map,CVector>&) const; /** * For the given subprocess and amplitude vectors * calculate the interference. */ double interference(const cPDVector&, const map,CVector>&, const map,CVector>&) const; /** * For the given subprocess and amplitude vector * calculate the colour correlated amplitude. */ double colourCorrelatedME2(const pair&, const cPDVector&, const map,CVector>&) const; /** * For the given subprocess and amplitude vector * calculate the amplitude squared. */ Complex interference(const cPDVector&, const CVector&, const CVector&) const; /** * For the given subprocess and amplitude vector * calculate the colour correlated amplitude. */ Complex colourCorrelatedInterference(const pair&, const cPDVector&, const CVector&, const CVector&) const; /** * For the given subprocess and amplitude given as amp amp^\dagger * calculate the amplitude squared. */ double me2(const cPDVector&, const matrix&) const; /** * For the given subprocess and amplitude given as amp amp^\dagger * calculate the colour correlated amplitude. */ double colourCorrelatedME2(const pair&, const cPDVector&, const matrix&) const; /** * Return the scalar product matrix for the given process. */ const symmetric_matrix& scalarProducts(const cPDVector&) const; /** - * Return the matrix representation of a colour charge. - */ - const compressed_matrix& charge(const cPDVector&, size_t) const; - - /** - * Return the non-vanishing elements of a colour charge. - */ - const vector >& chargeNonZero(const cPDVector&, size_t) const; - - /** * Return the correlator matrix for the given process. */ const symmetric_matrix& correlator(const cPDVector&, const pair&) const; /** * Return true, if the colour basis is capable of assigning colour * flows. */ virtual bool haveColourFlows() const { return false; } /** * Return a Selector with possible colour geometries for the selected * diagram weighted by their relative probabilities. */ Selector colourGeometries(tcDiagPtr diag, const map,CVector>& amps); /** * Return the colour tensor used for the selected colour flow */ size_t tensorIdFromFlow(tcDiagPtr diag, const ColourLines * cl); /** * Match colour representation. */ struct matchRep { PDT::Colour m; matchRep(PDT::Colour n) : m(n) {} bool operator()(PDT::Colour c) const { return c == m; } }; /** * Return true, if this basis is running in large-N mode */ virtual bool largeN() const { return theLargeN; } /** * Switch to large n */ void doLargeN(bool yes = true) { theLargeN = yes; } /** * Convert particle data to colour information */ vector projectColour(const cPDVector&) const; /** * Perform a normal ordering of the external legs. This default * implementation assumes normal ordered legs as 3 3bar ... 3 3bar 8 ... 8 * while removing all non-coloured particles. */ virtual vector normalOrder(const vector&) const; /** * Determine the mapping of process to colour indices and return the * normal ordered vector of colour indices */ vector normalOrderMap(const cPDVector& sub); /** * Get the normal ordered legs */ const vector& normalOrderedLegs(const cPDVector& sub) const; /** + * Generate the emission/splitting map for basis labels from sub-process + * information; we consider the splitting of leg ij from subprocess subFrom + * with the legs not participating in the splitting relabelled according to + * emissionMap. + */ + const std::tuple,vector, + size_t,size_t,size_t,map >& + normalOrderEmissionMap(const cPDVector& subFrom, + const cPDVector& subTo, + size_t ij, size_t i, size_t j, + const map& emissionMap); + + /** + * Return the colour charge matrix representation for the given splitting ij + * -> i,j with other legs relabbeled as indicated by the dictionary map. + */ + const pair,vector > >& + charge(const cPDVector& subFrom, + const cPDVector& subTo, + size_t ij, size_t i, size_t j, + const map& emissionMap); + + /** * Convert the legs to a string. */ string file(const vector&) const; /** * Calculate T_i^\dagger X T_j */ void chargeProduct(const compressed_matrix& ti, const vector >& tiNonZero, const symmetric_matrix& X, const compressed_matrix& tj, const vector >& tjNonZero, symmetric_matrix& result) const; /** * Calculate T_i X T_j^\dagger */ void chargeProductAdd(const compressed_matrix& ti, const vector >& tiNonZero, const matrix& X, const compressed_matrix& tj, const vector >& tjNonZero, matrix& result, double factor = 1.) const; public: /** * Find a coloured path from a to b within the given diagram. */ static list > colouredPath(pair a, pair b, Ptr::tcptr); /** * Get all colour flows for the given diagram. */ static list > > > colourFlows(Ptr::tcptr); /** * Convert a flow to a string representation appropriate for * ColourLines */ static string cfstring(const list > >&); + /** + * Returns a map of how the order of the vectors of a colour basis are + * changed when the indices are changed. + */ + virtual map indexChange(const vector&, + const size_t, + const map&) const { + map aMap; + return aMap; + } + + protected: /** * Prepare the basis for the normal ordered legs and return the * dimensionality of the basis. */ virtual size_t prepareBasis(const vector&) = 0; /** * Return the scalar product of basis tensors labelled a and b in * the basis used for the given normal ordered legs. */ virtual double scalarProduct(size_t a, size_t b, const vector& abBasis) const = 0; /** - * Return the matrix element of a colour charge - * between basis tensors a and b, with - * respect to aBasis and bBasis + * Return the matrix element of a colour charge or quark splitting + * between basis tensors a and b, with respect to + * aBasis and bBasis; k and l index the splitting product's labels, and dict + * encodes how legs not participating in the splitting are relabeled to the + * larger basis. */ virtual double tMatrixElement(size_t i, size_t a, size_t b, const vector& aBasis, - const vector& bBasis) const = 0; + const vector& bBasis, + size_t k, size_t l, + const map& dict) const = 0; /** * Return true, if a large-N colour connection exists for the * given external legs and basis tensor. */ virtual bool colourConnected(const cPDVector&, const vector&, const pair&, const pair&, size_t) const; /** * Return true, if a large-N colour connection exists for the * given external legs and basis tensor. */ virtual bool colourConnected(const vector&, int, int, size_t) const { return false; } /** * Match up colour flows for given diagram to basis tensors. */ vector makeFlows(Ptr::tcptr, size_t) const; /** * Return the colour line map. */ map::tcptr,vector >& colourLineMap(); /** * Update the colour line map for a given diagram. */ void updateColourLines(Ptr::tcptr); + 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(); // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). protected: /** @name Standard Interfaced functions. */ //@{ /** * Initialize this object after the setup phase before saving an * EventGenerator to disk. * @throws InitException if object could not be initialized properly. */ virtual void doinit(); /** * Initialize this object. Called in the run phase just before * a run begins. */ virtual void doinitrun(); /** * Finalize this object. Called in the run phase just after a * run has ended. Used eg. to write out statistics. */ virtual void dofinish(); //@} private: typedef map,symmetric_matrix > ScalarProductMap; - typedef map,map > > ChargeMap; - typedef map,map > > > ChargeNonZeroMap; + typedef map,map,symmetric_matrix > > + CorrelatorMap; - typedef map,map,symmetric_matrix > > CorrelatorMap; + typedef map,vector, + size_t,size_t,size_t,map >, + pair,vector > > > TSMap; /** * True, if this basis is running in large-N mode */ bool theLargeN; /** * Map external legs to normal ordered versions */ map > theNormalOrderedLegs; /** * Index mappings to normal order from given leg assignments, * indexed by the original leg assignment. */ map > theIndexMap; /** + * Translations of emission maps + */ + map >, + std::tuple,vector, + size_t,size_t,size_t,map > > + theEmissionMaps; + + /** * The scalar product matrix S_n = , indexed * by normal ordered leg assignments. */ ScalarProductMap theScalarProducts; /** - * The colour charge matrices indexed by - * the `n' normal ordered legs and the index i. - */ - ChargeMap theCharges; - - /** - * The nonzero elements of the charge matrices. - */ - ChargeNonZeroMap theChargeNonZeros; - - /** * The correlator matrices T_i\cdot T_j -> T_i^\dagger S_{n+1} T_j * with T_i = indexed by the `n' basis * normal ordered legs and indices i,j */ CorrelatorMap theCorrelators; /** + * Colour charge or quark splitting matrix representations + */ + TSMap theCharges; + + /** * Map diagrams to colour flows indexed by basis tensor. */ map::tcptr,vector > theFlowMap; /** * Map diagrams to colour line objects. */ map::tcptr,vector > theColourLineMap; /** * Store ordering identifiers */ map > theOrderingStringIdentifiers; /** * Store ordering identifiers */ map > > > theOrderingIdentifiers; /** * Write out yet unknown basis computations. */ void writeBasis(const string& prefix = "") const; /** * Read in the basis computation which are supposed to be known. */ void readBasis(); /** * Read in the basis computation which are supposed to be known. */ bool readBasis(const vector&); /** * Gather any implementation dependend details when reading a basis */ virtual void readBasisDetails(const vector&) {} /** * Write out symmetric matrices. */ void write(const symmetric_matrix&, ostream&) const; /** * Read in symmetric matrices. */ void read(symmetric_matrix&, istream&); /** * Write out compressed matrices. */ void write(const compressed_matrix&, ostream&, const vector >&) const; /** * Read in compressed matrices. */ void read(compressed_matrix&, istream&, vector >&); /** * True, if an attempt to read in basis information has been * completed. */ bool didRead; /** * True, if an attempt to write out basis information has been * completed. */ mutable bool didWrite; /** * Temporary storage. */ matrix tmp; /** * The search path */ string theSearchPath; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ ColourBasis & operator=(const ColourBasis &) = delete; }; } #endif /* HERWIG_ColourBasis_H */ diff --git a/MatrixElement/Matchbox/Utility/DensityOperator.cc b/MatrixElement/Matchbox/Utility/DensityOperator.cc new file mode 100644 --- /dev/null +++ b/MatrixElement/Matchbox/Utility/DensityOperator.cc @@ -0,0 +1,479 @@ +// -*- C++ -*- +// +// DensityOperator.cc is a part of Herwig - A multi-purpose Monte Carlo event generator +// Copyright (C) 2002-2007 The Herwig Collaboration +// +// Herwig is licenced under version 2 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 DensityOperator class. +// + +#include "DensityOperator.h" +#include "ThePEG/Interface/ClassDocumentation.h" +#include "ThePEG/EventRecord/Particle.h" +#include "ThePEG/Repository/UseRandom.h" +#include "ThePEG/Repository/EventGenerator.h" +#include "ThePEG/Utilities/DescribeClass.h" + +#include "Herwig/MatrixElement/Matchbox/Utility/MatchboxXCombData.h" +#include "Herwig/MatrixElement/Matchbox/Base/MatchboxMEBase.h" + +#include "ThePEG/Persistency/PersistentOStream.h" +#include "ThePEG/Persistency/PersistentIStream.h" + +using namespace Herwig; + + +DensityOperator::DensityOperator() : Nc(3.0), TR(0.5) { } + +DensityOperator::~DensityOperator() {} + +void DensityOperator::clear() { + theCorrelatorMap.clear(); +} + +// Prepare density operator if not done before +void DensityOperator::prepare(const cPDVector& mePartonData) { + + // Take parton data and create key of type 33bar888 to use as key for basis + vector mePartonDataColoured = theColourBasis->normalOrderMap(mePartonData); + + if( theDensityOperatorMap.count( mePartonDataColoured ) == 0 ){ + // Get basis dimension + size_t dim = theColourBasis->prepare( mePartonData, false ); + // Allocate space for density matrix + theDensityOperatorMap.insert(make_pair(mePartonDataColoured,matrix (dim,dim))); + } +} + +// Fill the density matrix for the first time +void DensityOperator::fill(const Ptr::ptr MBXCombPtr, + const cPDVector& partons, + const vector& momenta){ + + // Map from helicity structure to amplitude vector in the color basis + const map,CVector>& amplitudeMap = MBXCombPtr->lastAmplitudes(); + const cPDVector& mePartonData = MBXCombPtr->mePartonData(); + // Get the dimension of the basis for the indexChange method + size_t dim = theColourBasis->prepare(mePartonData,false); + // Normal order partons according to ColourBasis + const vector mePartonDataColoured = theColourBasis->normalOrderMap(mePartonData); + + // Get the colour basis to colour basis map for this hard subprocess + map >::iterator cb2cbit = theColourBasisToColourBasisMap.find(mePartonData); + // Fill the map with this hard subprocess if it doesn't have it yet + if ( cb2cbit == theColourBasisToColourBasisMap.end() ) { + // Get the index map for the partons as they are ordered in the MatchboxXComb + // object + const map& mbIndexMap = theColourBasis->indexMap().at(mePartonData); + + // Get the index map for the partons as they are ordered + // in the shower. + // Use prepare, as it might not have been done for this order of the partons + theColourBasis->prepare(partons,false); + const map& showerIndexMap = theColourBasis->indexMap().at(partons); + + // ensure the maps are of the same size + assert( mbIndexMap.size() == showerIndexMap.size() ); + + // Loop over both sets of partons to determine how the order between them + // differs, then translate the index to the colour basis and put the + // key-value pair into the map + map cb2cbMap; + + // Get the momenta for comparison + const vector& meMomenta = MBXCombPtr->matchboxME()->lastMEMomenta(); + // Make sure there's the same number of momenta in both vectors + assert( momenta.size() == meMomenta.size() ); + bool done; + // Boost the momenta to the same frame + const vector momentaCM = boostToRestFrame(momenta); + for ( size_t i = 0; i < momentaCM.size(); i++ ) { + // The cb2cb map is intended to translate how the indices + // are different in the colour basis due to the different + // orderings of the partons, so it should only bother + // with coloured particles. + if ( partons[i]->coloured() ) { + done = false; + for ( size_t j = 0; j < meMomenta.size(); j++ ) { + if ( !done ) { + if ( compareMomentum(momentaCM[i],meMomenta[j]) ) { + cb2cbMap[mbIndexMap.at(i)] = showerIndexMap.at(j); + done = true; + } + } + } + } + } + + // Make sure all momenta have been identified + assert( cb2cbMap.size() == mePartonDataColoured.size() ); + + // Add the map to the cache + theColourBasisToColourBasisMap[mePartonData] = cb2cbMap; + + // Get the iterator + cb2cbit = theColourBasisToColourBasisMap.find(mePartonData); + } + + // With the cb2cb index map we can create the basis vector index map + const map vectorMap = theColourBasis->indexChange(mePartonDataColoured, + dim,cb2cbit->second); + + // Prepare density operator (allocate) for set of particles + prepare(mePartonData); + + // Check that density operator (place holder for) exist in density operator map + map,matrix >::iterator dOit = theDensityOperatorMap.find(mePartonDataColoured); + assert(dOit != theDensityOperatorMap.end()); + + // Initialize the density operator + matrix& densOp = dOit->second; + for(unsigned int i = 0; i < (amplitudeMap.begin()->second).size(); i++){ + for(unsigned int j = 0; j < (amplitudeMap.begin()->second).size(); j++){ + densOp (i,j) = 0; + } + } + + // Fill the density operator, ok to sum over helicities since the density operator + // exist at the probability level + CVector amplitude; + for ( map,CVector>::const_iterator itHel = amplitudeMap.begin(); + itHel != amplitudeMap.end(); itHel++ ) { + amplitude = itHel->second; + for ( unsigned int i = 0; i < amplitude.size(); i++ ) { + for ( unsigned int j = 0; j < amplitude.size(); j++ ) { + // vectorMap is used such that densOp is filled according to the + // basis order defined by the input cPDVector partons. + densOp (vectorMap.at(i),vectorMap.at(j)) += amplitude(i)*std::conj(amplitude(j)); + } + } + } + + // Colour conservation check + colourConservation(mePartonData); +} + +// Update density matrix after emitting or splitting a gluon +// The emissionsMap argument contains the relation of the indices in the smaller (before) +// and larger basis (after). the first 3-tuple contains the indices +// (emitter before, emitter after, emitted parton) +// The map contains the old and new indices of all other partons (not involved) +void DensityOperator::evolve(const map,Complex>& Vijk, + const cPDVector& before, + const cPDVector& after, + const map,map >& emissionsMap, + const bool splitAGluon, + const bool initialGluonSplitting) { + + size_t dimBefore = theColourBasis->prepare( before, false ); + size_t dimAfter = theColourBasis->prepare( after, false ); + + vector beforeColoured = theColourBasis->normalOrderMap(before); + vector afterColoured = theColourBasis->normalOrderMap(after); + + const map,matrix >::iterator dOit = theDensityOperatorMap.find(beforeColoured); + assert(dOit != theDensityOperatorMap.end()); + + const matrix& densOpBefore = dOit->second; + + prepare(after); + matrix& densOpAfter = theDensityOperatorMap[afterColoured]; + for(size_t i = 0; i < densOpAfter.size1(); i++){ + for(size_t j = 0; j < densOpAfter.size2(); j++){ + densOpAfter(i,j) = 0; + } + } + + compressed_matrix Tij; + matrix TijMn (dimAfter,dimBefore); + compressed_matrix Tk; + matrix TijMnTkdagger (dimAfter,dimAfter); + Complex V; + // Compensate for sign from updated density matrix + // TODO Check signs again + double sign = -1.0; + if ( splitAGluon ) + sign = 1.0; + // Loop over emitter legs ij and recoil legs k and add the contribution, + // for Vijk is assumed to contain the factor 4*pi*\alpha_s/pi.pj + typedef map,map > dictMap; + int ij,k; + + // Loop over emitters + for(dictMap::const_iterator ijit = emissionsMap.begin(); + ijit != emissionsMap.end(); ijit++) { + // get first element in 3-tuple, i.e., emitter before + ij = std::get<0>(ijit->first); + assert(before[ij]->coloured()); + // Get rectangular matrices T_{ij} or S_{ij} taking us from the + // smaller to the larger basis depending on + // the involved particles before and after, the emitter index before ij, + // the emitter after and the emitted parton index + int i=std::get<1>(ijit->first); // emitter after + int j=std::get<2>(ijit->first);// emitted parton + Tij = theColourBasis->charge(before,after, + ij,i,j,ijit->second).first; + TijMn = prodSparseDense(Tij,densOpBefore); + + // Loop over spectators + for(dictMap::const_iterator kit = emissionsMap.begin(); + kit != emissionsMap.end(); kit++) { + k = std::get<0>(kit->first); + assert(before[k]->coloured()); + // Standard case of gluon radiation + if ( ijit != kit || splitAGluon || initialGluonSplitting ) { + int k_after=std::get<1>(kit->first); // For color structure k now has role of emitter + int k_emission= std::get<2>(kit->first); // Emitted parton index + Tk = theColourBasis->charge(before,after, + k, k_after, k_emission, kit->second).first; + TijMnTkdagger = prodDenseSparse(TijMn,Tk); + // sign == -1.0 if it isn't a gluon splitting into a qqbar pair + V = sign*(1.0/colourNorm(before[ij]))* + Vijk.at(make_pair(ij,k)); + densOpAfter += V*TijMnTkdagger; + } + } + } + // Check that the density operator does not vanish + assert( theColourBasis->me2(after,densOpAfter) != 0.0 ); + colourConservation(after); +} + +// The 3-tuple contains (emitter index, spectator index, emission pid) +double DensityOperator::colourMatrixElementCorrection(const std::tuple& ikemission, + const cPDVector& particles) { + const int i = std::get<0>(ikemission); + const int k = std::get<1>(ikemission); + const long emissionID = std::get<2>(ikemission); + + + // Get the density operator + // normal order particles as in ColourBasis + vector particlesColoured = theColourBasis->normalOrderMap(particles); + // ... and find corresponding density operator + const map,matrix >::iterator particlesit = theDensityOperatorMap.find(particlesColoured); + assert(particlesit != theDensityOperatorMap.end()); + const matrix& densOp = particlesit->second; + + double Ti2 = colourNorm(particles[i]); + + // Emitter-spectator pair + const pair ik = make_pair(i,k); + // Create key for color matrix element correction map + pair,pair > particlesAndLegs = make_pair(particlesColoured,ik); + + // Result + double res = 0; + + // Check if it has already been calculated + // TODO: move this check earlier (we only need particlesColoured to check if it has been + // calculated). + // Check for color matrix element associated with key, and calculate if not done + const map,pair >,double >::const_iterator corrit = theCorrelatorMap.find(particlesAndLegs); + if ( corrit == theCorrelatorMap.end() ) { + double corrME2 = theColourBasis->colourCorrelatedME2(ik,particles,densOp); + double me2 = theColourBasis->me2(particles,densOp); + res = -(1/Ti2)*corrME2/me2; + + if ( particles[i]->id() == ParticleID::g ) + res *= 2.; + + theCorrelatorMap.insert(make_pair(particlesAndLegs,res)); + + } else { + res = corrit->second; + } + + return res; +} + +double DensityOperator::colourNorm(const cPDPtr particle) { + if ( particle->id() == ParticleID::g ) { + return Nc; //is 3.0 for Nc = 3, TR = 1/2 + } else if ( particle->iColour() == PDT::Colour3 || particle->iColour() == PDT::Colour3bar ) { + return TR*(Nc*Nc-1.)/Nc; // is 4.0/3.0 for Nc = 3, TR = 1/2 + } else { + throw Exception() << "Colour matrix element corrections only work " + << "on quark and gluon legs. " + << Exception::runerror; + } +} + +void DensityOperator::colourConservation(const cPDVector& particles) { + // To contain (emitter, spectator, emission pid) + std::tuple ikemission; + // Normal order particles as defined in ColourBasis + const vector particlesColoured = theColourBasis->normalOrderMap(particles); + vector sum(particlesColoured.size(),0.0); + size_t iterm = 0; + + // To compensate for the CMEC having a 1/(1+\delta(i is gluon)) factor + // in the splitting kernel + double gluonFactor = 1.0; + // Loop over "emitters" to check color conservation for + for ( size_t i = 0; i < particles.size(); i++ ) { + if ( particles[i]->coloured() ) { + for ( size_t k = 0; k < particles.size(); k++ ) { + if ( particles[k]->coloured() && i != k ) { + ikemission = std::make_tuple(i,k,ParticleID::g); + if ( particles[i]->id() != ParticleID::g ) { + gluonFactor = 1.0; + } else { + gluonFactor = 1./2.; + } + sum[iterm] += gluonFactor*colourMatrixElementCorrection(ikemission,particles); + } + } + iterm++; + } + } + for ( size_t i = 0; i < sum.size(); i++ ) + assert( std::abs(sum[i]-1.0) < pow(10.0,-10.0)); +} + +matrix DensityOperator::prodSparseDense(const compressed_matrix& Tij, + const matrix& Mn){ + // Dimension before emission + size_t dimBefore = Tij.size2(); + // Dimension after emission + size_t dimAfter = Tij.size1(); + + //Check matrix dimensions + assert( dimBefore == Mn.size1() ); + + // Allocate memory for the matrix + matrix TijMn (dimAfter,dimBefore,0); + // Use iterators for the compressed matrix to iterate only over the non-zero + // elements. + size_t ii; + size_t jj; + for ( compressed_matrix::const_iterator1 it1 = Tij.begin1(); + it1 != Tij.end1(); it1++ ) { + for ( compressed_matrix::const_iterator2 it2 = it1.begin(); + it2 != it1.end(); it2++ ) { + ii = it2.index1(); + jj = it2.index2(); + for ( size_t kk = 0; kk < dimBefore; kk++ ) { + // *it2 is Tij(ii,jj) + TijMn(ii,kk) += (*it2)*Mn(jj,kk); + } + } + } + return TijMn; +} + +matrix DensityOperator::prodDenseSparse(const matrix& TijMn, + const compressed_matrix& Tk){ + // The compressed matrix comes from the charge method, do not transpose yet + // Dimension after emission + size_t dimAfter = Tk.size1();//Since this method returns TijMn*Tk^\dagger + + //Check matrix dimensions + assert( TijMn.size2() == Tk.size2() ); + + // Allocate memory for the matrix + matrix TijMnTkdagger (dimAfter,dimAfter,0); + size_t jj; + size_t kk; + for ( compressed_matrix::const_iterator1 it1 = Tk.begin1(); + it1 != Tk.end1(); it1++ ) { + for ( compressed_matrix::const_iterator2 it2 = it1.begin(); + it2 != it1.end(); it2++ ) { + jj = it2.index2();//transposing Tk jj is index2(), not index1() + kk = it2.index1();//transposing Tk + for ( size_t ii = 0; ii < dimAfter; ii++ ) { + // *it2 is Tk(kk,jj) = trans(Tk)(jj,kk) + TijMnTkdagger(ii,kk) += TijMn(ii,jj)*(*it2); + } + } + } + return TijMnTkdagger; +} + +vector DensityOperator::boostToRestFrame(const vector& momenta) { + // We need 2 initial particles + assert(momenta.size() >= 2); + // The boosted vectors + vector vboosted = momenta; + + // The boost should be to the rest frame of the initial particles + Boost b = (momenta[0] + momenta[1]).findBoostToCM(); + + // Boost all of the vectors + for ( size_t i = 0; i < momenta.size(); i++ ) { + vboosted[i].boost(b); + } + + return vboosted; +} + +bool DensityOperator::compareMomentum(const Lorentz5Momentum& p, const Lorentz5Momentum& q) { + bool equal = true; + // Compares two momentum vectors p and q, if they are close enough (defined by the + // double eps below) it returns true. This is sufficient to distinguish particles + // in the matrix element as we are not interested in the matrix element for extremely + // collinear radiation (better described by the parton shower). + + // Create the difference of the two vectors + const Lorentz5Momentum l = p - q; + // A relevant size that is guaranteed to be larger than 0 + const Energy2 e2 = p.t()*p.t(); + // Size of the difference that would be considered equal + const double eps = pow(10.,-15.); + + if ( l.x()*l.x()/e2 > eps ) + equal = false; + if ( l.y()*l.y()/e2 > eps ) + equal = false; + if ( l.z()*l.z()/e2 > eps ) + equal = false; + if ( l.t()*l.t()/e2 > eps ) + equal = false; + + return equal; +} + + + + +IBPtr DensityOperator::clone() const { + return new_ptr(*this); +} + +IBPtr DensityOperator::fullclone() const { + return new_ptr(*this); +} + + +// If needed, insert default implementations of virtual function defined +// in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). + + +void DensityOperator::persistentOutput(PersistentOStream &) const { + // *** ATTENTION *** os << ; // Add all member variable which should be written persistently here. +} + +void DensityOperator::persistentInput(PersistentIStream &, int) { + // *** ATTENTION *** is >> ; // Add all member variable which should be read persistently here. +} + + +// *** Attention *** The following static variable is needed for the type +// description system in ThePEG. Please check that the template arguments +// are correct (the class and its base class), and that the constructor +// arguments are correct (the class name and the name of the dynamically +// loadable library where the class implementation can be found). +DescribeClass +describeHerwigDensityOperator("Herwig::DensityOperator", "DensityOperator.so"); + +void DensityOperator::Init() { + + static ClassDocumentation documentation + ("There is no documentation for the DensityOperator class"); + +} + diff --git a/MatrixElement/Matchbox/Utility/DensityOperator.h b/MatrixElement/Matchbox/Utility/DensityOperator.h new file mode 100644 --- /dev/null +++ b/MatrixElement/Matchbox/Utility/DensityOperator.h @@ -0,0 +1,248 @@ +// -*- C++ -*- +// +// DensityOperator.h is a part of Herwig - A multi-purpose Monte Carlo event generator +// Copyright (C) 2002-2007 The Herwig Collaboration +// +// Herwig is licenced under version 2 of the GPL, see COPYING for details. +// Please respect the MCnet academic guidelines, see GUIDELINES for details. +// +#ifndef Herwig_DensityOperator_H +#define Herwig_DensityOperator_H +// +// This is the declaration of the DensityOperator class. +// + +#include "ThePEG/Handlers/HandlerBase.h" + +#include "Herwig/MatrixElement/Matchbox/Utility/ColourBasis.h" + +#include +#include +#include + +namespace Herwig { + +using namespace ThePEG; + +typedef boost::numeric::ublas::vector CVector; + + + +/** + * Here is the documentation of the DensityOperator class. + * + * @see \ref DensityOperatorInterfaces "The interfaces" + * defined for DensityOperator. + */ +class DensityOperator: public HandlerBase { + +public: + + /** @name Standard constructors and destructors. */ + //@{ + /** + * The default constructor. + */ + DensityOperator(); + + /** + * The destructor. + */ + virtual ~DensityOperator(); + //@} + +public: + + /** + * Clears theDensityOperatorMap. + */ + void clear(); + + /** + * Prepare for the given sub process. + */ + void prepare(const cPDVector&); + + /** + * Fill the density operator for the given hard subprocess, summing over all + * helicity configurations. + */ + void fill(const Ptr::ptr, + const cPDVector&, const vector& momenta); + + /** + * Evolve the density operator, by + * M_{n+1} = -\sum_{i,k}{-4*pi*alpha_s/Ti2*V_{ij,k} T_{i,n}M_nT_{k,n}^\dag}, + * see arXiv:1206.0180 eq. (5), note that the pi*pj factor is assumed to be + * included in V_{ij,k}. + */ + void evolve(const map,Complex>& Vijk, + const cPDVector& before, + const cPDVector& after, + const map,map >& emissionsMap, + const bool splitAGluon, + const bool initialGluonSplitting); + + /** + * Calculate the colour matrix element correction. + * -(1+delta(i,gluon))/Ti^2 Tr(Sn+1 Ti Mn Tk^dagger)/Tr(Sn Mn) + * where the bracket in front compensates for the gluon symmetry factor, + * Ti^2 is C_f or C_a, Sn+1 is the matrix of scalar products, and + * Ti is the radiation matrix. + * The first arg contains (emitter index, spectator index, emission pid) + * + */ + double colourMatrixElementCorrection(const std::tuple& ikemission, + const cPDVector& particles); + + /** + * Checking colour conservation for the colour matrix element corrections. + */ + void colourConservation(const cPDVector& particles); + + /** + * Get the colour basis. + */ + Ptr::tptr colourBasis() { return theColourBasis; } + + /** + * Get the colour basis. + */ + const Ptr::tptr colourBasis() const { return theColourBasis; } + + /** + * Set the colour basis. + */ + void colourBasis(Ptr::ptr ptr) { theColourBasis = ptr; } + + /** + * Get the correlator map. + */ + const map,pair >,double>& correlatorMap() const { + return theCorrelatorMap; + } + + /** @name Functions used by the persistent I/O system. */ + //@{ + /** + * Function used to write out object persistently. + * @param os the persistent output stream written to. + */ + void persistentOutput(PersistentOStream & os) const; + + /** + * Function used to read in object persistently. + * @param is the persistent input stream read from. + * @param version the version number of the object when written. + */ + void persistentInput(PersistentIStream & is, int version); + //@} + + /** + * The standard Init function used to initialize the interfaces. + * Called exactly once for each class by the class description system + * before the main function starts or + * when this class is dynamically loaded. + */ + static void Init(); + +protected: + + /** @name Clone Methods. */ + //@{ + /** + * Make a simple clone of this object. + * @return a pointer to the new object. + */ + virtual IBPtr clone() const; + + /** Make a clone of this object, possibly modifying the cloned object + * to make it sane. + * @return a pointer to the new object. + */ + virtual IBPtr fullclone() const; + //@} + + +// If needed, insert declarations of virtual function defined in the +// InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). + + +private: + + /** + * Number of colours used in colourNorm. + */ + double Nc; + + /** + * QCD vertex normalization. + */ + double TR; + + /** + * Normalization of colour charges \mathbf{T}_{ij}^2. + */ + double colourNorm(const cPDPtr particle); + + /** + * Fast evaluation of Tij*Mn, where a Tij is the matrix from ColourBasis::charge, + * which is a sparse matrix, and Mn is the density operator, a dense matrix. + * + */ + matrix prodSparseDense(const compressed_matrix&, + const matrix&); + /** + * Fast evaluation of TijMn*Tkdagger, where a TijMn is the result from the method + * prodSparseDense, a dense matrix, and Tkdagger is the transponse conjugate of + * the matrix from ColourBasis::charge, a sparse matrix. + * + */ + matrix prodDenseSparse(const matrix&, + const compressed_matrix&); + + /** + * Boosts a vector of momenta to the rest frame of the initial pair + * of particles (the first 2 elements of the argument vector). Returns + * the boosted vectors + */ + vector boostToRestFrame(const vector& momenta); + + /** + * Boosts a vector of momenta to the rest frame of the initial pair + */ + bool compareMomentum(const Lorentz5Momentum& p, const Lorentz5Momentum& q); + + /** + * Mapping of colour structures to density operator matrices. + * + */ + map,matrix > theDensityOperatorMap; + + /** + * Mapping of colour structures and legs to colour correlators. + */ + map,pair >,double> theCorrelatorMap; + + /** + * A map from the hard subprocess particles to a map of amplitude colour + * basis order to the normal ordered colour basis. + */ + map > theColourBasisToColourBasisMap; + + /** + * Colour basis used. + */ + Ptr::ptr theColourBasis; + + /** + * The assignment operator is private and must never be called. + * In fact, it should not even be implemented. + */ + DensityOperator & operator=(const DensityOperator &); + +}; + +} + +#endif /* Herwig_DensityOperator_H */ diff --git a/MatrixElement/Matchbox/Utility/Makefile.am b/MatrixElement/Matchbox/Utility/Makefile.am --- a/MatrixElement/Matchbox/Utility/Makefile.am +++ b/MatrixElement/Matchbox/Utility/Makefile.am @@ -1,33 +1,35 @@ noinst_LTLIBRARIES = libHwMatchboxUtility.la libHwMatchboxUtility_la_SOURCES = \ AmplitudeCache.h \ AmplitudeCache.tcc \ ColourBasis.cc \ ColourBasis.h \ +DensityOperator.cc \ +DensityOperator.h \ DiagramDrawer.cc \ DiagramDrawer.h \ MatchboxScaleChoice.cc \ MatchboxScaleChoice.h \ ProcessData.h \ ProcessData.fh \ ProcessData.cc \ SimpleColourBasis.cc \ SimpleColourBasis.h \ SimpleColourBasis2.cc \ SimpleColourBasis2.h \ SpinCorrelationTensor.h \ SpinorHelicity.h \ SU2Helper.cc \ SU2Helper.h \ Tree2toNGenerator.cc \ Tree2toNGenerator.h \ MatchboxXComb.h \ MatchboxXComb.cc \ MatchboxXCombGroup.h \ MatchboxXCombGroup.cc \ MatchboxXCombData.h \ MatchboxXCombData.cc \ LastMatchboxXCombInfo.h \ MatchboxFactoryMatcher.h \ MatchboxFactoryMatcher.cc diff --git a/MatrixElement/Matchbox/Utility/SimpleColourBasis.cc b/MatrixElement/Matchbox/Utility/SimpleColourBasis.cc --- a/MatrixElement/Matchbox/Utility/SimpleColourBasis.cc +++ b/MatrixElement/Matchbox/Utility/SimpleColourBasis.cc @@ -1,396 +1,410 @@ // -*- C++ -*- // // SimpleColourBasis.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. // // // This is the implementation of the non-inlined, non-templated member // functions of the SimpleColourBasis class. // #include "SimpleColourBasis.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/EventRecord/Particle.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/StandardModel/StandardModelBase.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; SimpleColourBasis::SimpleColourBasis() {} SimpleColourBasis::~SimpleColourBasis() {} IBPtr SimpleColourBasis::clone() const { return new_ptr(*this); } IBPtr SimpleColourBasis::fullclone() const { return new_ptr(*this); } size_t SimpleColourBasis::prepareBasis(const vector& basis) { if ( id33bar.empty() ) makeIds(); if ( basis == id88 || basis == id33bar || basis == id33bar8 ) return 1; if ( basis == id888 || basis == id33bar88 || basis == id33bar33bar ) return 2; if ( basis == id8888 ) return 6; throw Exception() << "SimpleColourBasis::prepareBasis(): Cannot handle colour configuration" << Exception::runerror; return 0; } double SimpleColourBasis::scalarProduct(size_t a, size_t b, const vector& abBasis) const { if ( id33bar.empty() ) makeIds(); double Nc = SM().Nc(); double Nc2 = sqr(Nc); double Nc3 = Nc*Nc2; double Nc4 = sqr(Nc2); double Nc6 = Nc2*Nc4; if ( a > b ) swap(a,b); if ( !largeN() ) { if ( abBasis == id88 ) { return ( Nc2 - 1. )/4.; } if ( abBasis == id33bar ) { return Nc; } if ( abBasis == id888 ) { if ( a == b ) return ( Nc4 - 3.*Nc2 + 2. )/(8.*Nc); return -( Nc2 - 1. )/(4.*Nc); } if ( abBasis == id33bar8 ) { return ( Nc2 - 1. )/2.; } if ( abBasis == id8888 ) { if ( a == b ) return ( Nc6 - 4.*Nc4 + 6.*Nc2 - 3. )/(16.*Nc2); if ( ( a == 0 && b == 1 ) || ( a == 2 && b == 3 ) || ( a == 4 && b == 5 ) ) return ( Nc4 + 2.*Nc2 - 3. )/(16.*Nc2); return -( Nc2 - 4. + 3./Nc2 )/16.; } if ( abBasis == id33bar88 ) { if ( a == b ) return ( Nc4 - 2.*Nc2 + 1 )/(4.*Nc); return -( Nc2 - 1. )/(4.*Nc); } if ( abBasis == id33bar33bar ) { if ( a == b ) return Nc2; return Nc; } } else { if ( a != b ) return 0.; if ( abBasis == id88 ) { return Nc2/4.; } if ( abBasis == id33bar ) { return Nc; } if ( abBasis == id888 ) { return Nc3/8.; } if ( abBasis == id33bar8 ) { return Nc2/2.; } if ( abBasis == id8888 ) { return Nc4/16.; } if ( abBasis == id33bar88 ) { return Nc3/4.; } if ( abBasis == id33bar33bar ) { return Nc2; } } throw Exception() << "SimpleColourBasis::scalarProduct(): Cannot handle colour configuration" << Exception::runerror; } double SimpleColourBasis::tMatrixElement(size_t i, size_t a, size_t b, const vector&, - const vector& bBasis) const { + const vector& bBasis, + size_t k, size_t l, + const map& dict) const { + // Check indices k and l + assert( k == i ); + assert( l == bBasis.size() ); + // Check that dict is the standardMap + assert( dict.size()+1 == bBasis.size() ); + map::const_iterator tmp; + for ( size_t ii = 0; ii < bBasis.size(); ii++ ) + if ( ii != i ) { + tmp = dict.find(ii); + assert( tmp != dict.end() ); + assert( tmp->second == ii ); + } if ( id33bar.empty() ) makeIds(); if ( bBasis == id88 ) { if ( i == 0 ) return a == 0 ? -1. : 1.; else return a == 0 ? 1. : -1.; } if ( bBasis == id33bar ) { return i == 0 ? 1. : -1.; } if ( bBasis == id888 ) { if ( i == 0 ) { if ( a == 3 && b == 0 ) return 1.; if ( a == 0 && b == 0 ) return -1.; if ( a == 1 && b == 1 ) return 1.; if ( a == 2 && b == 1 ) return -1.; } if ( i == 1 ) { if ( a == 4 && b == 0 ) return 1.; if ( a == 3 && b == 0 ) return -1.; if ( a == 2 && b == 1 ) return 1.; if ( a == 5 && b == 1 ) return -1.; } if ( i == 2 ) { if ( a == 0 && b == 0 ) return 1.; if ( a == 4 && b == 0 ) return -1.; if ( a == 5 && b == 1 ) return 1.; if ( a == 1 && b == 1 ) return -1.; } return 0.; } if ( bBasis == id33bar8 ) { if ( i == 0 ) return a == 1 ? 1. : 0.; if ( i == 1 ) return a == 0 ? -1. : 0.; if ( i == 2 ) return a == 0 ? 1. : -1.; } throw Exception() << "SimpleColourBasis::tMatrixElement(): Cannot handle colour configuration" << Exception::runerror; return 0.; } bool SimpleColourBasis::colourConnected(const cPDVector& sub, const vector& basis, const pair& i, const pair& j, size_t a) const { if ( id33bar.empty() ) makeIds(); // translate process to basis ids map >::const_iterator trans = indexMap().find(sub); assert(trans != indexMap().end()); int idColoured = i.second ? j.first : i.first; idColoured = trans->second.find(idColoured)->second; int idAntiColoured = i.second ? i.first : j.first; idAntiColoured = trans->second.find(idAntiColoured)->second; if ( basis == id88 ) { return ( idColoured == 0 && idAntiColoured == 1 ) || ( idColoured == 1 && idAntiColoured == 0 ); } if ( basis == id33bar ) { return idColoured == 0 && idAntiColoured == 1; } if ( basis == id888 ) { if ( a == 0 ) return ( idColoured == 0 && idAntiColoured == 1 ) || ( idColoured == 1 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 0 ); if ( a == 1 ) return ( idColoured == 0 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 1 ) || ( idColoured == 1 && idAntiColoured == 0 ); } if ( basis == id33bar8 ) { return ( idColoured == 0 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 1 ); } if ( basis == id8888 ) { if ( a == 0 ) return ( idColoured == 0 && idAntiColoured == 1 ) || ( idColoured == 1 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 3 ) || ( idColoured == 3 && idAntiColoured == 0 ); if ( a == 1 ) return ( idColoured == 0 && idAntiColoured == 3 ) || ( idColoured == 3 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 1 ) || ( idColoured == 1 && idAntiColoured == 0 ); if ( a == 2 ) return ( idColoured == 0 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 1 ) || ( idColoured == 1 && idAntiColoured == 3 ) || ( idColoured == 3 && idAntiColoured == 0 ); if ( a == 3 ) return ( idColoured == 0 && idAntiColoured == 3 ) || ( idColoured == 3 && idAntiColoured == 1 ) || ( idColoured == 1 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 0 ); if ( a == 4 ) return ( idColoured == 0 && idAntiColoured == 1 ) || ( idColoured == 1 && idAntiColoured == 3 ) || ( idColoured == 3 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 0 ); if ( a == 5 ) return ( idColoured == 0 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 3 ) || ( idColoured == 3 && idAntiColoured == 1 ) || ( idColoured == 1 && idAntiColoured == 0 ); } if ( basis == id33bar88 ) { if ( a == 0 ) return ( idColoured == 0 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 3 ) || ( idColoured == 3 && idAntiColoured == 1 ); if ( a == 1 ) return ( idColoured == 0 && idAntiColoured == 3 ) || ( idColoured == 3 && idAntiColoured == 2 ) || ( idColoured == 2 && idAntiColoured == 1 ); } if ( basis == id33bar33bar ) { if ( a == 0 ) return ( idColoured == 0 && idAntiColoured == 1 ) || ( idColoured == 2 && idAntiColoured == 3 ); if ( a == 1 ) return ( idColoured == 0 && idAntiColoured == 3 ) || ( idColoured == 2 && idAntiColoured == 1 ); } return false; } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void SimpleColourBasis::makeIds() const { id88.push_back(PDT::Colour8); id88.push_back(PDT::Colour8); id33bar.push_back(PDT::Colour3); id33bar.push_back(PDT::Colour3bar); id888.push_back(PDT::Colour8); id888.push_back(PDT::Colour8); id888.push_back(PDT::Colour8); id33bar8.push_back(PDT::Colour3); id33bar8.push_back(PDT::Colour3bar); id33bar8.push_back(PDT::Colour8); id8888.push_back(PDT::Colour8); id8888.push_back(PDT::Colour8); id8888.push_back(PDT::Colour8); id8888.push_back(PDT::Colour8); id33bar88.push_back(PDT::Colour3); id33bar88.push_back(PDT::Colour3bar); id33bar88.push_back(PDT::Colour8); id33bar88.push_back(PDT::Colour8); id33bar33bar.push_back(PDT::Colour3); id33bar33bar.push_back(PDT::Colour3bar); id33bar33bar.push_back(PDT::Colour3); id33bar33bar.push_back(PDT::Colour3bar); } void SimpleColourBasis::persistentOutput(PersistentOStream &) const {} void SimpleColourBasis::persistentInput(PersistentIStream &, int) {} // *** Attention *** The following static variable is needed for the type // description system in ThePEG. Please check that the template arguments // are correct (the class and its base class), and that the constructor // arguments are correct (the class name and the name of the dynamically // loadable library where the class implementation can be found). DescribeClass describeHerwigSimpleColourBasis("Herwig::SimpleColourBasis", "Herwig.so"); void SimpleColourBasis::Init() { static ClassDocumentation documentation ("SimpleColourBasis implements the colour algebra needed for " "electroweak boson and electroweak boson + jet production at NLO. It mainly " "serves as an example for the general ColourBasis interface."); } diff --git a/MatrixElement/Matchbox/Utility/SimpleColourBasis.h b/MatrixElement/Matchbox/Utility/SimpleColourBasis.h --- a/MatrixElement/Matchbox/Utility/SimpleColourBasis.h +++ b/MatrixElement/Matchbox/Utility/SimpleColourBasis.h @@ -1,189 +1,209 @@ // -*- C++ -*- // // SimpleColourBasis.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_SimpleColourBasis_H #define Herwig_SimpleColourBasis_H // // This is the declaration of the SimpleColourBasis class. // #include "Herwig/MatrixElement/Matchbox/Utility/ColourBasis.h" namespace Herwig { using namespace ThePEG; /** * \ingroup Matchbox * \author Simon Platzer * * \brief SimpleColourBasis implements the colour algebra needed for * electroweak boson and electroweak boson + jet production at NLO. It * mainly serves as an example for the general ColourBasis interface. * */ class SimpleColourBasis: public ColourBasis { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ SimpleColourBasis(); /** * The destructor. */ virtual ~SimpleColourBasis(); //@} public: /** * Prepare the basis for the normal ordered legs and return the * dimensionality of the basis. */ virtual size_t prepareBasis(const vector&); /** * Return the scalar product of basis tensors labelled a and b in * the basis used for the given normal ordered legs. */ virtual double scalarProduct(size_t a, size_t b, const vector& abBasis) const; /** * Return the matrix element of a colour charge * between basis tensors a and b, with * respect to aBasis and bBasis */ virtual double tMatrixElement(size_t i, size_t a, size_t b, const vector& aBasis, - const vector& bBasis) const; + const vector& bBasis, + size_t k, size_t l, + const map& dict + ) const; + + virtual double sMatrixElement(size_t, size_t, size_t, + const vector&, + const vector&, + size_t, size_t, + const map& + ) const { + assert( 0 == 1 ); + return 0; + } + + /** + * Return true, if this colour basis supports gluon splittings. + */ + virtual bool canSplitGluons() const { + return false; + } /** * Return true, if a large-N colour connection exists for the * given external legs and basis tensor. */ virtual bool colourConnected(const cPDVector&, const vector&, const pair&, const pair&, size_t) const; /** * Return true, if the colour basis is capable of assigning colour * flows. */ virtual bool haveColourFlows() const { return true; } /** * Create ids for bases */ void makeIds() const; public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * id for 88 */ mutable vector id88; /** * id for 33bar */ mutable vector id33bar; /** * id for 888 */ mutable vector id888; /** * id for 33bar8 */ mutable vector id33bar8; /** * id for 8888 */ mutable vector id8888; /** * id for 33bar88 */ mutable vector id33bar88; /** * id for 33bar33bar */ mutable vector id33bar33bar; private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ SimpleColourBasis & operator=(const SimpleColourBasis &) = delete; }; } #endif /* Herwig_SimpleColourBasis_H */ diff --git a/MatrixElement/Matchbox/Utility/SimpleColourBasis2.cc b/MatrixElement/Matchbox/Utility/SimpleColourBasis2.cc --- a/MatrixElement/Matchbox/Utility/SimpleColourBasis2.cc +++ b/MatrixElement/Matchbox/Utility/SimpleColourBasis2.cc @@ -1,2864 +1,2878 @@ // -*- C++ -*- // // SimpleColourBasis2.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. // // // This is the implementation of the non-inlined, non-templated member // functions of the SimpleColourBasis2 class. // #include "SimpleColourBasis2.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/EventRecord/Particle.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Utilities/DescribeClass.h" #include "ThePEG/StandardModel/StandardModelBase.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; SimpleColourBasis2::SimpleColourBasis2() {} SimpleColourBasis2::~SimpleColourBasis2() {} IBPtr SimpleColourBasis2::clone() const { return new_ptr(*this); } IBPtr SimpleColourBasis2::fullclone() const { return new_ptr(*this); } size_t SimpleColourBasis2::prepareBasis(const vector& basis) { if ( id33bar.empty() ) makeIds(); if ( basis == id88 ) { return 1; } if ( basis == id33bar ) { return 1; } if ( basis == id888 ) { return 2; } if ( basis == id33bar8 ) { return 1; } if ( basis == id8888 ) { return 9; } if ( basis == id33bar88 ) { return 3; } if ( basis == id33bar33bar ) { return 2; } if ( basis == id88888 ) { return 44; } if ( basis == id33bar888 ) { return 11; } if ( basis == id33bar33bar8 ) { return 4; } throw Exception() << "SimpleColourBasis2::prepareBasis(): Cannot handle colour configuration" << Exception::runerror; return 0; } double SimpleColourBasis2::scalarProduct(size_t a, size_t b, const vector& basis) const { if ( id33bar.empty() ) makeIds(); double Nc = SM().Nc(); double Nc2 = sqr(Nc); double Nc3 = Nc*Nc2; double Nc4 = sqr(Nc2); double Nc5 = Nc*Nc4; double Nc6 = Nc2*Nc4; double Nc8 = Nc2*Nc6; if ( a > b ) swap(a,b); if ( !largeN() ) { if ( basis == id88 ) { if( a == 0 && b == 0 ) return (-1 + Nc2)/4.; } if ( basis == id33bar ) { if( a == 0 && b == 0 ) return Nc; } if ( basis == id888 ) { if( ( a == 0 && b == 0 ) || ( a == 1 && b == 1 ) ) return (2 - 3*Nc2 + Nc4)/(8.*Nc); if( ( a == 0 ) && ( b == 1 ) ) return -(-1 + Nc2)/(4.*Nc); } if ( basis == id33bar8 ) { if( a == 0 && b == 0 ) return (-1 + Nc2)/2.; } if ( basis == id8888 ) { if( ( a == 0 && b == 0 ) || ( a == 1 && b == 1 ) || ( a == 2 && b == 2 ) ) return sqr(-1 + Nc2)/16.; if( ( a == 0 && b == 1 ) || ( a == 0 && b == 2 ) || ( a == 1 && b == 2 ) ) return (-1 + Nc2)/16.; if( ( a == 0 && b == 3 ) || ( a == 0 && b == 5 ) || ( a == 0 && b == 7 ) || ( a == 0 && b == 8 ) || ( a == 1 && b == 4 ) || ( a == 1 && b == 5 ) || ( a == 1 && b == 6 ) || ( a == 1 && b == 7 ) || ( a == 2 && b == 3 ) || ( a == 2 && b == 4 ) || ( a == 2 && b == 6 ) || ( a == 2 && b == 8 ) ) return sqr(-1 + Nc2)/(16.*Nc); if( ( a == 0 && b == 4 ) || ( a == 0 && b == 6 ) || ( a == 1 && b == 3 ) || ( a == 1 && b == 8 ) || ( a == 2 && b == 5 ) || ( a == 2 && b == 7 ) ) return -(-1 + Nc2)/(16.*Nc); if( ( a == 3 && b == 3 ) || ( a == 4 && b == 4 ) || ( a == 5 && b == 5 ) || ( a == 6 && b == 6 ) || ( a == 7 && b == 7 ) || ( a == 8 && b == 8 ) ) return (-3 + 6*Nc2 - 4*Nc4 + Nc6)/(16.*Nc2); if( ( a == 3 && b == 4 ) || ( a == 3 && b == 5 ) || ( a == 3 && b == 6 ) || ( a == 3 && b == 7 ) || ( a == 4 && b == 5 ) || ( a == 4 && b == 7 ) || ( a == 4 && b == 8 ) || ( a == 5 && b == 6 ) || ( a == 5 && b == 8 ) || ( a == 6 && b == 7 ) || ( a == 6 && b == 8 ) || ( a == 7 && b == 8 ) ) return (4 - 3/Nc2 - Nc2)/16.; if( ( a == 3 && b == 8 ) || ( a == 4 && b == 6 ) || ( a == 5 && b == 7 ) ) return (-3 + 2*Nc2 + Nc4)/(16.*Nc2); } if ( basis == id33bar88 ) { if( a == 0 && b == 0 ) return (Nc*(-1 + Nc2))/4.; if( ( a == 0 && b == 1 ) || ( a == 0 && b == 2 ) ) return (-1 + Nc2)/4.; if( ( a == 1 && b == 1 ) || ( a == 2 && b == 2 ) ) return sqr(-1 + Nc2)/(4.*Nc); if( a == 1 && b == 2 ) return -(-1 + Nc2)/(4.*Nc); } if ( basis == id33bar33bar ) { if( ( a == 0 && b == 0 ) || ( a == 1 && b == 1 ) ) return Nc2; if( a == 0 && b == 1 ) return Nc; } if ( basis == id88888 ) { if( ( a == 0 && b == 0 ) || ( a == 1 && b == 1 ) || ( a == 2 && b == 2 ) || ( a == 3 && b == 3 ) || ( a == 4 && b == 4 ) || ( a == 5 && b == 5 ) || ( a == 6 && b == 6 ) || ( a == 7 && b == 7 ) || ( a == 8 && b == 8 ) || ( a == 9 && b == 9 ) || ( a == 10 && b == 10 ) || ( a == 11 && b == 11 ) || ( a == 12 && b == 12 ) || ( a == 13 && b == 13 ) || ( a == 14 && b == 14 ) || ( a == 15 && b == 15 ) || ( a == 16 && b == 16 ) || ( a == 17 && b == 17 ) || ( a == 18 && b == 18 ) || ( a == 19 && b == 19 ) ) return ((-2 + Nc2)*sqr(-1 + Nc2))/(32.*Nc); if( ( a == 0 && b == 1 ) || ( a == 0 && b == 2 ) || ( a == 0 && b == 7 ) || ( a == 0 && b == 10 ) || ( a == 0 && b == 12 ) || ( a == 0 && b == 13 ) || ( a == 1 && b == 2 ) || ( a == 1 && b == 4 ) || ( a == 1 && b == 11 ) || ( a == 1 && b == 14 ) || ( a == 1 && b == 15 ) || ( a == 2 && b == 5 ) || ( a == 2 && b == 8 ) || ( a == 2 && b == 16 ) || ( a == 2 && b == 17 ) || ( a == 3 && b == 4 ) || ( a == 3 && b == 5 ) || ( a == 3 && b == 6 ) || ( a == 3 && b == 9 ) || ( a == 3 && b == 14 ) || ( a == 3 && b == 16 ) || ( a == 4 && b == 5 ) || ( a == 4 && b == 11 ) || ( a == 4 && b == 12 ) || ( a == 4 && b == 18 ) || ( a == 5 && b == 8 ) || ( a == 5 && b == 13 ) || ( a == 5 && b == 19 ) || ( a == 6 && b == 7 ) || ( a == 6 && b == 8 ) || ( a == 6 && b == 9 ) || ( a == 6 && b == 12 ) || ( a == 6 && b == 17 ) || ( a == 7 && b == 8 ) || ( a == 7 && b == 10 ) || ( a == 7 && b == 14 ) || ( a == 7 && b == 19 ) || ( a == 8 && b == 15 ) || ( a == 8 && b == 18 ) || ( a == 9 && b == 10 ) || ( a == 9 && b == 11 ) || ( a == 9 && b == 13 ) || ( a == 9 && b == 15 ) || ( a == 10 && b == 11 ) || ( a == 10 && b == 16 ) || ( a == 10 && b == 18 ) || ( a == 11 && b == 17 ) || ( a == 11 && b == 19 ) || ( a == 12 && b == 13 ) || ( a == 12 && b == 17 ) || ( a == 12 && b == 18 ) || ( a == 13 && b == 15 ) || ( a == 13 && b == 19 ) || ( a == 14 && b == 15 ) || ( a == 14 && b == 16 ) || ( a == 14 && b == 19 ) || ( a == 15 && b == 18 ) || ( a == 16 && b == 17 ) || ( a == 16 && b == 18 ) || ( a == 17 && b == 19 ) ) return (2 - 3*Nc2 + Nc4)/(32.*Nc); if( ( a == 0 && b == 3 ) || ( a == 1 && b == 6 ) || ( a == 2 && b == 9 ) || ( a == 4 && b == 7 ) || ( a == 5 && b == 10 ) || ( a == 8 && b == 11 ) || ( a == 12 && b == 14 ) || ( a == 13 && b == 16 ) || ( a == 15 && b == 17 ) || ( a == 18 && b == 19 ) ) return -sqr(-1 + Nc2)/(16.*Nc); if( ( a == 0 && b == 4 ) || ( a == 0 && b == 5 ) || ( a == 0 && b == 6 ) || ( a == 0 && b == 9 ) || ( a == 0 && b == 14 ) || ( a == 0 && b == 16 ) || ( a == 1 && b == 3 ) || ( a == 1 && b == 7 ) || ( a == 1 && b == 8 ) || ( a == 1 && b == 9 ) || ( a == 1 && b == 12 ) || ( a == 1 && b == 17 ) || ( a == 2 && b == 3 ) || ( a == 2 && b == 6 ) || ( a == 2 && b == 10 ) || ( a == 2 && b == 11 ) || ( a == 2 && b == 13 ) || ( a == 2 && b == 15 ) || ( a == 3 && b == 7 ) || ( a == 3 && b == 10 ) || ( a == 3 && b == 12 ) || ( a == 3 && b == 13 ) || ( a == 4 && b == 6 ) || ( a == 4 && b == 8 ) || ( a == 4 && b == 10 ) || ( a == 4 && b == 14 ) || ( a == 4 && b == 19 ) || ( a == 5 && b == 7 ) || ( a == 5 && b == 9 ) || ( a == 5 && b == 11 ) || ( a == 5 && b == 16 ) || ( a == 5 && b == 18 ) || ( a == 6 && b == 11 ) || ( a == 6 && b == 14 ) || ( a == 6 && b == 15 ) || ( a == 7 && b == 11 ) || ( a == 7 && b == 12 ) || ( a == 7 && b == 18 ) || ( a == 8 && b == 9 ) || ( a == 8 && b == 10 ) || ( a == 8 && b == 17 ) || ( a == 8 && b == 19 ) || ( a == 9 && b == 16 ) || ( a == 9 && b == 17 ) || ( a == 10 && b == 13 ) || ( a == 10 && b == 19 ) || ( a == 11 && b == 15 ) || ( a == 11 && b == 18 ) || ( a == 12 && b == 15 ) || ( a == 12 && b == 16 ) || ( a == 12 && b == 19 ) || ( a == 13 && b == 14 ) || ( a == 13 && b == 17 ) || ( a == 13 && b == 18 ) || ( a == 14 && b == 17 ) || ( a == 14 && b == 18 ) || ( a == 15 && b == 16 ) || ( a == 15 && b == 19 ) || ( a == 16 && b == 19 ) || ( a == 17 && b == 18 ) ) return -(-1 + Nc2)/(16.*Nc); if( ( a == 0 && b == 8 ) || ( a == 0 && b == 11 ) || ( a == 0 && b == 15 ) || ( a == 0 && b == 17 ) || ( a == 0 && b == 18 ) || ( a == 0 && b == 19 ) || ( a == 1 && b == 5 ) || ( a == 1 && b == 10 ) || ( a == 1 && b == 13 ) || ( a == 1 && b == 16 ) || ( a == 1 && b == 18 ) || ( a == 1 && b == 19 ) || ( a == 2 && b == 4 ) || ( a == 2 && b == 7 ) || ( a == 2 && b == 12 ) || ( a == 2 && b == 14 ) || ( a == 2 && b == 18 ) || ( a == 2 && b == 19 ) || ( a == 3 && b == 8 ) || ( a == 3 && b == 11 ) || ( a == 3 && b == 15 ) || ( a == 3 && b == 17 ) || ( a == 3 && b == 18 ) || ( a == 3 && b == 19 ) || ( a == 4 && b == 9 ) || ( a == 4 && b == 13 ) || ( a == 4 && b == 15 ) || ( a == 4 && b == 16 ) || ( a == 4 && b == 17 ) || ( a == 5 && b == 6 ) || ( a == 5 && b == 12 ) || ( a == 5 && b == 14 ) || ( a == 5 && b == 15 ) || ( a == 5 && b == 17 ) || ( a == 6 && b == 10 ) || ( a == 6 && b == 13 ) || ( a == 6 && b == 16 ) || ( a == 6 && b == 18 ) || ( a == 6 && b == 19 ) || ( a == 7 && b == 9 ) || ( a == 7 && b == 13 ) || ( a == 7 && b == 15 ) || ( a == 7 && b == 16 ) || ( a == 7 && b == 17 ) || ( a == 8 && b == 12 ) || ( a == 8 && b == 13 ) || ( a == 8 && b == 14 ) || ( a == 8 && b == 16 ) || ( a == 9 && b == 12 ) || ( a == 9 && b == 14 ) || ( a == 9 && b == 18 ) || ( a == 9 && b == 19 ) || ( a == 10 && b == 12 ) || ( a == 10 && b == 14 ) || ( a == 10 && b == 15 ) || ( a == 10 && b == 17 ) || ( a == 11 && b == 12 ) || ( a == 11 && b == 13 ) || ( a == 11 && b == 14 ) || ( a == 11 && b == 16 ) ) return 0; if( ( a == 0 && b == 20 ) || ( a == 0 && b == 21 ) || ( a == 0 && b == 23 ) || ( a == 0 && b == 25 ) || ( a == 0 && b == 36 ) || ( a == 0 && b == 42 ) || ( a == 1 && b == 21 ) || ( a == 1 && b == 22 ) || ( a == 1 && b == 23 ) || ( a == 1 && b == 24 ) || ( a == 1 && b == 30 ) || ( a == 1 && b == 40 ) || ( a == 2 && b == 20 ) || ( a == 2 && b == 22 ) || ( a == 2 && b == 24 ) || ( a == 2 && b == 25 ) || ( a == 2 && b == 28 ) || ( a == 2 && b == 34 ) || ( a == 3 && b == 26 ) || ( a == 3 && b == 27 ) || ( a == 3 && b == 29 ) || ( a == 3 && b == 31 ) || ( a == 3 && b == 37 ) || ( a == 3 && b == 43 ) || ( a == 4 && b == 24 ) || ( a == 4 && b == 27 ) || ( a == 4 && b == 28 ) || ( a == 4 && b == 29 ) || ( a == 4 && b == 30 ) || ( a == 4 && b == 38 ) || ( a == 5 && b == 22 ) || ( a == 5 && b == 26 ) || ( a == 5 && b == 28 ) || ( a == 5 && b == 30 ) || ( a == 5 && b == 31 ) || ( a == 5 && b == 32 ) || ( a == 6 && b == 31 ) || ( a == 6 && b == 32 ) || ( a == 6 && b == 33 ) || ( a == 6 && b == 35 ) || ( a == 6 && b == 37 ) || ( a == 6 && b == 41 ) || ( a == 7 && b == 25 ) || ( a == 7 && b == 33 ) || ( a == 7 && b == 34 ) || ( a == 7 && b == 35 ) || ( a == 7 && b == 36 ) || ( a == 7 && b == 39 ) || ( a == 8 && b == 20 ) || ( a == 8 && b == 26 ) || ( a == 8 && b == 32 ) || ( a == 8 && b == 34 ) || ( a == 8 && b == 36 ) || ( a == 8 && b == 37 ) || ( a == 9 && b == 29 ) || ( a == 9 && b == 35 ) || ( a == 9 && b == 38 ) || ( a == 9 && b == 39 ) || ( a == 9 && b == 41 ) || ( a == 9 && b == 43 ) || ( a == 10 && b == 23 ) || ( a == 10 && b == 33 ) || ( a == 10 && b == 39 ) || ( a == 10 && b == 40 ) || ( a == 10 && b == 41 ) || ( a == 10 && b == 42 ) || ( a == 11 && b == 21 ) || ( a == 11 && b == 27 ) || ( a == 11 && b == 38 ) || ( a == 11 && b == 40 ) || ( a == 11 && b == 42 ) || ( a == 11 && b == 43 ) || ( a == 12 && b == 20 ) || ( a == 12 && b == 28 ) || ( a == 12 && b == 32 ) || ( a == 12 && b == 38 ) || ( a == 12 && b == 41 ) || ( a == 12 && b == 42 ) || ( a == 13 && b == 21 ) || ( a == 13 && b == 30 ) || ( a == 13 && b == 32 ) || ( a == 13 && b == 35 ) || ( a == 13 && b == 36 ) || ( a == 13 && b == 38 ) || ( a == 14 && b == 22 ) || ( a == 14 && b == 26 ) || ( a == 14 && b == 34 ) || ( a == 14 && b == 39 ) || ( a == 14 && b == 40 ) || ( a == 14 && b == 43 ) || ( a == 15 && b == 23 ) || ( a == 15 && b == 26 ) || ( a == 15 && b == 29 ) || ( a == 15 && b == 30 ) || ( a == 15 && b == 36 ) || ( a == 15 && b == 39 ) || ( a == 16 && b == 24 ) || ( a == 16 && b == 27 ) || ( a == 16 && b == 33 ) || ( a == 16 && b == 34 ) || ( a == 16 && b == 37 ) || ( a == 16 && b == 40 ) || ( a == 17 && b == 25 ) || ( a == 17 && b == 27 ) || ( a == 17 && b == 28 ) || ( a == 17 && b == 31 ) || ( a == 17 && b == 33 ) || ( a == 17 && b == 42 ) || ( a == 18 && b == 20 ) || ( a == 18 && b == 23 ) || ( a == 18 && b == 24 ) || ( a == 18 && b == 29 ) || ( a == 18 && b == 37 ) || ( a == 18 && b == 41 ) || ( a == 19 && b == 21 ) || ( a == 19 && b == 22 ) || ( a == 19 && b == 25 ) || ( a == 19 && b == 31 ) || ( a == 19 && b == 35 ) || ( a == 19 && b == 43 ) ) return ((-2 + Nc2)*sqr(-1 + Nc2))/(32.*Nc2); if( ( a == 0 && b == 22 ) || ( a == 0 && b == 24 ) || ( a == 0 && b == 32 ) || ( a == 0 && b == 33 ) || ( a == 0 && b == 38 ) || ( a == 0 && b == 39 ) || ( a == 1 && b == 20 ) || ( a == 1 && b == 25 ) || ( a == 1 && b == 26 ) || ( a == 1 && b == 27 ) || ( a == 1 && b == 38 ) || ( a == 1 && b == 39 ) || ( a == 2 && b == 21 ) || ( a == 2 && b == 23 ) || ( a == 2 && b == 26 ) || ( a == 2 && b == 27 ) || ( a == 2 && b == 32 ) || ( a == 2 && b == 33 ) || ( a == 3 && b == 28 ) || ( a == 3 && b == 30 ) || ( a == 3 && b == 34 ) || ( a == 3 && b == 35 ) || ( a == 3 && b == 40 ) || ( a == 3 && b == 41 ) || ( a == 4 && b == 20 ) || ( a == 4 && b == 21 ) || ( a == 4 && b == 26 ) || ( a == 4 && b == 31 ) || ( a == 4 && b == 40 ) || ( a == 4 && b == 41 ) || ( a == 5 && b == 20 ) || ( a == 5 && b == 21 ) || ( a == 5 && b == 27 ) || ( a == 5 && b == 29 ) || ( a == 5 && b == 34 ) || ( a == 5 && b == 35 ) || ( a == 6 && b == 28 ) || ( a == 6 && b == 29 ) || ( a == 6 && b == 34 ) || ( a == 6 && b == 36 ) || ( a == 6 && b == 42 ) || ( a == 6 && b == 43 ) || ( a == 7 && b == 22 ) || ( a == 7 && b == 23 ) || ( a == 7 && b == 32 ) || ( a == 7 && b == 37 ) || ( a == 7 && b == 42 ) || ( a == 7 && b == 43 ) || ( a == 8 && b == 22 ) || ( a == 8 && b == 23 ) || ( a == 8 && b == 28 ) || ( a == 8 && b == 29 ) || ( a == 8 && b == 33 ) || ( a == 8 && b == 35 ) || ( a == 9 && b == 30 ) || ( a == 9 && b == 31 ) || ( a == 9 && b == 36 ) || ( a == 9 && b == 37 ) || ( a == 9 && b == 40 ) || ( a == 9 && b == 42 ) || ( a == 10 && b == 24 ) || ( a == 10 && b == 25 ) || ( a == 10 && b == 36 ) || ( a == 10 && b == 37 ) || ( a == 10 && b == 38 ) || ( a == 10 && b == 43 ) || ( a == 11 && b == 24 ) || ( a == 11 && b == 25 ) || ( a == 11 && b == 30 ) || ( a == 11 && b == 31 ) || ( a == 11 && b == 39 ) || ( a == 11 && b == 41 ) || ( a == 12 && b == 21 ) || ( a == 12 && b == 24 ) || ( a == 12 && b == 29 ) || ( a == 12 && b == 31 ) || ( a == 12 && b == 33 ) || ( a == 12 && b == 36 ) || ( a == 13 && b == 20 ) || ( a == 13 && b == 22 ) || ( a == 13 && b == 29 ) || ( a == 13 && b == 31 ) || ( a == 13 && b == 39 ) || ( a == 13 && b == 42 ) || ( a == 14 && b == 23 ) || ( a == 14 && b == 25 ) || ( a == 14 && b == 27 ) || ( a == 14 && b == 30 ) || ( a == 14 && b == 35 ) || ( a == 14 && b == 37 ) || ( a == 15 && b == 20 ) || ( a == 15 && b == 22 ) || ( a == 15 && b == 35 ) || ( a == 15 && b == 37 ) || ( a == 15 && b == 38 ) || ( a == 15 && b == 40 ) || ( a == 16 && b == 23 ) || ( a == 16 && b == 25 ) || ( a == 16 && b == 26 ) || ( a == 16 && b == 28 ) || ( a == 16 && b == 41 ) || ( a == 16 && b == 43 ) || ( a == 17 && b == 21 ) || ( a == 17 && b == 24 ) || ( a == 17 && b == 32 ) || ( a == 17 && b == 34 ) || ( a == 17 && b == 41 ) || ( a == 17 && b == 43 ) || ( a == 18 && b == 26 ) || ( a == 18 && b == 28 ) || ( a == 18 && b == 33 ) || ( a == 18 && b == 36 ) || ( a == 18 && b == 38 ) || ( a == 18 && b == 40 ) || ( a == 19 && b == 27 ) || ( a == 19 && b == 30 ) || ( a == 19 && b == 32 ) || ( a == 19 && b == 34 ) || ( a == 19 && b == 39 ) || ( a == 19 && b == 42 ) ) return -(2 - 3*Nc2 + Nc4)/(32.*Nc2); if( ( a == 0 && b == 26 ) || ( a == 0 && b == 27 ) || ( a == 0 && b == 29 ) || ( a == 0 && b == 31 ) || ( a == 0 && b == 37 ) || ( a == 0 && b == 43 ) || ( a == 1 && b == 31 ) || ( a == 1 && b == 32 ) || ( a == 1 && b == 33 ) || ( a == 1 && b == 35 ) || ( a == 1 && b == 37 ) || ( a == 1 && b == 41 ) || ( a == 2 && b == 29 ) || ( a == 2 && b == 35 ) || ( a == 2 && b == 38 ) || ( a == 2 && b == 39 ) || ( a == 2 && b == 41 ) || ( a == 2 && b == 43 ) || ( a == 3 && b == 20 ) || ( a == 3 && b == 21 ) || ( a == 3 && b == 23 ) || ( a == 3 && b == 25 ) || ( a == 3 && b == 36 ) || ( a == 3 && b == 42 ) || ( a == 4 && b == 25 ) || ( a == 4 && b == 33 ) || ( a == 4 && b == 34 ) || ( a == 4 && b == 35 ) || ( a == 4 && b == 36 ) || ( a == 4 && b == 39 ) || ( a == 5 && b == 23 ) || ( a == 5 && b == 33 ) || ( a == 5 && b == 39 ) || ( a == 5 && b == 40 ) || ( a == 5 && b == 41 ) || ( a == 5 && b == 42 ) || ( a == 6 && b == 21 ) || ( a == 6 && b == 22 ) || ( a == 6 && b == 23 ) || ( a == 6 && b == 24 ) || ( a == 6 && b == 30 ) || ( a == 6 && b == 40 ) || ( a == 7 && b == 24 ) || ( a == 7 && b == 27 ) || ( a == 7 && b == 28 ) || ( a == 7 && b == 29 ) || ( a == 7 && b == 30 ) || ( a == 7 && b == 38 ) || ( a == 8 && b == 21 ) || ( a == 8 && b == 27 ) || ( a == 8 && b == 38 ) || ( a == 8 && b == 40 ) || ( a == 8 && b == 42 ) || ( a == 8 && b == 43 ) || ( a == 9 && b == 20 ) || ( a == 9 && b == 22 ) || ( a == 9 && b == 24 ) || ( a == 9 && b == 25 ) || ( a == 9 && b == 28 ) || ( a == 9 && b == 34 ) || ( a == 10 && b == 22 ) || ( a == 10 && b == 26 ) || ( a == 10 && b == 28 ) || ( a == 10 && b == 30 ) || ( a == 10 && b == 31 ) || ( a == 10 && b == 32 ) || ( a == 11 && b == 20 ) || ( a == 11 && b == 26 ) || ( a == 11 && b == 32 ) || ( a == 11 && b == 34 ) || ( a == 11 && b == 36 ) || ( a == 11 && b == 37 ) || ( a == 12 && b == 22 ) || ( a == 12 && b == 26 ) || ( a == 12 && b == 34 ) || ( a == 12 && b == 39 ) || ( a == 12 && b == 40 ) || ( a == 12 && b == 43 ) || ( a == 13 && b == 24 ) || ( a == 13 && b == 27 ) || ( a == 13 && b == 33 ) || ( a == 13 && b == 34 ) || ( a == 13 && b == 37 ) || ( a == 13 && b == 40 ) || ( a == 14 && b == 20 ) || ( a == 14 && b == 28 ) || ( a == 14 && b == 32 ) || ( a == 14 && b == 38 ) || ( a == 14 && b == 41 ) || ( a == 14 && b == 42 ) || ( a == 15 && b == 25 ) || ( a == 15 && b == 27 ) || ( a == 15 && b == 28 ) || ( a == 15 && b == 31 ) || ( a == 15 && b == 33 ) || ( a == 15 && b == 42 ) || ( a == 16 && b == 21 ) || ( a == 16 && b == 30 ) || ( a == 16 && b == 32 ) || ( a == 16 && b == 35 ) || ( a == 16 && b == 36 ) || ( a == 16 && b == 38 ) || ( a == 17 && b == 23 ) || ( a == 17 && b == 26 ) || ( a == 17 && b == 29 ) || ( a == 17 && b == 30 ) || ( a == 17 && b == 36 ) || ( a == 17 && b == 39 ) || ( a == 18 && b == 21 ) || ( a == 18 && b == 22 ) || ( a == 18 && b == 25 ) || ( a == 18 && b == 31 ) || ( a == 18 && b == 35 ) || ( a == 18 && b == 43 ) || ( a == 19 && b == 20 ) || ( a == 19 && b == 23 ) || ( a == 19 && b == 24 ) || ( a == 19 && b == 29 ) || ( a == 19 && b == 37 ) || ( a == 19 && b == 41 ) ) return -sqr(-1 + Nc2)/(16.*Nc2); if( ( a == 0 && b == 28 ) || ( a == 0 && b == 30 ) || ( a == 0 && b == 34 ) || ( a == 0 && b == 35 ) || ( a == 0 && b == 40 ) || ( a == 0 && b == 41 ) || ( a == 1 && b == 28 ) || ( a == 1 && b == 29 ) || ( a == 1 && b == 34 ) || ( a == 1 && b == 36 ) || ( a == 1 && b == 42 ) || ( a == 1 && b == 43 ) || ( a == 2 && b == 30 ) || ( a == 2 && b == 31 ) || ( a == 2 && b == 36 ) || ( a == 2 && b == 37 ) || ( a == 2 && b == 40 ) || ( a == 2 && b == 42 ) || ( a == 3 && b == 22 ) || ( a == 3 && b == 24 ) || ( a == 3 && b == 32 ) || ( a == 3 && b == 33 ) || ( a == 3 && b == 38 ) || ( a == 3 && b == 39 ) || ( a == 4 && b == 22 ) || ( a == 4 && b == 23 ) || ( a == 4 && b == 32 ) || ( a == 4 && b == 37 ) || ( a == 4 && b == 42 ) || ( a == 4 && b == 43 ) || ( a == 5 && b == 24 ) || ( a == 5 && b == 25 ) || ( a == 5 && b == 36 ) || ( a == 5 && b == 37 ) || ( a == 5 && b == 38 ) || ( a == 5 && b == 43 ) || ( a == 6 && b == 20 ) || ( a == 6 && b == 25 ) || ( a == 6 && b == 26 ) || ( a == 6 && b == 27 ) || ( a == 6 && b == 38 ) || ( a == 6 && b == 39 ) || ( a == 7 && b == 20 ) || ( a == 7 && b == 21 ) || ( a == 7 && b == 26 ) || ( a == 7 && b == 31 ) || ( a == 7 && b == 40 ) || ( a == 7 && b == 41 ) || ( a == 8 && b == 24 ) || ( a == 8 && b == 25 ) || ( a == 8 && b == 30 ) || ( a == 8 && b == 31 ) || ( a == 8 && b == 39 ) || ( a == 8 && b == 41 ) || ( a == 9 && b == 21 ) || ( a == 9 && b == 23 ) || ( a == 9 && b == 26 ) || ( a == 9 && b == 27 ) || ( a == 9 && b == 32 ) || ( a == 9 && b == 33 ) || ( a == 10 && b == 20 ) || ( a == 10 && b == 21 ) || ( a == 10 && b == 27 ) || ( a == 10 && b == 29 ) || ( a == 10 && b == 34 ) || ( a == 10 && b == 35 ) || ( a == 11 && b == 22 ) || ( a == 11 && b == 23 ) || ( a == 11 && b == 28 ) || ( a == 11 && b == 29 ) || ( a == 11 && b == 33 ) || ( a == 11 && b == 35 ) || ( a == 12 && b == 23 ) || ( a == 12 && b == 25 ) || ( a == 12 && b == 27 ) || ( a == 12 && b == 30 ) || ( a == 12 && b == 35 ) || ( a == 12 && b == 37 ) || ( a == 13 && b == 23 ) || ( a == 13 && b == 25 ) || ( a == 13 && b == 26 ) || ( a == 13 && b == 28 ) || ( a == 13 && b == 41 ) || ( a == 13 && b == 43 ) || ( a == 14 && b == 21 ) || ( a == 14 && b == 24 ) || ( a == 14 && b == 29 ) || ( a == 14 && b == 31 ) || ( a == 14 && b == 33 ) || ( a == 14 && b == 36 ) || ( a == 15 && b == 21 ) || ( a == 15 && b == 24 ) || ( a == 15 && b == 32 ) || ( a == 15 && b == 34 ) || ( a == 15 && b == 41 ) || ( a == 15 && b == 43 ) || ( a == 16 && b == 20 ) || ( a == 16 && b == 22 ) || ( a == 16 && b == 29 ) || ( a == 16 && b == 31 ) || ( a == 16 && b == 39 ) || ( a == 16 && b == 42 ) || ( a == 17 && b == 20 ) || ( a == 17 && b == 22 ) || ( a == 17 && b == 35 ) || ( a == 17 && b == 37 ) || ( a == 17 && b == 38 ) || ( a == 17 && b == 40 ) || ( a == 18 && b == 27 ) || ( a == 18 && b == 30 ) || ( a == 18 && b == 32 ) || ( a == 18 && b == 34 ) || ( a == 18 && b == 39 ) || ( a == 18 && b == 42 ) || ( a == 19 && b == 26 ) || ( a == 19 && b == 28 ) || ( a == 19 && b == 33 ) || ( a == 19 && b == 36 ) || ( a == 19 && b == 38 ) || ( a == 19 && b == 40 ) ) return (1 - 1/Nc2)/16.; if( ( a == 20 && b == 20 ) || ( a == 21 && b == 21 ) || ( a == 22 && b == 22 ) || ( a == 23 && b == 23 ) || ( a == 24 && b == 24 ) || ( a == 25 && b == 25 ) || ( a == 26 && b == 26 ) || ( a == 27 && b == 27 ) || ( a == 28 && b == 28 ) || ( a == 29 && b == 29 ) || ( a == 30 && b == 30 ) || ( a == 31 && b == 31 ) || ( a == 32 && b == 32 ) || ( a == 33 && b == 33 ) || ( a == 34 && b == 34 ) || ( a == 35 && b == 35 ) || ( a == 36 && b == 36 ) || ( a == 37 && b == 37 ) || ( a == 38 && b == 38 ) || ( a == 39 && b == 39 ) || ( a == 40 && b == 40 ) || ( a == 41 && b == 41 ) || ( a == 42 && b == 42 ) || ( a == 43 && b == 43 ) ) return (4 - 10*Nc2 + 10*Nc4 - 5*Nc6 + Nc8)/(32.*Nc3); if( ( a == 20 && b == 21 ) || ( a == 20 && b == 22 ) || ( a == 20 && b == 26 ) || ( a == 20 && b == 29 ) || ( a == 20 && b == 38 ) || ( a == 21 && b == 24 ) || ( a == 21 && b == 27 ) || ( a == 21 && b == 31 ) || ( a == 21 && b == 32 ) || ( a == 22 && b == 23 ) || ( a == 22 && b == 32 ) || ( a == 22 && b == 35 ) || ( a == 22 && b == 39 ) || ( a == 23 && b == 25 ) || ( a == 23 && b == 26 ) || ( a == 23 && b == 33 ) || ( a == 23 && b == 37 ) || ( a == 24 && b == 25 ) || ( a == 24 && b == 33 ) || ( a == 24 && b == 38 ) || ( a == 24 && b == 41 ) || ( a == 25 && b == 27 ) || ( a == 25 && b == 39 ) || ( a == 25 && b == 43 ) || ( a == 26 && b == 27 ) || ( a == 26 && b == 28 ) || ( a == 26 && b == 40 ) || ( a == 27 && b == 30 ) || ( a == 27 && b == 34 ) || ( a == 28 && b == 29 ) || ( a == 28 && b == 33 ) || ( a == 28 && b == 34 ) || ( a == 28 && b == 41 ) || ( a == 29 && b == 31 ) || ( a == 29 && b == 35 ) || ( a == 29 && b == 36 ) || ( a == 30 && b == 31 ) || ( a == 30 && b == 35 ) || ( a == 30 && b == 39 ) || ( a == 30 && b == 40 ) || ( a == 31 && b == 41 ) || ( a == 31 && b == 42 ) || ( a == 32 && b == 33 ) || ( a == 32 && b == 34 ) || ( a == 32 && b == 42 ) || ( a == 33 && b == 36 ) || ( a == 34 && b == 35 ) || ( a == 34 && b == 43 ) || ( a == 35 && b == 37 ) || ( a == 36 && b == 37 ) || ( a == 36 && b == 38 ) || ( a == 36 && b == 42 ) || ( a == 37 && b == 40 ) || ( a == 37 && b == 43 ) || ( a == 38 && b == 39 ) || ( a == 38 && b == 40 ) || ( a == 39 && b == 42 ) || ( a == 40 && b == 41 ) || ( a == 41 && b == 43 ) || ( a == 42 && b == 43 ) ) return -(-4 + 7*Nc2 - 4*Nc4 + Nc6)/(32.*Nc3); if( ( a == 20 && b == 23 ) || ( a == 20 && b == 24 ) || ( a == 20 && b == 28 ) || ( a == 20 && b == 32 ) || ( a == 20 && b == 36 ) || ( a == 21 && b == 22 ) || ( a == 21 && b == 25 ) || ( a == 21 && b == 30 ) || ( a == 21 && b == 38 ) || ( a == 21 && b == 42 ) || ( a == 22 && b == 25 ) || ( a == 22 && b == 26 ) || ( a == 22 && b == 30 ) || ( a == 22 && b == 34 ) || ( a == 23 && b == 24 ) || ( a == 23 && b == 36 ) || ( a == 23 && b == 39 ) || ( a == 23 && b == 40 ) || ( a == 24 && b == 27 ) || ( a == 24 && b == 28 ) || ( a == 24 && b == 40 ) || ( a == 25 && b == 33 ) || ( a == 25 && b == 34 ) || ( a == 25 && b == 42 ) || ( a == 26 && b == 29 ) || ( a == 26 && b == 30 ) || ( a == 26 && b == 34 ) || ( a == 26 && b == 37 ) || ( a == 27 && b == 28 ) || ( a == 27 && b == 31 ) || ( a == 27 && b == 40 ) || ( a == 27 && b == 43 ) || ( a == 28 && b == 31 ) || ( a == 28 && b == 32 ) || ( a == 29 && b == 30 ) || ( a == 29 && b == 37 ) || ( a == 29 && b == 38 ) || ( a == 29 && b == 41 ) || ( a == 30 && b == 38 ) || ( a == 31 && b == 32 ) || ( a == 31 && b == 35 ) || ( a == 31 && b == 43 ) || ( a == 32 && b == 35 ) || ( a == 32 && b == 36 ) || ( a == 33 && b == 34 ) || ( a == 33 && b == 37 ) || ( a == 33 && b == 41 ) || ( a == 33 && b == 42 ) || ( a == 34 && b == 37 ) || ( a == 35 && b == 36 ) || ( a == 35 && b == 39 ) || ( a == 35 && b == 43 ) || ( a == 36 && b == 39 ) || ( a == 37 && b == 41 ) || ( a == 38 && b == 41 ) || ( a == 38 && b == 42 ) || ( a == 39 && b == 40 ) || ( a == 39 && b == 43 ) || ( a == 40 && b == 43 ) || ( a == 41 && b == 42 ) ) return (2 - 3*Nc2 + Nc4)/(16.*Nc3); if( ( a == 20 && b == 25 ) || ( a == 20 && b == 34 ) || ( a == 20 && b == 37 ) || ( a == 20 && b == 41 ) || ( a == 20 && b == 42 ) || ( a == 21 && b == 23 ) || ( a == 21 && b == 35 ) || ( a == 21 && b == 36 ) || ( a == 21 && b == 40 ) || ( a == 21 && b == 43 ) || ( a == 22 && b == 24 ) || ( a == 22 && b == 28 ) || ( a == 22 && b == 31 ) || ( a == 22 && b == 40 ) || ( a == 22 && b == 43 ) || ( a == 23 && b == 29 ) || ( a == 23 && b == 30 ) || ( a == 23 && b == 41 ) || ( a == 23 && b == 42 ) || ( a == 24 && b == 29 ) || ( a == 24 && b == 30 ) || ( a == 24 && b == 34 ) || ( a == 24 && b == 37 ) || ( a == 25 && b == 28 ) || ( a == 25 && b == 31 ) || ( a == 25 && b == 35 ) || ( a == 25 && b == 36 ) || ( a == 26 && b == 31 ) || ( a == 26 && b == 32 ) || ( a == 26 && b == 36 ) || ( a == 26 && b == 39 ) || ( a == 26 && b == 43 ) || ( a == 27 && b == 29 ) || ( a == 27 && b == 33 ) || ( a == 27 && b == 37 ) || ( a == 27 && b == 38 ) || ( a == 27 && b == 42 ) || ( a == 28 && b == 30 ) || ( a == 28 && b == 38 ) || ( a == 28 && b == 42 ) || ( a == 29 && b == 39 ) || ( a == 29 && b == 43 ) || ( a == 30 && b == 32 ) || ( a == 30 && b == 36 ) || ( a == 31 && b == 33 ) || ( a == 31 && b == 37 ) || ( a == 32 && b == 37 ) || ( a == 32 && b == 38 ) || ( a == 32 && b == 41 ) || ( a == 33 && b == 35 ) || ( a == 33 && b == 39 ) || ( a == 33 && b == 40 ) || ( a == 34 && b == 36 ) || ( a == 34 && b == 39 ) || ( a == 34 && b == 40 ) || ( a == 35 && b == 38 ) || ( a == 35 && b == 41 ) || ( a == 38 && b == 43 ) || ( a == 39 && b == 41 ) || ( a == 40 && b == 42 ) ) return (4 - 3*Nc2 - 2*Nc4 + Nc6)/(32.*Nc3); if( ( a == 20 && b == 27 ) || ( a == 20 && b == 31 ) || ( a == 20 && b == 35 ) || ( a == 20 && b == 39 ) || ( a == 20 && b == 40 ) || ( a == 21 && b == 26 ) || ( a == 21 && b == 29 ) || ( a == 21 && b == 33 ) || ( a == 21 && b == 34 ) || ( a == 21 && b == 41 ) || ( a == 22 && b == 29 ) || ( a == 22 && b == 33 ) || ( a == 22 && b == 37 ) || ( a == 22 && b == 38 ) || ( a == 22 && b == 42 ) || ( a == 23 && b == 27 ) || ( a == 23 && b == 28 ) || ( a == 23 && b == 32 ) || ( a == 23 && b == 35 ) || ( a == 23 && b == 43 ) || ( a == 24 && b == 31 ) || ( a == 24 && b == 32 ) || ( a == 24 && b == 36 ) || ( a == 24 && b == 39 ) || ( a == 24 && b == 43 ) || ( a == 25 && b == 26 ) || ( a == 25 && b == 30 ) || ( a == 25 && b == 37 ) || ( a == 25 && b == 38 ) || ( a == 25 && b == 41 ) || ( a == 26 && b == 33 ) || ( a == 26 && b == 38 ) || ( a == 26 && b == 41 ) || ( a == 27 && b == 32 ) || ( a == 27 && b == 35 ) || ( a == 27 && b == 39 ) || ( a == 28 && b == 35 ) || ( a == 28 && b == 36 ) || ( a == 28 && b == 40 ) || ( a == 28 && b == 43 ) || ( a == 29 && b == 33 ) || ( a == 29 && b == 34 ) || ( a == 29 && b == 42 ) || ( a == 30 && b == 34 ) || ( a == 30 && b == 37 ) || ( a == 30 && b == 41 ) || ( a == 30 && b == 42 ) || ( a == 31 && b == 36 ) || ( a == 31 && b == 39 ) || ( a == 31 && b == 40 ) || ( a == 32 && b == 39 ) || ( a == 32 && b == 43 ) || ( a == 33 && b == 38 ) || ( a == 34 && b == 41 ) || ( a == 34 && b == 42 ) || ( a == 35 && b == 40 ) || ( a == 36 && b == 40 ) || ( a == 36 && b == 43 ) || ( a == 37 && b == 38 ) || ( a == 37 && b == 42 ) ) return -(-1 + Nc2)/(8.*Nc3); if( ( a == 20 && b == 30 ) || ( a == 20 && b == 33 ) || ( a == 21 && b == 28 ) || ( a == 21 && b == 39 ) || ( a == 22 && b == 27 ) || ( a == 22 && b == 36 ) || ( a == 23 && b == 34 ) || ( a == 23 && b == 38 ) || ( a == 24 && b == 26 ) || ( a == 24 && b == 42 ) || ( a == 25 && b == 32 ) || ( a == 25 && b == 40 ) || ( a == 26 && b == 35 ) || ( a == 27 && b == 41 ) || ( a == 28 && b == 37 ) || ( a == 29 && b == 32 ) || ( a == 29 && b == 40 ) || ( a == 30 && b == 43 ) || ( a == 31 && b == 34 ) || ( a == 31 && b == 38 ) || ( a == 33 && b == 43 ) || ( a == 35 && b == 42 ) || ( a == 36 && b == 41 ) || ( a == 37 && b == 39 ) ) return (4 - 5*Nc2 + Nc4)/(32.*Nc3); if( ( a == 20 && b == 43 ) || ( a == 21 && b == 37 ) || ( a == 22 && b == 41 ) || ( a == 23 && b == 31 ) || ( a == 24 && b == 35 ) || ( a == 25 && b == 29 ) || ( a == 26 && b == 42 ) || ( a == 27 && b == 36 ) || ( a == 28 && b == 39 ) || ( a == 30 && b == 33 ) || ( a == 32 && b == 40 ) || ( a == 34 && b == 38 ) ) return -(-1 + Nc4)/(8.*Nc3); } if ( basis == id33bar888 ) { if( ( a == 0 && b == 0 ) || ( a == 1 && b == 1 ) ) return (2 - 3*Nc2 + Nc4)/8.; if( a == 0 && b == 1 ) return (1 - Nc2)/4.; if( ( a == 0 && b == 2 ) || ( a == 0 && b == 3 ) || ( a == 0 && b == 4 ) || ( a == 1 && b == 2 ) || ( a == 1 && b == 3 ) || ( a == 1 && b == 4 ) ) return 0; if( ( a == 0 && b == 5 ) || ( a == 0 && b == 8 ) || ( a == 0 && b == 9 ) || ( a == 1 && b == 6 ) || ( a == 1 && b == 7 ) || ( a == 1 && b == 10 ) ) return (2 - 3*Nc2 + Nc4)/(8.*Nc); if( ( a == 0 && b == 6 ) || ( a == 0 && b == 7 ) || ( a == 0 && b == 10 ) || ( a == 1 && b == 5 ) || ( a == 1 && b == 8 ) || ( a == 1 && b == 9 ) ) return -(-1 + Nc2)/(4.*Nc); if( ( a == 2 && b == 2 ) || ( a == 3 && b == 3 ) || ( a == 4 && b == 4 ) ) return sqr(-1 + Nc2)/8.; if( ( a == 2 && b == 3 ) || ( a == 2 && b == 4 ) || ( a == 3 && b == 4 ) ) return (-1 + Nc2)/8.; if( ( a == 2 && b == 5 ) || ( a == 2 && b == 6 ) || ( a == 2 && b == 8 ) || ( a == 2 && b == 10 ) || ( a == 3 && b == 6 ) || ( a == 3 && b == 7 ) || ( a == 3 && b == 8 ) || ( a == 3 && b == 9 ) || ( a == 4 && b == 5 ) || ( a == 4 && b == 7 ) || ( a == 4 && b == 9 ) || ( a == 4 && b == 10 ) ) return sqr(-1 + Nc2)/(8.*Nc); if( ( a == 2 && b == 7 ) || ( a == 2 && b == 9 ) || ( a == 3 && b == 5 ) || ( a == 3 && b == 10 ) || ( a == 4 && b == 6 ) || ( a == 4 && b == 8 ) ) return -(-1 + Nc2)/(8.*Nc); if( ( a == 5 && b == 5 ) || ( a == 6 && b == 6 ) || ( a == 7 && b == 7 ) || ( a == 8 && b == 8 ) || ( a == 9 && b == 9 ) || ( a == 10 && b == 10 ) ) return pow(-1 + Nc2,3.)/(8.*Nc2); if( ( a == 5 && b == 6 ) || ( a == 5 && b == 7 ) || ( a == 6 && b == 9 ) || ( a == 7 && b == 8 ) || ( a == 8 && b == 10 ) || ( a == 9 && b == 10 ) ) return -sqr(-1 + Nc2)/(8.*Nc2); if( ( a == 5 && b == 8 ) || ( a == 5 && b == 9 ) || ( a == 6 && b == 7 ) || ( a == 6 && b == 10 ) || ( a == 7 && b == 10 ) || ( a == 8 && b == 9 ) ) return 0.125 - 1/(8.*Nc2); if( ( a == 5 && b == 10 ) || ( a == 6 && b == 8 ) || ( a == 7 && b == 9 ) ) return (-1 + Nc4)/(8.*Nc2); } if ( basis == id33bar33bar8 ) { if( ( a == 0 && b == 0 ) || ( a == 1 && b == 1 ) || ( a == 2 && b == 2 ) || ( a == 3 && b == 3 ) ) return (Nc*(-1 + Nc2))/2.; if( ( a == 0 && b == 1 ) || ( a == 0 && b == 3 ) || ( a == 1 && b == 2 ) || ( a == 2 && b == 3 ) ) return (-1 + Nc2)/2.; if( ( a == 0 && b == 2 ) || ( a == 1 && b == 3 ) ) return 0; } } else { if ( a != b ) return 0.; if ( basis == id88 ) { return Nc2/4.; } if ( basis == id33bar ) { return Nc; } if ( basis == id888 ) { return Nc3/8.; } if ( basis == id33bar8 ) { return Nc2/2.; } if ( basis == id8888 ) { return Nc4/16.; } if ( basis == id33bar88 ) { return Nc3/4.; } if ( basis == id33bar33bar ) { return Nc2; } if ( basis == id88888 ) { return Nc5/32.; } if ( basis == id33bar888 ) { return Nc4/8.; } if ( basis == id33bar33bar8 ) { return Nc3/2.; } } throw Exception() << "SimpleColourBasis2::scalarProduct(): Cannot handle colour configuration" << Exception::runerror; } double SimpleColourBasis2::tMatrixElement(size_t i, size_t a, size_t b, const vector&, - const vector& basis) const { + const vector& basis, + size_t k, size_t l, + const map& dict) const { + // Check indices k and l + assert( k == i ); + assert( l == basis.size() ); + // Check that dict is the standardMap + assert( dict.size()+1 == basis.size() ); + map::const_iterator tmp; + for ( size_t ii = 0; ii < basis.size(); ii++ ) + if ( ii != i ) { + tmp = dict.find(ii); + assert( tmp != dict.end() ); + assert( tmp->second == ii ); + } if ( id33bar.empty() ) makeIds(); if ( basis == id88 ) { if(i == 0 && a == 0 && b == 0) return -1; if(i == 0 && a == 1 && b == 0) return 1; if(i == 1 && a == 0 && b == 0) return 1; if(i == 1 && a == 1 && b == 0) return -1; return 0.; } if ( basis == id33bar ) { if(i == 0 && a == 0 && b == 0) return 1; if(i == 1 && a == 0 && b == 0) return -1; return 0.; } if ( basis == id888 ) { if(i == 0 && a == 3 && b == 0) return -1; if(i == 0 && a == 5 && b == 1) return -1; if(i == 0 && a == 7 && b == 0) return 1; if(i == 0 && a == 8 && b == 1) return 1; if(i == 1 && a == 4 && b == 0) return 1; if(i == 1 && a == 5 && b == 1) return 1; if(i == 1 && a == 6 && b == 1) return -1; if(i == 1 && a == 7 && b == 0) return -1; if(i == 2 && a == 3 && b == 0) return 1; if(i == 2 && a == 4 && b == 0) return -1; if(i == 2 && a == 6 && b == 1) return 1; if(i == 2 && a == 8 && b == 1) return -1; return 0.; } if ( basis == id33bar8 ) { if(i == 0 && a == 2 && b == 0) return 1; if(i == 1 && a == 1 && b == 0) return -1; if(i == 2 && a == 1 && b == 0) return 1; if(i == 2 && a == 2 && b == 0) return -1; return 0.; } if ( basis == id8888 ) { if(i == 0 && a == 2 && b == 2) return -1; if(i == 0 && a == 5 && b == 1) return -1; if(i == 0 && a == 8 && b == 0) return -1; if(i == 0 && a == 9 && b == 2) return 1; if(i == 0 && a == 10 && b == 1) return 1; if(i == 0 && a == 11 && b == 0) return 1; if(i == 0 && a == 20 && b == 3) return -1; if(i == 0 && a == 22 && b == 4) return -1; if(i == 0 && a == 26 && b == 5) return -1; if(i == 0 && a == 28 && b == 6) return -1; if(i == 0 && a == 32 && b == 7) return -1; if(i == 0 && a == 34 && b == 8) return -1; if(i == 0 && a == 38 && b == 3) return 1; if(i == 0 && a == 39 && b == 4) return 1; if(i == 0 && a == 40 && b == 5) return 1; if(i == 0 && a == 41 && b == 6) return 1; if(i == 0 && a == 42 && b == 7) return 1; if(i == 0 && a == 43 && b == 8) return 1; if(i == 1 && a == 2 && b == 2) return 1; if(i == 1 && a == 9 && b == 2) return -1; if(i == 1 && a == 13 && b == 0) return -1; if(i == 1 && a == 15 && b == 1) return -1; if(i == 1 && a == 16 && b == 0) return 1; if(i == 1 && a == 17 && b == 1) return 1; if(i == 1 && a == 24 && b == 3) return 1; if(i == 1 && a == 25 && b == 4) return 1; if(i == 1 && a == 27 && b == 5) return 1; if(i == 1 && a == 28 && b == 6) return 1; if(i == 1 && a == 29 && b == 6) return -1; if(i == 1 && a == 30 && b == 5) return -1; if(i == 1 && a == 33 && b == 7) return 1; if(i == 1 && a == 34 && b == 8) return 1; if(i == 1 && a == 35 && b == 8) return -1; if(i == 1 && a == 36 && b == 7) return -1; if(i == 1 && a == 38 && b == 3) return -1; if(i == 1 && a == 39 && b == 4) return -1; if(i == 2 && a == 5 && b == 1) return 1; if(i == 2 && a == 10 && b == 1) return -1; if(i == 2 && a == 13 && b == 0) return 1; if(i == 2 && a == 16 && b == 0) return -1; if(i == 2 && a == 18 && b == 2) return -1; if(i == 2 && a == 19 && b == 2) return 1; if(i == 2 && a == 21 && b == 3) return 1; if(i == 2 && a == 22 && b == 4) return 1; if(i == 2 && a == 23 && b == 4) return -1; if(i == 2 && a == 24 && b == 3) return -1; if(i == 2 && a == 30 && b == 5) return 1; if(i == 2 && a == 31 && b == 6) return 1; if(i == 2 && a == 32 && b == 7) return 1; if(i == 2 && a == 33 && b == 7) return -1; if(i == 2 && a == 35 && b == 8) return 1; if(i == 2 && a == 37 && b == 8) return -1; if(i == 2 && a == 40 && b == 5) return -1; if(i == 2 && a == 41 && b == 6) return -1; if(i == 3 && a == 8 && b == 0) return 1; if(i == 3 && a == 11 && b == 0) return -1; if(i == 3 && a == 15 && b == 1) return 1; if(i == 3 && a == 17 && b == 1) return -1; if(i == 3 && a == 18 && b == 2) return 1; if(i == 3 && a == 19 && b == 2) return -1; if(i == 3 && a == 20 && b == 3) return 1; if(i == 3 && a == 21 && b == 3) return -1; if(i == 3 && a == 23 && b == 4) return 1; if(i == 3 && a == 25 && b == 4) return -1; if(i == 3 && a == 26 && b == 5) return 1; if(i == 3 && a == 27 && b == 5) return -1; if(i == 3 && a == 29 && b == 6) return 1; if(i == 3 && a == 31 && b == 6) return -1; if(i == 3 && a == 36 && b == 7) return 1; if(i == 3 && a == 37 && b == 8) return 1; if(i == 3 && a == 42 && b == 7) return -1; if(i == 3 && a == 43 && b == 8) return -1; return 0.; } if ( basis == id33bar88 ) { if(i == 0 && a == 4 && b == 0) return 1; if(i == 0 && a == 9 && b == 1) return 1; if(i == 0 && a == 10 && b == 2) return 1; if(i == 1 && a == 4 && b == 0) return -1; if(i == 1 && a == 5 && b == 1) return -1; if(i == 1 && a == 7 && b == 2) return -1; if(i == 2 && a == 0 && b == 0) return -1; if(i == 2 && a == 1 && b == 0) return 1; if(i == 2 && a == 6 && b == 1) return 1; if(i == 2 && a == 7 && b == 2) return 1; if(i == 2 && a == 8 && b == 2) return -1; if(i == 2 && a == 9 && b == 1) return -1; if(i == 3 && a == 0 && b == 0) return 1; if(i == 3 && a == 1 && b == 0) return -1; if(i == 3 && a == 5 && b == 1) return 1; if(i == 3 && a == 6 && b == 1) return -1; if(i == 3 && a == 8 && b == 2) return 1; if(i == 3 && a == 10 && b == 2) return -1; return 0.; } if ( basis == id33bar33bar ) { if(i == 0 && a == 0 && b == 0) return 1; if(i == 0 && a == 1 && b == 1) return 1; if(i == 1 && a == 1 && b == 1) return -1; if(i == 1 && a == 2 && b == 0) return -1; if(i == 2 && a == 2 && b == 0) return 1; if(i == 2 && a == 3 && b == 1) return 1; if(i == 3 && a == 0 && b == 0) return -1; if(i == 3 && a == 3 && b == 1) return -1; return 0.; } throw Exception() << "SimpleColourBasis2::tMatrixElement(): Cannot handle colour configuration" << Exception::runerror; return 0.; } bool SimpleColourBasis2::colourConnected(const cPDVector& sub, const vector& basis, const pair& i, const pair& j, size_t a) const { if ( id33bar.empty() ) makeIds(); // translate process to basis ids map >::const_iterator trans = indexMap().find(sub); assert(trans != indexMap().end()); int idColoured = i.second ? j.first : i.first; idColoured = trans->second.find(idColoured)->second; int idAntiColoured = i.second ? i.first : j.first; idAntiColoured = trans->second.find(idAntiColoured)->second; if ( basis == id88 ) { return a == 0 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0)); } if ( basis == id33bar ) { return a == 0 && (idColoured == 0 && idAntiColoured == 1); } if ( basis == id888 ) { return (a == 0 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 1 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))); } if ( basis == id33bar8 ) { return a == 0 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1)); } if ( basis == id8888 ) { return (a == 0 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1))) || (a == 1 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1))) || (a == 2 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2))) || (a == 3 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 4 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 5 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 6 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))) || (a == 7 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 8 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))); } if ( basis == id33bar88 ) { return (a == 0 && ((idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 0 && idAntiColoured == 1))) || (a == 1 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 2 && idAntiColoured == 3))) || (a == 2 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 3 && idAntiColoured == 2))); } if ( basis == id33bar33bar ) { return (a == 0 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 2 && idAntiColoured == 1))) || (a == 1 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 2 && idAntiColoured == 3))); } if ( basis == id88888 ) { return (a == 0 && ((idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 1 && ((idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 2 && ((idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0))) || (a == 3 && ((idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))) || (a == 4 && ((idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 5 && ((idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0))) || (a == 6 && ((idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))) || (a == 7 && ((idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 8 && ((idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0))) || (a == 9 && ((idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))) || (a == 10 && ((idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 11 && ((idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 12 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1))) || (a == 13 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1))) || (a == 14 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1))) || (a == 15 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1))) || (a == 16 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0) || (idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1))) || (a == 17 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0) || (idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1))) || (a == 18 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2))) || (a == 19 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2))) || (a == 20 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0))) || (a == 21 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 22 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0))) || (a == 23 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 24 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 25 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 26 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0))) || (a == 27 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 28 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0))) || (a == 29 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))) || (a == 30 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 31 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))) || (a == 32 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0))) || (a == 33 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 34 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 0))) || (a == 35 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))) || (a == 36 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 37 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))) || (a == 38 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 39 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 40 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 0))) || (a == 41 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))) || (a == 42 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 0))) || (a == 43 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 1 && idAntiColoured == 0))); } if ( basis == id33bar888 ) { return (a == 0 && ((idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 0 && idAntiColoured == 1))) || (a == 1 && ((idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 0 && idAntiColoured == 1))) || (a == 2 && ((idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 0 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 1))) || (a == 3 && ((idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 0 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 1))) || (a == 4 && ((idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1))) || (a == 5 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 2 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 4))) || (a == 6 && ((idColoured == 0 && idAntiColoured == 2) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3))) || (a == 7 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 3 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 4))) || (a == 8 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 3 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 2))) || (a == 9 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 3 && idAntiColoured == 1) || (idColoured == 4 && idAntiColoured == 2) || (idColoured == 2 && idAntiColoured == 3))) || (a == 10 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 2 && idAntiColoured == 1) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 3 && idAntiColoured == 2))); } if ( basis == id33bar33bar8 ) { return (a == 0 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3) || (idColoured == 2 && idAntiColoured == 1))) || (a == 1 && ((idColoured == 0 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1) || (idColoured == 2 && idAntiColoured == 3))) || (a == 2 && ((idColoured == 0 && idAntiColoured == 3) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 1))) || (a == 3 && ((idColoured == 0 && idAntiColoured == 1) || (idColoured == 2 && idAntiColoured == 4) || (idColoured == 4 && idAntiColoured == 3))); } throw Exception() << "SimpleColourBasis2::colourConnected(): Cannot handle colour configuration" << Exception::runerror; return false; } map > > SimpleColourBasis2::basisList(const vector& basis) const { if ( id33bar.empty() ) makeIds(); map > > blist; vector > structures; vector structure; if ( basis == id88 ) { structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); blist[0] = structures; return blist; } if ( basis == id33bar ) { structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); blist[0] = structures; return blist; } if ( basis == id888 ) { structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(2); structures.push_back(structure); blist[0] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[1] = structures; return blist; } if ( basis == id33bar8 ) { structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[0] = structures; return blist; } if ( basis == id8888 ) { structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(1); structure.push_back(2); structures.push_back(structure); blist[0] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structures.push_back(structure); structure.clear(); structure.push_back(1); structure.push_back(3); structures.push_back(structure); blist[1] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); structure.clear(); structure.push_back(2); structure.push_back(3); structures.push_back(structure); blist[2] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(2); structure.push_back(3); structures.push_back(structure); blist[3] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(3); structure.push_back(2); structures.push_back(structure); blist[4] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(1); structure.push_back(3); structures.push_back(structure); blist[5] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(3); structure.push_back(1); structures.push_back(structure); blist[6] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(1); structure.push_back(2); structures.push_back(structure); blist[7] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[8] = structures; return blist; } if ( basis == id33bar88 ) { structures.clear(); structure.clear(); structure.push_back(2); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); blist[0] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(3); structure.push_back(1); structures.push_back(structure); blist[1] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[2] = structures; return blist; } if ( basis == id33bar33bar ) { structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[0] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); structure.clear(); structure.push_back(2); structure.push_back(3); structures.push_back(structure); blist[1] = structures; return blist; } if ( basis == id88888 ) { structures.clear(); structure.clear(); structure.push_back(3); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(2); structures.push_back(structure); blist[0] = structures; structures.clear(); structure.clear(); structure.push_back(2); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(3); structures.push_back(structure); blist[1] = structures; structures.clear(); structure.clear(); structure.push_back(2); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(4); structures.push_back(structure); blist[2] = structures; structures.clear(); structure.clear(); structure.push_back(3); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[3] = structures; structures.clear(); structure.clear(); structure.push_back(1); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(3); structures.push_back(structure); blist[4] = structures; structures.clear(); structure.clear(); structure.push_back(1); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(4); structures.push_back(structure); blist[5] = structures; structures.clear(); structure.clear(); structure.push_back(2); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(1); structures.push_back(structure); blist[6] = structures; structures.clear(); structure.clear(); structure.push_back(1); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(2); structures.push_back(structure); blist[7] = structures; structures.clear(); structure.clear(); structure.push_back(1); structure.push_back(2); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(4); structures.push_back(structure); blist[8] = structures; structures.clear(); structure.clear(); structure.push_back(2); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(1); structures.push_back(structure); blist[9] = structures; structures.clear(); structure.clear(); structure.push_back(1); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(2); structures.push_back(structure); blist[10] = structures; structures.clear(); structure.clear(); structure.push_back(1); structure.push_back(2); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(3); structures.push_back(structure); blist[11] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(1); structure.push_back(2); structure.push_back(3); structures.push_back(structure); blist[12] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(1); structure.push_back(2); structure.push_back(4); structures.push_back(structure); blist[13] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(1); structure.push_back(3); structure.push_back(2); structures.push_back(structure); blist[14] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structures.push_back(structure); structure.clear(); structure.push_back(1); structure.push_back(3); structure.push_back(4); structures.push_back(structure); blist[15] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(1); structure.push_back(4); structure.push_back(2); structures.push_back(structure); blist[16] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structures.push_back(structure); structure.clear(); structure.push_back(1); structure.push_back(4); structure.push_back(3); structures.push_back(structure); blist[17] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); structure.clear(); structure.push_back(2); structure.push_back(3); structure.push_back(4); structures.push_back(structure); blist[18] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); structure.clear(); structure.push_back(2); structure.push_back(4); structure.push_back(3); structures.push_back(structure); blist[19] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(2); structure.push_back(3); structure.push_back(4); structures.push_back(structure); blist[20] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(2); structure.push_back(4); structure.push_back(3); structures.push_back(structure); blist[21] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(3); structure.push_back(2); structure.push_back(4); structures.push_back(structure); blist[22] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(3); structure.push_back(4); structure.push_back(2); structures.push_back(structure); blist[23] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(4); structure.push_back(2); structure.push_back(3); structures.push_back(structure); blist[24] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structure.push_back(4); structure.push_back(3); structure.push_back(2); structures.push_back(structure); blist[25] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(1); structure.push_back(3); structure.push_back(4); structures.push_back(structure); blist[26] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(1); structure.push_back(4); structure.push_back(3); structures.push_back(structure); blist[27] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(3); structure.push_back(1); structure.push_back(4); structures.push_back(structure); blist[28] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(3); structure.push_back(4); structure.push_back(1); structures.push_back(structure); blist[29] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(4); structure.push_back(1); structure.push_back(3); structures.push_back(structure); blist[30] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(4); structure.push_back(3); structure.push_back(1); structures.push_back(structure); blist[31] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(1); structure.push_back(2); structure.push_back(4); structures.push_back(structure); blist[32] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(1); structure.push_back(4); structure.push_back(2); structures.push_back(structure); blist[33] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(2); structure.push_back(1); structure.push_back(4); structures.push_back(structure); blist[34] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(2); structure.push_back(4); structure.push_back(1); structures.push_back(structure); blist[35] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(4); structure.push_back(1); structure.push_back(2); structures.push_back(structure); blist[36] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(4); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[37] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(1); structure.push_back(2); structure.push_back(3); structures.push_back(structure); blist[38] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(1); structure.push_back(3); structure.push_back(2); structures.push_back(structure); blist[39] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(2); structure.push_back(1); structure.push_back(3); structures.push_back(structure); blist[40] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(2); structure.push_back(3); structure.push_back(1); structures.push_back(structure); blist[41] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(3); structure.push_back(1); structure.push_back(2); structures.push_back(structure); blist[42] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(3); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[43] = structures; return blist; } if ( basis == id33bar888 ) { structures.clear(); structure.clear(); structure.push_back(2); structure.push_back(3); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); blist[0] = structures; structures.clear(); structure.clear(); structure.push_back(2); structure.push_back(4); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); blist[1] = structures; structures.clear(); structure.clear(); structure.push_back(3); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[2] = structures; structures.clear(); structure.clear(); structure.push_back(2); structure.push_back(4); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(1); structures.push_back(structure); blist[3] = structures; structures.clear(); structure.clear(); structure.push_back(2); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(1); structures.push_back(structure); blist[4] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(3); structure.push_back(4); structure.push_back(1); structures.push_back(structure); blist[5] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(2); structure.push_back(4); structure.push_back(3); structure.push_back(1); structures.push_back(structure); blist[6] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(2); structure.push_back(4); structure.push_back(1); structures.push_back(structure); blist[7] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structure.push_back(4); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[8] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(2); structure.push_back(3); structure.push_back(1); structures.push_back(structure); blist[9] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(3); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[10] = structures; return blist; } if ( basis == id33bar33bar8 ) { structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(2); structure.push_back(1); structures.push_back(structure); blist[0] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(4); structure.push_back(1); structures.push_back(structure); structure.clear(); structure.push_back(2); structure.push_back(3); structures.push_back(structure); blist[1] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(3); structures.push_back(structure); structure.clear(); structure.push_back(2); structure.push_back(4); structure.push_back(1); structures.push_back(structure); blist[2] = structures; structures.clear(); structure.clear(); structure.push_back(0); structure.push_back(1); structures.push_back(structure); structure.clear(); structure.push_back(2); structure.push_back(4); structure.push_back(3); structures.push_back(structure); blist[3] = structures; return blist; } throw Exception() << "SimpleColourBasis2::basisList(): Cannot handle colour configuration" << Exception::runerror; return blist; } void SimpleColourBasis2::makeIds() const { id88 = vector(2,PDT::Colour8); id33bar.push_back(PDT::Colour3); id33bar.push_back(PDT::Colour3bar); id888 = vector(3,PDT::Colour8); id33bar8 = id33bar; id33bar8.push_back(PDT::Colour8); id8888 = vector(4,PDT::Colour8); id33bar88 = id33bar8; id33bar88.push_back(PDT::Colour8); id33bar33bar = id33bar; id33bar33bar.push_back(PDT::Colour3); id33bar33bar.push_back(PDT::Colour3bar); id88888 = vector(5,PDT::Colour8); id33bar888 = id33bar88; id33bar888.push_back(PDT::Colour8); id33bar33bar8 = id33bar33bar; id33bar33bar8.push_back(PDT::Colour8); } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void SimpleColourBasis2::persistentOutput(PersistentOStream &) const {} void SimpleColourBasis2::persistentInput(PersistentIStream &, int) {} // *** Attention *** The following static variable is needed for the type // description system in ThePEG. Please check that the template arguments // are correct (the class and its base class), and that the constructor // arguments are correct (the class name and the name of the dynamically // loadable library where the class implementation can be found). DescribeClass describeHerwigSimpleColourBasis2("Herwig::SimpleColourBasis2", "Herwig.so"); void SimpleColourBasis2::Init() { static ClassDocumentation documentation ("SimpleColourBasis2 implements the colour algebra needed for " "processes with four coloured legs at NLO. It mainly " "serves as an example for the general ColourBasis interface."); } diff --git a/MatrixElement/Matchbox/Utility/SimpleColourBasis2.h b/MatrixElement/Matchbox/Utility/SimpleColourBasis2.h --- a/MatrixElement/Matchbox/Utility/SimpleColourBasis2.h +++ b/MatrixElement/Matchbox/Utility/SimpleColourBasis2.h @@ -1,211 +1,233 @@ // -*- C++ -*- // // SimpleColourBasis2.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_SimpleColourBasis2_H #define Herwig_SimpleColourBasis2_H // // This is the declaration of the SimpleColourBasis2 class. // #include "Herwig/MatrixElement/Matchbox/Utility/ColourBasis.h" namespace Herwig { using namespace ThePEG; /** * \ingroup Matchbox * \author Simon Platzer * * \brief SimpleColourBasis2 implements the colour algebra needed for * processes with four coloured legs at NLO. It mainly serves as an * example for the general ColourBasis interface. * */ class SimpleColourBasis2: public ColourBasis { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ SimpleColourBasis2(); /** * The destructor. */ virtual ~SimpleColourBasis2(); //@} public: /** * Prepare the basis for the normal ordered legs and return the * dimensionality of the basis. */ virtual size_t prepareBasis(const vector&); /** * Return the scalar product of basis tensors labelled a and b in * the basis used for the given normal ordered legs. */ virtual double scalarProduct(size_t a, size_t b, const vector& abBasis) const; /** * Return the matrix element of a colour charge * between basis tensors a and b, with * respect to aBasis and bBasis */ virtual double tMatrixElement(size_t i, size_t a, size_t b, const vector& aBasis, - const vector& bBasis) const; + const vector& bBasis, + size_t k, size_t l, + const map& dict) const; + + /* + * Temporary to make it compile + */ + virtual double sMatrixElement(size_t, size_t, size_t, + const vector&, + const vector&, + size_t, size_t, + const map& + ) const { + assert( 0 == 1 ); + return 0; + } + + /** + * Return true, if this colour basis supports gluon splittings. + */ + virtual bool canSplitGluons() const { + return false; + } /** * Return true, if a large-N colour connection exists for the * given external legs and basis tensor. */ virtual bool colourConnected(const cPDVector&, const vector&, const pair&, const pair&, size_t) const; /** * Return true, if the colour basis is capable of assigning colour * flows. */ virtual bool haveColourFlows() const { return true; } /** * Create ids for bases */ void makeIds() const; /** * Return a map of basis tensor indices to vectors identifying a * certain ordering corresponding to the given colour structure. May * not be supported by all colour basis implementations. */ virtual map > > basisList(const vector&) const; public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * id for 88 */ mutable vector id88; /** * id for 33bar */ mutable vector id33bar; /** * id for 888 */ mutable vector id888; /** * id for 33bar8 */ mutable vector id33bar8; /** * id for 8888 */ mutable vector id8888; /** * id for 33bar88 */ mutable vector id33bar88; /** * id for 33bar33bar */ mutable vector id33bar33bar; /** * id for 88888 */ mutable vector id88888; /** * id for 33bar888 */ mutable vector id33bar888; /** * id for 33bar33bar8 */ mutable vector id33bar33bar8; private: /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ SimpleColourBasis2 & operator=(const SimpleColourBasis2 &) = delete; }; } #endif /* Herwig_SimpleColourBasis2_H */ diff --git a/Sampling/exsample/cell.h b/Sampling/exsample/cell.h --- a/Sampling/exsample/cell.h +++ b/Sampling/exsample/cell.h @@ -1,307 +1,304 @@ // -*- C++ -*- // // cell.h is part of ExSample -- A Library for Sampling Sudakov-Type Distributions // // Copyright (C) 2008-2017 Simon Platzer -- simon.plaetzer@desy.de, The Herwig Collaboration // // ExSample is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // // #ifndef EXSAMPLE_cell_h_included #define EXSAMPLE_cell_h_included #include "utility.h" #include "adaption_info.h" #include "statistics.h" namespace exsample { /// \brief Information contained in a leaf cell class cell_info { public: /// the default constructor cell_info(); /// construct from boundaries and adaption info cell_info(const std::vector& ll, const std::vector& ur, const adaption_info& ainfo); /// construct from boundaries, flags for variables to be sampled, /// and adaption info cell_info(const std::vector& ll, const std::vector& ur, const std::vector& sampled_variables, const adaption_info& ainfo); public: /// generate a flat trial point in this cell template void select(Random&, std::vector&); /// generate a flat trial point in this cell /// only for the variables falgged as true template void select(Random&, std::vector&, const std::vector&); /// indicate a function value for the given point void selected(const std::vector&, double, const adaption_info&); /// indicate that a point has been /// accepted in this cell void accept() { ++accepted_; } /// reject a previously accepted event void reject() { --accepted_; } public: /// return true, if below efficiency threshold bool bad(const adaption_info& ainfo) const { return ((static_cast(accepted_)/static_cast(attempted_)) < ainfo.efficiency_threshold); } /// suggest a split and indicate wether it is worth /// to be performed std::pair get_split(const adaption_info&, bool&) const; /// explore this cell performing a flat sampling, /// updating the given statistics object and pre-filling /// the efficiency histogram by a trial unweighting template void explore(Random&, const adaption_info&, Function*, statistics*, - SlaveStatistics& opt, double detuning); + SlaveStatistics& opt); /// explore this cell in a more refined way, which /// is however not suited for already calculating integrals /// and stuff template - void explore(Random&, const adaption_info&, Function*, - double detuning); + void explore(Random&, const adaption_info&, Function*); public: /// get the current overestimate double overestimate() const { return overestimate_; } /// return the position of the last maximum const std::vector& last_max_position() const { return last_max_position_; } /// set the current overestimate and maximum position - void overestimate(double v, const std::vector& pos, - double detuning) { - overestimate_ = detuning * v; + void overestimate(double v, const std::vector& pos) { + overestimate_ = v; last_max_position_ = pos; } /// get the volume double volume() const { return volume_; } /// get the lower left corner const std::vector& lower_left() const { return lower_left_; } /// get the upper right corner const std::vector& upper_right() const { return upper_right_; } /// get the number of attempted events unsigned long attempted() const { return attempted_; } /// get the number of accepted events unsigned long accepted() const { return accepted_; } public: /// return the number of missing events /// for the given parameter bin id int parametric_missing(const bit_container& id) const; /// set the number of missing events /// for the given parameter bin id void parametric_missing(const bit_container& id, int n); /// increase to the number of missing events /// for the given parameter bin id void increase_parametric_missing(const bit_container& id); /// decrease to the number of missing events /// for the given parameter bin id void decrease_parametric_missing(const bit_container& id); /// return true, if the cell is compensating in /// at least one parameter bin bool parametric_compensating() const { return !parametric_missing_map_.empty(); } /// return true, if the cell contains the /// indicated parameter point bool contains_parameter(const std::vector& point, const std::vector& sampled) const; public: /// put to ostream template void put(OStream& os) const; /// get from istream template void get(IStream& is); private: /// the value of the overestimate in this cell double overestimate_; /// the volume of this cell double volume_; /// the lower left corner of this cell std::vector lower_left_; /// the upper right corner of this cell std::vector upper_right_; /// midpoint of this cell std::vector mid_point_; /// the position of the last encountered /// maximum in this cell std::vector last_max_position_; /// left-right statistics of average weight std::vector > avg_weight_; /// the number of attempts in this cell unsigned long attempted_; /// the number of accepted events in this cell unsigned long accepted_; /// an optional map of parameter bin ids /// to the number of missing events std::map,int> parametric_missing_map_; }; /// \brief the general cell class class cell { public: /// default constructor cell(); /// construct from boundaries and adaption info cell(const std::vector& ll, const std::vector& ur, const adaption_info& ainfo); /// construct from boundaries, flags for variables to be sampled, /// and adaption info cell(const std::vector& ll, const std::vector& ur, const std::vector& sampled_variables, const adaption_info& ainfo); /// copy constructor cell(const cell& x); /// assignment cell& operator=(const cell& x); public: /// split this cell, exploring the /// child not containing the current overestimate template std::pair split(std::pair split_d, Random& rnd_gen, Function* f, const adaption_info& ainfo, const std::vector& sampled = - std::vector(), - double detuning = 1.0); + std::vector()); public: /// return the split dimension std::size_t split_dimension() const { return split_dimension_; } /// return the split value double split_point() const { return split_point_; } /// return the integral double integral() const { return integral_; } /// access the integral double& integral() { return integral_; } /// set the integral void integral(double v) { integral_ = v; } /// access the number of missing events int& missing_events() { return missing_events_; } /// return the number of missing events int missing_events() const { return missing_events_; } /// set the number of missing events void missing_events(int n) { missing_events_ = n; } /// access the cell_info object cell_info& info() { assert(cell_info_); return *cell_info_; } /// return the cell_info object const cell_info& info() const { assert(cell_info_); return *cell_info_; } public: /// put to ostream template void put(OStream& os) const; /// get from istream template void get(IStream& is); private: /// the dimension along this cell /// was split std::size_t split_dimension_; /// the value, where this cell was split double split_point_; /// the integral of the absolute value /// of the overestimate over all the /// children cells double integral_; /// the number of missing events in this cell int missing_events_; /// a pointer to the cell info object, /// if this is a leaf cell std::unique_ptr cell_info_; }; } #include "cell.icc" #endif // EXSAMPLE_cell_h_included diff --git a/Sampling/exsample/cell.icc b/Sampling/exsample/cell.icc --- a/Sampling/exsample/cell.icc +++ b/Sampling/exsample/cell.icc @@ -1,490 +1,487 @@ // -*- C++ -*- // // cell.icc is part of ExSample -- A Library for Sampling Sudakov-Type Distributions // // Copyright (C) 2008-2017 Simon Platzer -- simon.plaetzer@desy.de, The Herwig Collaboration // // ExSample is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // // namespace exsample { template void cell_info::select (Random& rnd_gen, std::vector& p) { std::transform(lower_left_.begin(),lower_left_.end(), upper_right_.begin(),p.begin(), rnd_gen); ++attempted_; } template void cell_info::select (Random& rnd_gen, std::vector& p, const std::vector& sample) { conditional_transform(lower_left_.begin(),lower_left_.end(), upper_right_.begin(),sample.begin(), p.begin(),rnd_gen); ++attempted_; } template void cell_info::explore(Random& rnd_gen, const adaption_info& ainfo, Function* function, statistics* stats, - SlaveStatistics& opt, - double detuning) { + SlaveStatistics& opt) { function->start_presampling(); unsigned long n_sampled = 0; std::vector ll = lower_left_; std::vector ur = upper_right_; double val = 0.; std::vector pos (ll.size()); std::vector< std::pair > > vals; unsigned long ivalnonzero = 0; while (n_sampled < ainfo.presampling_points) { std::transform(ll.begin(),ll.end(), ur.begin(),pos.begin(), rnd_gen); - val = function->evaluate(pos) * detuning; + val = function->evaluate(pos); vals.push_back( std::pair > (val,pos) ); if ( val != 0 ) ivalnonzero++; ++n_sampled; } while ( ivalnonzero > 0 ) { double avg = 0; double err = 0; double maxval = 0; std::vector maxpos (ll.size()); unsigned long imax(0); for ( unsigned long ival=0; ival < vals.size(); ival++ ) { val = std::abs(vals[ival].first); if ( val == 0 ) continue; avg += val; err += sqr(val); if ( val > maxval ) { maxval = val; maxpos = vals[ival].second; imax = ival; } } avg /= ivalnonzero; err /= ivalnonzero; err = sqrt(err-sqr(avg)); if ( maxval <= avg+sqrt(ivalnonzero/2.)*err ) { overestimate_ = maxval; last_max_position_ = maxpos; break; } vals.erase(vals.begin()+imax); ivalnonzero--; } for ( unsigned long ival=0; ival < vals.size(); ival++ ) { val = vals[ival].first; stats->presampled(val); opt.select(val); selected(pos,std::abs(val),ainfo); } function->stop_presampling(); } template void cell_info::explore (Random& rnd_gen, - const adaption_info& ainfo, Function* function, - double detuning) { + const adaption_info& ainfo, Function* function) { function->start_presampling(); unsigned long n_sampled = 0; std::vector ll = lower_left_; std::vector ur = upper_right_; double val = 0.; std::vector pos (ll.size()); std::vector< std::pair > > vals; while (n_sampled < ainfo.presampling_points) { std::transform(ll.begin(),ll.end(), ur.begin(),pos.begin(), rnd_gen); - val = function->evaluate(pos) * detuning; + val = function->evaluate(pos); if ( std::abs(val) > 0 ) vals.push_back( std::pair > (std::abs(val),pos) ); ++n_sampled; } while ( vals.size() > 0 ) { double avg = 0; double err = 0; double maxval = 0; std::vector maxpos (ll.size()); unsigned long imax(0); for ( unsigned long ival=0; ival < vals.size(); ival++ ) { double thisval = vals[ival].first; avg += thisval; err += sqr(thisval); if ( thisval > maxval ) { maxval = thisval; maxpos = vals[ival].second; imax = ival; } } avg /= vals.size(); err /= vals.size(); err = sqrt(err-sqr(avg)); if ( maxval <= avg+sqrt(vals.size()/2.)*err ) { overestimate_ = maxval; last_max_position_ = maxpos; break; } vals.erase(vals.begin()+imax); } function->stop_presampling(); } template void cell_info::put (OStream& os) const { os << overestimate_; ostream_traits::separator(os); os << volume_; ostream_traits::separator(os); os << lower_left_.size(); ostream_traits::separator(os); for (std::size_t k = 0; k < lower_left_.size(); ++k) { os << lower_left_[k]; ostream_traits::separator(os); } for (std::size_t k = 0; k < upper_right_.size(); ++k) { os << upper_right_[k]; ostream_traits::separator(os); } for (std::size_t k = 0; k < mid_point_.size(); ++k) { os << mid_point_[k]; ostream_traits::separator(os); } for (std::size_t k = 0; k < last_max_position_.size(); ++k) { os << last_max_position_[k]; ostream_traits::separator(os); } for (std::size_t k = 0; k < avg_weight_.size(); ++k) { os << avg_weight_[k].first; ostream_traits::separator(os); os << avg_weight_[k].second; ostream_traits::separator(os); } os << attempted_; ostream_traits::separator(os); os << accepted_; ostream_traits::separator(os); os << parametric_missing_map_.size(); ostream_traits::separator(os); for ( std::map,int>::const_iterator p = parametric_missing_map_.begin(); p != parametric_missing_map_.end(); ++p ) { p->first.put(os); os << p->second; ostream_traits::separator(os); } } template void cell_info::get (IStream& is) { std::size_t dim; is >> overestimate_ >> volume_ >> dim; lower_left_.resize(dim); for (std::size_t k = 0; k < lower_left_.size(); ++k) { is >> lower_left_[k]; } upper_right_.resize(dim); for (std::size_t k = 0; k < upper_right_.size(); ++k) { is >> upper_right_[k]; } mid_point_.resize(dim); for (std::size_t k = 0; k < mid_point_.size(); ++k) { is >> mid_point_[k]; } last_max_position_.resize(dim); for (std::size_t k = 0; k < last_max_position_.size(); ++k) { is >> last_max_position_[k]; } avg_weight_.resize(dim); for (std::size_t k = 0; k < avg_weight_.size(); ++k) { is >> avg_weight_[k].first >> avg_weight_[k].second; } is >> attempted_ >> accepted_ >> dim; for ( size_t k = 0; k < dim; ++k ) { bit_container in; in.get(is); is >> parametric_missing_map_[in]; } } template std::pair cell::split (std::pair split_d, Random& rnd_gen, Function* function, const adaption_info& ainfo, - const std::vector& sampled, - double detuning) { + const std::vector& sampled) { assert(!missing_events() && !info().parametric_compensating()); split_dimension_ = split_d.first; split_point_ = split_d.second; std::vector lower_left1 = info().lower_left(); std::vector upper_right1 = info().upper_right(); std::vector lower_left2 = info().lower_left(); std::vector upper_right2 = info().upper_right(); upper_right1[split_dimension_] = split_point_; lower_left2[split_dimension_] = split_point_; std::pair children; if (sampled.empty()) children = std::pair(cell(lower_left1,upper_right1,ainfo), cell(lower_left2,upper_right2,ainfo)); else children = std::pair (cell(lower_left1,upper_right1,sampled,ainfo), cell(lower_left2,upper_right2,sampled,ainfo)); if (info().last_max_position()[split_dimension_] <= split_point_) { - children.first.info().overestimate(info().overestimate(),info().last_max_position(),1.0); - children.second.info().explore(rnd_gen,ainfo,function,detuning); + children.first.info().overestimate(info().overestimate(),info().last_max_position()); + children.second.info().explore(rnd_gen,ainfo,function); } else { - children.second.info().overestimate(info().overestimate(),info().last_max_position(),1.0); - children.first.info().explore(rnd_gen,ainfo,function,detuning); + children.second.info().overestimate(info().overestimate(),info().last_max_position()); + children.first.info().explore(rnd_gen,ainfo,function); } cell_info_.reset(0); children.first.integral(children.first.info().volume() * children.first.info().overestimate()); children.second.integral(children.second.info().volume() * children.second.info().overestimate()); return children; } template void cell::put (OStream& os) const { os << split_dimension_; ostream_traits::separator(os); os << split_point_; ostream_traits::separator(os); os << integral_; ostream_traits::separator(os); os << missing_events_; ostream_traits::separator(os); if (cell_info_) { os << "has_cell_info"; ostream_traits::separator(os); cell_info_->put(os); } else { os << "has_no_cell_info"; ostream_traits::separator(os); } } template void cell::get (IStream& is) { std::string info_tag; is >> split_dimension_ >> split_point_ >> integral_ >> missing_events_ >> info_tag; if (info_tag == "has_cell_info") { cell_info_.reset(new cell_info()); cell_info_->get(is); } } inline cell_info::cell_info() : overestimate_(0.), volume_(0.), lower_left_(), upper_right_(), mid_point_(), last_max_position_(), avg_weight_(), attempted_(0), accepted_(0) {} inline cell_info::cell_info(const std::vector& ll, const std::vector& ur, const adaption_info& ainfo) : overestimate_(0.), volume_(), lower_left_(ll), upper_right_(ur), mid_point_(), last_max_position_(), avg_weight_(std::vector > (ainfo.dimension,std::make_pair(0.,0.))), attempted_(0), accepted_(0) { std::vector delta; std::transform(ur.begin(),ur.end(), ll.begin(),std::back_inserter(delta), std::minus()); volume_ = std::accumulate(delta.begin(),delta.end(),1.,std::multiplies()); std::transform(ur.begin(),ur.end(), ll.begin(),std::back_inserter(mid_point_), std::plus()); for (std::size_t k = 0; k < ainfo.dimension; ++k) mid_point_[k] /= 2.; } inline cell_info::cell_info(const std::vector& ll, const std::vector& ur, const std::vector& sampled_variables, const adaption_info& ainfo) : overestimate_(0.), volume_(), lower_left_(ll), upper_right_(ur), mid_point_(), last_max_position_(), avg_weight_(std::vector > (ainfo.dimension,std::make_pair(0.,0.))), attempted_(0), accepted_(0) { std::vector delta; conditional_transform(ur.begin(),ur.end(), ll.begin(),sampled_variables.begin(), std::back_inserter(delta), std::minus()); volume_ = std::accumulate(delta.begin(),delta.end(),1.,std::multiplies()); std::transform(ur.begin(),ur.end(), ll.begin(),std::back_inserter(mid_point_), std::plus()); for (std::size_t k = 0; k < ainfo.dimension; ++k) mid_point_[k] /= 2.; } inline int cell_info::parametric_missing(const bit_container& id) const { std::map,int>::const_iterator mit = parametric_missing_map_.find(id); if (mit == parametric_missing_map_.end()) return 0; return mit->second; } inline void cell_info::parametric_missing(const bit_container& id, int n) { if (n == 0) { std::map,int>::iterator mit = parametric_missing_map_.find(id); if (mit != parametric_missing_map_.end()) parametric_missing_map_.erase(mit); return; } parametric_missing_map_[id] = n; } inline void cell_info::increase_parametric_missing(const bit_container& id) { std::map,int>::iterator mit = parametric_missing_map_.find(id); if (mit != parametric_missing_map_.end()) { mit->second += 1; if (mit->second == 0) parametric_missing_map_.erase(mit); } else parametric_missing_map_[id] = 1; } inline void cell_info::decrease_parametric_missing(const bit_container& id) { std::map,int>::iterator mit = parametric_missing_map_.find(id); if (mit != parametric_missing_map_.end()) { mit->second -= 1; if (mit->second == 0) parametric_missing_map_.erase(mit); } else assert(false); } inline void cell_info::selected(const std::vector& p, double weight, const adaption_info& ainfo) { for (std::size_t k = 0; k < p.size(); ++k) { if (ainfo.adapt[k]) { if (p[k] < mid_point_[k]) avg_weight_[k].first += weight; else avg_weight_[k].second += weight; } } } inline std::pair cell_info::get_split (const adaption_info& ainfo, bool& worth) const { std::size_t split_d = 0; double gain = 0.; for (std::size_t k = 0; k < ainfo.dimension; ++k) { double xgain = 0.; double left = avg_weight_[k].first; double right = avg_weight_[k].second; if (left+right > 0.) { xgain = std::abs(left-right)/(left+right); } if (xgain > gain) { gain = xgain; split_d = k; } } worth = (gain >= ainfo.gain_threshold); return std::make_pair(split_d,mid_point_[split_d]); } inline bool cell_info::contains_parameter (const std::vector& point, const std::vector& sampled) const { std::vector::const_iterator p = point.begin(); std::vector::const_iterator l = lower_left_.begin(); std::vector::const_iterator u = upper_right_.begin(); std::vector::const_iterator f = sampled.begin(); for (; p < point.end(); ++p, ++f, ++l, ++u) if (!(*f)) { if (((*l) > (*p)) || ((*u) < (*p))) return false; } return true; } inline cell::cell() : split_dimension_(0), split_point_(0.), integral_(0.), missing_events_(0), cell_info_(nullptr) {} inline cell::cell(const std::vector& ll, const std::vector& ur, const adaption_info& ainfo) : split_dimension_(0), split_point_(0.), integral_(0.), missing_events_(0), cell_info_(new cell_info(ll,ur,ainfo)) {} inline cell::cell(const std::vector& ll, const std::vector& ur, const std::vector& sampled_variables, const adaption_info& ainfo) : split_dimension_(0), split_point_(0.), integral_(0.), missing_events_(0), cell_info_(new cell_info(ll,ur,sampled_variables,ainfo)) {} inline cell::cell(const cell& x) : split_dimension_(x.split_dimension_), split_point_(x.split_point_), integral_(x.integral_), missing_events_(x.missing_events_), cell_info_(nullptr) { if (x.cell_info_) cell_info_.reset(new cell_info(*x.cell_info_)); } inline cell& cell::operator=(const cell& x) { if (this == &x) return *this; split_dimension_ = x.split_dimension_; split_point_ = x.split_point_; integral_ = x.integral_; missing_events_ = x.missing_events_; if (x.cell_info_) cell_info_.reset(new cell_info(*x.cell_info_)); return *this; } } diff --git a/Sampling/exsample/exponential_generator.icc b/Sampling/exsample/exponential_generator.icc --- a/Sampling/exsample/exponential_generator.icc +++ b/Sampling/exsample/exponential_generator.icc @@ -1,386 +1,388 @@ // -*- C++ -*- // // exponential_generator.icc is part of ExSample -- A Library for Sampling Sudakov-Type Distributions // // Copyright (C) 2008-2017 Simon Platzer -- simon.plaetzer@desy.de, The Herwig Collaboration // // ExSample is licenced under version 3 of the GPL, see COPYING for details. // Please respect the MCnet academic guidelines, see GUIDELINES for details. // // namespace exsample { template void exponential_generator::initialize() { adaption_info_.dimension = function_->dimension(); adaption_info_.lower_left = function_->support().first; adaption_info_.upper_right = function_->support().second; if (adaption_info_.adapt.empty()) adaption_info_.adapt = std::vector(adaption_info_.dimension,true); evolution_variable_ = function_->evolution_variable(); evolution_cutoff_ = function_->evolution_cutoff(); sample_variables_ = function_->variable_flags(); sample_other_variables_ = sample_variables_; sample_other_variables_[evolution_variable_] = false; last_point_.resize(adaption_info_.dimension); parametric_selector_ = parametric_selector(&last_point_,sample_other_variables_); exponent_selector_ = parametric_selector(&last_point_,sample_variables_); missing_accessor_ = parametric_missing_accessor(&last_parameter_bin_); parametric_sampler_ = parametric_sampling_selector > (&last_point_,&last_parameter_bin_,sample_other_variables_,rnd_gen_); if (initialized_) return; splits_ = 0; for ( std::size_t k = 0; k < adaption_info_.dimension; ++k ) { if ( sample_other_variables_[k] ) continue; parameter_splits_[k].push_back(adaption_info_.lower_left[k]); parameter_splits_[k].push_back(adaption_info_.upper_right[k]); } root_cell_ = binary_tree(cell(adaption_info_.lower_left, adaption_info_.upper_right, sample_other_variables_, adaption_info_)); - root_cell_.value().info().explore(rnd_gen_,adaption_info_,function_,detuning_); + root_cell_.value().info().explore(rnd_gen_,adaption_info_,function_); root_cell_.value().integral(root_cell_.value().info().volume() * root_cell_.value().info().overestimate()); last_exponent_integrand_.resize(1); check_events_ = adaption_info_.presampling_points; initialized_ = true; } template bool exponential_generator::split () { if (adaption_info_.freeze_grid <= accepts_) return false; if (compensating_) return false; if (!(*last_cell_).info().bad(adaption_info_)) return false; bool dosplit = false; std::pair sp = (*last_cell_).info().get_split(adaption_info_,dosplit); if (!dosplit) return false; if (!adaption_info_.adapt[sp.first]) return false; if (splits_ == parameter_hash_bits/2) return false; ++splits_; last_cell_.node().split((*last_cell_).split(sp,rnd_gen_,function_,adaption_info_, - sample_other_variables_,detuning_)); + sample_other_variables_)); if ( !sample_other_variables_[sp.first] ) { if ( std::find(parameter_splits_[sp.first].begin(),parameter_splits_[sp.first].end(),sp.second) == parameter_splits_[sp.first].end() ) { parameter_splits_[sp.first].push_back(sp.second); std::sort(parameter_splits_[sp.first].begin(),parameter_splits_[sp.first].end()); if ( sp.first == evolution_variable_ ) { last_exponent_integrand_.push_back(0.); } } } did_split_ = true; last_point_ = function_->parameter_point(); root_cell_.tree_accumulate(parametric_selector_,integral_accessor_,std::plus()); exponents_.clear(); get_exponent(); return true; } template void exponential_generator::get_exponent () { last_parameter_bin_.reset(); root_cell_.subtree_hash (exponent_selector_,last_parameter_bin_); last_exponent_ = exponents_.find(last_parameter_bin_); if (last_exponent_ != exponents_.end()) return; exponents_[last_parameter_bin_] = linear_interpolator(); last_exponent_ = exponents_.find(last_parameter_bin_); double old_evo = last_point_[evolution_variable_]; std::vector::iterator exp_it = last_exponent_integrand_.begin(); for (std::vector::iterator esp = parameter_splits_[evolution_variable_].begin(); esp < std::prev(parameter_splits_[evolution_variable_].end()); ++esp, ++exp_it) { last_point_[evolution_variable_] = (*esp + *std::next(esp))/2.; *exp_it = root_cell_.accumulate(parametric_selector_,integral_accessor_,std::plus()); } exp_it = std::prev(last_exponent_integrand_.end()); double total = 0.; for (std::vector::iterator esp = std::prev(parameter_splits_[evolution_variable_].end()); esp > parameter_splits_[evolution_variable_].begin(); --esp, --exp_it) { last_exponent_->second.set_interpolation(*esp,total); total += (*exp_it) * ((*esp) - (*std::prev(esp))); } last_exponent_->second.set_interpolation(parameter_splits_[evolution_variable_].front(),total); last_point_[evolution_variable_] = old_evo; } template std::set > exponential_generator::parameter_points() { std::set > res; std::vector pt(adaption_info_.dimension,0.); recursive_parameter_points(res,pt,0); return res; } template void exponential_generator:: recursive_parameter_points(std::set >& res, std::vector& pt, size_t current) { if ( current == adaption_info_.dimension ) { res.insert(pt); return; } if ( sample_variables_[current] ) { recursive_parameter_points(res,pt,current+1); return; } for ( std::vector::const_iterator sp = parameter_splits_[current].begin(); sp != std::prev(parameter_splits_[current].end()); ++sp ) { pt[current] = (*sp + *std::next(sp))/2.; recursive_parameter_points(res,pt,current+1); } } template void exponential_generator::compensate() { if (!did_split_ || !docompensate_) { assert(did_split_ || last_cell_ == root_cell_.begin()); exponents_.clear(); - last_cell_->info().overestimate(last_value_,last_point_,detuning_); + last_cell_->info().overestimate(last_value_,last_point_); last_cell_->integral(last_cell_->info().volume() * last_cell_->info().overestimate()); last_point_ = function_->parameter_point(); get_exponent(); return; } std::vector themaxpoint = last_point_; std::set > id_points = parameter_points(); for ( std::set >::const_iterator id = id_points.begin(); id != id_points.end(); ++id ) { last_point_ = *id; get_exponent(); } std::map,linear_interpolator > old_exponents = exponents_; double old_oe = last_cell_->info().overestimate(); - last_cell_->info().overestimate(last_value_,themaxpoint,detuning_); + last_cell_->info().overestimate(last_value_,themaxpoint); last_cell_->integral(last_cell_->info().volume() * last_cell_->info().overestimate()); exponents_.clear(); for ( std::set >::const_iterator id = id_points.begin(); id != id_points.end(); ++id ) { last_point_ = *id; get_exponent(); std::map,linear_interpolator >::iterator old_exp = old_exponents.find(last_parameter_bin_); std::map,linear_interpolator >::iterator new_exp = exponents_.find(last_parameter_bin_); assert(old_exp != old_exponents.end() && new_exp != exponents_.end()); double old_norm = 1. - std::exp(-(old_exp->second)(adaption_info_.lower_left[evolution_variable_])); double new_norm = 1. - std::exp(-(new_exp->second)(adaption_info_.lower_left[evolution_variable_])); for (binary_tree::iterator it = root_cell_.begin(); it != root_cell_.end(); ++it) { if ( !it->info().contains_parameter(last_point_,sample_variables_) ) continue; double old_int = 0.; double new_int = 0.; for ( std::vector::const_iterator sp = parameter_splits_[evolution_variable_].begin(); sp != std::prev(parameter_splits_[evolution_variable_].end()); ++sp ) { if ( *sp >= it->info().lower_left()[evolution_variable_] && *sp < it->info().upper_right()[evolution_variable_] ) { double xl = *sp; double xxl = *std::next(sp); double old_al = (old_exp->second.interpolation()[xxl] - old_exp->second.interpolation()[xl]) / (xxl-xl); double old_bl = (xxl * old_exp->second.interpolation()[xl] - xl * old_exp->second.interpolation()[xxl]) / (xxl-xl); double new_al = (new_exp->second.interpolation()[xxl] - new_exp->second.interpolation()[xl]) / (xxl-xl); double new_bl = (xxl * new_exp->second.interpolation()[xl] - xl * new_exp->second.interpolation()[xxl]) / (xxl-xl); if ( std::abs(old_al) > std::numeric_limits::epsilon() ) { old_int += (exp(-(old_al*xl+old_bl)) - exp(-(old_al*xxl+old_bl)))/old_al; } else { old_int += (xxl-xl)*exp(-old_bl); } if ( std::abs(new_al) > std::numeric_limits::epsilon() ) { new_int += (exp(-(new_al*xl+new_bl)) - exp(-(new_al*xxl+new_bl)))/new_al; } else { new_int += (xxl-xl)*exp(-new_bl); } } } double scaling; if (it != last_cell_) { if (old_int > std::numeric_limits::epsilon() && new_int > std::numeric_limits::epsilon()) scaling = ((old_norm * new_int) / (new_norm * old_int)) - 1.; else scaling = 0.; } else { if (old_int > std::numeric_limits::epsilon() && new_int > std::numeric_limits::epsilon()) scaling = ((last_value_ * old_norm * new_int) / (old_oe * new_norm * old_int)) - 1.; else scaling = 0.; } it->info().parametric_missing(last_parameter_bin_, it->info().parametric_missing(last_parameter_bin_) + static_cast(round(scaling * it->info().attempted()))); if (it->info().parametric_missing(last_parameter_bin_) != 0) { compensating_ = true; } } } last_point_ = function_->parameter_point(); } template double exponential_generator::generate(double enhance) { + if ( enhance == 0.0 ) + return 0.; if (compensating_) { compensating_ = false; for (binary_tree::iterator it = root_cell_.begin(); it != root_cell_.end(); ++it) if (it->info().parametric_compensating()) { compensating_ = true; break; } parametric_sampler_.compensate(compensating_); } last_point_ = function_->parameter_point(); if (last_point_[evolution_variable_] < evolution_cutoff_) { return 0.; } unsigned long n_hit_miss = 0; unsigned long n_select = 0; double minus_log_r; root_cell_.tree_accumulate(parametric_selector_,integral_accessor_,std::plus()); get_exponent(); while (true) { n_select = 0; - minus_log_r = -std::log(rnd_gen_())/enhance + + minus_log_r = -std::log(rnd_gen_())/enhance/detuning_ + last_exponent_->second(last_point_[evolution_variable_]); if (!last_exponent_->second.invertible(minus_log_r)) { return 0.; } try { last_point_[evolution_variable_] = last_exponent_->second.unique_inverse(minus_log_r); } catch (constant_interpolation& c) { last_point_[evolution_variable_] = rnd_gen_(c.range.first,c.range.second); } assert(std::isfinite(last_point_[evolution_variable_])); if (last_point_[evolution_variable_] < evolution_cutoff_) { return 0.; } ++attempts_; if (compensating_) { root_cell_.tree_accumulate(missing_accessor_,std::plus()); } if (parameter_splits_[evolution_variable_].size() > 2) root_cell_.tree_accumulate(parametric_selector_,integral_accessor_,std::plus()); if (did_split_) while ((last_cell_ = root_cell_.select(parametric_sampler_)) == root_cell_.end()) { root_cell_.tree_accumulate(missing_accessor_,std::plus()); if(++n_select > adaption_info_.maxtry) throw selection_maxtry(); } else last_cell_ = root_cell_.begin(); last_cell_->info().select(rnd_gen_,last_point_,sample_other_variables_); last_value_ = function_->evaluate(last_point_); assert(last_value_ >= 0.); last_cell_->info().selected(last_point_,last_value_,adaption_info_); if (last_value_ > last_cell_->info().overestimate()) { if ( std::abs(last_value_)/last_cell_->info().overestimate() > 2. ) { last_value_ = last_cell_->info().overestimate()* (1.+exp(2.*(2.-std::abs(last_value_)/last_cell_->info().overestimate()))); } compensate(); throw exponential_regenerate(); } if (last_cell_->info().attempted() % check_events_ == 0) { if (split()) { throw exponential_regenerate(); } } - if (last_value_/last_cell_->info().overestimate() > rnd_gen_()) { - function_->accept(last_point_,enhance*last_value_,enhance*last_cell_->info().overestimate()); + if (last_value_/last_cell_->info().overestimate()/detuning_ > rnd_gen_()) { + function_->accept(last_point_,enhance*last_value_,enhance*detuning_*last_cell_->info().overestimate()); break; } if ( last_value_ != 0.0 ) { - function_->veto(last_point_,enhance*last_value_,enhance*last_cell_->info().overestimate()); + function_->veto(last_point_,enhance*last_value_,enhance*detuning_*last_cell_->info().overestimate()); } if(++n_hit_miss > adaption_info_.maxtry) throw hit_and_miss_maxtry(); } if (last_value_ == 0.) return 0.; ++accepts_; ++check_events_; last_cell_->info().accept(); return 1.; } template template void exponential_generator::put (OStream& os) const { os << check_events_; ostream_traits::separator(os); adaption_info_.put(os); root_cell_.put(os); os << did_split_; ostream_traits::separator(os); os << initialized_; ostream_traits::separator(os); os << evolution_variable_; ostream_traits::separator(os); os << evolution_cutoff_; ostream_traits::separator(os); os << sample_variables_; ostream_traits::separator(os); os << sample_other_variables_; ostream_traits::separator(os); os << parameter_splits_; ostream_traits::separator(os); // last_cell_ is selected new so we ignore it here os << last_point_; ostream_traits::separator(os); os << last_value_; ostream_traits::separator(os); last_parameter_bin_.put(os); os << exponents_.size(); ostream_traits::separator(os); for ( std::map,linear_interpolator >::const_iterator ex = exponents_.begin(); ex != exponents_.end() ; ++ex ) { ex->first.put(os); ex->second.put(os); } os << last_exponent_integrand_; ostream_traits::separator(os); os << compensating_; ostream_traits::separator(os); os << attempts_; ostream_traits::separator(os); os << accepts_; ostream_traits::separator(os); os << splits_; ostream_traits::separator(os); os << docompensate_; ostream_traits::separator(os); } template template void exponential_generator::get (IStream& is) { is >> check_events_; adaption_info_.get(is); root_cell_.get(is); is >> did_split_ >> initialized_ >> evolution_variable_ >> evolution_cutoff_ >> sample_variables_ >> sample_other_variables_ >> parameter_splits_; // last_cell_ is selected new so we ignore it here is >> last_point_ >> last_value_; last_parameter_bin_.get(is); size_t dim; is >> dim; for ( size_t k = 0; k < dim ; ++k ) { bit_container key; key.get(is); exponents_[key].get(is); } is >> last_exponent_integrand_; last_exponent_ = exponents_.find(last_parameter_bin_); is >> compensating_ >> attempts_ >> accepts_ >> splits_ >> docompensate_; } } diff --git a/Shower/Dipole/Base/Dipole.cc b/Shower/Dipole/Base/Dipole.cc --- a/Shower/Dipole/Base/Dipole.cc +++ b/Shower/Dipole/Base/Dipole.cc @@ -1,438 +1,455 @@ // -*- C++ -*- // // Dipole.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 Dipole class. // #include "Dipole.h" #include "Herwig/Shower/Dipole/Utility/DipolePartonSplitter.h" using namespace Herwig; Dipole::Dipole() : theParticles(), thePDFs(), theFractions(1.0,1.0), theIndices(), theDecaying(false,false), theOffShell(false,false), theScales(0.0*GeV,0.0*GeV) {} Dipole::Dipole(const pair& newParticles, const pair& newPDFs, pair newFractions, pair newScales) : theParticles(newParticles), thePDFs(newPDFs), theFractions(newFractions), theIndices(), theDecaying(false,false), theOffShell(false,false), theScales(newScales) { theIndices.first = DipoleIndex(theParticles.first->dataPtr(), theParticles.second->dataPtr(), newPDFs.first,newPDFs.second, theDecaying.first,theDecaying.second, theOffShell.first,theOffShell.second); theIndices.second = theIndices.first; theIndices.second.swap(); } Dipole::Dipole(const pair& newParticles, const pair& newPDFs, pair newFractions, pair decaying, pair offShell, pair newScales) : theParticles(newParticles), thePDFs(newPDFs), theFractions(newFractions), theIndices(), theDecaying(decaying), theOffShell(offShell), theScales(newScales) { theIndices.first = DipoleIndex(theParticles.first->dataPtr(), theParticles.second->dataPtr(), newPDFs.first,newPDFs.second, theDecaying.first,theDecaying.second, theOffShell.first,theOffShell.second); theIndices.second = theIndices.first; theIndices.second.swap(); } void Dipole::update() { theIndices.first = DipoleIndex(theParticles.first->dataPtr(), theParticles.second->dataPtr(), thePDFs.first,thePDFs.second, theDecaying.first,theDecaying.second, theOffShell.first,theOffShell.second); theIndices.second = theIndices.first; theIndices.second.swap(); assert(DipolePartonSplitter::colourConnected(theParticles.first, theParticles.second)); } pair Dipole::split(DipoleSplittingInfo& dsplit, - bool colourSpectator) const { + bool colourSpectator, + bool subleadingNc) const { // check contracts assert(dsplit.splittingKinematics()); assert(dsplit.emitterData() && dsplit.emissionData() && dsplit.spectatorData()); if ( !colourSpectator ) { assert(index(dsplit.configuration()) == dsplit.index()); assert(emitterX(dsplit.configuration()) == dsplit.emitterX()); assert(spectatorX(dsplit.configuration()) == dsplit.spectatorX()); } else { assert(emitterX(dsplit.configuration()) == dsplit.emitterX()); assert(emitterPDF(dsplit.configuration()) == dsplit.index().emitterPDF()); assert((dsplit.configuration().first ? theParticles.first->dataPtr() : theParticles.second->dataPtr()) == dsplit.index().emitterData()); } // generate full kinematics dsplit.splittingKinematics()->generateKinematics( emitter(dsplit.configuration())->momentum(), spectator(dsplit.configuration())->momentum(), dsplit); // Treat the case of decay splittings as backward evolution. // i.e. Put the new emitter and new emission or new spectator // and new emission respectively into the new dipoles. bool emitter_decay = dsplit.index().incomingDecayEmitter(); if ( emitter_decay ) assert(false); bool spectator_decay = dsplit.index().incomingDecaySpectator(); tPPtr oldSpectator = spectator(dsplit.configuration()); PPtr newSpectator; // get a new spectator if ( !colourSpectator ) { newSpectator = dsplit.spectatorData()->produceParticle( dsplit.splittingKinematics()->lastSpectatorMomentum()); DipolePartonSplitter::change(oldSpectator, newSpectator, spectatorPDF(dsplit.configuration()).pdf(), spectator_decay); dsplit.spectator(oldSpectator); dsplit.splitSpectator(newSpectator); } else { newSpectator = oldSpectator; } // perform the splitting tPPtr oldEmitter = emitter(dsplit.configuration()); - PPtr newEmitter = - dsplit.emitterData()->produceParticle( - dsplit.splittingKinematics()->lastEmitterMomentum()); - PPtr newEmission = - dsplit.emissionData()->produceParticle( - dsplit.splittingKinematics()->lastEmissionMomentum()); + PPtr newEmitter, newEmission; + double z = dsplit.lastZ(); + // Do not swap momenta for splittings different from g->gg or + // initial state emitters + bool noSwap = !(dsplit.emitterData()->id() == ParticleID::g + && dsplit.emissionData()->id() == ParticleID::g ) + || dsplit.index().initialStateEmitter(); + if ( noSwap || z > UseRandom::rnd(1.0) ) { + newEmitter = + dsplit.emitterData()->produceParticle( + dsplit.splittingKinematics()->lastEmitterMomentum()); + newEmission = + dsplit.emissionData()->produceParticle( + dsplit.splittingKinematics()->lastEmissionMomentum()); + } else { + newEmitter = + dsplit.emitterData()->produceParticle( + dsplit.splittingKinematics()->lastEmissionMomentum()); + newEmission = + dsplit.emissionData()->produceParticle( + dsplit.splittingKinematics()->lastEmitterMomentum()); + } newEmitter->scale(sqr(dsplit.lastPt())); newEmission->scale(sqr(dsplit.lastPt())); newSpectator->scale(oldSpectator->scale()); DipolePartonSplitter::split(oldEmitter,newEmitter,newEmission, oldSpectator,emitterPDF(dsplit.configuration()).pdf(), emitter_decay); dsplit.emitter(oldEmitter); dsplit.splitEmitter(newEmitter); dsplit.emission(newEmission); double emitter_x = emitterX(dsplit.configuration()) / dsplit.lastEmitterZ(); double spectator_x = spectatorX(dsplit.configuration()) / dsplit.lastSpectatorZ(); PDF emitter_pdf = emitterPDF(dsplit.configuration()); PDF spectator_pdf = spectatorPDF(dsplit.configuration()); // Communicate off-shell parton flags // Note that as gluons aren't off-shell, the only possibility // in qcd splittings is gluon emissions off an off-shell emitter // -> off-shell emitter => off-shell new emitter bool emitter_off_shell = dsplit.index().offShellEmitter(); bool spectator_off_shell = dsplit.index().offShellSpectator(); // now check how we need to arrange the children // assignment is 0 = emitter, 1 = emission, 2 = spectator int left = 0; int middle = 1; int right = 2; if (dsplit.configuration().first) { // spectator is unique right = 2; // middle is the one connecting to the spectator if (DipolePartonSplitter::colourConnected(newSpectator,newEmission)) { middle = 1; left = 0; } else { assert(DipolePartonSplitter::colourConnected(newSpectator,newEmitter)); middle = 0; left = 1; } } else { // spectator is unique left = 2; // middle is the one connecting to the spectator if (DipolePartonSplitter::colourConnected(newSpectator,newEmission)) { middle = 1; right = 0; } else { assert(DipolePartonSplitter::colourConnected(newSpectator,newEmitter)); middle = 0; right = 1; } } pair left_particles; pair right_particles; pair left_pdfs; pair right_pdfs; pair left_fractions; pair right_fractions; // Pairs containing indicators for decayed particles pair left_decays = {false,false}; pair right_decays = {false,false}; // Pairs containing indicators for off-shell particles pair left_off_shells = {false,false}; pair right_off_shells = {false,false}; switch (left) { case 0: if (emitter_decay) { assert(false); left_decays.first = emitter_decay; } left_particles.first = newEmitter; left_pdfs.first = emitter_pdf; left_fractions.first = emitter_x; left_off_shells.first = emitter_off_shell; break; case 1: left_particles.first = newEmission; left_pdfs.first = PDF(); left_fractions.first = 1.; left_decays.first = false; left_off_shells.first = false; break; case 2: left_particles.first = newSpectator; left_pdfs.first = spectator_pdf; left_fractions.first = spectator_x; left_decays.first = spectator_decay; left_off_shells.first = spectator_off_shell; break; } switch (middle) { case 0: if (emitter_decay) { assert(false); left_decays.second = emitter_decay; } left_particles.second = newEmitter; left_pdfs.second = emitter_pdf; left_fractions.second = emitter_x; left_off_shells.second = emitter_off_shell; break; case 1: left_particles.second = newEmission; left_pdfs.second = PDF(); left_fractions.second = 1.; left_decays.second = false; left_off_shells.second = false; break; case 2: left_decays.second = spectator_decay; left_particles.second = newSpectator; left_pdfs.second = spectator_pdf; left_fractions.second = spectator_x; left_off_shells.second = spectator_off_shell; break; } right_particles.first = left_particles.second; right_pdfs.first = left_pdfs.second; right_fractions.first = left_fractions.second; right_decays.first = left_decays.second; right_off_shells.first = left_off_shells.second; switch (right) { case 0: if (emitter_decay) { assert(false); right_decays.second = emitter_decay; } right_particles.second = newEmitter; right_pdfs.second = emitter_pdf; right_fractions.second = emitter_x; right_off_shells.second = emitter_off_shell; break; case 1: right_particles.second = newEmission; right_pdfs.second = PDF(); right_fractions.second = 1.; right_decays.second = false; right_off_shells.second = false; break; case 2: right_particles.second = newSpectator; right_pdfs.second = spectator_pdf; right_fractions.second = spectator_x; right_decays.second = spectator_decay; right_off_shells.second = spectator_off_shell; break; } Energy scale = dsplit.lastPt(); return { Dipole(left_particles, left_pdfs, left_fractions, left_decays, left_off_shells, {scale,scale}), Dipole(right_particles, right_pdfs, right_fractions, right_decays, right_off_shells, {scale,scale})}; } void Dipole::tmpsplit(DipoleSplittingInfo& dsplit, bool colourSpectator) const { // generate full kinematics dsplit.splittingKinematics()->generateKinematics(emitter(dsplit.configuration())->momentum(), spectator(dsplit.configuration())->momentum(), dsplit); tPPtr oldSpectator = spectator(dsplit.configuration()); PPtr newSpectator; // get a new spectator if ( !colourSpectator ) { newSpectator = dsplit.spectatorData()->produceParticle(dsplit.splittingKinematics()->lastSpectatorMomentum()); dsplit.spectator(oldSpectator); dsplit.splitSpectator(newSpectator); } else { newSpectator = oldSpectator; } // perform the splitting tPPtr oldEmitter = emitter(dsplit.configuration()); PPtr newEmitter = dsplit.emitterData()->produceParticle(dsplit.splittingKinematics()->lastEmitterMomentum()); PPtr newEmission = dsplit.emissionData()->produceParticle(dsplit.splittingKinematics()->lastEmissionMomentum()); dsplit.emitter(oldEmitter); dsplit.splitEmitter(newEmitter); dsplit.emission(newEmission); } void Dipole::recoil (DipoleSplittingInfo& dsplit) { // check contracts assert(dsplit.splittingKinematics()); assert(dsplit.spectatorData()); assert(spectatorX(dsplit.spectatorConfiguration()) == dsplit.spectatorX()); assert(spectatorPDF(dsplit.spectatorConfiguration()) == dsplit.index().spectatorPDF()); assert((dsplit.spectatorConfiguration().first ? theParticles.first->dataPtr() : theParticles.second->dataPtr()) == dsplit.index().spectatorData()); tPPtr oldSpectator = spectator(dsplit.spectatorConfiguration()); PPtr newSpectator = dsplit.spectatorData()->produceParticle( dsplit.splittingKinematics()->lastSpectatorMomentum()); DipolePartonSplitter::change(oldSpectator,newSpectator, spectatorPDF(dsplit.spectatorConfiguration()).pdf()); newSpectator->scale(sqr(dsplit.lastPt())); dsplit.spectator(oldSpectator); dsplit.splitSpectator(newSpectator); if ( dsplit.spectatorConfiguration().first ) { theParticles.second = newSpectator; theFractions.second /= dsplit.lastSpectatorZ(); } else { theParticles.first = newSpectator; theFractions.first /= dsplit.lastSpectatorZ(); } } void Dipole::print(ostream& os) const { os << "--- "; // Check for decays first if ( theDecaying.first || theDecaying.second) { assert(!(theDecaying.first && theDecaying.second)); if ( theDecaying.first && !theDecaying.second ) os << "Decay IF"; else if ( theDecaying.second && !theDecaying.first ) os << "Decay FI"; } else if ( !thePDFs.first.pdf() && !thePDFs.second.pdf() ) os << "FF"; else if ( thePDFs.first.pdf() && !thePDFs.second.pdf() ) os << "IF"; else if ( !thePDFs.first.pdf() && thePDFs.second.pdf() ) os << "FI"; else os << "II"; os << " Dipole ------------------------------------------------------------------\n"; if ( !theParticles.first || !theParticles.second ) { os << " *** This Dipole has not been setup properly. ***\n"; } else { os << " particles\n" << *theParticles.first << *theParticles.second; os << " scales/GeV = (" << (theScales.first/GeV) << "," << (theScales.second/GeV) << ") fractions = (" << theFractions.first << "," << theFractions.second << ")\n"; } os << "--------------------------------------------------------------------------------\n"; os << flush; } diff --git a/Shower/Dipole/Base/Dipole.h b/Shower/Dipole/Base/Dipole.h --- a/Shower/Dipole/Base/Dipole.h +++ b/Shower/Dipole/Base/Dipole.h @@ -1,337 +1,338 @@ // -*- C++ -*- // // Dipole.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_Dipole_H #define HERWIG_Dipole_H // // This is the declaration of the Dipole class. // #include "Herwig/Shower/Dipole/Kinematics/DipoleSplittingKinematics.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Stephen Webster, Johannes Bellm * * \brief The Dipole class is used by the dipole shower to * represent a dipole of two coloured partons. * */ class Dipole { public: /** * The default constructor */ Dipole(); /** * The standard constructor */ Dipole(const pair& newParticles, const pair& newPDFs, pair newFractions, pair newScales); /** * The standard constructor */ Dipole(const pair& newParticles, const pair& newPDFs, pair newFractions, const pair decaying = pair(false,false), const pair offShell = pair(false,false), pair newScales = pair(ZERO,ZERO)); public: /** * Get the left particle. */ tPPtr leftParticle() const { return theParticles.first; } /** * Get the right particle. */ tPPtr rightParticle() const { return theParticles.second; } /** * Get the left PDF. */ const PDF& leftPDF() const { return thePDFs.first; } /** * Get the right PDF. */ const PDF& rightPDF() const { return thePDFs.second; } /** * Get the left fraction. */ double leftFraction() const { return theFractions.first; } /** * Get the right fraction. */ double rightFraction() const { return theFractions.second; } /** * Get the bool indicating * incoming decay for the left * particle, for debugging only. */ bool leftDecaying() { return theDecaying.first; } /** * Get the bool indicating * incoming decay for the right * particle, for debugging only. */ bool rightDecaying() { return theDecaying.second; } /** * Set the left particle. */ void leftParticle(PPtr p) { theParticles.first = p; } /** * Set the right particle. */ void rightParticle(PPtr p) { theParticles.second = p; } /** * Set the left PDF */ void leftPDF(const PDF& p) { thePDFs.first = p; } /** * Set the right PDF */ void rightPDF(const PDF& p) { thePDFs.second = p; } /** * Set the momentum fraction for the left particle. */ void leftFraction(double x) { theFractions.first = x; } /** * Set the momentum fraction for the right particle. */ void rightFraction(double x) { theFractions.second = x; } /** * Get the scale for the left particle. */ Energy leftScale() const { return theScales.first; } /** * Set the scale for the left particle. */ void leftScale(Energy s) { theScales.first = s; } /** * Get the scale for the right particle. */ Energy rightScale() const { return theScales.second; } /** * Set the scale for the right particle. */ void rightScale(Energy s) { theScales.second = s; } /** * Set the decayed particle indicator * for the left particle */ void leftDecaying(bool decaying) { theDecaying.first = decaying; } /** * Set the decayed particle indicator * for the right particle */ void rightDecaying(bool decaying) { theDecaying.second = decaying; } /** * Update information, if modified. */ void update(); public: /** * Return the dipole index for the selected * emitter-spectator assignment. */ const DipoleIndex& index(pair conf) const { return conf.first ? theIndices.first : theIndices.second; } /** * Set the first index */ void setFirstIndex(DipoleIndex s){theIndices.first=s;} /** * Set the first index */ void setSecondIndex(DipoleIndex s){theIndices.second=s;} /** * Return the emitter particle for the * selected configuration. */ tPPtr emitter(pair conf) const { return conf.first ? theParticles.first : theParticles.second; } /** * Return the spectator particle for the * selected configuration. */ tPPtr spectator(pair conf) const { return conf.first ? theParticles.second : theParticles.first; } /** * Return the scale associated to the emitter * for the selected configuration. */ Energy emitterScale(pair conf) const { return conf.first ? theScales.first : theScales.second; } /** * Set the scale associated to the emitter * for the selected configuration. */ void emitterScale(pair conf, Energy scale) { (conf.first ? theScales.first : theScales.second) = scale; } /** * Return the momentum fraction of the emitter * for the selected configuration. */ double emitterX(pair conf) const { return conf.first ? theFractions.first : theFractions.second; } /** * Return the PDF of the emitter * for the selected configuration. */ const PDF& emitterPDF(pair conf) const { return conf.first ? thePDFs.first : thePDFs.second; } /** * Return the momentum fraction of the spectator * for the selected configuration. */ double spectatorX(pair conf) const { return conf.first ? theFractions.second : theFractions.first; } /** * Return the PDF of the spectator * for the selected configuration. */ const PDF& spectatorPDF(pair conf) const { return conf.first ? thePDFs.second : thePDFs.first; } public: /** * Split this dipole according to the given splitting. * If colourSpectator is true, do not change the spectator. */ pair split (DipoleSplittingInfo& dsplit, - bool colourSpectator) const; + bool colourSpectator, + bool subleadingNc = false) const; /** * As split, but without touching the event record. * Needed to produce a phase space point as it would * be after calling split. */ void tmpsplit (DipoleSplittingInfo& dsplit, bool colourSpectator) const; /** * Produce a new spectator according to the * given splitting. */ void recoil (DipoleSplittingInfo& dsplit); public: /** * Put information to ostream */ void print(ostream&) const; private: /** * The particles forming the dipole */ pair theParticles; /** * The PDF objects. */ pair thePDFs; /** * The momentum fractions associated * to the incoming particles */ pair theFractions; /** * The dipole indices, if the first or second particle * is considered as emitter. */ pair theIndices; /** * Indicates if either the first or the second parton * is incoming to a decay. */ pair theDecaying; /** * Indicates if either the first or second parton * can be off-shell (required for sampling). **/ pair theOffShell; /** * The scale associated to the first and second * particle, respectively. */ pair theScales; }; inline ostream& operator << (ostream& os, const Dipole& di) { di.print(os); return os; } } #endif /* HERWIG_Dipole_H */ diff --git a/Shower/Dipole/Base/DipoleEventRecord.cc b/Shower/Dipole/Base/DipoleEventRecord.cc --- a/Shower/Dipole/Base/DipoleEventRecord.cc +++ b/Shower/Dipole/Base/DipoleEventRecord.cc @@ -1,1608 +1,1965 @@ // -*- C++ -*- // // DipoleEventRecord.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 DipoleEventRecord class. // #include "DipoleEventRecord.h" #include "Herwig/Shower/Dipole/DipoleShowerHandler.h" #include "Herwig/Shower/Dipole/Utility/DipolePartonSplitter.h" #include "Herwig/Shower/ShowerHandler.h" #include "ThePEG/PDT/DecayMode.h" #include "Herwig/Decay/HwDecayerBase.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/PDF/PartonExtractor.h" #include "Herwig/Shower/RealEmissionProcess.h" +#include "Herwig/MatrixElement/Matchbox/Base/MatchboxMEBase.h" +#include "Herwig/MatrixElement/Matchbox/Base/MatchboxAmplitude.h" + #include #include #include using namespace Herwig; PList DipoleEventRecord::colourOrdered(PPair & in, PList & out) { PList colour_ordered; size_t done_size = out.size(); if (in.first->coloured()) ++done_size; if (in.second && in.second->coloured()) ++done_size; while (colour_ordered.size() != done_size) { PPtr current; // start with singlets, as long as we have some if (find(colour_ordered.begin(),colour_ordered.end(),in.first) == colour_ordered.end() && in.first->coloured()) { if (!in.first->hasColour() || !in.first->hasAntiColour()) current = in.first; } if (!current) { for (PList::iterator p = out.begin(); p != out.end(); ++p) { if (find(colour_ordered.begin(),colour_ordered.end(),*p) == colour_ordered.end() && (**p).coloured()) { if (!(**p).hasColour() || !(**p).hasAntiColour()) { current = *p; break; } } } } if (!current) { if (in.second && find(colour_ordered.begin(),colour_ordered.end(),in.second) == colour_ordered.end() && in.second->coloured()) { if (!in.second->hasColour() || !in.second->hasAntiColour()) current = in.second; } } // then go on with anything else if (!current) { if (find(colour_ordered.begin(),colour_ordered.end(),in.first) == colour_ordered.end() && in.first->coloured()) { current = in.first; } } if (!current) { for (PList::iterator p = out.begin(); p != out.end(); ++p) { if (find(colour_ordered.begin(),colour_ordered.end(),*p) == colour_ordered.end() && (**p).coloured()) { current = *p; break; } } } if (!current) { if (in.second && find(colour_ordered.begin(),colour_ordered.end(),in.second) == colour_ordered.end() && in.second->coloured()) { current = in.second; } } assert(current); PPtr next; Ptr::ptr walk_the_line; while (true) { if (!walk_the_line) { if (current->hasColour()) { walk_the_line = current->colourLine(); } else if (current->hasAntiColour()) { walk_the_line = current->antiColourLine(); } } if (!next) for (tPVector::const_iterator p = walk_the_line->coloured().begin(); p != walk_the_line->coloured().end(); ++p) { if (*p == current) continue; if (find(out.begin(),out.end(),*p) != out.end() || *p == in.first || (in.second && *p == in.second)) { next = *p; if (next->hasColour() && next->hasAntiColour()) { walk_the_line = walk_the_line == next->colourLine() ? next->antiColourLine() : next->colourLine(); } break; } } if (!next) for (tPVector::const_iterator p = walk_the_line->antiColoured().begin(); p != walk_the_line->antiColoured().end(); ++p) { if (*p == current) continue; if (find(out.begin(),out.end(),*p) != out.end() || *p == in.first || (in.second && *p == in.second)) { next = *p; if (next->hasColour() && next->hasAntiColour()) { walk_the_line = walk_the_line == next->colourLine() ? next->antiColourLine() : next->colourLine(); } break; } } assert(next); colour_ordered.push_back(current); current = next; // done if next is not a gluon or next is already in colour_ordered if ((current->hasColour() && !current->hasAntiColour()) || (!current->hasColour() && current->hasAntiColour())) { colour_ordered.push_back(current); break; } if (next->hasColour() && next->hasAntiColour()) { if (find(colour_ordered.begin(),colour_ordered.end(),next) != colour_ordered.end()) break; } next = PPtr(); } } return colour_ordered; } void DipoleEventRecord::popChain() { assert(!theChains.empty()); theDoneChains.push_back(DipoleChain()); theDoneChains.back().dipoles().splice(theDoneChains.back().dipoles().begin(),theChains.front().dipoles()); theChains.pop_front(); } void DipoleEventRecord::popChain(list::iterator ch) { assert(!theChains.empty()); theDoneChains.push_back(DipoleChain()); theDoneChains.back().dipoles().splice(theDoneChains.back().dipoles().begin(),ch->dipoles()); theChains.erase(ch); } void DipoleEventRecord::popChains(const list::iterator>& chs) { assert(!theChains.empty()); for ( list::iterator>::const_iterator ch = chs.begin(); ch != chs.end(); ++ch ) { theDoneChains.push_back(DipoleChain()); theDoneChains.back().dipoles().splice(theDoneChains.back().dipoles().begin(),(*ch)->dipoles()); } for ( list::iterator>::const_iterator ch = chs.begin(); ch != chs.end(); ++ch ) theChains.erase(*ch); } DipoleIndex DipoleEventRecord::mergeIndex(list::iterator firstDipole, const pair& whichFirst, list::iterator secondDipole, const pair& whichSecond) const { tcPDPtr emitterData = whichFirst.first ? firstDipole->leftParticle()->dataPtr() : firstDipole->rightParticle()->dataPtr(); tcPDPtr spectatorData = whichSecond.first ? secondDipole->leftParticle()->dataPtr() : secondDipole->rightParticle()->dataPtr(); const PDF& emitterPDF = whichFirst.first ? firstDipole->leftPDF() : firstDipole->rightPDF(); const PDF& spectatorPDF = whichSecond.first ? secondDipole->leftPDF() : secondDipole->rightPDF(); return DipoleIndex(emitterData,spectatorData,emitterPDF,spectatorPDF); } SubleadingSplittingInfo DipoleEventRecord::mergeSplittingInfo(list::iterator firstChain, list::iterator firstDipole, const pair& whichFirst, list::iterator secondChain, list::iterator secondDipole, const pair& whichSecond) const { SubleadingSplittingInfo res; res.index(mergeIndex(firstDipole,whichFirst,secondDipole,whichSecond)); res.emitter(whichFirst.first ? firstDipole->leftParticle() : firstDipole->rightParticle()); res.spectator(whichSecond.first ? secondDipole->leftParticle() : secondDipole->rightParticle()); res.emitterX(whichFirst.first ? firstDipole->leftFraction() : firstDipole->rightFraction()); res.spectatorX(whichSecond.first ? secondDipole->leftFraction() : secondDipole->rightFraction()); res.configuration(whichFirst); res.spectatorConfiguration(whichSecond); res.emitterChain(firstChain); res.emitterDipole(firstDipole); res.spectatorChain(secondChain); res.spectatorDipole(secondDipole); return res; } void DipoleEventRecord::getSubleadingSplittings(list& res) { static pair left(true,false); static pair right(false,true); res.clear(); for ( list::iterator cit = theChains.begin(); cit != theChains.end(); ++cit ) { for ( list::iterator dit = cit->dipoles().begin(); dit != cit->dipoles().end(); ++dit ) { for ( list::iterator djt = dit; djt != cit->dipoles().end(); ++djt ) { res.push_back(mergeSplittingInfo(cit,dit,left,cit,djt,left)); res.push_back(mergeSplittingInfo(cit,dit,right,cit,djt,right)); if ( dit != djt ) { res.push_back(mergeSplittingInfo(cit,dit,left,cit,djt,right)); res.push_back(mergeSplittingInfo(cit,dit,right,cit,djt,left)); } } } list::iterator cjt = cit; ++cjt; for ( ; cjt != theChains.end(); ++cjt ) { for ( list::iterator dit = cit->dipoles().begin(); dit != cit->dipoles().end(); ++dit ) { for ( list::iterator djt = cjt->dipoles().begin(); djt != cjt->dipoles().end(); ++djt ) { res.push_back(mergeSplittingInfo(cit,dit,left,cjt,djt,left)); res.push_back(mergeSplittingInfo(cit,dit,right,cjt,djt,right)); res.push_back(mergeSplittingInfo(cit,dit,left,cjt,djt,right)); res.push_back(mergeSplittingInfo(cit,dit,right,cjt,djt,left)); } } } } } void DipoleEventRecord::splitSubleading(SubleadingSplittingInfo& dsplit, pair::iterator,list::iterator>& childIterators, DipoleChain*& firstChain, DipoleChain*& secondChain) { if ( dsplit.emitterDipole() == dsplit.spectatorDipole() ) { assert(dsplit.emitterChain() == dsplit.spectatorChain()); split(dsplit.emitterDipole(),dsplit.emitterChain(),dsplit, childIterators,firstChain,secondChain,false); } else { // first need to recoil, then split recoil(dsplit.spectatorDipole(),dsplit.spectatorChain(),dsplit); split(dsplit.emitterDipole(),dsplit.emitterChain(),dsplit, childIterators,firstChain,secondChain,true); } } void DipoleEventRecord::findChains(const PList& ordered, const set& offShellPartons, const bool decay) { // All uses of findChains should guarantee // a non-empty list of particles assert( !ordered.empty() ); theChains.clear(); theDoneChains.clear(); DipoleChain current_chain; // this whole thing needs to have a more elegant implementation at some point bool startIsTriplet = (ordered.front()->hasColour() && !ordered.front()->hasAntiColour()) || (!ordered.front()->hasColour() && ordered.front()->hasAntiColour()); bool endIsTriplet = (ordered.back()->hasColour() && !ordered.back()->hasAntiColour()) || (!ordered.back()->hasColour() && ordered.back()->hasAntiColour()); if (!( ordered.size() == 2 && startIsTriplet && endIsTriplet)) { PList::const_iterator theStart = ordered.begin(); bool onceMore = false; for (PList::const_iterator p = ordered.begin(); p != ordered.end(); ++p) { PList::const_iterator next_it = p != --ordered.end() ? std::next(p) : ordered.begin(); if (!DipolePartonSplitter::colourConnected(*p,*next_it)) { // it may have happened that we need to close the chain due to another // chain starting right now; see the above global comment for this fix bool startIsOctet = (**theStart).hasColour() && (**theStart).hasAntiColour(); bool endIsOctet = (**p).hasColour() && (**p).hasAntiColour(); if ( DipolePartonSplitter::colourConnected(*p,*theStart) && startIsOctet && endIsOctet ) { swap(next_it,theStart); onceMore = true; } else { theStart = next_it; current_chain.check(); // Randomize the chains agains biasing of directions. if(UseRandom::rndbool()) theChains.push_back(current_chain); else theChains.insert(theChains.begin(),current_chain); current_chain.dipoles().clear(); continue; } } pair initial_state (false,false); initial_state.first = (*p == incoming().first || *p == incoming().second); initial_state.second = (*next_it == incoming().first || *next_it == incoming().second); pair which_in (-1,-1); if (initial_state.first) which_in.first = *p == incoming().first ? 0 : 1; if (initial_state.second) which_in.second = *next_it == incoming().first ? 0 : 1; pair xs (1.,1.); if (initial_state.first) xs.first = *p == incoming().first ? fractions().first : fractions().second; if (initial_state.second) xs.second = *next_it == incoming().first ? fractions().first : fractions().second; pair pdf; if ( which_in.first == 0 ) pdf.first = pdfs().first; else if ( which_in.first == 1 ) pdf.first = pdfs().second; if ( which_in.second == 0 ) pdf.second = pdfs().first; else if ( which_in.second == 1 ) pdf.second = pdfs().second; // In the case of a decay process register which // parton is incoming to the decay pair decayed_parton (false,false); if (decay) { decayed_parton.first = (*p == currentDecay()->incoming()[0].first); decayed_parton.second = (*next_it == currentDecay()->incoming()[0].first); } // Identify if either parton can have an off-shell mass // The first test for partons with zero nominal mass should // avoid issues of e.g. non-zero mass gluons pair off_shell (false,false); // Note we could do away with the offShellPartons set but, // to be safe in the case of an off-shell parton with a mass // *very* close to its on-shell mass, we would need to include tests on the // offShell indicators in the DipoleIndex == and < operators AND // in canHandle and canHandleEquivalent in each massive kernel. // Testing these in every splitting will probably be more expensive // than doing the following checks for each hard process and decay process // Only do off-shell check if the nominal mass is non-zero if ( (*p)->nominalMass() != ZERO ) { if ( offShellPartons.find(abs((*p)->id())) != offShellPartons.end() ) off_shell.first = true; else assert( abs((*p)->mass() - (*p)->nominalMass()) < (*p)->nominalMass()*1.e-5 && "There is an off-shell coloured particle in the hard process or a decay" "which needs to be added to DipoleShowerHandler:OffShellInShower." ); } if ( (*next_it)->nominalMass() != ZERO ) { if ( offShellPartons.find(abs((*next_it)->id())) != offShellPartons.end() ) off_shell.second = true; else assert( abs((*next_it)->mass() - (*next_it)->nominalMass()) < (*next_it)->nominalMass()*1.e-5 && "There is an off-shell coloured particle in the hard process or a decay" "which needs to be added to DipoleShowerHandler:OffShellInShower." ); } current_chain.dipoles().push_back(Dipole({*p,*next_it},pdf,xs, decayed_parton, off_shell)); if ( onceMore ) { next_it = theStart; current_chain.check(); // Randomize the chains agains biasing of directions. if(UseRandom::rndbool()) theChains.push_back(current_chain); else theChains.insert(theChains.begin(),current_chain); current_chain.dipoles().clear(); onceMore = false; } } } else { // treat 2 -> singlet, singlet -> 2 and 1 + singlet -> 1 + singlet special // to prevent duplicate dipole assert(DipolePartonSplitter::colourConnected(ordered.front(),ordered.back())); pair initial_state (false,false); initial_state.first = (ordered.front() == incoming().first || ordered.front() == incoming().second); initial_state.second = (ordered.back() == incoming().first || ordered.back() == incoming().second); pair which_in (-1,-1); if (initial_state.first) which_in.first = ordered.front() == incoming().first ? 0 : 1; if (initial_state.second) which_in.second = ordered.back() == incoming().first ? 0 : 1; pair xs (1.,1.); if (initial_state.first) xs.first = ordered.front() == incoming().first ? fractions().first : fractions().second; if (initial_state.second) xs.second = ordered.back() == incoming().first ? fractions().first : fractions().second; pair pdf; if ( which_in.first == 0 ) pdf.first = pdfs().first; else if ( which_in.first == 1 ) pdf.first = pdfs().second; if ( which_in.second == 0 ) pdf.second = pdfs().first; else if ( which_in.second == 1 ) pdf.second = pdfs().second; // In the case of a decay process register which // parton is incoming to the decay pair decayed_parton (false,false); if (decay) { decayed_parton.first = (ordered.front() == currentDecay()->incoming()[0].first); decayed_parton.second = (ordered.back() == currentDecay()->incoming()[0].first); } // Identify if either parton can have an off-shell mass // The first test for partons with zero nominal mass should // avoid issues of e.g. non-zero mass gluons pair off_shell (false,false); // Only do off-shell check if the nominal mass is non-zero if ( ordered.front()->nominalMass() != ZERO ) { if ( offShellPartons.find(abs(ordered.front()->id())) != offShellPartons.end() ) off_shell.first = true; else assert( abs(ordered.front()->mass() - ordered.front()->nominalMass()) < ordered.front()->nominalMass()*1.e-5 && "There is an off-shell coloured particle in the hard process or a decay" "which needs to be added to DipoleShowerHandler:OffShellInShower." ); } if ( ordered.back()->nominalMass() != ZERO ) { if ( offShellPartons.find(abs(ordered.back()->id())) != offShellPartons.end() ) off_shell.second = true; else assert( abs(ordered.back()->mass() - ordered.back()->nominalMass()) < ordered.back()->nominalMass()*1.e-5 && "There is an off-shell coloured particle in the hard process or a decay" "which needs to be added to DipoleShowerHandler:OffShellInShower." ); } current_chain.dipoles().push_back(Dipole({ordered.front(),ordered.back()}, pdf,xs, decayed_parton, off_shell)); } if (!current_chain.dipoles().empty()) { current_chain.check(); // Randomize the chains agains biasing of directions. if(UseRandom::rndbool()) theChains.push_back(current_chain); else theChains.insert(theChains.begin(),current_chain); } } const map& DipoleEventRecord::prepare(tSubProPtr subpro, tStdXCombPtr xc, StepPtr step, const pair& pdf,tPPair beam, bool firstInteraction, const set& offShellPartons, bool dipoles) { // set the subprocess subProcess(subpro); // clear the event record outgoing().clear(); theHard.clear(); theOriginals.clear(); theDecays.clear(); theCurrentDecay = PerturbativeProcessPtr(); + + subEmDone = 0; + if ( doSubleadingNc && firstInteraction ) { + + theDensityOperator.clear(); + continueSubleadingNc = true; + + const Ptr::tptr MBXCombPtr + = dynamic_ptr_cast::tptr >(xc); + if ( !MBXCombPtr ) { + throw Exception() << "Cannot cast StandardXComb as MatchboxXComb. " + << "Matchbox is required for " + << "colour matrix element corrections." + << Exception::runerror; + } + // Set the colour basis if it has not been set + if ( !theDensityOperator.colourBasis() ) { + theDensityOperator.colourBasis(MBXCombPtr->matchboxME()->matchboxAmplitude()->colourBasis()); + } else if ( theDensityOperator.colourBasis() != + MBXCombPtr->matchboxME()->matchboxAmplitude()->colourBasis() ) { + throw Exception() << "The colour basis used in the colour matrix " + << "element corrections should not change between events. " + << Exception::runerror; + } + + } else { + continueSubleadingNc = false; + } + // extract incoming particles PPair in = subpro->incoming(); // get the incoming momentum fractions // don't take these from the XComb as it may be null pair xs; ThePEG::Direction<0> dir(true); xs.first = in.first->momentum().dirPlus()/beam.first->momentum().dirPlus(); dir.reverse(); xs.second = in.second->momentum().dirPlus()/beam.second->momentum().dirPlus(); + xcombPtr(xc); pdfs() = pdf; fractions() = xs; // use ShowerHandler to split up the hard process PerturbativeProcessPtr hard; DecayProcessMap decay; // Special handling for the first interaction: // If a post subprocess handler (e.g. QED radiation) // is applied, there may be particles in the step object not // present in the subprocess object (other than any remnants). // These need to be included in any transformations due to // II splittings in ::update. if ( firstInteraction ) { // Initialise a PVector for the outgoing tPVector hardProcOutgoing; // Include all outgoing particles that are not remnants for ( auto & part : step->particles() ) if ( part->id() != 82 ) { hardProcOutgoing.push_back(part); } ShowerHandler::currentHandler()->splitHardProcess(hardProcOutgoing, hard, decay); } // For secondary collisions we must use the // subProcess object and not the step as the // step stores all outgoing from the entire collision else ShowerHandler::currentHandler()->splitHardProcess(tPVector(subpro->outgoing().begin(), subpro->outgoing().end()), hard,decay); // vectors for originals and copies of the particles vector original; vector copies; // fill originals for(unsigned int ix=0;ix<2;++ix) original.push_back(hard->incoming()[ix].first); for(unsigned int ix=0;ixoutgoing().size();++ix) original.push_back(hard->outgoing()[ix].first); for(DecayProcessMap::const_iterator it=decay.begin();it!=decay.end();++it) { fillFromDecays(it->second, original); } // and make copies for ( vector::const_iterator p = original.begin(); p != original.end(); ++p ) { PPtr copy = new_ptr(Particle(**p)); copies.push_back(copy); theOriginals[*p] = copy; } // isolate the colour of the copies from the originals colourIsolate(original,copies); // set the incoming particles incoming().first = copies[0]; ParticleVector children = incoming().first->children(); for ( ParticleVector::const_iterator c = children.begin(); c != children.end(); ++c ) incoming().first->abandonChild(*c); incoming().second = copies[1]; children = incoming().second->children(); for ( ParticleVector::const_iterator c = children.begin(); c != children.end(); ++c ) incoming().second->abandonChild(*c); // set the outgoing particles for the hard process for(unsigned int ix=0;ixoutgoing().size();++ix) { if(hard->outgoing()[ix].first->coloured()) outgoing().push_back(theOriginals[hard->outgoing()[ix].first]); else theHard.push_back(theOriginals[hard->outgoing()[ix].first]); } if ( dipoles ) { PList cordered = colourOrdered(incoming(),outgoing()); if ( !cordered.empty() ) findChains(cordered, offShellPartons, false); } // sort out the decays for(auto const & dec : decay) { // If the decay particle is in original it needs // to be added to the decays and the decay needs to be // changed to the copied particles. if ( theOriginals.find(dec.second->incoming()[0].first) != theOriginals.end() ) { theDecays[theOriginals[dec.second->incoming()[0].first]] = dec.second; PerturbativeProcessPtr decayProc = theDecays[theOriginals[dec.second->incoming()[0].first]]; separateDecay(decayProc); } else { assert( find( copies.begin(), copies.end(), dec.second->incoming()[0].first ) != copies.end() ); theDecays[dec.second->incoming()[0].first] = dec.second; } } PList::const_iterator XFirst, XLast; if ( !theHard.empty() ) { XFirst = theHard.begin(); XLast = theHard.end(); } else { XFirst = outgoing().begin(); XLast = outgoing().end(); } thePX = (**XFirst).momentum(); ++XFirst; for ( ; XFirst != XLast; ++XFirst ) thePX += (**XFirst).momentum(); identifyEventType(); + + + if ( doSubleadingNc ) { + theParticlesBefore.clear(); + theParticlesAfter.clear(); + theMomentaAfter.clear(); + theParticleIndices.clear(); + + // Set the particles and fill the dictionary + theParticleIndices[incoming().first] = 0; + theParticleIndices[incoming().second] = 1; + size_t i = 2; + theParticlesAfter.reserve(2 + outgoing().size() + theHard.size()); + theParticlesAfter.push_back(incoming().first->dataPtr()); + theParticlesAfter.push_back(incoming().second->dataPtr()); + theMomentaAfter.push_back(incoming().first->momentum()); + theMomentaAfter.push_back(incoming().second->momentum()); + for ( PList::const_iterator it = outgoing().begin(); it != outgoing().end(); it++ ) { + theParticlesAfter.push_back((*it)->dataPtr()); + theMomentaAfter.push_back((*it)->momentum()); + theParticleIndices[*it] = i; + i++; + } + // theHard is not added to theParticleIndices, as they aren't needed there + for ( PList::const_iterator it = theHard.begin(); it != theHard.end(); it++ ) { + theParticlesAfter.push_back((*it)->dataPtr()); + theMomentaAfter.push_back((*it)->momentum()); + } + + // theParticlesAfter is required for fill + const Ptr::tptr MBXCombPtr + = dynamic_ptr_cast::tptr >(xc); + if ( !MBXCombPtr ) { + throw Exception() << "Cannot cast StandardXComb as MatchboxXComb. " + << "Matchbox is required for " + << "colour matrix element corrections." + << Exception::runerror; + } + theDensityOperator.fill(MBXCombPtr,theParticlesAfter,theMomentaAfter); + } + return theOriginals; } void DipoleEventRecord::slimprepare(tSubProPtr subpro, tStdXCombPtr xc, const pair& pdf,tPPair beam, const set& offShellPartons, bool dipoles) { // set the subprocess subProcess(subpro); // clear the event record outgoing().clear(); theHard.clear(); theOriginals.clear(); theDecays.clear(); theCurrentDecay = PerturbativeProcessPtr(); // extract incoming particles PPair in = subpro->incoming(); // get the beam // get the incoming momentum fractions // don't take these from the XComb as it may be null pair xs; ThePEG::Direction<0> dir(true); xs.first = in.first->momentum().dirPlus()/beam.first->momentum().dirPlus(); dir.reverse(); xs.second = in.second->momentum().dirPlus()/beam.second->momentum().dirPlus(); xcombPtr(xc); pdfs() = pdf; fractions() = xs; incoming() = in; for(unsigned int ix=0;ixoutgoing().size();++ix) { if(subpro->outgoing()[ix]->coloured()) outgoing().push_back(subpro->outgoing()[ix]); } if ( dipoles ) { PList cordered = colourOrdered(incoming(),outgoing()); if ( !cordered.empty() ) findChains(cordered, offShellPartons, false); } } void DipoleEventRecord::fillFromDecays(PerturbativeProcessPtr decayProc, vector& original) { // Loop over the outgoing of the given perturbative process for ( auto const & outIt : decayProc->outgoing() ) { // Add the outgoing particle to the vector of original particles original.push_back(outIt.first); // Iterate through the outgoing if ( outIt.second ) fillFromDecays( outIt.second, original); } } void DipoleEventRecord::separateDecay(PerturbativeProcessPtr decayProc) { // Iteratively replace all entries in the incoming // with their copies. for ( auto & inIt : decayProc->incoming() ) { if ( theOriginals.find( inIt.first ) != theOriginals.end() ) inIt.first = theOriginals[inIt.first]; } // Iteratively replace all entries in the outgoing // with their copies. for ( auto & outIt : decayProc->outgoing()) { if ( theOriginals.count( outIt.first ) ) outIt.first = theOriginals[outIt.first]; if ( outIt.second ) separateDecay(outIt.second); } } void DipoleEventRecord::clear() { ShowerEventRecord::clear(); theDecays.clear(); theHard.clear(); theChains.clear(); theDoneChains.clear(); theOriginals.clear(); + theDensityOperator.clear(); + theParticlesBefore.clear(); + theParticlesAfter.clear(); + theMomentaAfter.clear(); + theNextDecays.clear(); } - pair DipoleEventRecord::tmpupdate(DipoleSplittingInfo& dsplit) { PVector inc; PVector out; tcPPtr IF = incoming().first; tcPPtr IS = incoming().second; tcPPtr DE = dsplit.emitter(); tcPPtr DS = dsplit.spectator(); if ( IF != DE && IF != DS ) { PPtr p = IF->data().produceParticle(IF->momentum()); inc.push_back(p); } else if ( IF == DE ) inc.push_back( dsplit.splitEmitter() ); else if ( IF == DS ) inc.push_back( dsplit.splitSpectator() ); if ( IS != DE && IS != DS ) { PPtr p = IS->data().produceParticle(IS->momentum()); inc.push_back(p); } else if ( IS == DE ) inc.push_back( dsplit.splitEmitter() ); else if ( IS == DS ) inc.push_back( dsplit.splitSpectator() ); if ( IF != DE && IS != DE) out.push_back( dsplit.splitEmitter()); if ( IF != DS && IS != DS) out.push_back( dsplit.splitSpectator()); out.push_back( dsplit.emission()); for ( tcPPtr h : theHard ){ PPtr p = h->data().produceParticle(h->momentum()); if ( dsplit.splittingKinematics()->doesTransform() ) { - p->set5Momentum( dsplit.splittingKinematics()->transform(p->momentum()) ); + dsplit.splittingKinematics()->transform(p); } out.push_back(p); } for ( tcPPtr p : outgoing() ) if ( p != DE && - p != DS && - p != dsplit.emission() ){ + p != DS && + p != dsplit.emission() ){ PPtr ou = p->data().produceParticle(p->momentum());; if ( dsplit.splittingKinematics()->doesTransform() ){ - ou->set5Momentum( dsplit.splittingKinematics()->transform(ou->momentum()) ); + dsplit.splittingKinematics()->transform(ou); } out.push_back(ou); } return {inc,out}; } + void DipoleEventRecord::update(DipoleSplittingInfo& dsplit) { + + if ( continueSubleadingNc ) { + subEmDone++; + theParticlesBefore = theParticlesAfter; + } if ( incoming().first == dsplit.emitter() ) { intermediates().push_back(dsplit.emitter()); incoming().first = dsplit.splitEmitter(); fractions().first /= dsplit.lastEmitterZ(); + if ( continueSubleadingNc ) { + theParticleIndices[dsplit.splitEmitter()] = 0; + theParticlesAfter[0] = dsplit.splitEmitter()->dataPtr(); + theEmitterEmissionIndices.first = 0; + theEmitterEmissionIndices.second.first = 0; + } } else if ( incoming().first == dsplit.spectator() ) { intermediates().push_back(dsplit.spectator()); incoming().first = dsplit.splitSpectator(); - fractions().first /= dsplit.lastSpectatorZ(); + fractions().first /= dsplit.lastSpectatorZ(); + if ( continueSubleadingNc ) { + theParticleIndices[dsplit.splitSpectator()] = 0; + theParticlesAfter[0] = dsplit.splitSpectator()->dataPtr(); + theSpectatorIndices.first = 0; + theSpectatorIndices.second = 0; + } } if ( incoming().second == dsplit.emitter() ) { intermediates().push_back(dsplit.emitter()); incoming().second = dsplit.splitEmitter(); fractions().second /= dsplit.lastEmitterZ(); + if ( continueSubleadingNc ) { + theParticleIndices[dsplit.splitEmitter()] = 1; + theParticlesAfter[1] = dsplit.splitEmitter()->dataPtr(); + theEmitterEmissionIndices.first = 1; + theEmitterEmissionIndices.second.first = 1; + } } else if ( incoming().second == dsplit.spectator() ) { intermediates().push_back(dsplit.spectator()); incoming().second = dsplit.splitSpectator(); fractions().second /= dsplit.lastSpectatorZ(); + if ( continueSubleadingNc ) { + theParticleIndices[dsplit.splitSpectator()] = 1; + theParticlesAfter[1] = dsplit.splitSpectator()->dataPtr(); + theSpectatorIndices.first = 1; + theSpectatorIndices.second = 1; + } } PList::iterator pos; pos = find(outgoing().begin(), outgoing().end(), dsplit.emitter()); if (pos != outgoing().end()) { intermediates().push_back(*pos); *pos = dsplit.splitEmitter(); + if ( continueSubleadingNc ) { + // The two first elements in theParticlesBefore/After are the incoming + theEmitterEmissionIndices.first = 2 + distance(outgoing().begin(), pos); + theEmitterEmissionIndices.second.first = theEmitterEmissionIndices.first; + theParticlesAfter[theEmitterEmissionIndices.second.first] = dsplit.splitEmitter()->dataPtr(); + theParticleIndices[dsplit.splitEmitter()] = theEmitterEmissionIndices.second.first; + } } pos = find(outgoing().begin(), outgoing().end(), dsplit.spectator()); if (pos != outgoing().end()) { intermediates().push_back(*pos); *pos = dsplit.splitSpectator(); + if ( continueSubleadingNc ) { + // The two first elements in theParticlesBefore/After are the incoming + theSpectatorIndices.first = 2 + distance(outgoing().begin(), pos); + theSpectatorIndices.second = theSpectatorIndices.first; + theParticlesAfter[theSpectatorIndices.second] = dsplit.splitSpectator()->dataPtr(); + theParticleIndices[dsplit.splitSpectator()] = theSpectatorIndices.second; + } + } + + if ( continueSubleadingNc ) { + theEmitterEmissionIndices.second.second = 2 + outgoing().size(); + theParticlesAfter.insert(theParticlesAfter.begin()+theEmitterEmissionIndices.second.second, + dsplit.emission()->dataPtr()); + theMomentaAfter.insert(theMomentaAfter.begin()+theEmitterEmissionIndices.second.second, + dsplit.emission()->momentum()); + theParticleIndices[dsplit.emission()] = theEmitterEmissionIndices.second.second; } outgoing().push_back(dsplit.emission()); if (dsplit.splittingKinematics()->doesTransform()) { + for (PList::iterator h = theHard.begin(); + h != theHard.end(); ++h) + dsplit.splittingKinematics()->transform(*h); + for (PList::iterator p = intermediates().begin(); - p != intermediates().end(); ++p) { - (**p).set5Momentum(dsplit.splittingKinematics()->transform((**p).momentum())); - } - - for (PList::iterator h = theHard.begin(); - h != theHard.end(); ++h) { - (**h).set5Momentum(dsplit.splittingKinematics()->transform((**h).momentum())); - } + p != intermediates().end(); ++p) + dsplit.splittingKinematics()->transform(*p); for (PList::iterator p = outgoing().begin(); p != outgoing().end(); ++p) { if ((*p) != dsplit.splitEmitter() && (*p) != dsplit.splitSpectator() && (*p) != dsplit.emission()) - (**p).set5Momentum(dsplit.splittingKinematics()->transform((**p).momentum())); + dsplit.splittingKinematics()->transform(*p); } + + if ( continueSubleadingNc ) { + theMomentaAfter[0] = incoming().first->momentum(); + theMomentaAfter[1] = incoming().second->momentum(); + size_t i = 2; + for (PList::iterator p = outgoing().begin(); + p != outgoing().end(); p++) { + theMomentaAfter[i] = (*p)->momentum(); + i++; + } + for (PList::iterator p = theHard.begin(); + p != theHard.end(); p++) { + theMomentaAfter[i] = (*p)->momentum(); + i++; + } + } + + } else if ( continueSubleadingNc ) { + theMomentaAfter[theEmitterEmissionIndices.second.first] = dsplit.splitEmitter()->momentum();// + theMomentaAfter[theSpectatorIndices.second] = dsplit.splitSpectator()->momentum();// } - + + // Stop with subleading emissions if the limit has been reached + if ( doSubleadingNc ) + if ( subEmDone == subleadingNcEmissionsLimit ) + continueSubleadingNc = false; + // Handle updates related to decays // Showering of decay processes // Treat the evolution of the incoming // decayed particle as in backward evolution if ( dsplit.isDecayProc() ) { // Create a pointer to the decay process PerturbativeProcessPtr decayProc = currentDecay(); // Add the emission to the outgoing of the decay process decayProc->outgoing().push_back( {dsplit.emission(), PerturbativeProcessPtr() }); // Bools to be used throughout const bool decayedEmtr = dsplit.index().incomingDecayEmitter(); const bool decayedSpec = dsplit.index().incomingDecaySpectator(); /* In the current implementation, **following the hard process** all particles in theDecays evolve independently e.g. if we have W -> XYZ where all X, Y and Z need to be showered and decayed, we only identify them as needing decaying (and hence put them in theDecays) AFTER showering the decay of W. Hence, XYZ are not even in theDecays until W has been fully showered and then they are decayed and showered completely independently KEY POINT - Never need to update other entries of theDecays Note: The PPtr in theDecays should remain unchanged and all changes should be made to the relative PerturbativeProcess. */ // Splittings from dipoles in the decay process which // do not have the decayed parton as emitter or spectator. // Update the decay process in theDecays if ( !decayedEmtr && !decayedSpec ) { // Find and replace the old spectator and // emitter in the outgoing of the decay process bool decayProcEm = false; bool decayProcSp = false; for ( auto & outIt : decayProc->outgoing() ) { if ( !decayProcEm && outIt.first == dsplit.emitter() ) { outIt = {dsplit.splitEmitter(), PerturbativeProcessPtr()}; decayProcEm = true; } if ( !decayProcSp && outIt.first == dsplit.spectator() ) { outIt = {dsplit.splitSpectator(), PerturbativeProcessPtr() }; decayProcSp = true; } - + if ( decayProcEm && decayProcSp ) break; } // Test that nothing strange is happening assert( (decayProcEm && decayProcSp) ); return; } // The spectator is the decayed particle else if ( decayedSpec ) { // Update the dipole event record intermediates intermediates().push_back(dsplit.splitSpectator()); // Update the the decayProcess incoming decayProc->incoming().clear(); decayProc->incoming().push_back({dsplit.splitSpectator(),decayProc}); // Update the decay process outgoing // Replace the old emitter with the new emitter for ( auto & outEmtrIt : decayProc->outgoing() ) { if ( outEmtrIt.first == dsplit.emitter() ){ outEmtrIt = {dsplit.splitEmitter(), PerturbativeProcessPtr() }; break; } } // Perform the recoil transformation // Find all particles in the recoil system PList recoilSystem; for ( auto const & outIt : decayProc->outgoing() ) { if ( outIt.first != dsplit.splitEmitter() && outIt.first != dsplit.emission() ) { recoilSystem.push_back(outIt.first); } } dsplit.splittingKinematics()->decayRecoil( recoilSystem ); return; } // The emitter is the decayed particle else { throw Exception() << "DipoleEventRecord: The emitter as a decayed particle is currently not implemented." << Exception::runerror; assert( currentDecay()->incoming()[0].first == dsplit.emitter() && decayedEmtr && !decayedSpec ); // Update the dipole event record intermediates intermediates().push_back(dsplit.splitEmitter()); // Update the the decayProcess incoming decayProc->incoming().clear(); decayProc->incoming().push_back({dsplit.splitEmitter(),decayProc}); // Update the decay process outgoing // Replace the old spectator with the new spectator for (auto & outSpecIt : decayProc->outgoing() ) { if ( outSpecIt.first == dsplit.spectator() ){ outSpecIt = { dsplit.splitSpectator(), PerturbativeProcessPtr() }; break; } } // Perform the recoil transformation assert(dsplit.splittingKinematics()->isDecay()); // Find all particles in the recoil system PList recoilSystem; for ( auto const & outIt : decayProc->outgoing() ) { if ( outIt.first != dsplit.splitSpectator() && outIt.first != dsplit.emission() ) { recoilSystem.push_back(outIt.first); } } dsplit.splittingKinematics()->decayRecoil( recoilSystem ); return; } } + + if ( continueSubleadingNc ) { + // Fixed alphaS + double alphaS = 0.118; + + map,Complex> Vijk; + double Vtemp; + const Lorentz5Momentum pEmission = dsplit.emission()->momentum(); + + // Special cases for the density operator evolution + // g->qqbar splitting + bool splitAGluon = (dsplit.emitter()->id() == ParticleID::g) && + (dsplit.emission()->id() != ParticleID::g); + // initial state g->qqbar splitting + bool initialGluonSplitting = (dsplit.splitEmitter()->id() == ParticleID::g) && + (dsplit.emission()->id() != ParticleID::g); + if ( initialGluonSplitting ) + assert(dsplit.splitEmitter() == incoming().first + || dsplit.splitEmitter() == incoming().second); + + // Set up the dictionary + std::tuple tmpTuple; + map tmpMap; + size_t n = theEmitterEmissionIndices.second.second; + theEmissionsMap.clear(); + if ( splitAGluon || initialGluonSplitting ) { + tmpTuple = std::make_tuple(theEmitterEmissionIndices.first, + theEmitterEmissionIndices.second.first, + theEmitterEmissionIndices.second.second); + tmpMap.clear(); + for ( size_t j = 0; j < theParticlesBefore.size(); j++ ) { + if ( j != theEmitterEmissionIndices.first ) + tmpMap[j] = j; + } + theEmissionsMap[tmpTuple] = tmpMap; + } else { + for ( size_t i = 0; i < theParticlesBefore.size(); i++ ) { + if ( theParticlesBefore[i]->coloured() ) { + tmpTuple = std::make_tuple(i,i,n); + tmpMap.clear(); + for ( size_t j = 0; j < theParticlesBefore.size(); j++ ) { + if ( j != i ) + tmpMap[j] = j; + } + theEmissionsMap[tmpTuple] = tmpMap; + } + } + } + + Energy2 pEmitpEmis; + Energy2 pEmispSpec; + + Lorentz5Momentum pEmitter; + Lorentz5Momentum pSpectator; + + // Calculate all required dipole factors + int i,k; + typedef map,map > dictMap; + for(dictMap::const_iterator ijit = theEmissionsMap.begin(); + ijit != theEmissionsMap.end(); ijit++) { + i = std::get<1>(ijit->first); + pEmitter = theMomentaAfter[i]; + pEmitpEmis = pEmitter*pEmission; + for(dictMap::const_iterator kit = theEmissionsMap.begin(); + kit != theEmissionsMap.end(); kit++) { + // For gluon splitting ijit == kit + if ( ijit != kit ) { + k = std::get<1>(kit->first); + pSpectator = theMomentaAfter[k]; + pEmispSpec = pEmission*pSpectator; + Vtemp = 4*Constants::pi*alphaS*dipoleKernelForEvolution(i, k, + pEmitter*pSpectator, pEmitpEmis, + pEmispSpec); + Vijk.insert(make_pair(make_pair(i,k),Complex(Vtemp,0.0))); + } else if ( splitAGluon || initialGluonSplitting ) { + k = std::get<1>(kit->first); + Vijk.insert(make_pair(make_pair(i,k),Complex(1.0,0.0))); + } + } + } + + theDensityOperator.evolve(Vijk,theParticlesBefore,theParticlesAfter, + theEmissionsMap,splitAGluon,initialGluonSplitting); + } +} + +double +DipoleEventRecord::dipoleKernelForEvolution(size_t em, size_t spec, + Energy2 pEmitpSpec, Energy2 pEmitpEmis, + Energy2 pEmispSpec) { + double Vijk; + + if ( densityOperatorEvolution == 3 ) { + if ( em == theEmitterEmissionIndices.second.first && + spec == theSpectatorIndices.second ) { + Vijk = 1.0; + } else { + Vijk = 0.0; + } + } else if ( densityOperatorEvolution == 2 ) { + Vijk = 1.0; + } else { + if ( densityOperatorEvolution == 0 ) { + if ( pEmitpEmis < densityOperatorCutoff ) pEmitpEmis = densityOperatorCutoff; + if ( pEmispSpec < densityOperatorCutoff ) pEmispSpec = densityOperatorCutoff; + } + Vijk = ((pEmitpSpec)/GeV2)/((pEmitpEmis/GeV2)* + (pEmispSpec/GeV2)); + } + + return Vijk; } void DipoleEventRecord::split(list::iterator dip, list::iterator ch, DipoleSplittingInfo& dsplit, pair::iterator,list::iterator>& childIterators, DipoleChain*& firstChain, DipoleChain*& secondChain, bool colourSpectator) { static DipoleChain empty; - pair children = dip->split(dsplit,colourSpectator); + + pair children = dip->split(dsplit,colourSpectator, + continueSubleadingNc); list::iterator breakup = ch->insertSplitting(dip,children,childIterators); if ( breakup == ch->dipoles().end() ) { firstChain = &(*ch); secondChain = ∅ } else { DipoleChain other; other.dipoles().splice(other.dipoles().end(),ch->dipoles(),breakup,ch->dipoles().end()); chains().push_back(other); firstChain = &(*ch); secondChain = &(chains().back()); // explicitly fix iterators in case the splice implementation // at hand does invalidate iterators (the SGI docu says, it doesn't, // but it seems that this behaviour is not part of the standard) childIterators.first = --firstChain->dipoles().end(); childIterators.second = secondChain->dipoles().begin(); } if ( !colourSpectator ) { update(dsplit); // otherwise done by recoil(...) } } + pair DipoleEventRecord::tmpsplit(list::iterator dip, list::iterator , DipoleSplittingInfo& dsplit, pair::iterator,list::iterator>& , DipoleChain*& , DipoleChain*& , bool colourSpectator) { dip->tmpsplit(dsplit,colourSpectator); return tmpupdate(dsplit); // otherwise done by recoil(...) } void DipoleEventRecord::recoil(list::iterator dip, list::iterator ch, DipoleSplittingInfo& dsplit) { dip->recoil(dsplit); ch->updateDipole(dip); update(dsplit); } list::iterator,list::iterator> > DipoleEventRecord::inDipoles() { list::iterator,list::iterator> > res; for ( list::iterator chit = theDoneChains.begin(); chit != theDoneChains.end(); ++chit ) { bool haveOne = false; for ( list::iterator dit = chit->dipoles().begin(); dit != chit->dipoles().end(); ++dit ) { if ( dit->leftPDF().pdf() || dit->rightPDF().pdf() ) { haveOne = true; break; } } if ( haveOne ) { theChains.splice(theChains.begin(),theDoneChains,chit); for ( list::iterator dit = theChains.front().dipoles().begin(); dit != theChains.front().dipoles().end(); ++dit ) { if ( dit->leftPDF().pdf() || dit->rightPDF().pdf() ) { res.push_back({dit,theChains.begin()}); } } } } return res; } -void DipoleEventRecord::transform(const SpinOneLorentzRotation& rot) { +void DipoleEventRecord::transform(const LorentzRotation& rot) { Lorentz5Momentum tmp; for (PList::iterator p = intermediates().begin(); p != intermediates().end(); ++p) { - tmp = (**p).momentum(); tmp = rot * tmp; + tmp = (**p).momentum(); + if ( (*p)->spinInfo() ) + (*p)->spinInfo()->transform(tmp, rot); + tmp = rot * tmp; (**p).set5Momentum(tmp); } for (PList::iterator h = theHard.begin(); h != theHard.end(); ++h) { - tmp = (**h).momentum(); tmp = rot * tmp; + tmp = (**h).momentum(); + if ( (*h)->spinInfo() ) + (*h)->spinInfo()->transform(tmp, rot); + tmp = rot * tmp; (**h).set5Momentum(tmp); } for (PList::iterator p = outgoing().begin(); p != outgoing().end(); ++p) { - tmp = (**p).momentum(); tmp = rot * tmp; + tmp = (**p).momentum(); + if ( (*p)->spinInfo() ) + (*p)->spinInfo()->transform(tmp, rot); + tmp = rot * tmp; (**p).set5Momentum(tmp); } } tPPair DipoleEventRecord::fillEventRecord(StepPtr step, bool firstInteraction, bool) { PPtr inSubPro = subProcess()->incoming().first; PPtr inParticle; if ( !(inSubPro->parents().empty()) ) inParticle = inSubPro->parents()[0]; else inParticle = inSubPro; PPtr inParton = theOriginals[inSubPro]; theOriginals.erase(inSubPro); updateColour(incoming().first,true); if ( inParticle != inSubPro ) inParticle->abandonChild(inSubPro); inParton->addChild(inSubPro); if ( inParticle != inSubPro ) inParticle->addChild(incoming().first); intermediates().push_back(inSubPro); intermediates().push_back(inParton); // Repeat all the above for the second incoming particle inSubPro = subProcess()->incoming().second; if ( !(inSubPro->parents().empty()) ) inParticle = inSubPro->parents()[0]; else inParticle = inSubPro; inParton = theOriginals[inSubPro]; theOriginals.erase(inSubPro); updateColour(incoming().second,true); if ( inParticle != inSubPro ) inParticle->abandonChild(inSubPro); inParton->addChild(inSubPro); if ( inParticle != inSubPro ) inParticle->addChild(incoming().second); intermediates().push_back(inSubPro); intermediates().push_back(inParton); // theOriginals is populated in ::prepare and contains all of the incoming and outgoing particles of the original hard process // Here outgoing particles from theOriginals are added into the intermediates() while ( !theOriginals.empty() ) { PPtr outSubPro = theOriginals.begin()->first; PPtr outParton = theOriginals.begin()->second; // workaround for OS X Mavericks LLVM libc++ #ifdef _LIBCPP_VERSION map::const_iterator beg = theOriginals.begin(); #else map::iterator beg = theOriginals.begin(); #endif theOriginals.erase(beg); updateColour(outParton,true); outSubPro->addChild(outParton); intermediates().push_back(outSubPro); } // Update the intermediates of the step step->addIntermediates(intermediates().begin(),intermediates().end()); for (auto const & p : outgoing()) step->addDecayProduct( p ); for (auto const & p : theHard) step->addDecayProduct( p ); if ( firstInteraction && (incoming().first->coloured() || incoming().second->coloured() ) ) { ShowerHandler::currentHandler()->lastExtractor() ->newRemnants(subProcess()->incoming(),incoming(),step); } step->addIntermediate(incoming().first); step->addIntermediate(incoming().second); return incoming(); } bool DipoleEventRecord::prepareDecay( PerturbativeProcessPtr decayProc, const set& offShellPartons ) { // Create objects containing the incoming and outgoing partons, // required as inputs for colourOrdered. PList out; for( auto const & dec : decayProc->outgoing()) { if(dec.first->coloured()) { out.push_back(dec.first); } } // Only need to shower if we have coloured outgoing particles if ( out.empty() ) return false; else { // For the incoming, use a PPair containing the incoming and a null pointer PPair in; in.first = decayProc->incoming()[0].first; - // Create an ordered list of particles - PList cordered; - cordered = colourOrdered(in,out); + + // Chains are found later if the subleading shower is used + if ( !doSubleadingNc ) { + // Create an ordered list of particles + PList cordered; + cordered = colourOrdered(in,out); - // Find the dipole chains for this decay - findChains(cordered, offShellPartons, true); - + // Find the dipole chains for this decay + findChains(cordered,offShellPartons,true); + } return true; } } Energy DipoleEventRecord::decay(PPtr incoming, bool& powhegEmission) { // get the process PerturbativeProcessPtr process = theDecays[incoming]; assert(process); //tDMPtr decayMode = new_ptr(DecayMode()); tDMPtr decayMode = DMPtr(); // Do not decay particles that have already been decayed // Note the herwig decayer deals with colour connections if ( process->outgoing().empty() ) { process->incoming()[0].first = incoming; DecayProcessMap decay; // Decay the particle, returning a pointer to the decay mode decayMode = ShowerHandler::currentHandler()->decay(process,decay,true); } // Sort out the colour connections of particles already decayed else { // sort out the colour of the incoming map cmap; if(incoming->colourLine()) cmap[process->incoming()[0].first->colourLine()] = incoming->colourLine(); if(incoming->antiColourLine()) cmap[process->incoming()[0].first->antiColourLine()] = incoming->antiColourLine(); // fix colours of outgoing for(auto const & outg : process->outgoing()) { map::iterator it = cmap.find(outg.first->colourLine()); if(it!=cmap.end()) { ColinePtr c1=outg.first->colourLine(); c1->removeColoured(outg.first); it->second->addColoured(outg.first); } it = cmap.find(outg.first->antiColourLine()); if(it!=cmap.end()) { ColinePtr c1=outg.first->antiColourLine(); c1->removeAntiColoured(outg.first); it->second->addAntiColoured(outg.first); } } // swap the incoming process->incoming()[0].first = incoming; } // Set the scale of all particles involved in the decay process to the // mass of the decaying particle // Initialise the scale for the evolution of // the parton shower following the decay Energy showerScale = ZERO; // Set the scale for the evolution of the shower showerScale = process->incoming()[0].first->momentum().m(); Energy2 decayScaleSqr = sqr( showerScale ); process->incoming()[0].first->scale( decayScaleSqr ); for(auto & outg : process->outgoing()) { outg.first->scale( decayScaleSqr ); } // Update the decaying particle in the process and the event PList::iterator posOut = find(outgoing().begin(), outgoing().end(), incoming); PList::iterator posHard = find(hard().begin(), hard().end(), incoming); assert((posOut!=outgoing().end() && posHard==hard().end()) || (posOut==outgoing().end() && posHard!=hard().end()) ); if ( posOut!=outgoing().end() ) { outgoing().erase(posOut); } else { hard().erase(posHard); } intermediates().push_back(process->incoming()[0].first); // Populate the children of the incoming for(auto const & outg : process->outgoing()) { PPtr outgoing = outg.first; process->incoming()[0].first->addChild(outgoing); } // If a decayed particle is not decayed above, // e.g. a W in a 3-body top decay, find its decaymode. if ( powhegEmission && !decayMode ) { string tag = incoming->dataPtr()->name() + "->"; // Must use OrderedParticles for a tag search ShowerHandler::OrderedParticles decayOut; for(auto const & outg : process->outgoing()) { decayOut.insert(outg.first->dataPtr()); } // Construct the tag for(auto const & dec : decayOut) { if( dec!=*decayOut.begin() ) tag += ","; tag +=dec->name(); } tag += ";"; // Find the decay mode decayMode = ShowerHandler::currentHandler()->findDecayMode(tag); } // Perform the powheg emission if ( powhegEmission ) { if ( decayMode ) { HwDecayerBasePtr decayer; decayer = dynamic_ptr_cast(decayMode->decayer()); if ( decayer->hasPOWHEGCorrection() ) { // Construct a real emission process and populate its // incoming and outcoming prior to any powheg emission RealEmissionProcessPtr born = new_ptr( RealEmissionProcess() ); born->bornIncoming().push_back( incoming ); for(auto const & outg : process->outgoing()) { born->bornOutgoing().push_back(outg.first); } // Generate any powheg emission, returning 'real' RealEmissionProcessPtr real = decayer->generateHardest( born ); // If an emission has been attempted // (Note if the emission fails, a null ptr is returned) if ( real ) { showerScale = real->pT()[ShowerInteraction::QCD]; // If an emission is generated sort out the particles if ( !real->outgoing().empty() ) { // Update the decay process // Note: Do not use the new incoming particle PPtr oldEmitter; PPtr newEmitter; // Use the name recoiler to avoid confusion with // the spectator in the POWHEGDecayer // i.e. the recoiler can be coloured or non-coloured PPtr oldRecoiler; PPtr newRecoiler; if ( real->emitter() == 1 ) { oldEmitter = real->bornOutgoing()[0]; oldRecoiler = real->bornOutgoing()[1]; newEmitter = real->outgoing()[0]; newRecoiler = real->outgoing()[1]; } else if ( real->emitter() == 2) { oldEmitter = real->bornOutgoing()[1]; oldRecoiler = real->bornOutgoing()[0]; newEmitter = real->outgoing()[1]; newRecoiler = real->outgoing()[0]; } PPtr emitted = real->outgoing()[ real->emitted()-1]; // Update the scales newRecoiler->scale(oldRecoiler->scale()); newEmitter->scale(sqr(showerScale)); emitted->scale(sqr(showerScale)); // Update the colour flow of the new outgoing particles // Note the emitted and newEmitter are already colour // connected by the powheg emission function emitted->incomingColour(oldEmitter, oldEmitter->id()<0); if ( newRecoiler->coloured() ) newRecoiler->incomingColour(oldRecoiler, oldRecoiler->id()<0); // Update the children of the outgoing oldRecoiler->addChild( newRecoiler ); oldEmitter->addChild( newEmitter ); oldEmitter->addChild( emitted ); // Note: The particles in the pert proc outgoing and both outgoing // vectors of the real emission proc are in the same order for(unsigned int ix=0;ixbornOutgoing().size();++ix) { // Update the decay process assert(process->outgoing()[ix].first == real->bornOutgoing()[ix]); process->outgoing()[ix].first = real->outgoing()[ix]; // Add the outgoing from the born // decay to the event intermediates intermediates().push_back(real->bornOutgoing()[ix]); } // Add the emitted to the outgoing of the decay process process->outgoing().push_back( { emitted, PerturbativeProcessPtr() } ); } // Else, if no emission above pTmin, set particle scales else { for(auto & outg : process->outgoing()) { outg.first->scale( sqr(showerScale) ); } powhegEmission = false; } } // No powheg emission occurred: else powhegEmission = false; } // No powheg emission occurred: else powhegEmission = false; } // No powheg emission occurred: else powhegEmission = false; } // Copy the outgoing from the decay // process to the event record for(auto const & outg : process->outgoing()) { if ( outg.first->coloured() ) outgoing().push_back(outg.first); else hard().push_back(outg.first); } return showerScale; } void DipoleEventRecord::updateDecayMom( PPtr decayParent, PerturbativeProcessPtr decayProc ) { // Only particles that have already been decayed // should be passed to this function assert( !(decayProc->outgoing().empty()) ); // Create a list of the children to update their momenta PList children; for ( auto const & outg : decayProc->outgoing() ) { children.push_back( outg.first ); } // Boost the children PList::iterator beginChildren = children.begin(); PList::iterator endChildren = children.end(); - ThePEG::UtilityBase::setMomentum(beginChildren, endChildren, decayParent->momentum().vect() ); + + const Momentum3 transformMom = decayParent->momentum().vect(); + Lorentz5Momentum sum = ThePEG::UtilityBase::sumMomentum(beginChildren, endChildren); + LorentzRotation rot = ThePEG::UtilityBase::transformToCMS(sum); + rot = ThePEG::UtilityBase::transformFromCMS + (Lorentz5Momentum(transformMom, sqrt(transformMom.mag2() + sum.m2()))) * rot; + + // Must transform the spinInfo using the momentum prior to transforming + for ( const auto& p : children ) { + if ( p->spinInfo() ) + p->spinInfo()->transform(p->momentum(),rot); + } + + ThePEG::UtilityBase::transform(beginChildren, endChildren, rot ); + } void DipoleEventRecord::updateDecayChainMom( PPtr decayParent, PerturbativeProcessPtr decayProc ) { // Note - this updates the momenta of the // outgoing of the given decay process // Update the momenta of the outgoing from this decay updateDecayMom( decayParent, decayProc ); // Iteratively update the momenta of the rest of the decay chain for ( auto & outg : decayProc->outgoing() ) { // If a child has a corresponding pert proc // then it has decay products if ( outg.second ) { for ( auto & dec : theDecays ) { - if(dec.second==outg.second) { - dec.first->setMomentum(outg.first->momentum()); + if ( dec.second == outg.second ) { + + // If the particle has spininfo + if ( dec.first->spinInfo() ) { + + // Copied from DipoleVertexRecord::updateSpinInfo, + // would be better to use a common function + // Update any spin information + const Lorentz5Momentum& oldMom = dec.first->momentum(); + const Lorentz5Momentum& newMom = outg.first->momentum(); + + // Rotation from old momentum to +ve z-axis + LorentzRotation oldToZAxis; + Axis axisOld(oldMom.vect().unit()); + if( axisOld.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axisOld.z()))); + oldToZAxis.rotate( -acos(axisOld.z()),Axis(-axisOld.y()/sinth,axisOld.x()/sinth,0.)); + } + + // Rotation from new momentum to +ve z-axis + LorentzRotation newToZAxis; + Axis axisNew(newMom.vect().unit()); + if( axisNew.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axisNew.z()))); + newToZAxis.rotate( -acos(axisNew.z()),Axis(-axisNew.y()/sinth,axisNew.x()/sinth,0.)); + } + + // Boost from old momentum to new momentum along z-axis + Lorentz5Momentum momOldRotated = oldToZAxis*Lorentz5Momentum(oldMom); + Lorentz5Momentum momNewRotated = newToZAxis*Lorentz5Momentum(newMom); + + Energy2 a = sqr(momOldRotated.z()) + sqr(momNewRotated.t()); + Energy2 b = 2.*momOldRotated.t()*momOldRotated.z(); + Energy2 c = sqr(momOldRotated.t()) - sqr(momNewRotated.t()); + double beta; + + // The rotated momentum should always lie along the +ve z-axis + if ( momOldRotated.z() > ZERO ) + beta = (-b + sqrt(sqr(b)-4.*a*c)) / 2. / a; + else + beta = (-b - sqrt(sqr(b)-4.*a*c)) / 2. / a; + + LorentzRotation boostOldToNew(0., 0., beta); + + // Total transform + LorentzRotation transform = (newToZAxis.inverse())*boostOldToNew*oldToZAxis; + + // Transform spin info and mom + dec.first->spinInfo()->transform(oldMom, transform); + } + + dec.first->setMomentum(outg.first->momentum()); break; } } // Iteratively update any decay products if ( !outg.second->outgoing().empty() ) updateDecayChainMom( outg.first, outg.second ); } } } void DipoleEventRecord::updateDecays(PerturbativeProcessPtr decayProc, bool iterate) { // Note - This does not update the momenta of the outgoing // of decayProc. // i.e. it is for use following the (non-)showering // of a decay when the daughter momentum are correct. // With iterate = true, this updates the rest of the decay chain. + + // Update the list of next decays + if ( decayProc == theCurrentDecay && !theNextDecays.empty() ) { + assert( theNextDecays.back() == decayProc->incoming()[0].first ); + theNextDecays.pop_back(); + } // Loop over the outgoing from this decay for ( auto & outg : decayProc->outgoing() ) { if ( outg.second && !outg.second->outgoing().empty() ) { // Outgoing particles which have already been decayed PPtr newDecayed = outg.first; PerturbativeProcessPtr newDecayProc = outg.second; // Update the outgoing momenta from this decay updateDecayMom( newDecayed, newDecayProc); // If this decay is already in theDecays then erase it for ( auto const & dec : theDecays ) { if(dec.second==newDecayProc) { theDecays.erase(dec.first); break; } } // Add to theDecays theDecays[newDecayed] = newDecayProc; - //assert(theDecays[newDecayed]->incoming()[0].second==decayProc); - + + // Update the list of next decays + if ( decayProc = theCurrentDecay ) + theNextDecays.push_back(newDecayed); + // Iteratively update theDecays from the decay chain if ( iterate ) updateDecays( newDecayProc ); } // Deal with any outgoing which need to be decayed else if ( ShowerHandler::currentHandler()->decaysInShower(outg.first->id()) ) { PerturbativeProcessPtr newDecay=new_ptr(PerturbativeProcess()); newDecay->incoming().push_back({ outg.first , decayProc } ); theDecays[outg.first] = newDecay; + // Update the list of next decays + if ( decayProc ) + theNextDecays.push_back(outg.first); + } } } void DipoleEventRecord::debugLastEvent(ostream& os) const { bool first = ShowerHandler::currentHandler()->firstInteraction(); os << "--- DipoleEventRecord ----------------------------------------------------------\n"; os << " the " << (first ? "hard" : "secondary") << " subprocess is:\n" << (*subProcess()); os << " using PDF's " << pdfs().first.pdf() << " and " << pdfs().second.pdf() << "\n"; os << " chains showering currently:\n"; for ( list::const_iterator chit = theChains.begin(); chit != theChains.end(); ++chit ) os << (*chit); os << " chains which finished showering:\n"; for ( list::const_iterator chit = theDoneChains.begin(); chit != theDoneChains.end(); ++chit ) os << (*chit); os << "--------------------------------------------------------------------------------\n"; os << flush; } diff --git a/Shower/Dipole/Base/DipoleEventRecord.h b/Shower/Dipole/Base/DipoleEventRecord.h --- a/Shower/Dipole/Base/DipoleEventRecord.h +++ b/Shower/Dipole/Base/DipoleEventRecord.h @@ -1,511 +1,719 @@ // -*- C++ -*- // // DipoleEventRecord.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_DipoleEventRecord_H #define HERWIG_DipoleEventRecord_H // // This is the declaration of the DipoleEventRecord class. // #include "Herwig/Shower/ShowerEventRecord.h" #include "Herwig/Shower/PerturbativeProcess.h" #include "ThePEG/PDF/PDF.h" #include "Dipole.h" #include "DipoleChain.h" +#include "Herwig/MatrixElement/Matchbox/Utility/DensityOperator.h" + +#include namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Johannes Bellm * * \brief Generalized dipole splitting info to deal with subleading-N * splittings. */ class SubleadingSplittingInfo : public DipoleSplittingInfo { public: /** * Default constructor */ SubleadingSplittingInfo() : DipoleSplittingInfo() {} /** * Get the iterator of the emitter dipole chain */ list::iterator emitterChain() const { return theEmitterChain; } /** * Get the iterator of the emitter dipole */ list::iterator emitterDipole() const { return theEmitterDipole; } /** * Get the iterator of the spectator dipole chain */ list::iterator spectatorChain() const { return theSpectatorChain; } /** * Get the iterator of the spectator dipole */ list::iterator spectatorDipole() const { return theSpectatorDipole; } /** * Get the starting scale */ Energy startScale() const { return theStartScale; } - + /** * Set the iterator of the emitter dipole chain */ void emitterChain(list::iterator it) { theEmitterChain = it; } /** * Set the iterator of the emitter dipole */ void emitterDipole(list::iterator it) { theEmitterDipole = it; } /** * Set the iterator of the spectator dipole chain */ void spectatorChain(list::iterator it) { theSpectatorChain = it; } /** * Set the iterator of the spectator dipole */ void spectatorDipole(list::iterator it) { theSpectatorDipole = it; } /** * Set the starting scale */ void startScale(Energy s) { theStartScale = s; } private: /** * Iterator of the emitter dipole chain */ list::iterator theEmitterChain; /** * Iterator of the emitter dipole */ list::iterator theEmitterDipole; /** * Iterator of the spectator dipole chain */ list::iterator theSpectatorChain; /** * Iterator of the spectator dipole */ list::iterator theSpectatorDipole; /** * The starting scale */ Energy theStartScale; }; /** * \ingroup DipoleShower * \author Simon Platzer, Stephen Webster * * \brief The DipoleEventRecord class is * used internally by the dipole shower. */ class DipoleEventRecord : public ShowerEventRecord { public: /** * The default constructor. */ DipoleEventRecord() {} /** * The default destructor just cleans up. */ ~DipoleEventRecord() { clear(); } public: /** * Return any non-coloured outgoing particles in the * current subprocess. */ PList& hard() { return theHard; } /** * Return any non-coloured outgoing particles in the * current subprocess. */ const PList& hard() const { return theHard; } /** * Return the momentum of the hard system */ const Lorentz5Momentum& pX() const { return thePX; } /** + * Return the particles after emission. + */ + cPDVector& particlesAfter() { return theParticlesAfter; } + + /** + * Return the particles after emission. + */ + const cPDVector& particlesAfter() const { return theParticlesAfter; } + + /** + * Return the particles before emission. + */ + cPDVector& particlesBefore() { return theParticlesBefore; } + + /** + * Return the particles before emission. + */ + const cPDVector& particlesBefore() const { return theParticlesBefore; } + + /** + * Return the momenta after emission. + */ + vector& momentaAfter() { return theMomentaAfter; } + + /** + * Return the momenta after emission. + */ + const vector& momentaAfter() const { return theMomentaAfter; } + + /** + * Return the dictionary for the particles + */ + map& particleIndices() { return theParticleIndices; } + + /** + * Return the dictionary for the particles + */ + const map& particleIndices() const { return theParticleIndices; } + + /** + * Return the density operator + */ + DensityOperator& densityOperator() { return theDensityOperator; } + + /** + * Return the density operator + */ + const DensityOperator& densityOperator() const { return theDensityOperator; } + + /** + * Set the subleading Nc flag and the number of emissions to calculate + * subleading Nc corrections for. + */ + void setSubleadingNc( bool doSub, size_t emissionsLimit ) { + doSubleadingNc = doSub; + continueSubleadingNc = doSub; + subleadingNcEmissionsLimit = emissionsLimit; + } + + /** + * Get the continue subleading Nc flag. + */ + bool getContinueSubleadingNc() const { return continueSubleadingNc; } + + /** + * Set the scheme and cutoff for the density operator evolution. + */ + void setDensityOperatorEvolution( int scheme, Energy2 cutoff ) { + densityOperatorEvolution = scheme; + densityOperatorCutoff = cutoff; + } + + /** + * Calculates the dipole kernel to use for the density operator evolution. Takes + * the index of the emitter and spectator, and momentum invariants. + */ + double dipoleKernelForEvolution(size_t em, size_t spec, + Energy2 pEmitpSpec, Energy2 pEmitpEmis, + Energy2 pEmispSpec); + + /** * Transform all intermediate, hard and outgoing - * partciles using the given transformation. + * particles using the given transformation. + * Also update their spinInfo if applicable. */ - void transform(const SpinOneLorentzRotation& rot); + void transform(const LorentzRotation& rot); public: /** * Return the dipole chains to be showered. */ const list& chains() const { return theChains; } /** * Access the dipole chains to be showered. */ list& chains() { return theChains; } /** * Return the dipole chains which ceased evolving. */ const list& doneChains() const { return theDoneChains; } /** * Access the dipole chains which ceased evolving. */ list& doneChains() { return theDoneChains; } /** * Return true, if there are chains to be * showered. */ bool haveChain() const { return !theChains.empty(); } /** * Return the current dipole chain */ DipoleChain& currentChain() { assert(haveChain()); return theChains.front(); } /** * Pop the current dipole chain */ void popChain(); /** * Remove the given chain. */ void popChain(list::iterator); /** * Remove the given chains. */ void popChains(const list::iterator>&); /** * Create a merged dipole index given two independent dipoles; * the first dipole is to provide the emitter. */ DipoleIndex mergeIndex(list::iterator firstDipole, const pair& whichFirst, list::iterator secondDipole, const pair& whichSecond) const; /** * Create a SubleadingSplitingInfo given two independent dipoles; * the first dipole is to provide the emitter. */ SubleadingSplittingInfo mergeSplittingInfo(list::iterator firstChain, list::iterator firstDipole, const pair& whichFirst, list::iterator secondChain, list::iterator secondDipole, const pair& whichSecond) const; /** * Return a list of all possible subleading-N emitting pairs */ void getSubleadingSplittings(list&); public: /** * Split the dipole pointed to by the given iterator. * Return references to the affected chains, and update * iterators pointing to the children in the returned * chains. */ void split(list::iterator dip, DipoleSplittingInfo& dsplit, pair::iterator,list::iterator>& childIterators, DipoleChain*& firstChain, DipoleChain*& secondChain) { split(dip,theChains.begin(),dsplit,childIterators,firstChain,secondChain,false); } /** * Split the dipole pointed to by the given iterator * in the indicated chain, indicating a splitting with * a colour spectator. * Return references to the affected chains, and update * iterators pointing to the children in the returned * chains. */ void split(list::iterator dip, list::iterator ch, DipoleSplittingInfo& dsplit, pair::iterator,list::iterator>& childIterators, DipoleChain*& firstChain, DipoleChain*& secondChain, bool colourSpectator = true); /** * As split, but not touching the acctual event record. */ pair tmpsplit(list::iterator dip, DipoleSplittingInfo& dsplit, pair::iterator,list::iterator>& childIterators, DipoleChain*& firstChain, DipoleChain*& secondChain) { return tmpsplit(dip,theChains.begin(),dsplit,childIterators,firstChain,secondChain,false); } /** * As split, but not touching the acctual event record. */ pair tmpsplit(list::iterator dip, list::iterator ch, DipoleSplittingInfo& dsplit, pair::iterator,list::iterator>& childIterators, DipoleChain*& firstChain, DipoleChain*& secondChain, bool colourSpectator = true); /** * Let the given dipole take the recoil of * the indicated splitting. */ void recoil(list::iterator dip, list::iterator ch, DipoleSplittingInfo& dsplit); /** * Peform a subleading-N splitting */ void splitSubleading(SubleadingSplittingInfo& dsplit, pair::iterator,list::iterator>& childIterators, DipoleChain*& firstChain, DipoleChain*& secondChain); /** * Update the particles upon insertion of the * given splitting. */ void update(DipoleSplittingInfo& dsplit); /** * As update, but not touching the acctual event record. */ pair tmpupdate(DipoleSplittingInfo& dsplit); /** + * Inverse of update, updateInverse(update(dsplit)) would return the + * event record to the state if update would not have been called. + */ + void updateInverse(DipoleSplittingInfo& dsplit); + + /** * Return the dipole(s) containing the incoming * partons after the evolution has ended. Put back * the chains containing these to the chains to be * showered. */ list::iterator,list::iterator> > inDipoles(); /** * Fill the given step and return incoming partons. */ tPPair fillEventRecord(StepPtr step, bool firstInteraction, bool realigned); public: /** * Prepare the event record for the given * subprocess. */ const map& prepare(tSubProPtr subpro, tStdXCombPtr xc, StepPtr step, const pair& pdf, tPPair beam, bool firstInteraction, const set& offShellPartons, bool dipoles = true); /** * Prepare the event record for the given * subprocess. */ void slimprepare(tSubProPtr subpro, tStdXCombPtr xc, const pair& pdf,tPPair beam, const set& offShellPartons, bool dipoles = true); /** * Clear the event record: Give up ownership * on any object involved in the evolution. */ virtual void clear(); + /** + * Prepare the dipole chains for the eventRecord after + * the subleading shower + */ + void prepareChainsSubleading(const bool decay) { + static set empty; + continueSubleadingNc = false; + PList cordered = colourOrdered(incoming(),outgoing()); + findChains(cordered,empty,decay); + } + public: /** * Print event record at current state. */ void debugLastEvent(ostream&) const; public: /** * Get the decays */ map & decays() {return theDecays;} /** * Used in DipoleEventRecord::prepare. * Add the outgoing particles from a perturbative * process to the vector of original particles. * Iterates through decay chains. **/ void fillFromDecays(PerturbativeProcessPtr decayProc, vector& original); /** * Used in DipoleEventRecord::prepare. * Replace the particles in the given * perturbative process with their copies from the * map, theOriginals. * Iterates through decays chains. **/ void separateDecay(PerturbativeProcessPtr decayProc); /** * Decay the particle */ Energy decay(PPtr incoming, bool& powhegEmission); /** * Prepare the event record for the showering of a decay. * Return false if the decay does not need to be showered. **/ bool prepareDecay(PerturbativeProcessPtr decayProc, const set& offShellPartons); /** * Boost the momentum of the outgoing of the given * perturbative process to the momentum of given particle. **/ void updateDecayMom(PPtr decayParent, PerturbativeProcessPtr decayProc); /** * Iteratively update the momenta of all * particles in a decay chain, starting * with the outgoing from the given parent **/ void updateDecayChainMom(PPtr decayParent, PerturbativeProcessPtr decayProc); /** * Update theDecays following the decay and/or * showering of a decay particle. * With iteration switched on (true) this will * update theDecays with the entire decay chain. `**/ void updateDecays(PerturbativeProcessPtr decayProc, bool iterate = true); /** * Access current decay process */ PerturbativeProcessPtr currentDecay() {return theCurrentDecay;} /** * Set current decay process */ void currentDecay(PerturbativeProcessPtr in) {theCurrentDecay=in;} + /** + * Return the next particle to be decayed. + */ + PPtr nextDecay() { + if ( !theNextDecays.empty() ) + return theNextDecays.back(); + else + return PPtr(); + } + // SW - Changed from protected to public so that functions can be used in DipoleShowerHandler public: /** * Find the chains to be showered. * The decay bool avoids mixing up decaying particles in hard and decay processes */ void findChains(const PList& ordered, const set& offShellpartons, const bool decay = false); /** * Sort the coloured partons into a colour ordered ensemble. */ PList colourOrdered(PPair & in,PList & out); private: struct getMomentum { const Lorentz5Momentum& operator() (PPtr particle) const { return particle->momentum(); } }; /** * The momentum of the hard system */ Lorentz5Momentum thePX; /** * Any non-coloured outgoing particles in the * current subprocess. */ PList theHard; /** * Map originals to copies. */ map theOriginals; /** * The dipole chains currently showered. */ list theChains; /** * The dipole chains which ceased evolving. */ list theDoneChains; + + /** + * Particles after the emission. + */ + cPDVector theParticlesAfter; + + /** + * Particles before the emission. + */ + cPDVector theParticlesBefore; + + /** + * Momenta of the particles after emission. + */ + vector theMomentaAfter; + + /** + * Pair of the emitters index before emission and both the emitters + * and the emissions indices after emission, + * > + */ + pair > theEmitterEmissionIndices; + + /** + * Pair of the spectators index before and after emission. + */ + pair theSpectatorIndices; + + /** + * The density operator. + */ + DensityOperator theDensityOperator; + + /** + * Switch on or off for subleading Nc corrections. + */ + bool doSubleadingNc; + + /** + * Flag to keep track of when to stop calculating colour + * matrix element corrections. + */ + bool continueSubleadingNc; + + /** + * Number of emissions to calculate subleading Nc corrections for. + */ + size_t subleadingNcEmissionsLimit; + + /** + * Current number of subleading emissions. + */ + size_t subEmDone; + + + /** + * Dictionary for particles before emission. + */ + map theParticleIndices; + + /** + * Integer used to set which method of evolving the density operator + * to use: + * 0 - Vijk is Eikonal but there is a cutoff. + * 1 - Vijk is Eikonal. + * 2 - Vijk=1 for all i,j,k. + * 3 - Semi-leading Nc, Vijk=0 for all + */ + int densityOperatorEvolution; + + /** + * Cutoff scale for the invariants (e.g. pEmitter*pEmission) in the + * Eikonal dipole kernel, Vijk. + */ + Energy2 densityOperatorCutoff; + + /** + * Dictionary for emissions, maps the three indices + * - emitter before emission + * - emitter after emission + * - emission + * to another map of the indices before emission mapped to the indices after + * emission (except the emitters index) + */ + map,map > theEmissionsMap; /** * The coloured partons that can be off-shell * To only be filled by DipoleShowerHandler. **/ private: /** * Storage of the particles which need to be decayed */ map theDecays; /** * */ PerturbativeProcessPtr theCurrentDecay; + + /** + * List of unstable particles, the decay to be performed + * and/or showered next is always at the back. + * This list is required to force the dipole shower to shower one decay + * chain at a time, without jumping between decay chains. + * Note this is not *required* but this is the most + * obvious/intuitive way to treat the decays. + **/ + PList theNextDecays; + }; } #endif /* HERWIG_DipoleEventRecord_H */ diff --git a/Shower/Dipole/Base/DipoleSplittingGenerator.cc b/Shower/Dipole/Base/DipoleSplittingGenerator.cc --- a/Shower/Dipole/Base/DipoleSplittingGenerator.cc +++ b/Shower/Dipole/Base/DipoleSplittingGenerator.cc @@ -1,875 +1,910 @@ // -*- C++ -*- // // DipoleSplittingGenerator.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 DipoleSplittingGenerator class. // #include #include "DipoleSplittingGenerator.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Reference.h" #include "ThePEG/Repository/EventGenerator.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "Herwig/Shower/Dipole/DipoleShowerHandler.h" +#include "ThePEG/Repository/UseRandom.h" using namespace Herwig; DipoleSplittingGenerator::DipoleSplittingGenerator() : HandlerBase(), theExponentialGenerator(0), prepared(false), presampling(false), theDoCompensate(false), theSplittingWeight(1.) { if ( ShowerHandler::currentHandler() ) setGenerator(ShowerHandler::currentHandler()->generator()); } DipoleSplittingGenerator::~DipoleSplittingGenerator() { if ( theExponentialGenerator ) { delete theExponentialGenerator; theExponentialGenerator = 0; } } IBPtr DipoleSplittingGenerator::clone() const { return new_ptr(*this); } IBPtr DipoleSplittingGenerator::fullclone() const { return new_ptr(*this); } void DipoleSplittingGenerator::wrap(Ptr::ptr other) { assert(!prepared); theOtherGenerator = other; } void DipoleSplittingGenerator::resetVariations() { for ( map::iterator w = currentWeights.begin(); w != currentWeights.end(); ++w ) w->second = 1.; } void DipoleSplittingGenerator::veto(const vector&, double p, double r) { double factor = 1.; if ( splittingReweight() ) { if ( ( ShowerHandler::currentHandler()->firstInteraction() && splittingReweight()->firstInteraction() ) || ( !ShowerHandler::currentHandler()->firstInteraction() && splittingReweight()->secondaryInteractions() ) ) { - factor = splittingReweight()->evaluate(generatedSplitting); - theSplittingWeight *= (r-factor*p)/(r-p); + if ( !splittingReweight()->hintOnly(generatedSplitting) ) { + factor = + splittingReweight()->evaluate(generatedSplitting)/splittingReweight()->hint(generatedSplitting); + theSplittingWeight *= (r-factor*p)/(r-p); + theSplittingWeightVector.push_back(std::make_tuple(generatedSplitting.lastPt(),(r-factor*p)/(r-p),false)); + } } } splittingKernel()->veto(generatedSplitting, factor*p, r, currentWeights); } void DipoleSplittingGenerator::accept(const vector&, double p, double r) { double factor = 1.; if ( splittingReweight() ) { if ( ( ShowerHandler::currentHandler()->firstInteraction() && splittingReweight()->firstInteraction() ) || ( !ShowerHandler::currentHandler()->firstInteraction() && splittingReweight()->secondaryInteractions() ) ) { - factor = splittingReweight()->evaluate(generatedSplitting); - theSplittingWeight *= factor; + if ( !splittingReweight()->hintOnly(generatedSplitting) ) { + factor = + splittingReweight()->evaluate(generatedSplitting)/splittingReweight()->hint(generatedSplitting); + theSplittingWeight *= factor; + theSplittingWeightVector.push_back(std::make_tuple(generatedSplitting.lastPt(),factor,true)); + } else { + theSplittingWeightVector.push_back(std::make_tuple(generatedSplitting.lastPt(),1.0,true)); + } } } splittingKernel()->accept(generatedSplitting, factor*p, r, currentWeights); } void DipoleSplittingGenerator::prepare(const DipoleSplittingInfo& sp) { generatedSplitting = sp; generatedSplitting.splittingKinematics(splittingKernel()->splittingKinematics()); + // The splitting kernel is needed for spin correlations + generatedSplitting.splittingKernel(splittingKernel()); generatedSplitting.splittingParameters().resize(splittingKernel()->nDimAdditional()); if ( wrapping() ) { generatedSplitting.emitterData(theSplittingKernel->emitter(generatedSplitting.index())); generatedSplitting.spectatorData(theSplittingKernel->spectator(generatedSplitting.index())); generatedSplitting.emissionData(theSplittingKernel->emission(generatedSplitting.index())); parameters.resize(theOtherGenerator->nDim()); prepared = true; return; } generatedSplitting.emitterData(splittingKernel()->emitter(generatedSplitting.index())); generatedSplitting.spectatorData(splittingKernel()->spectator(generatedSplitting.index())); generatedSplitting.emissionData(splittingKernel()->emission(generatedSplitting.index())); presampledSplitting = generatedSplitting; prepared = true; parameters.resize(nDim()); theExponentialGenerator = new exsample::exponential_generator(); theExponentialGenerator->sampling_parameters().maxtry = maxtry(); theExponentialGenerator->sampling_parameters().presampling_points = presamplingPoints(); theExponentialGenerator->sampling_parameters().freeze_grid = freezeGrid(); theExponentialGenerator->detuning(detuning()); theExponentialGenerator->docompensate(theDoCompensate); theExponentialGenerator->function(this); theExponentialGenerator->initialize(); } void DipoleSplittingGenerator::fixParameters(const DipoleSplittingInfo& sp, Energy optHardPt) { assert(generator()); assert(!presampling); assert(prepared); assert(sp.index() == generatedSplitting.index()); generatedSplitting.scale(sp.scale()); // If dealing with a decay, need to set recoilMass if ( generatedSplitting.index().incomingDecaySpectator() || generatedSplitting.index().incomingDecayEmitter() ) generatedSplitting.recoilMass(sp.recoilMass()); // Need to copy emitter and spectator masses generatedSplitting.emitterMass(sp.emitterMass()); generatedSplitting.spectatorMass(sp.spectatorMass()); // Counter to track if there is an off-shell // emitter AND/OR spectator int count = parameters.size()-1; // Off shell spectator mass if ( sp.index().offShellSpectator() ) { parameters[count] = sp.spectatorMass()/generator()->maximumCMEnergy(); count -= 1; } // Off shell emitter mass if ( sp.index().offShellEmitter() ) parameters[count] = sp.emitterMass()/generator()->maximumCMEnergy(); // If not a decay, point[3] samples over the dipole scale if ( !sp.index().incomingDecaySpectator() && !sp.index().incomingDecayEmitter() ) parameters[3] = sp.scale()/generator()->maximumCMEnergy(); // If it is a decay, point[3] samples over the recoilMass else parameters[3] = sp.recoilMass()/generator()->maximumCMEnergy(); generatedSplitting.hardPt(sp.hardPt()); parameters[0] = splittingKinematics()->ptToRandom(optHardPt == ZERO ? generatedSplitting.hardPt() : min(generatedSplitting.hardPt(),optHardPt), sp.scale(), sp.emitterX(), sp.spectatorX(), generatedSplitting.index(), *splittingKernel()); size_t shift = 4; if ( generatedSplitting.index().emitterPDF().pdf() && generatedSplitting.index().spectatorPDF().pdf() ) { generatedSplitting.emitterX(sp.emitterX()); generatedSplitting.spectatorX(sp.spectatorX()); parameters[4] = sp.emitterX(); parameters[5] = sp.spectatorX(); shift += 2; } if ( generatedSplitting.index().emitterPDF().pdf() && !generatedSplitting.index().spectatorPDF().pdf() ) { generatedSplitting.emitterX(sp.emitterX()); parameters[4] = sp.emitterX(); ++shift; } if ( !generatedSplitting.index().emitterPDF().pdf() && generatedSplitting.index().spectatorPDF().pdf() ) { generatedSplitting.spectatorX(sp.spectatorX()); parameters[4] = sp.spectatorX(); ++shift; } if ( splittingKernel()->nDimAdditional() ) copy(sp.lastSplittingParameters().begin(), sp.lastSplittingParameters().end(), parameters.begin()+shift); if ( sp.emitter() ) generatedSplitting.emitter(sp.emitter()); if ( sp.spectator() ) generatedSplitting.spectator(sp.spectator()); } int DipoleSplittingGenerator::nDim() const { assert(!wrapping()); assert(prepared); // Note this use of [3] for either the scale or the recoil mass // is a bit of a nasty hack. int ret = 4; // 0 pt, 1 z, 2 phi, 3 scale or recoilMass, 4/5 xs + parameters if ( generatedSplitting.index().emitterPDF().pdf() ) { ++ret; } if ( generatedSplitting.index().spectatorPDF().pdf() ) { ++ret; } ret += splittingKernel()->nDimAdditional(); assert(splittingKernel()->nDimAdditional() == 0); // Put off-shell spectator mass at back [-1] // followed by off-shell emitter mass (i.e. [-1] or [-2]) // Off-shell emitter if ( generatedSplitting.index().offShellEmitter() ) ++ret; // Off-shell spectator if ( generatedSplitting.index().offShellSpectator() ) ++ret; return ret; } const vector& DipoleSplittingGenerator::sampleFlags() { assert(!wrapping()); if ( !theFlags.empty() ) return theFlags; theFlags.resize(nDim(),false); theFlags[0] = true; theFlags[1] = true; theFlags[2] = true; // 0 pt, 1 z, 2 phi return theFlags; } const pair,vector >& DipoleSplittingGenerator::support() { assert(!wrapping()); if ( !theSupport.first.empty() ) return theSupport; vector lower(nDim(),0.); vector upper(nDim(),1.); pair kSupport = generatedSplitting.splittingKinematics()->kappaSupport(generatedSplitting); pair xSupport = generatedSplitting.splittingKinematics()->xiSupport(generatedSplitting); lower[0] = kSupport.first; lower[1] = xSupport.first; upper[0] = kSupport.second; upper[1] = xSupport.second; theSupport.first = lower; theSupport.second = upper; return theSupport; } void DipoleSplittingGenerator::startPresampling() { assert(!wrapping()); splittingKernel()->startPresampling(generatedSplitting.index()); presampling = true; } void DipoleSplittingGenerator::stopPresampling() { assert(!wrapping()); splittingKernel()->stopPresampling(generatedSplitting.index()); presampling = false; } bool DipoleSplittingGenerator::haveOverestimate() const { assert(!wrapping()); assert(prepared); return generatedSplitting.splittingKinematics()->haveOverestimate() && splittingKernel()->haveOverestimate(generatedSplitting); } double DipoleSplittingGenerator::overestimate(const vector& point) { assert(!wrapping()); assert(prepared); assert(!presampling); assert(haveOverestimate()); if ( ! generatedSplitting.splittingKinematics()->generateSplitting(point[0],point[1],point[2], generatedSplitting, *splittingKernel()) ) return 0.; generatedSplitting.splittingKinematics()->prepareSplitting(generatedSplitting); return ( generatedSplitting.splittingKinematics()->jacobianOverestimate() * splittingKernel()->overestimate(generatedSplitting) ); } double DipoleSplittingGenerator::invertOverestimateIntegral(double value) const { assert(!wrapping()); assert(prepared); assert(!presampling); assert(haveOverestimate()); return splittingKernel()->invertOverestimateIntegral(generatedSplitting,value); } double DipoleSplittingGenerator::evaluate(const vector& point) { assert(!wrapping()); assert(prepared); assert(generator()); DipoleSplittingInfo& split = ( !presampling ? generatedSplitting : presampledSplitting ); split.continuesEvolving(); size_t shift = 4; if ( presampling ) { // Counter to track if there is an off-shell // emitter AND/OR spectator int count = parameters.size()-1; // Sample over off-shell emitter and spectator masss // Do not sample if zero mass or off-shell if ( split.index().spectatorData()->mass() != ZERO ) { if ( !split.index().offShellSpectator() ) split.spectatorMass(split.index().spectatorData()->mass()); else { split.spectatorMass(point[count] * generator()->maximumCMEnergy()); count -= 1; } } if ( split.index().emitterData()->mass() != ZERO ) { if ( !split.index().offShellEmitter() ) split.emitterMass(split.index().emitterData()->mass()); else split.emitterMass(point[count] * generator()->maximumCMEnergy()); } // If not a decay, point[3] samples over the dipole scale if ( ! split.index().incomingDecaySpectator() && ! split.index().incomingDecayEmitter() ) split.scale(point[3] * generator()->maximumCMEnergy()); // For dipoles containing a decayed spectator: // 1) Use point[3] to sample over the recoil mass // 2) The dipole scale is the spectator mass else if ( split.index().incomingDecaySpectator() ) { split.recoilMass(point[3] * generator()->maximumCMEnergy()); assert(split.spectatorMass() != ZERO ); split.scale(split.spectatorMass()); } // Not currently intended to work with decaying emitters else assert(false); if ( split.index().emitterPDF().pdf() && split.index().spectatorPDF().pdf() ) { split.emitterX(point[4]); split.spectatorX(point[5]); shift += 2; } if ( split.index().emitterPDF().pdf() && !split.index().spectatorPDF().pdf() ) { split.emitterX(point[4]); ++shift; } if ( !split.index().emitterPDF().pdf() && split.index().spectatorPDF().pdf() ) { split.spectatorX(point[4]); ++shift; } if ( splittingKernel()->nDimAdditional() ) copy(point.begin()+shift,point.end(),split.splittingParameters().begin()); split.hardPt(split.splittingKinematics()->ptMax(split.scale(), split.emitterX(), split.spectatorX(), split, *splittingKernel())); } if ( ! split.splittingKinematics()->generateSplitting(point[0], point[1], point[2], split, *splittingKernel()) ) { split.lastValue(0.); return 0.; } split.splittingKinematics()->prepareSplitting(split); if ( split.stoppedEvolving() ) { split.lastValue(0.); return 0.; } if ( !presampling ) splittingKernel()->clearAlphaPDFCache(); double kernel = splittingKernel()->evaluate(split); double jac = split.splittingKinematics()->jacobian(); // multiply in the profile scales when relevant assert(ShowerHandler::currentHandler()); if ( ShowerHandler::currentHandler()->firstInteraction() && ShowerHandler::currentHandler()->profileScales() && !presampling ) { Energy hard = ShowerHandler::currentHandler()->hardScale(); if ( hard > ZERO ) kernel *= ShowerHandler::currentHandler()->profileScales()-> hardScaleProfile(hard,split.lastPt()); } split.lastValue( abs(jac) * kernel ); if ( ! isfinite(split.lastValue()) ) { generator()->log() << "DipoleSplittingGenerator:evaluate():" <<"problematic splitting kernel encountered for " << splittingKernel()->name() << "\n" << flush; split.lastValue(0.0); } if ( kernel < 0. ) return 0.; return split.lastValue(); } void DipoleSplittingGenerator::doGenerate(map& variations, Energy optCutoff) { assert(!wrapping()); double res = 0.; Energy startPt = generatedSplitting.hardPt(); double optKappaCutoff = 0.0; if ( optCutoff > splittingKinematics()->IRCutoff() ) { optKappaCutoff = splittingKinematics()->ptToRandom(optCutoff, generatedSplitting.scale(), generatedSplitting.emitterX(), generatedSplitting.spectatorX(), generatedSplitting.index(), *splittingKernel()); } resetVariations(); theSplittingWeight = 1.; double enhance = 1.; + bool detuningOff = false; if ( splittingReweight() ) { if ( ( ShowerHandler::currentHandler()->firstInteraction() && splittingReweight()->firstInteraction() ) || ( !ShowerHandler::currentHandler()->firstInteraction() && splittingReweight()->secondaryInteractions() ) ) { enhance = splittingReweight()->hint(generatedSplitting); + if ( splittingReweight()->hintOnly(generatedSplitting) ) + detuningOff = true; } } + bool hintOnly = false; + if ( splittingReweight() ) hintOnly = splittingReweight()->hintOnly(generatedSplitting); while (true) { + theExponentialGenerator->detuning(detuning()); + if ( detuningOff ) + theExponentialGenerator->detuning(1.0); try { if ( optKappaCutoff == 0.0 ) { + theSplittingWeightVector.clear(); res = theExponentialGenerator->generate(enhance); } else { + theSplittingWeightVector.clear(); res = theExponentialGenerator->generate(optKappaCutoff,enhance); } + //Partial unweighting + if ( partialUnweighting && !hintOnly ) { + if ( abs(theSplittingWeight)/theReferenceWeight < 1.0 ) { + double r = UseRandom::rnd(1.0); + if ( abs(theSplittingWeight)/theReferenceWeight < r ) { + theSplittingWeight = 1.; + continue; + } else { + theSplittingWeight = theSplittingWeight/abs(theSplittingWeight)*theReferenceWeight; + } + } + } } catch (exsample::exponential_regenerate&) { resetVariations(); theSplittingWeight = 1.; generatedSplitting.hardPt(startPt); continue; } catch (exsample::hit_and_miss_maxtry&) { throw DipoleShowerHandler::RedoShower(); } catch (exsample::selection_maxtry&) { throw DipoleShowerHandler::RedoShower(); } break; } for ( map::const_iterator w = currentWeights.begin(); w != currentWeights.end(); ++w ) { map::iterator v = variations.find(w->first); if ( v != variations.end() ) v->second *= w->second; else variations[w->first] = w->second; } if ( res == 0. ) { generatedSplitting.lastPt(0.0*GeV); generatedSplitting.didStopEvolving(); } else { generatedSplitting.continuesEvolving(); if ( theMCCheck ) theMCCheck->book(generatedSplitting.emitterX(), generatedSplitting.spectatorX(), generatedSplitting.scale(), startPt, generatedSplitting.lastPt(), generatedSplitting.lastZ(), 1.); } } Energy DipoleSplittingGenerator::generate(const DipoleSplittingInfo& split, map& variations, Energy optHardPt, Energy optCutoff) { fixParameters(split,optHardPt); if ( wrapping() ) { return theOtherGenerator->generateWrapped(generatedSplitting,variations,optHardPt,optCutoff); } doGenerate(variations,optCutoff); return generatedSplitting.lastPt(); } double DipoleSplittingGenerator::sudakovExpansion(const DipoleSplittingInfo& split, Energy down,Energy fixedScale){ fixParameters(split); if ( wrapping() ) { return theOtherGenerator->wrappedSudakovExpansion( generatedSplitting, down,fixedScale); } return dosudakovExpansion( split, down,fixedScale); } double DipoleSplittingGenerator::sudakov(const DipoleSplittingInfo& split,Energy down){ fixParameters(split); if ( wrapping() ) { return theOtherGenerator->wrappedSudakov( generatedSplitting, down); } return dosudakov( split, down); } double DipoleSplittingGenerator::dosudakovExpansion(const DipoleSplittingInfo& , Energy down,Energy fixedScale){ assert(down > splittingKinematics()->IRCutoff()); double optKappaCutoffd = splittingKinematics()->ptToRandom(down, generatedSplitting.scale(), generatedSplitting.emitterX(), generatedSplitting.spectatorX(), generatedSplitting.index(), *splittingKernel()); double optKappaCutoffu = splittingKinematics()->ptToRandom(generatedSplitting.hardPt(), generatedSplitting.scale(), generatedSplitting.emitterX(), generatedSplitting.spectatorX(), generatedSplitting.index(), *splittingKernel()); pair xSupport = generatedSplitting.splittingKinematics()->xiSupport(generatedSplitting); vector RN; RN.resize(3); double res=0.; double resq=0.; double varx=10.; int k=0; generatedSplitting.setCalcFixedExpansion(true); generatedSplitting.fixedScale(fixedScale); while ( k<1000 ){ k+=1.; RN[0]= optKappaCutoffd+(optKappaCutoffu-optKappaCutoffd)*UseRandom::rnd(); //PT RN[1]=xSupport.first+UseRandom::rnd()*(xSupport.second-xSupport.first); // RN[2]= UseRandom::rnd(); //PHI double tmp=(xSupport.second-xSupport.first)* (optKappaCutoffu-optKappaCutoffd)* evaluate(RN); res+= tmp; resq+=pow(tmp,2.); if(k%50==0.){ varx=sqrt((resq/pow(1.*k,2)-pow(res,2)/pow(1.*k,3)))/(res/(1.0*k)); if(varxptToRandom(down, generatedSplitting.scale(), generatedSplitting.emitterX(), generatedSplitting.spectatorX(), generatedSplitting.index(), *splittingKernel()); double optKappaCutoffu = splittingKinematics()->ptToRandom(generatedSplitting.hardPt(), generatedSplitting.scale(), generatedSplitting.emitterX(), generatedSplitting.spectatorX(), generatedSplitting.index(), *splittingKernel()); pair kSupport = generatedSplitting.splittingKinematics()->kappaSupport(generatedSplitting); assert(kSupport.first==0&&kSupport.second==1); pair xSupport = generatedSplitting.splittingKinematics()->xiSupport(generatedSplitting); vector RN; RN.resize(3); double res=0.; double resq=0.; double var=10.; double varx=10.; int k=0; while (((k<40.||var>theSudakovAccuracy)&&k<50000)){ k+=1.; RN[0]= optKappaCutoffd+(optKappaCutoffu-optKappaCutoffd)*UseRandom::rnd(); //PT RN[1]=xSupport.first+UseRandom::rnd()*(xSupport.second-xSupport.first); //Z RN[2]=UseRandom::rnd(); //PHI double tmp=(xSupport.second-xSupport.first)* (optKappaCutoffu-optKappaCutoffd)* evaluate(RN); res+= tmp; resq+=pow(tmp,2.); if(k%20==0.){ varx=sqrt((resq/pow(1.*k,2)-pow(res,2)/pow(1.*k,3))); var= (exp(-(res)/(1.0*k)+varx)-exp(-(res)/(1.0*k)-varx))/exp(-res/(1.0*k)); } } return exp(-res/(1.0*k)); } double DipoleSplittingGenerator::wrappedSudakovExpansion(DipoleSplittingInfo& split, Energy down,Energy fixedScale) { assert(!wrapping()); DipoleSplittingInfo backup = generatedSplitting; generatedSplitting = split; fixParameters(split); double res=dosudakovExpansion( split, down,fixedScale); split = generatedSplitting; generatedSplitting = backup; return res; } double DipoleSplittingGenerator::wrappedSudakov(DipoleSplittingInfo& split, Energy down) { assert(!wrapping()); DipoleSplittingInfo backup = generatedSplitting; generatedSplitting = split; fixParameters(split); double res=dosudakov( split, down); split = generatedSplitting; generatedSplitting = backup; return res; } Energy DipoleSplittingGenerator::generateWrapped(DipoleSplittingInfo& split, map& variations, Energy optHardPt, Energy optCutoff) { assert(!wrapping()); DipoleSplittingInfo backup = generatedSplitting; generatedSplitting = split; fixParameters(split,optHardPt); try { doGenerate(variations,optCutoff); } catch (...) { split = generatedSplitting; generatedSplitting = backup; throw; } Energy pt = generatedSplitting.lastPt(); split = generatedSplitting; generatedSplitting = backup; return pt; } void DipoleSplittingGenerator::completeSplitting(DipoleSplittingInfo& sp) const { pair conf = sp.configuration(); sp = generatedSplitting; sp.configuration(conf); } Ptr::tptr DipoleSplittingGenerator::splittingKernel() const { if ( wrapping() ) return theOtherGenerator->splittingKernel(); return theSplittingKernel; } Ptr::tptr DipoleSplittingGenerator::splittingReweight() const { if ( wrapping() ) return theOtherGenerator->splittingReweight(); return theSplittingReweight; } Ptr::tptr DipoleSplittingGenerator::splittingKinematics() const { if ( wrapping() ) return theOtherGenerator->splittingKinematics(); return theSplittingKernel->splittingKinematics(); } void DipoleSplittingGenerator::splittingKernel(Ptr::tptr sp) { theSplittingKernel = sp; if ( theSplittingKernel->mcCheck() ) theMCCheck = theSplittingKernel->mcCheck(); } void DipoleSplittingGenerator::splittingReweight(Ptr::tptr sp) { theSplittingReweight = sp; } void DipoleSplittingGenerator::debugGenerator(ostream& os) const { os << "--- DipoleSplittingGenerator ---------------------------------------------------\n"; os << " generating splittings using\n" << " splittingKernel = " << splittingKernel()->name() << " splittingKinematics = " << generatedSplitting.splittingKinematics()->name() << "\n" << " to sample splittings of type:\n"; os << generatedSplitting; os << "--------------------------------------------------------------------------------\n"; } void DipoleSplittingGenerator::debugLastEvent(ostream& os) const { os << "--- DipoleSplittingGenerator ---------------------------------------------------\n"; os << " last generated event:\n"; os << generatedSplitting; os << "--------------------------------------------------------------------------------\n"; } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void DipoleSplittingGenerator::persistentOutput(PersistentOStream & os) const { os << theOtherGenerator << theSplittingKernel << theSplittingReweight << theMCCheck << theDoCompensate; } void DipoleSplittingGenerator::persistentInput(PersistentIStream & is, int) { is >> theOtherGenerator >> theSplittingKernel >> theSplittingReweight >> theMCCheck >> theDoCompensate; } ClassDescription DipoleSplittingGenerator::initDipoleSplittingGenerator; // Definition of the static class description member. void DipoleSplittingGenerator::Init() { static ClassDocumentation documentation ("DipoleSplittingGenerator is used by the dipole shower " "to sample splittings from a given dipole splitting kernel."); static Reference interfaceSplittingKernel ("SplittingKernel", "Set the splitting kernel to sample from.", &DipoleSplittingGenerator::theSplittingKernel, false, false, true, false, false); static Reference interfaceSplittingReweight ("SplittingReweight", "Set the splitting reweight.", &DipoleSplittingGenerator::theSplittingReweight, false, false, true, true, false); static Reference interfaceMCCheck ("MCCheck", "[debug option] MCCheck", &DipoleSplittingGenerator::theMCCheck, false, false, true, true, false); interfaceMCCheck.rank(-1); } diff --git a/Shower/Dipole/Base/DipoleSplittingGenerator.h b/Shower/Dipole/Base/DipoleSplittingGenerator.h --- a/Shower/Dipole/Base/DipoleSplittingGenerator.h +++ b/Shower/Dipole/Base/DipoleSplittingGenerator.h @@ -1,513 +1,548 @@ // -*- C++ -*- // // DipoleSplittingGenerator.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_DipoleSplittingGenerator_H #define HERWIG_DipoleSplittingGenerator_H // // This is the declaration of the DipoleSplittingGenerator class. // #include "ThePEG/Handlers/HandlerBase.h" #include "Herwig/Shower/Dipole/Kernels/DipoleSplittingKernel.h" #include "DipoleSplittingReweight.h" #include "Herwig/Shower/Dipole/Utility/DipoleMCCheck.h" #include "Herwig/Sampling/exsample/exponential_generator.h" +#include + namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Johannes Bellm * * \brief DipoleSplittingGenerator is used by the dipole shower * to sample splittings from a given dipole splitting kernel. * * @see \ref DipoleSplittingGeneratorInterfaces "The interfaces" * defined for DipoleSplittingGenerator. */ class DipoleSplittingGenerator: public HandlerBase { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ DipoleSplittingGenerator(); /** * The destructor. */ virtual ~DipoleSplittingGenerator(); //@} public: /** * Return the dipole splitting kernel. */ Ptr::tptr splittingKernel() const; /** * Return the dipole splitting reweight. */ Ptr::tptr splittingReweight() const; /** * Return the dipole splitting kinematics. */ Ptr::tptr splittingKinematics() const; /** * Set the dipole splitting kernel. */ void splittingKernel(Ptr::tptr sp); /** * Set the dipole splitting reweight. */ void splittingReweight(Ptr::tptr sp); /** * Make a wrapper around another generator. */ void wrap(Ptr::ptr other); /** * Return true, if this is actually a wrapper around * another splitting generator. */ bool wrapping() const { return theOtherGenerator; } public: /** * Reset the current variations to one */ void resetVariations(); /** * Prepare to fill the given splitting. */ void prepare(const DipoleSplittingInfo&); /** * Fix parameters from the given DipoleSplittingInfo * and generate the next splitting. Return the * pt selected for the next splitting. */ Energy generate(const DipoleSplittingInfo&, map& variations, Energy optHardPt = ZERO, Energy optCutoff = ZERO); /** * Fix parameters from the fiven DipoleSplittingInfo * and generate the next splitting. Return the * pt selected for the next splitting when called * from a wrapping generator. */ Energy generateWrapped(DipoleSplittingInfo&, map& variations, Energy optHardPt = ZERO, Energy optCutoff = ZERO); /** * Complete the given splitting. */ void completeSplitting(DipoleSplittingInfo&) const; /** * Return the last generated splitting */ const DipoleSplittingInfo& lastSplitting() const { return generatedSplitting; } /// Sample the Sudakov in monte carlo fashion. double sudakov(const DipoleSplittingInfo&,Energy down); /// do the actiual calculation of the sudakov exponent. double dosudakov(const DipoleSplittingInfo&,Energy down); /// wrapper for sudakovExpansion for identical dipoles. double wrappedSudakov(DipoleSplittingInfo& split,Energy down); /// Sample the Sudakov exponent for sudakovExpansion weights double sudakovExpansion(const DipoleSplittingInfo&,Energy down,Energy fixedScale); /// do the actual calculation for the sudakov expansion. double dosudakovExpansion(const DipoleSplittingInfo&,Energy down,Energy fixedScale); /// wrapper for sudakovExpansion double wrappedSudakovExpansion(DipoleSplittingInfo& split,Energy down,Energy fixedScale); + /** + * Turn on partial unweighting and set the reference weight. + */ + void doPartialUnweighting(double wref) { + partialUnweighting = true; + theReferenceWeight = wref; + } public: /** * Print debug information on the splitting * handled. */ void debugGenerator(ostream&) const; /** * Print debug information on the last * generated event. */ void debugLastEvent(ostream&) const; protected: /** * Update parameters given a splitting. */ void fixParameters(const DipoleSplittingInfo&, Energy optHardPt = ZERO); /** * With the parameters previuosly supplied * through fixParameters generate the next * splitting. */ void doGenerate(map& variations, Energy optCutoff = ZERO); public: /** * Return the number of random numbers * needed to sample this kernel. */ int nDim() const; /** * Flag, which variables are free variables. */ const vector& sampleFlags(); /** * Return the support of the splitting kernel. * The lower bound on the first variable is * assumed to correspond to the cutoff on the * evolution variable. */ const pair,vector >& support(); /** * Return the parameter point associated to the splitting * previously supplied through fixParameters. */ const vector& parameterPoint() const { return parameters; } /** * Indicate that presampling of this kernel * will be performed in the next calls to * evaluate until stopPresampling() is called. */ void startPresampling(); /** * Indicate that presampling of this kernel * is done until startPresampling() is called. */ void stopPresampling(); /** * Return the number of points to presample this * splitting generator. */ unsigned long presamplingPoints() const { return splittingKernel()->presamplingPoints(); } /** * Return the maximum number of trials * to generate a splitting. */ unsigned long maxtry() const { return splittingKernel()->maxtry(); } /** * Return the number of accepted points after which the grid should * be frozen */ unsigned long freezeGrid() const { return splittingKernel()->freezeGrid(); } /** * Return the detuning factor applied to the sampling overestimate kernel */ double detuning() const { return splittingKernel()->detuning(); } /** * Return true, if this splitting generator * is able to deliver an overestimate to the sampled * kernel. */ bool haveOverestimate() const; /** * Return an overestimate to the sampled kernel. */ double overestimate(const vector&); /** * Invert the integral over the overestimate to equal * the given value. */ double invertOverestimateIntegral(double) const; /** * Evalute the splitting kernel. */ double evaluate(const vector&); /** * Indicate that a veto with the given kernel value and overestimate has occured. */ void veto(const vector&, double p, double r); /** * Indicate that an accept with the given kernel value and overestimate has occured. */ void accept(const vector&, double p, double r); /** * Return the weight associated to the currently generated splitting */ double splittingWeight() const { if ( wrapping() ) return theOtherGenerator->splittingWeight(); return theSplittingWeight; } /** * True, if sampler should apply compensation */ void doCompensate(bool yes = true) { theDoCompensate = yes; } + /** + * Return the weight vector associated to the currently generated splitting + */ + vector > splittingWeightVector() const { + if ( wrapping() ) + return theOtherGenerator->splittingWeightVector(); + return theSplittingWeightVector; + } + public: /**@name Wrap to the exsample2 interface until this is finally cleaned up. */ //@{ inline const vector& variable_flags () { return sampleFlags(); } inline size_t evolution_variable () const { return 0; } inline double evolution_cutoff () { return support().first[0]; } inline const vector& parameter_point () const { return parameterPoint(); } inline void start_presampling () { startPresampling(); } inline void stop_presampling () { stopPresampling(); } inline size_t dimension () const { return nDim(); } inline unsigned long presampling_points () const { return presamplingPoints(); } //@} public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * Pointer to another generator to wrap around. */ Ptr::ptr theOtherGenerator; /** * The dipole splitting kernel to sample * splitting from. */ Ptr::ptr theSplittingKernel; /** * The dipole splitting reweight. */ Ptr::ptr theSplittingReweight; /** * Pointer to the exponential generator */ exsample::exponential_generator* theExponentialGenerator; /** * The dipole splitting to be completed. */ DipoleSplittingInfo generatedSplitting; /** * A backup of the dipole splitting to be * completed, if this generator is presampled. */ DipoleSplittingInfo presampledSplitting; /** * True, if prepared to sample splittings * of a given kind. */ bool prepared; /** * Wether or not the kernel is currently * being presampled. */ bool presampling; /** * The parameter point. */ vector parameters; /** * The sampling flags */ vector theFlags; /** * The support. */ pair,vector > theSupport; /** * Pointer to a check histogram object */ Ptr::ptr theMCCheck; /** * True, if sampler should apply compensation */ bool theDoCompensate; /** * The currently used weight map */ map currentWeights; /** * The weight associated to the currently generated splitting */ double theSplittingWeight; /** * Sudakov sampling accuracy */ double theSudakovAccuracy=0.05; + /** + * Reference weight to improve convergence for subleading Nc + * corrections (by reducing time spent on events with very + * small weights) + */ + double theReferenceWeight; + + /** + * Flag for partial unweighting. + */ + bool partialUnweighting = false; + + /** + * The scale, weight and a bool for all veto steps and the accept step. + * The bool is false for a veto step and true for an accept step. + */ + vector > theSplittingWeightVector; private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initDipoleSplittingGenerator; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ DipoleSplittingGenerator & operator=(const DipoleSplittingGenerator &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of DipoleSplittingGenerator. */ template <> struct BaseClassTrait { /** Typedef of the first base class of DipoleSplittingGenerator. */ typedef HandlerBase NthBase; }; /** This template specialization informs ThePEG about the name of * the DipoleSplittingGenerator class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::DipoleSplittingGenerator"; } /** * The name of a file containing the dynamic library where the class * DipoleSplittingGenerator is implemented. It may also include several, space-separated, * libraries if the class DipoleSplittingGenerator depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_DipoleSplittingGenerator_H */ diff --git a/Shower/Dipole/Base/DipoleSplittingInfo.h b/Shower/Dipole/Base/DipoleSplittingInfo.h --- a/Shower/Dipole/Base/DipoleSplittingInfo.h +++ b/Shower/Dipole/Base/DipoleSplittingInfo.h @@ -1,791 +1,807 @@ // -*- C++ -*- // // DipoleSplittingInfo.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_DipoleSplittingInfo_H #define HERWIG_DipoleSplittingInfo_H // // This is the declaration of the DipoleIndex and DipoleSplittingInfo classes. // #include "ThePEG/PDF/PDF.h" #include "ThePEG/PDT/ParticleData.h" #include "Herwig/Shower/Dipole/Kinematics/DipoleSplittingKinematics.h" +#include "Herwig/Shower/Dipole/Kernels/DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; class DipoleSplittingKinematics; /** * \ingroup DipoleShower * \author Simon Platzer, Stephen Webster * * \brief DipoleIndex is used to index splitting generators * for a particular dipole. * */ class DipoleIndex { public: /** * The default constructor. */ DipoleIndex(); /** * The standard constructor */ DipoleIndex(tcPDPtr newEmitter, tcPDPtr newSpectator, const PDF& newEmitterPDF = PDF(), const PDF& newSpectatorPDF = PDF(), const bool decayingEmitter = false, const bool decayingSpectator = false, const bool offShellEmitter = false, const bool offShellSpectator = false); public: /** * Compare for equality. */ bool operator ==(const DipoleIndex& x) const; /** * Compare for ordering. */ bool operator <(const DipoleIndex& x) const; /** * Swap emitter and spectator. */ void swap(); /** * Produce a pair of dipole indices given * a particle data object for the emission. * The ME correction is ignored in the children. * The emission is inserted between the emitter * and spectator, being a spectator in the first * dipole index containing the original emitter, * and an emitter in the second dipole, containing * the original spectator. */ pair split(tcPDPtr) const; public: /** * Return the emitter particle data object. */ tcPDPtr emitterData() const { return theEmitterData; } /** * Return true, if the emitter is an incoming parton */ bool initialStateEmitter() const { return theInitialStateEmitter; } /** * Return true, if the emitter is incoming to a decay */ bool incomingDecayEmitter() const { return theIncomingDecayEmitter; } /** * Return true, if the emitter can be off-shell */ bool offShellEmitter() const { return theOffShellEmitter; } //bool offShellEmitter() const { return theEmitterData->width() != ZERO; } /** * Return the PDF object associated with the emitter */ const PDF& emitterPDF() const { return theEmitterPDF; } /** * Return the spectator particle data object. */ tcPDPtr spectatorData() const { return theSpectatorData; } /** * Return true, if the spectator is an incoming parton */ bool initialStateSpectator() const { return theInitialStateSpectator; } /** * Return true, if the spectator is incoming to a decay */ bool incomingDecaySpectator() const { return theIncomingDecaySpectator; } /** * Return true, if the spectator can be off-shell */ bool offShellSpectator() const { return theOffShellSpectator; } //bool offShellSpectator() const { return theSpectatorData->width() != ZERO; } /** * Return the PDF object associated with the spectator */ const PDF& spectatorPDF() const { return theSpectatorPDF; } public: /** * Put information to ostream */ void print(ostream&) const; private: /** * The particle data object of the emitter. */ tcPDPtr theEmitterData; /** * Whether or not the emitter is an incoming parton. */ bool theInitialStateEmitter; /** * Whether or not the emitter is incoming to a decay. */ bool theIncomingDecayEmitter; /** * Can the emitter be off-shell? */ bool theOffShellEmitter; /** * The PDF object for the emitter. */ PDF theEmitterPDF; /** * The particle data object of the spectator. */ tcPDPtr theSpectatorData; /** * Whether or not the spectator is an incoming parton. */ bool theInitialStateSpectator; /** * Whether or not the spectator is incoming to a decay. */ bool theIncomingDecaySpectator; /** * Can the spectator be off-shell? */ bool theOffShellSpectator; /** * The PDF object for the spectator. */ PDF theSpectatorPDF; }; inline ostream& operator << (ostream& os, const DipoleIndex& di) { di.print(os); return os; } /** * \ingroup DipoleShower * \author Simon Platzer * * \brief DipoleSplittingInfo contains all parameters to generate a full * dipole splitting. * */ class DipoleSplittingInfo { public: /** * The default constructor. */ DipoleSplittingInfo(); /** * Destructor */ virtual ~DipoleSplittingInfo() {} /** * Standard constructor. */ DipoleSplittingInfo(DipoleIndex ind,pair conf,double emitX, double spectX,tPPtr emit,tPPtr spect){ theIndex=ind; theConfiguration=conf; theEmitterX=emitX; theSpectatorX=spectX; theEmitter=emit; theSpectator=spect; } public: /** * Assign data from another splitting info */ void fill(const DipoleSplittingInfo&); public: /** * Return the dipole index */ const DipoleIndex& index() const { return theIndex; } /** * Return which of the particles * in the dipole should be considered emitter (true) * and spectator (false) */ const pair& configuration() const { return theConfiguration; } /** * Get the configuration marking the spectator */ const pair& spectatorConfiguration() const { return theSpectatorConfiguration; } /** * Return the particle data object of the emitter * after the splitting. */ tcPDPtr emitterData() const { return theEmitterData; } /** * Return the particle data object of the emission * after the splitting. */ tcPDPtr emissionData() const { return theEmissionData; } /** * Return the particle data object of the spectator * after the splitting. */ tcPDPtr spectatorData() const { return theSpectatorData; } /** * Return the momentum fraction of the emitter. */ double emitterX() const { return theEmitterX; } /** * Return the momentum fraction of the spectator. */ double spectatorX() const { return theSpectatorX; } public: /** * Return a pointer to the DipoleSplittingKinematics object * which is to be used to perform the splitting. */ Ptr::tptr splittingKinematics() const { return theSplittingKinematics; } /** + * Return a pointer to the DipoleSplittingKernel object + * which is used to perform the splitting. + **/ + Ptr::tptr splittingKernel() const { return theSplittingKernel;} + + /** * Return the dipole scale */ Energy scale() const { return theScale; } /** * Return whether or not this dipole is * part of a decay process. **/ bool isDecayProc() const { return theIsDecayProc; } /** * Return the mass of the recoil system * in decay dipoles. */ Energy recoilMass() const { return theRecoilMass; } /** * Return the spectator mass * (to cope with off-shell particles) **/ Energy spectatorMass() const { return theSpectatorMass; } /** * Return the emitter mass * (to cope with off-shell particles) **/ Energy emitterMass() const { return theEmitterMass; } /** * Return the pt below which this * splitting has been generated. */ Energy hardPt() const { return theHardPt; } /** * Return the last generated pt */ Energy lastPt() const { return theLastPt; } /** * Return the last generated momentum fraction. */ double lastZ() const { return theLastZ; } /** * Return the last generated azimuthal angle. */ double lastPhi() const { return theLastPhi; } /** * Return the momentum fraction, by which the emitter's * momentum fraction should be divided after the splitting. */ double lastEmitterZ() const { return theLastEmitterZ; } /** * Return the momentum fraction, by which the spectator's * momentum fraction should be divided after the splitting. */ double lastSpectatorZ() const { return theLastSpectatorZ; } /** * Return any additional parameters needed to * evaluate the splitting kernel or to generate the * full splitting. */ const vector& lastSplittingParameters() const { return theLastSplittingParameters; } /** * Return true, if this splitting will terminate * the evolution of the dipole considered. */ bool stoppedEvolving() const { return theStoppedEvolving; } public: /** * Set the index. */ void index(const DipoleIndex& ind) { theIndex = ind; } /** * Set the DipoleSplittingKinematics object */ void splittingKinematics(Ptr::tptr newSplittingKinematics) { theSplittingKinematics = newSplittingKinematics; } /** + * Set the DipoleSplittingKernel object + */ + void splittingKernel( Ptr::tptr newSplittingKernel){ + theSplittingKernel = newSplittingKernel; + } + + /** * Set the particle data object of the emitter * after the splitting. */ void emitterData(tcPDPtr p) { theEmitterData = p; } /** * Set the particle data object of the emission * after the splitting. */ void emissionData(tcPDPtr p) { theEmissionData = p; } /** * Set the particle data object of the spectator * after the splitting. */ void spectatorData(tcPDPtr p) { theSpectatorData = p; } /** * Set the dipole scale */ void scale(Energy s) { theScale = s; } /** * Set whether or not this dipole is * part of a decay process. **/ void isDecayProc(bool isDecayProc) { theIsDecayProc = isDecayProc; } /** * Set the mass of the recoil system * in decay dipoles */ - void recoilMass(Energy mass) { theRecoilMass = mass; } + void recoilMass(Energy mass) { theRecoilMass = mass; } /** * Set the spectator mass * (to cope with off-shell particles) **/ void spectatorMass(Energy mass){ theSpectatorMass = mass; } /** * Set the emitter mass * (to cope with off-shell particles) **/ void emitterMass(Energy mass){ theEmitterMass = mass; } /** * Set the emitter's momentum fraction */ void emitterX(double x) { theEmitterX = x; } /** * Set the spectator's momentum fraction */ void spectatorX(double x) { theSpectatorX = x; } /** * Set the pt below which this * splitting has been generated. */ void hardPt(Energy p) { theHardPt = p; } - + /** * Set the last generated pt */ void lastPt(Energy p) { theLastPt = p; } /** * Set the last generated momentum fraction. */ void lastZ(double z) { theLastZ = z; } /** * Set the last generated azimuthal angle. */ void lastPhi(double p) { theLastPhi = p; } /** * Set the momentum fraction, by which the emitter's * momentum fraction should be divided after the splitting. */ void lastEmitterZ(double z) { theLastEmitterZ = z; } /** * Set the momentum fraction, by which the spectator's * momentum fraction should be divided after the splitting. */ void lastSpectatorZ(double z) { theLastSpectatorZ = z; } /** * Return the last splitting kernel value encountered. */ double lastValue() const { return theLastValue; } /** * Set the last splitting kernel value encountered. */ void lastValue(double v) { theLastValue = v; } /** * Set the flag to calculate the Sudakov with fixed scales. */ void setCalcFixedExpansion(bool c){theCalcFixedExpansion=c;} /** * Flag to calculate the Sudakov with fixed scales. */ bool calcFixedExpansion()const{ return theCalcFixedExpansion;} /** * Fixed scale for Sudakov sampling with fixed scales. */ Energy fixedScale() const{return theFixedScale;} /** * Set the fixed scale. */ void fixedScale(Energy fix){ theFixedScale=fix;} /** * Set the last splitting parameters. */ void lastSplittingParameters(const vector& p) { theLastSplittingParameters = p; } /** * Access the splitting parameters */ vector& splittingParameters() { return theLastSplittingParameters; } /** * Indicate that this splitting will terminate * the evolution of the dipole considered. */ void didStopEvolving() { theStoppedEvolving = true; } /** * Indicate that this splitting will not terminate * the evolution of the dipole considered. */ void continuesEvolving() { theStoppedEvolving = false; } /** * Reset the configuration. */ void configuration(const pair& newConfig) { theConfiguration = newConfig; } /** * Set the configuration marking the spectator */ void spectatorConfiguration(const pair& conf) { theSpectatorConfiguration = conf; } public: /** * Set a pointer to the emitter parton before emission. */ void emitter(tPPtr newEmitter) { theEmitter = newEmitter; } /** * Set a pointer to the spectator parton before emission. */ void spectator(tPPtr newSpectator) { theSpectator = newSpectator; } /** * Set a pointer to the emitter parton after emission. */ void splitEmitter(tPPtr newEmitter) { theSplitEmitter = newEmitter; } /** * Set a pointer to the spectator parton after emission. */ void splitSpectator(tPPtr newSpectator) { theSplitSpectator = newSpectator; } /** * Set a pointer to the emitted parton. */ void emission(tPPtr newEmission) { theEmission = newEmission; } /** * Return a pointer to the emitter parton before emission. */ tPPtr emitter() const { return theEmitter; } /** * Return a pointer to the spectator parton before emission. */ tPPtr spectator() const { return theSpectator; } /** * Return a pointer to the emitter parton after emission. */ tPPtr splitEmitter() const { return theSplitEmitter; } /** * Return a pointer to the spectator parton after emission. */ tPPtr splitSpectator() const { return theSplitSpectator; } /** * Return a pointer to the emitted parton. */ tPPtr emission() const { return theEmission; } public: /** * Put information to ostream */ void print(ostream&) const; private: /** * The DipoleIndex associated * with this splitting. */ DipoleIndex theIndex; /** * Flags indicateing which of the particles * in the dipole should be considered emitter (true) * and spectator (false) */ pair theConfiguration; /** * The configuration marking the spectator */ pair theSpectatorConfiguration; /** * The particle data object of the emitter * after the splitting. */ tcPDPtr theEmitterData; /** * The particle data object of the emission * after the splitting. */ tcPDPtr theEmissionData; /** * The particle data object of the spectator * after the splitting. */ tcPDPtr theSpectatorData; /** * A pointer to the DipoleSplittingKinematics object * which is to be used to perform the splitting. */ Ptr::tptr theSplittingKinematics; + + /** + * A pointer to the DipoleSplittingKernel object + * which is used to perform the splitting. + **/ + Ptr::tptr theSplittingKernel; /** * The scale for this dipole. */ Energy theScale; /** * Whether or not this dipole comes from a decay process. */ bool theIsDecayProc; /** * The mass of the recoil system in * decay dipoles. */ Energy theRecoilMass; - /** * The mass of the emitter. * (To account for off-shell). */ Energy theEmitterMass; /** * The mass of the spectator. * (To account for off-shell). */ Energy theSpectatorMass; - - - /** * The momentum fraction of the emitter. */ double theEmitterX; /** * The momentum fraction of the spectator. */ double theSpectatorX; /** * The pt below which this splitting has * been generated. */ Energy theHardPt; /** * The last generated pt */ Energy theLastPt; /** * The last generated momentum fraction. */ double theLastZ; /** * The last calculated zPrime required for massive FF * and decay kinematics dipoles. * zPrime := qi.nk / (qi+qj).nk (qj = emission momentum) */ // Note: Not required in current implementation //double theLastZPrime; /** * The last generated azimuthal angle. */ double theLastPhi; /** * The momentum fraction, by which the emitter's * momentum fraction should be divided after the splitting. */ double theLastEmitterZ; /** * The momentum fraction, by which the spectator's * momentum fraction should be divided after the splitting. */ double theLastSpectatorZ; /** * The last splitting kernel value encountered. */ double theLastValue; /** * Any additional parameters needed to * evaluate the splitting kernel or to generate the * full splitting. */ vector theLastSplittingParameters; /** * True, if this splitting will terminate * the evolution of the dipole considered. */ bool theStoppedEvolving; /** * A pointer to the emitter parton before emission. */ PPtr theEmitter; /** * A pointer to the spectator parton before emission. */ PPtr theSpectator; /** * A pointer to the emitter parton after emission. */ PPtr theSplitEmitter; /** * A pointer to the spectator parton after emission. */ PPtr theSplitSpectator; /** * A pointer to the emitted parton. */ PPtr theEmission; /** * Flag to calculate Splitting kernels with a fixed scale * and without alphas/2pi **/ bool theCalcFixedExpansion; /** * Fixed scale for Sudakov evaluation. */ Energy theFixedScale; }; inline ostream& operator << (ostream& os, const DipoleSplittingInfo& di) { di.print(os); return os; } } #endif /* HERWIG_DipoleSplittingInfo_H */ diff --git a/Shower/Dipole/Base/DipoleSplittingReweight.h b/Shower/Dipole/Base/DipoleSplittingReweight.h --- a/Shower/Dipole/Base/DipoleSplittingReweight.h +++ b/Shower/Dipole/Base/DipoleSplittingReweight.h @@ -1,175 +1,197 @@ // -*- C++ -*- // // DipoleSplittingReweight.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_DipoleSplittingReweight_H #define HERWIG_DipoleSplittingReweight_H // // This is the declaration of the DipoleSplittingReweight class. // #include "ThePEG/Handlers/HandlerBase.h" #include "DipoleSplittingInfo.h" #include "Herwig/Shower/Dipole/DipoleShowerHandler.fh" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief DipoleSplittingReweight is used by the dipole shower * to reweight splittings from a given dipole splitting kernel. * * @see \ref DipoleSplittingReweightInterfaces "The interfaces" * defined for DipoleSplittingReweight. */ class DipoleSplittingReweight: public HandlerBase { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ DipoleSplittingReweight(); /** * The destructor. */ virtual ~DipoleSplittingReweight(); //@} public: /** * Return true, if the reweighting should be applied to the first * interaction */ virtual bool firstInteraction() const { return true; } /** * Return true, if the reweighting should be applied to the secondary * interactions */ virtual bool secondaryInteractions() const { return false; } /** * Update the pointer to the currently active dipole shower handler object. */ void updateCurrentHandler(); /** * Return the pointer to the currently active dipole shower handler object. */ Ptr::tptr currentHandler() const; /** * Return the reweighting factor for the given splitting type. */ virtual double evaluate(const DipoleSplittingInfo&) const = 0; /** * Return an enhancement hint for the sampling of the un-reweighted * splitting kernel */ virtual double hint(const DipoleSplittingInfo&) const { return 1.; } + /** + * Return true, if the reweight can be entirely absorbed into the hint. A + * possible detuning will be switched off. + */ + virtual bool hintOnly(const DipoleSplittingInfo&) const { + return false; + } + + /** + * Set the factor in front of enhance used by the veto algorithm. + */ + virtual void reweightFactor(const double) { + return; + } + + /** + * Scaling factor for negative reweights. + */ + virtual void negativeScaling(const double) { + return; + } + 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(); // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static AbstractClassDescription initDipoleSplittingReweight; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ DipoleSplittingReweight & operator=(const DipoleSplittingReweight &) = delete; /** * A pointer to the currently active dipole shower handler object. */ Ptr::tptr theCurrentHandler; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of DipoleSplittingReweight. */ template <> struct BaseClassTrait { /** Typedef of the first base class of DipoleSplittingReweight. */ typedef HandlerBase NthBase; }; /** This template specialization informs ThePEG about the name of * the DipoleSplittingReweight class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::DipoleSplittingReweight"; } /** * The name of a file containing the dynamic library where the class * DipoleSplittingReweight is implemented. It may also include several, space-separated, * libraries if the class DipoleSplittingReweight depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_DipoleSplittingReweight_H */ diff --git a/Shower/Dipole/DipoleShowerHandler.cc b/Shower/Dipole/DipoleShowerHandler.cc --- a/Shower/Dipole/DipoleShowerHandler.cc +++ b/Shower/Dipole/DipoleShowerHandler.cc @@ -1,1384 +1,1918 @@ // -*- C++ -*- // // DipoleShowerHandler.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 DipoleShowerHandler class. // #include #include "DipoleShowerHandler.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Reference.h" #include "ThePEG/Interface/RefVector.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Interface/ParVector.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" // include theses to have complete types #include "Herwig/PDF/MPIPDF.h" #include "Herwig/PDF/MinBiasPDF.h" #include "Herwig/PDF/HwRemDecayer.h" #include "Herwig/Shower/Dipole/Utility/DipolePartonSplitter.h" #include "Herwig/MatrixElement/Matchbox/Base/MergerBase.h" #include "Herwig/MatrixElement/Matchbox/Base/SubtractedME.h" #include "Herwig/MatrixElement/Matchbox/MatchboxFactory.h" #include using namespace Herwig; bool DipoleShowerHandler::firstWarn = true; DipoleShowerHandler::DipoleShowerHandler() : ShowerHandler(), chainOrderVetoScales(true), nEmissions(0), discardNoEmissions(false), firstMCatNLOEmission(false), thePowhegDecayEmission(true), + //theAnalyseSpinCorrelations(false), realignmentScheme(0), + doSubleadingNc(false),subleadingNcEmissionsLimit(0), + densityOperatorEvolution(0),densityOperatorCutoff(1.0*GeV2), + doPartialUnweightingAtEmission(false), + doPartialUnweighting(false),referenceWeight(0.1), + cmecReweightFactor(1.0),negCMECScaling(1.0), verbosity(0), printEvent(0), nTries(0), didRadiate(false), didRealign(false), theRenormalizationScaleFreeze(1.*GeV), theFactorizationScaleFreeze(2.*GeV), theDoCompensate(false), theFreezeGrid(500000), theDetuning(1.0), maxPt(ZERO), muPt(ZERO), theInputColouredOffShellInShower(), theZBoundaries(1) {} DipoleShowerHandler::~DipoleShowerHandler() {} IBPtr DipoleShowerHandler::clone() const { return new_ptr(*this); } IBPtr DipoleShowerHandler::fullclone() const { return new_ptr(*this); } void DipoleShowerHandler::cascade(tPVector ) { throw Exception() << "DipoleShowerHandler: Dipoleshower not implemented as second shower." << "Check your setup or contact Herwig authors." << Exception::runerror; } tPPair DipoleShowerHandler::cascade(tSubProPtr sub, XCombPtr, Energy optHardPt, Energy optCutoff) { - + useMe(); prepareCascade(sub); resetWeights(); if ( !doFSR() && ! doISR() ) + return sub->incoming(); + + eventRecord().setSubleadingNc(doSubleadingNc, + subleadingNcEmissionsLimit); + eventRecord().clear(); + eventRecord().prepare(sub,dynamic_ptr_cast(lastXCombPtr()),newStep(),pdfs(), + ShowerHandler::currentHandler()->generator()->currentEvent()->incoming(), + firstInteraction(), offShellPartons(), + !doSubleadingNc); + if ( doSubleadingNc ) { + if ( !theSplittingReweight ) { + throw Exception() << "No splitting reweight was found. " + << "A ColourMatrixElementCorrection " + << "splitting reweight is required " + << "for the subleading colour shower." + << Exception::runerror; + } + //Set the evolution scheme for the density operator + eventRecord().setDensityOperatorEvolution( densityOperatorEvolution, densityOperatorCutoff ); + //Set the CMEC reweight factor + theSplittingReweight->reweightFactor(cmecReweightFactor); + theSplittingReweight->negativeScaling(negCMECScaling); + theSplittingReweight->updateCurrentHandler(); + } + + // SW: Removed simple test on doFSR and doISR and moved + // here to account for the case of a hard event involving + // no coloured particles but with unstable outgoing particles + if ( !doFSR() && ! doISR() && eventRecord().decays().empty() ) return sub->incoming(); - - eventRecord().clear(); - eventRecord().prepare(sub, dynamic_ptr_cast(lastXCombPtr()), newStep(), pdfs(), - ShowerHandler::currentHandler()->generator()->currentEvent()->incoming(), - firstInteraction(), offShellPartons()); - if ( eventRecord().outgoing().empty() && !doISR() ) - return sub->incoming(); - if ( !eventRecord().incoming().first->coloured() && + if ( !doISR() && + eventRecord().outgoing().empty() && + eventRecord().decays().empty() ) + return sub->incoming(); + if ( !doFSR() && + !eventRecord().incoming().first->coloured() && !eventRecord().incoming().second->coloured() && - !doFSR() ) + eventRecord().decays().empty() ) return sub->incoming(); nTries = 0; + // Clear the vertex record for spin correlations + if ( spinCorrelations() ) //|| theAnalyseSpinCorrelations ) + vertexRecord().clear(); + while ( true ) { try { didRadiate = false; didRealign = false; if ( eventRecord().truncatedShower() ) { throw Exception() << "Inconsistent hard emission set-up in DipoleShowerHandler::cascade. " << "No truncated shower needed with DipoleShowerHandler. Add " << "'set MEMatching:TruncatedShower No' to input file." << Exception::runerror; } hardScales(lastXCombPtr()->lastShowerScale()); if ( verbosity > 1 ) { generator()->log() << "DipoleShowerHandler starting off:\n"; eventRecord().debugLastEvent(generator()->log()); generator()->log() << flush; } unsigned int nEmitted = 0; if ( firstMCatNLOEmission ) { if ( !eventRecord().isMCatNLOHEvent() ) nEmissions = 1; else nEmissions = 0; } if ( !firstMCatNLOEmission ) { doCascade(nEmitted,optHardPt,optCutoff); if ( discardNoEmissions ) { if ( !didRadiate ) throw Veto(); if ( nEmissions ) if ( nEmissions < nEmitted ) throw Veto(); } } else { if ( nEmissions == 1 ) doCascade(nEmitted,optHardPt,optCutoff); } if ( intrinsicPtGenerator ) { if ( eventRecord().incoming().first->coloured() && eventRecord().incoming().second->coloured() ) { - SpinOneLorentzRotation rot = + LorentzRotation rot = intrinsicPtGenerator->kick(eventRecord().incoming(), eventRecord().intermediates()); eventRecord().transform(rot); } } didRealign = realign(); + constituentReshuffle(); + + // backup subleading switch if decays fail + bool doneSubleadingNc = doSubleadingNc; + + // subleading N can't handle decays + doSubleadingNc = false; + + try { // Decay and shower any particles that require decaying while ( !eventRecord().decays().empty() ) { map::const_iterator decayIt = eventRecord().decays().begin(); + if ( eventRecord().nextDecay() ) { + decayIt = eventRecord().decays().find(eventRecord().nextDecay() ); + } + else { // find the decay to do, one with greatest width and parent showered while(find(eventRecord().outgoing().begin(),eventRecord().outgoing().end(),decayIt->first)== eventRecord().outgoing().end() && find(eventRecord().hard().begin(),eventRecord().hard().end(),decayIt->first)== eventRecord().hard().end()) ++decayIt; + } + assert(decayIt!=eventRecord().decays().end()); PPtr incoming = decayIt->first; eventRecord().currentDecay(decayIt->second); // Use this to record if an emission actually happens bool powhegEmission = !( nEmissions && nEmitted==nEmissions) ? thePowhegDecayEmission : false; // Decay the particle / sort out its pert proc Energy showerScale = eventRecord().decay(incoming, powhegEmission); // Following the decay, the bool powheg emission is updated // to indicate whether or not an emission occurred if ( powhegEmission ) nEmitted += 1; // Check that there is only one particle incoming to the decay assert(eventRecord().currentDecay()->incoming().size()==1); // Prepare the event record for the showering of the decay bool needToShower = eventRecord().prepareDecay(eventRecord().currentDecay(), offShellPartons()); // Only need to shower if we have coloured outgoing particles if ( needToShower ) { // The decays currently considered produce a maximum of 2 chains (with powheg emission) // so all dipole should have the same scale as returned by the decay function. assert( eventRecord().chains().size() <= 2 ); for ( auto & ch : eventRecord().chains()) { for ( auto & dip : ch.dipoles()) { assert ( showerScale > ZERO ); dip.leftScale( showerScale ); dip.rightScale( showerScale ); } } + + // Prepare vertex record for spin correlations in decay shower + if ( spinCorrelations() ) + vertexRecord().prepareParticleDecay(incoming); + // Perform the cascade doCascade(nEmitted,optHardPt,optCutoff,true); + if ( spinCorrelations() ) + vertexRecord().updateParticleDecay(); + // Do the constituent mass shell reshuffling decayConstituentReshuffle(eventRecord().currentDecay()); } // Update the decays, adding any decays and updating momenta eventRecord().updateDecays(eventRecord().currentDecay()); - eventRecord().decays().erase(decayIt); + eventRecord().decays().erase(decayIt); + + } + + } catch(...) { + + // reset flag + doSubleadingNc = doneSubleadingNc; + throw; + } - + + doSubleadingNc = doneSubleadingNc; break; } catch (RedoShower&) { resetWeights(); if ( ++nTries > maxtry() ) throw ShowerTriesVeto(maxtry()); eventRecord().clear(); eventRecord().prepare(sub, dynamic_ptr_cast(lastXCombPtr()), newStep(), pdfs(), ShowerHandler::currentHandler()->generator()->currentEvent()->incoming(), - firstInteraction(), offShellPartons()); + firstInteraction(), offShellPartons(), + !doSubleadingNc); + if ( doSubleadingNc ) { + theSplittingReweight->updateCurrentHandler(); + } continue; } catch (...) { throw; } } tPPair incoming=eventRecord().fillEventRecord(newStep(),firstInteraction(),didRealign); setDidRunCascade(true); return incoming; } + // Reshuffle the outgoing partons from the hard process onto their constituent mass shells void DipoleShowerHandler::constituentReshuffle() { if ( constituentReshuffler && ShowerHandler::currentHandler()->retConstituentMasses() ) { if ( eventRecord().decays().empty() ) { constituentReshuffler->reshuffle(eventRecord().outgoing(), eventRecord().incoming(), eventRecord().intermediates()); return; } else { PList decaying; for(auto const & dec : eventRecord().decays()) decaying.push_back(dec.first); constituentReshuffler->hardProcDecayReshuffle( decaying, eventRecord().outgoing(), eventRecord().hard(), eventRecord().incoming(), eventRecord().intermediates()); } } // After reshuffling the hard process, the decays need to be updated // as this is not done in reshuffle vector > decays; for(auto const & dec : eventRecord().decays() ) decays.push_back({dec.first,dec.second}); for(auto const & dec : decays) { PPtr unstable = dec.first; PList::iterator pos = find(eventRecord().intermediates().begin(), eventRecord().intermediates().end(), dec.first); // Update the PPtr in theDecays if(pos!=eventRecord().intermediates().end()) { unstable = *pos; while(!unstable->children().empty()) { unstable = unstable->children()[0]; } eventRecord().decays().erase(dec.first); eventRecord().decays()[unstable] = dec.second; // Update the momenta of any other particles in the decay chain // (for externally provided events) if ( !(eventRecord().decays()[unstable]->outgoing().empty()) ) eventRecord().updateDecayChainMom( unstable , eventRecord().decays()[unstable]); } else { if ( !(eventRecord().decays()[unstable]->outgoing().empty()) ) { // Update the momenta of any other particles in the decay chain // (for externally provided events) // Note this needs to be done for all decaying particles in the // outgoing/hard regardless of whether that particle radiated // or was involved in the reshuffling, this is due to the // transformation performed for IILightKinematics. if ( (find(eventRecord().outgoing().begin(), eventRecord().outgoing().end(), unstable) != eventRecord().outgoing().end()) || (find(eventRecord().hard().begin(), eventRecord().hard().end(), unstable) != eventRecord().hard().end()) ) eventRecord().updateDecayChainMom( unstable , eventRecord().decays()[unstable]); } } } eventRecord().currentDecay(PerturbativeProcessPtr()); } // Reshuffle outgoing partons from a decay process onto their constituent mass shells void DipoleShowerHandler::decayConstituentReshuffle(PerturbativeProcessPtr decayProc) { if ( Debug::level > 2 ){ // Test this function by comparing the // invariant mass of the outgoing decay // systems before and after reshuffling Lorentz5Momentum testOutMomBefore (ZERO,ZERO,ZERO,ZERO); Energy testInvMassBefore = ZERO; for ( auto const & testDecayOutItBefore : decayProc->outgoing() ) { testOutMomBefore += testDecayOutItBefore.first->momentum(); } testInvMassBefore = testOutMomBefore.m(); // decayReshuffle updates both the event record and the decay perturbative process if ( constituentReshuffler && ShowerHandler::currentHandler()->retConstituentMasses()) { constituentReshuffler->decayReshuffle(decayProc, eventRecord().outgoing(), eventRecord().hard(), eventRecord().intermediates()); } Lorentz5Momentum testOutMomAfter (ZERO,ZERO,ZERO,ZERO); Energy testInvMassAfter = ZERO; for ( auto const & testDecayOutItAfter : decayProc->outgoing() ) { testOutMomAfter += testDecayOutItAfter.first->momentum(); } testInvMassAfter = testOutMomAfter.m(); Energy incomingMass = decayProc->incoming()[0].first->momentum().m(); assert( abs(testInvMassBefore-incomingMass)/GeV < 1e-5 ); assert( abs(testInvMassBefore-testInvMassAfter)/GeV < 1e-5); }else{ // decayReshuffle updates both the event record and the decay perturbative process if ( constituentReshuffler && ShowerHandler::currentHandler()->retConstituentMasses() ) { constituentReshuffler->decayReshuffle(decayProc, eventRecord().outgoing(), eventRecord().hard(), eventRecord().intermediates()); } return; } } // Sets the scale of each particle in the dipole chains by finding the smallest //of several upper bound energy scales: the CMEnergy of the event, //the transverse mass of outgoing particles, the hardScale (maxPT or maxQ) //calculated for each dipole (in both configurations) and the veto scale for each particle void DipoleShowerHandler::hardScales(Energy2 muf) { // Initalise maximum pt as max CMEnergy of the event maxPt = generator()->maximumCMEnergy(); if ( restrictPhasespace() ) { // First interaction == hard collision (i.e. not a MPI collision) if ( !hardScaleIsMuF() || !firstInteraction() ) { if ( !eventRecord().outgoing().empty() ) { for ( auto const & p : eventRecord().outgoing() ) maxPt = min(maxPt,p->momentum().mt()); } //Look at any non-coloured outgoing particles in the current subprocess else { assert(!eventRecord().hard().empty()); Lorentz5Momentum phard(ZERO,ZERO,ZERO,ZERO); for ( auto const & p : eventRecord().hard()) phard += p->momentum(); Energy mhard = phard.m(); maxPt = mhard; } maxPt *= hardScaleFactor(); } else { maxPt = hardScaleFactor()*sqrt(muf); } muPt = maxPt; } else { muPt = hardScaleFactor()*sqrt(muf); } - - - for ( auto & ch : eventRecord().chains()) { + + if ( doSubleadingNc ) { + return; + } + + for ( auto & ch : eventRecord().chains()) { // Note that minVetoScale is a value for each DipoleChain, not each dipole // It will contain the minimum veto scale from all of the dipoles in the chain Energy minVetoScale = -1.*GeV; for ( auto & dip : ch.dipoles()) { // max scale per config Energy maxFirst = ZERO; Energy maxSecond = ZERO; // Loop over the kernels for the given dipole. // For each dipole configuration, calculate ptMax (or QMax if virtuality ordering) // for each kernel and find the maximum for ( auto const & k : kernels) { pair conf = {true,false}; if ( k->canHandle(dip.index(conf)) ) { // Look in DipoleChainOrdering for this Energy scale = evolutionOrdering()->hardScale(dip.emitter(conf),dip.spectator(conf), dip.emitterX(conf),dip.spectatorX(conf), *k,dip.index(conf)); maxFirst = max(maxFirst,scale); } conf = {false,true}; if ( k->canHandle(dip.index(conf)) ) { Energy scale = evolutionOrdering()->hardScale(dip.emitter(conf),dip.spectator(conf), dip.emitterX(conf),dip.spectatorX(conf), *k,dip.index(conf)); maxSecond = max(maxSecond,scale); } } // Find the maximum value from comparing the maxScale found from maxPt and the vetoScale of the particle if ( dip.leftParticle()->vetoScale() >= ZERO ) { maxFirst = min(maxFirst,sqrt(dip.leftParticle()->vetoScale())); // minVetoScale is a value for each DipoleChain, not each dipole // It contains the minimum veto scale for all the dipoles in the entire DipoleChain if ( minVetoScale >= ZERO ) minVetoScale = min(minVetoScale,sqrt(dip.leftParticle()->vetoScale())); else minVetoScale = sqrt(dip.leftParticle()->vetoScale()); } if ( dip.rightParticle()->vetoScale() >= ZERO ) { maxSecond = min(maxSecond,sqrt(dip.rightParticle()->vetoScale())); if ( minVetoScale >= ZERO ) minVetoScale = min(minVetoScale,sqrt(dip.rightParticle()->vetoScale())); else minVetoScale = sqrt(dip.rightParticle()->vetoScale()); } // Set the emitterScale for both members of each dipole maxFirst = min(maxPt,maxFirst); dip.emitterScale({true,false},maxFirst); maxSecond = min(maxPt,maxSecond); dip.emitterScale({false,true},maxSecond); } // if the smallest veto scale (i.e. from all of the dipoles) // is smaller than the scale calculated for a particular // particle in a particular dipole, // replace the scale with the veto scale if ( !evolutionOrdering()->independentDipoles() && chainOrderVetoScales && minVetoScale >= ZERO ) { for ( auto & dip : ch.dipoles() ) { dip.leftScale(min(dip.leftScale(),minVetoScale)); dip.rightScale(min(dip.rightScale(),minVetoScale)); } } } } +void DipoleShowerHandler::hardScalesSubleading(list candidates, + Energy hardPt) { + + maxPt = hardPt;//generator()->maximumCMEnergy(); + + // Note that minVetoScale is a value for each competing dipole (i.e. all dipoles + // for the subleading shower. + // It will contain the minimum veto scale from all of the dipoles + Energy minVetoScale = -1.*GeV; + + for ( list::iterator cand = candidates.begin(); + cand != candidates.end(); ++cand ) { + + // max scale + Energy maxScale = ZERO; + + // Loop over kernels + for ( vector::ptr>::iterator k = + kernels.begin(); k != kernels.end(); ++k ) { + + if ( (**k).canHandle(cand->index()) ) { + Energy scale = + evolutionOrdering()->hardScale(cand->emitter(),cand->spectator(), + cand->emitterX(),cand->spectatorX(), + **k,cand->index()); + maxScale = max(maxScale,scale); + } + + } + + if ( cand->emitter()->vetoScale() >= ZERO ) { + maxScale = min(maxScale,sqrt(cand->emitter()->vetoScale())); + if ( minVetoScale >= ZERO ) + minVetoScale = min(minVetoScale,sqrt(cand->emitter()->vetoScale())); + else + minVetoScale = sqrt(cand->emitter()->vetoScale()); + } + + maxScale = min(maxPt,maxScale); + cand->scale(maxScale); + + } + + if ( !evolutionOrdering()->independentDipoles() && + chainOrderVetoScales && + minVetoScale >= ZERO ) { + for ( list::iterator cand = candidates.begin(); + cand != candidates.end(); ++cand ) { + cand->scale(min(cand->scale(),minVetoScale)); + } + } + +} + + + +void DipoleShowerHandler::addCandidates(PPair particles, + list& clist) const { + + DipoleSplittingInfo candidate; + Energy2 scale = ZERO; + pair is(particles.first == eventRecord().incoming().first, + particles.second == eventRecord().incoming().second); + if ( (is.first && !is.second) || + (!is.first && is.second) ) { + scale = -(particles.first->momentum() - particles.second->momentum()).m2(); + } else { + scale = (particles.first->momentum() + particles.second->momentum()).m2(); + } + + DipoleIndex index(particles.first->dataPtr(),particles.second->dataPtr(), + is.first ? eventRecord().pdfs().first : PDF(), + is.second ? eventRecord().pdfs().second : PDF()); + + candidate.scale(sqrt(scale)); + + candidate.index(index); + candidate.configuration(make_pair(true,false)); + candidate.emitter(particles.first); + candidate.emitterX(is.first ? eventRecord().fractions().first : 1.0); + candidate.spectator(particles.second); + candidate.spectatorX(is.second ? eventRecord().fractions().second : 1.0); + + clist.push_back(candidate); + + index.swap(); + + candidate.index(index); + candidate.configuration(make_pair(false,true)); + candidate.emitter(particles.second); + candidate.emitterX(is.second ? eventRecord().fractions().second : 1.0); + candidate.spectator(particles.first); + candidate.spectatorX(is.first ? eventRecord().fractions().first : 1.0); + + clist.push_back(candidate); + +} + +void DipoleShowerHandler::getCandidates(list& clist) const { + + clist.clear(); + + for ( PList::const_iterator i = eventRecord().outgoing().begin(); + i != eventRecord().outgoing().end(); ++i ) { + PList::const_iterator j = i; ++j; + for ( ; j != eventRecord().outgoing().end(); ++j ) { + addCandidates(make_pair(*i,*j),clist); + } + // Changed order of *i and inc().first + if ( eventRecord().incoming().first->coloured() ) + addCandidates(make_pair(eventRecord().incoming().first,*i),clist); + if ( eventRecord().incoming().second->coloured() ) + addCandidates(make_pair(*i,eventRecord().incoming().second),clist); + } + + if ( eventRecord().incoming().first->coloured() && eventRecord().incoming().second->coloured() ) { + addCandidates(eventRecord().incoming(),clist); + } + +} + +void DipoleShowerHandler::performSplitting(DipoleSplittingInfo& split) const { + + Ptr::tptr kinematics = split.splittingKinematics(); + kinematics->generateKinematics(split.emitter()->momentum(), + split.spectator()->momentum(), + split); + + split.splitEmitter(split.emitterData()->produceParticle(kinematics->lastEmitterMomentum())); + split.splitSpectator(split.spectatorData()->produceParticle(kinematics->lastSpectatorMomentum())); + split.emission(split.emissionData()->produceParticle(kinematics->lastEmissionMomentum())); + + // Setting resolution scales for the particles + split.emission()->scale(sqr(split.lastPt())); + split.splitEmitter()->scale(sqr(split.lastPt())); + split.splitSpectator()->scale(split.spectator()->scale()); + + PVector neighbours; + if ( DipolePartonSplitter::colourConnected(split.emitter(), + eventRecord().incoming().first) && + split.emitter() != eventRecord().incoming().first ) + neighbours.push_back(eventRecord().incoming().first); + if ( DipolePartonSplitter::colourConnected(split.emitter(), + eventRecord().incoming().second) && + split.emitter() != eventRecord().incoming().second ) + neighbours.push_back(eventRecord().incoming().second); + for ( PList::const_iterator p = eventRecord().outgoing().begin(); + p != eventRecord().outgoing().end(); ++p ) { + if ( *p == split.emitter() ) + continue; + if ( DipolePartonSplitter::colourConnected(split.emitter(),*p) ) + neighbours.push_back(*p); + } + assert(neighbours.size() == 1 || neighbours.size() == 2 ); + if ( neighbours.size() == 2 ) { + if ( UseRandom::rnd() < 0.5 ) + swap(neighbours[0],neighbours[1]); + } + + DipolePartonSplitter::split(split.emitter(),split.splitEmitter(),split.emission(), + neighbours.front(),split.index().initialStateEmitter(),false); + DipolePartonSplitter::change(split.spectator(),split.splitSpectator(), + split.index().initialStateSpectator(),false); +} + +Energy DipoleShowerHandler::nextSubleadingSplitting(Energy hardPt, + Energy optHardPt, Energy optCutoff, + const bool decay) { + + list candidates; + getCandidates(candidates); + + hardScalesSubleading(candidates,hardPt); + for ( list::iterator cand = candidates.begin(); + cand != candidates.end(); cand++ ) { + cand->scale(hardPt); + } + + + list::iterator split = candidates.end(); + + // Winner of all dipoles + DipoleSplittingInfo winner; + // Winner for the current iteration of the for loop + DipoleSplittingInfo candWinner; + Energy winnerScale = 0.0*GeV; + + Energy nextScale = 0.0*GeV; + + for ( list::iterator cand = candidates.begin(); + cand != candidates.end(); cand++ ) { + nextScale = getWinner(candWinner, + cand->index(), + cand->emitterX(),cand->spectatorX(), + make_pair(true,false), + cand->emitter(),cand->spectator(), + hardPt, + optHardPt, + optCutoff); + if ( nextScale > winnerScale ) { + winnerScale = nextScale; + winner = candWinner; + split = cand; + winnerIndex = winningKernelIndex;//check + } + } + + if ( split == candidates.end() ) + return ZERO; + + if ( decay ) + winner.isDecayProc( true ); + + split->fill(winner); + + performSplitting(*split); + eventRecord().update(*split); + + for ( list::iterator dip = candidates.begin(); + dip != candidates.end(); ++dip ) { + if ( dip == split ) + continue; + dip->emission(split->emission()); + if ( dip->emitter() == split->emitter() ) { + dip->splitEmitter(split->splitEmitter()); + } else { + dip->splitEmitter(dip->emitter()); + } + if ( dip->spectator() == split->spectator() ) { + dip->splitSpectator(split->splitSpectator()); + } else { + dip->splitSpectator(dip->spectator()); + } + } + + // Update the ShowerHandler of the splitting reweight. + if ( doSubleadingNc ) { + theSplittingReweight->updateCurrentHandler(); + } + + return split->lastPt(); + +} + + Energy DipoleShowerHandler::getWinner(DipoleSplittingInfo& winner, const Dipole& dip, pair conf, Energy optHardPt, Energy optCutoff) { return getWinner(winner,dip.index(conf), dip.emitterX(conf),dip.spectatorX(conf), conf,dip.emitter(conf),dip.spectator(conf), dip.emitterScale(conf),optHardPt,optCutoff); } Energy DipoleShowerHandler::getWinner(SubleadingSplittingInfo& winner, Energy optHardPt, Energy optCutoff) { return getWinner(winner,winner.index(), winner.emitterX(),winner.spectatorX(), winner.configuration(), winner.emitter(),winner.spectator(), winner.startScale(),optHardPt,optCutoff); } Energy DipoleShowerHandler::getWinner(DipoleSplittingInfo& winner, const DipoleIndex& index, double emitterX, double spectatorX, pair conf, tPPtr emitter, tPPtr spectator, Energy startScale, Energy optHardPt, Energy optCutoff) { if ( !index.initialStateEmitter() && !doFSR() ) { winner.didStopEvolving(); return 0.0*GeV; } if ( index.initialStateEmitter() && !doISR() ) { winner.didStopEvolving(); return 0.0*GeV; } + + if ( index.incomingDecaySpectator() + && !doFSR() ) { + winner.didStopEvolving(); + return 0.0*GeV; + } // Currently do not split IF dipoles so // don't evaluate them in order to avoid // exceptions in the log if ( index.incomingDecayEmitter() ) { winner.didStopEvolving(); return 0.0*GeV; } DipoleSplittingInfo candidate; candidate.index(index); candidate.configuration(conf); candidate.emitterX(emitterX); candidate.spectatorX(spectatorX); - + candidate.emitter(emitter); + candidate.spectator(spectator); + if ( generators().find(candidate.index()) == generators().end() ) getGenerators(candidate.index(),theSplittingReweight); // // NOTE -- needs proper fixing at some point // // For some very strange reason, equal_range gives back // key ranges it hasn't been asked for. This particularly // happens e.g. for FI dipoles of the same kind, but different // PDF (hard vs MPI PDF). I can't see a reason for this, // as DipoleIndex properly implements comparison for equality // and (lexicographic) ordering; for the time being, we // use equal_range, extented by an explicit check for wether // the key is indeed what we wanted. See line after (*) comment // below. // // SW - Update 04/01/2016: Note - This caused a bug for me as I did not // include equality checks on the decay booleans in the == definition pair gens = generators().equal_range(candidate.index()); Energy winnerScale = 0.0*GeV; GeneratorMap::iterator winnerGen = generators().end(); for ( GeneratorMap::iterator gen = gens.first; gen != gens.second; ++gen ) { - - // (*) see NOTE above + + if ( doPartialUnweighting ) + gen->second->doPartialUnweighting(referenceWeight); + + // (*) see NOTE above if ( !(gen->first == candidate.index()) ) continue; if ( startScale <= gen->second->splittingKinematics()->IRCutoff() ) continue; Energy dScale = gen->second->splittingKinematics()->dipoleScale(emitter->momentum(), spectator->momentum()); // in very exceptional cases happening in DIS if ( std::isnan( double(dScale/MeV) ) ) throw RedoShower(); candidate.scale(dScale); // Calculate the mass of the recoil system // for decay dipoles if ( candidate.index().incomingDecaySpectator() || candidate.index().incomingDecayEmitter() ) { Energy recoilMass = gen->second->splittingKinematics()->recoilMassKin(emitter->momentum(), spectator->momentum()); candidate.recoilMass(recoilMass); } // Store emitter and spectator masses, needed in kinematics if ( candidate.index().emitterData()->mass() != ZERO ) { if ( !candidate.index().offShellEmitter() ) candidate.emitterMass( emitter->nominalMass() ); else candidate.emitterMass( emitter->mass() ); } if ( candidate.index().spectatorData()->mass() != ZERO ) { if ( !candidate.index().offShellSpectator() ) candidate.spectatorMass( spectator->nominalMass() ); else candidate.spectatorMass( spectator->mass() ); } candidate.continuesEvolving(); Energy hardScale = evolutionOrdering()->maxPt(startScale,candidate,*(gen->second->splittingKernel())); Energy maxPossible = gen->second->splittingKinematics()->ptMax(candidate.scale(), candidate.emitterX(), candidate.spectatorX(), candidate, *gen->second->splittingKernel()); Energy ircutoff = optCutoff < gen->second->splittingKinematics()->IRCutoff() ? gen->second->splittingKinematics()->IRCutoff() : optCutoff; if ( maxPossible <= ircutoff ) { continue; } if ( maxPossible >= hardScale ){ candidate.hardPt(hardScale); } else { hardScale = maxPossible; candidate.hardPt(maxPossible); } gen->second->generate(candidate,currentWeights(),optHardPt,optCutoff); Energy nextScale = evolutionOrdering()->evolutionScale( gen->second->lastSplitting(),*(gen->second->splittingKernel())); if ( nextScale > winnerScale ) { winner.fill(candidate); gen->second->completeSplitting(winner); winnerGen = gen; winnerScale = nextScale; + if ( continueSubleadingNc() ) + winningKernelIndex = kernelIndex+1;//check } + if ( continueSubleadingNc() ) { + kernelIndex++;//check + scales.push_back(nextScale);//check + theWeightsVector.push_back(gen->second->splittingWeightVector()); + } + reweight(reweight() * gen->second->splittingWeight()); } if ( winnerGen == generators().end() ) { winner.didStopEvolving(); return 0.0*GeV; } if ( winner.stoppedEvolving() ) return 0.0*GeV; return winnerScale; } void DipoleShowerHandler::doCascade(unsigned int& emDone, Energy optHardPt, Energy optCutoff, const bool decay) { if ( nEmissions ) if ( emDone == nEmissions ) return; - + + if ( doSubleadingNc ) { + unsigned int subEmDone = 0; + // Set the starting scale + Energy hardPt = muPt; + + double wref = referenceWeight; + while ( subEmDone < subleadingNcEmissionsLimit && hardPt != ZERO && continueSubleadingNc() ) { + // Clear out the weights from the earlier step + theWeightsVector.clear(); + kernelIndex = 0;//check + scales.clear();//check + + hardPt = nextSubleadingSplitting( hardPt, optHardPt, optCutoff, decay ); + + // Partial unweighting + if ( doPartialUnweightingAtEmission ) { + const double w = reweight(); + if ( abs(w) < wref ) { + if ( abs(w)/wref < UseRandom::rnd() ) { + // Set weight to zero and end this event + reweight(0.0); + return; + } else + reweight( wref*w/abs(w) ); + } + // Update the reference weight after emission + wref *= referenceWeight; + } + + // When the winning scale is larger than the cutoff + // remove the added weights that are under the winning scale + if ( hardPt != ZERO ) { + Energy maxq = 0.0*GeV; + size_t iwinner = theWeightsVector.size();//check + for ( size_t i = 0; i < theWeightsVector.size(); i++ ) { + if ( theWeightsVector[i].size() > 0 ) { + // get<2> is true for an accept step. + if ( std::get<2>(theWeightsVector[i].back()) + && std::get<0>(theWeightsVector[i].back()) > maxq) { + maxq = std::get<0>(theWeightsVector[i].back()); + iwinner = i;//check + } + } + } + + assert(winnerIndex-1 == iwinner);//check + + double correctionWeight = 1.0; + for ( size_t i = 0; i < theWeightsVector.size(); i++ ) { + for ( size_t j = 0; j < theWeightsVector[i].size(); j++ ) { + if ( std::get<0>(theWeightsVector[i][j]) < maxq ) + correctionWeight *= std::get<1>(theWeightsVector[i][j]); + } + } + reweight(reweight()/correctionWeight); + } + + // Increment the number of subleading Nc emissions done + subEmDone++; + // Stop if the limit of emissions is reached + if ( nEmissions ) + if ( ++emDone == nEmissions ) + return; + } + + // Subleading shower done, prepare chains for the standard + // dipole shower + eventRecord().prepareChainsSubleading( decay ); + // Set scales + for ( list::iterator ch = eventRecord().chains().begin(); + ch != eventRecord().chains().end(); ch++ ) { + for ( list::iterator dp = ch->dipoles().begin(); + dp != ch->dipoles().end(); dp++ ) { + dp->emitterScale(make_pair(true,false),hardPt); + dp->emitterScale(make_pair(false,true),hardPt); + } + } + + } + + DipoleSplittingInfo winner; DipoleSplittingInfo dipoleWinner; while ( eventRecord().haveChain() ) { // allow the dipole chain to be rearranged according to arXiv:1801.06113 if( _rearrange && ( _rearrangeNEmissions < 0 || _rearrangeNEmissions >= int(emDone) ) ){ eventRecord().currentChain().rearrange(_dipmax,_diplong); } if ( verbosity > 2 ) { generator()->log() << "DipoleShowerHandler selecting splittings for the chain:\n" << eventRecord().currentChain() << flush; } list::iterator winnerDip = eventRecord().currentChain().dipoles().end(); Energy winnerScale = 0.0*GeV; Energy nextLeftScale = 0.0*GeV; Energy nextRightScale = 0.0*GeV; for ( list::iterator dip = eventRecord().currentChain().dipoles().begin(); dip != eventRecord().currentChain().dipoles().end(); ++dip ) { nextLeftScale = getWinner(dipoleWinner,*dip,{true,false},optHardPt,optCutoff); if ( nextLeftScale > winnerScale ) { winnerScale = nextLeftScale; winner = dipoleWinner; winnerDip = dip; } nextRightScale = getWinner(dipoleWinner,*dip,{false,true},optHardPt,optCutoff); if ( nextRightScale > winnerScale ) { winnerScale = nextRightScale; winner = dipoleWinner; winnerDip = dip; } if ( evolutionOrdering()->independentDipoles() ) { Energy dipScale = max(nextLeftScale,nextRightScale); if ( dip->leftScale() > dipScale ) dip->leftScale(dipScale); if ( dip->rightScale() > dipScale ) dip->rightScale(dipScale); } } if ( verbosity > 1 ) { if ( winnerDip != eventRecord().currentChain().dipoles().end() ) generator()->log() << "DipoleShowerHandler selected the splitting:\n" << winner << " for the dipole\n" << (*winnerDip) << flush; else generator()->log() << "DipoleShowerHandler could not select a splitting above the IR cutoff\n" << flush; } // pop the chain if no dipole did radiate if ( winnerDip == eventRecord().currentChain().dipoles().end() ) { eventRecord().popChain(); if ( theEventReweight && eventRecord().chains().empty() ) if ( (theEventReweight->firstInteraction() && firstInteraction()) || (theEventReweight->secondaryInteractions() && !firstInteraction()) ) { double w = theEventReweight->weightCascade(eventRecord().incoming(), eventRecord().outgoing(), eventRecord().hard(),theGlobalAlphaS); reweight(reweight()*w); } continue; } // otherwise perform the splitting // but first see if the emission would produce a configuration in the ME region. if ( theMergingHelper && eventHandler()->currentCollision() && !decay && firstInteraction() ) { if (theMergingHelper->maxLegs()>eventRecord().outgoing().size()+ eventRecord().hard().size() +2){//incoming if (theMergingHelper->mergingScale()emissionProbability() < UseRandom::rnd()) { theMergingHelper->setEmissionProbability(0.); const bool transparent=true; if (transparent) { pair::iterator,list::iterator> tmpchildren; DipoleSplittingInfo tmpwinner=winner; DipoleChain* tmpfirstChain = nullptr; DipoleChain* tmpsecondChain = nullptr; auto New=eventRecord().tmpsplit(winnerDip,tmpwinner, tmpchildren,tmpfirstChain, tmpsecondChain); if (theMergingHelper->matrixElementRegion(New.first, New.second, winnerScale, theMergingHelper->mergingScale())) { optHardPt=winnerScale; continue; } }else{ optHardPt=winnerScale; continue; } } } } if(theMergingHelper&&firstInteraction()) optHardPt=ZERO; didRadiate = true; eventRecord().isMCatNLOSEvent(false); eventRecord().isMCatNLOHEvent(false); pair::iterator,list::iterator> children; DipoleChain* firstChain = nullptr; DipoleChain* secondChain = nullptr; + // Generate the azimuthal angle + if ( spinCorrelations() ) + vertexRecord().generatePhi(winner,*winnerDip); + + if ( decay ) + winner.isDecayProc( true ); + // Note: the dipoles are updated in eventRecord().split(....) after the splitting, // hence the entire cascade is handled in doCascade // The dipole scales are updated in dip->split(....) if ( decay ) winner.isDecayProc( true ); eventRecord().split(winnerDip,winner,children,firstChain,secondChain); + + // Update the vertex record following the splitting + if ( spinCorrelations() ) + vertexRecord().update(winner); + assert(firstChain && secondChain); evolutionOrdering()->setEvolutionScale(winnerScale,winner,*firstChain,children); if ( !secondChain->dipoles().empty() ) evolutionOrdering()->setEvolutionScale(winnerScale,winner,*secondChain,children); if ( verbosity > 1 ) { generator()->log() << "DipoleShowerHandler did split the last selected dipole into:\n" << (*children.first) << (*children.second) << flush; } if ( verbosity > 2 ) { generator()->log() << "After splitting the last selected dipole, " << "DipoleShowerHandler encountered the following chains:\n" << (*firstChain) << (*secondChain) << flush; } if ( theEventReweight ) if ( (theEventReweight->firstInteraction() && firstInteraction()) || (theEventReweight->secondaryInteractions() && !firstInteraction()) ) { double w = theEventReweight->weight(eventRecord().incoming(), eventRecord().outgoing(), eventRecord().hard(),theGlobalAlphaS); reweight(reweight()*w); } if ( nEmissions ) if ( ++emDone == nEmissions ) return; } } bool DipoleShowerHandler::realign() { if ( !didRadiate && !intrinsicPtGenerator ) return false; if ( eventRecord().incoming().first->coloured() || eventRecord().incoming().second->coloured() ) { if ( eventRecord().incoming().first->momentum().perp2()/GeV2 < 1e-10 && eventRecord().incoming().second->momentum().perp2()/GeV2 < 1e-10 ) return false; pair inMomenta (eventRecord().incoming().first->momentum(), eventRecord().incoming().second->momentum()); - SpinOneLorentzRotation transform((inMomenta.first+inMomenta.second).findBoostToCM()); + LorentzRotation transform((inMomenta.first+inMomenta.second).findBoostToCM()); Axis dir = (transform * inMomenta.first).vect().unit(); Axis rot (-dir.y(),dir.x(),0); double theta = dir.theta(); if ( lastParticles().first->momentum().z() < ZERO ) theta = -theta; transform.rotate(-theta,rot); inMomenta.first = transform*inMomenta.first; inMomenta.second = transform*inMomenta.second; assert(inMomenta.first.z() > ZERO && inMomenta.second.z() < ZERO); Energy2 sHat = (eventRecord().incoming().first->momentum() + eventRecord().incoming().second->momentum()).m2(); pair masses(eventRecord().incoming().first->mass(), eventRecord().incoming().second->mass()); pair qs; if ( !eventRecord().incoming().first->coloured() ) { assert(masses.second == ZERO); qs.first = eventRecord().incoming().first->momentum().z(); qs.second = (sHat-sqr(masses.first))/(2.*(qs.first+sqrt(sqr(masses.first)+sqr(qs.first)))); } else if ( !eventRecord().incoming().second->coloured() ) { assert(masses.first == ZERO); qs.second = eventRecord().incoming().second->momentum().z(); qs.first = (sHat-sqr(masses.second))/(2.*(qs.second+sqrt(sqr(masses.second)+sqr(qs.second)))); } else { assert(masses.first == ZERO && masses.second == ZERO); if ( realignmentScheme == 0 ) { double yX = eventRecord().pX().rapidity(); double yInt = (transform*eventRecord().pX()).rapidity(); double dy = yX-yInt; qs.first = (sqrt(sHat)/2.)*exp(dy); qs.second = (sqrt(sHat)/2.)*exp(-dy); } else if ( realignmentScheme == 1 ) { Energy sS = sqrt((lastParticles().first->momentum() + lastParticles().second->momentum()).m2()); qs.first = eventRecord().fractions().first * sS / 2.; qs.second = eventRecord().fractions().second * sS / 2.; } } double beta = (qs.first-qs.second) / ( sqrt(sqr(masses.first)+sqr(qs.first)) + sqrt(sqr(masses.second)+sqr(qs.second)) ); transform.boostZ(beta); Lorentz5Momentum tmp; if ( eventRecord().incoming().first->coloured() ) { tmp = eventRecord().incoming().first->momentum(); tmp = transform * tmp; eventRecord().incoming().first->set5Momentum(tmp); } if ( eventRecord().incoming().second->coloured() ) { tmp = eventRecord().incoming().second->momentum(); tmp = transform * tmp; eventRecord().incoming().second->set5Momentum(tmp); } eventRecord().transform(transform); return true; } return false; } void DipoleShowerHandler::resetAlphaS(Ptr::tptr as) { for ( auto & k : kernels) { if ( !k->alphaS() ) k->alphaS(as); k->renormalizationScaleFreeze(theRenormalizationScaleFreeze); k->factorizationScaleFreeze(theFactorizationScaleFreeze); } // clear the generators to be rebuild // actually, there shouldn't be any generators // when this happens. generators().clear(); } void DipoleShowerHandler::resetReweight(Ptr::tptr rw) { for ( auto & g : generators() ) g.second->splittingReweight(rw); } void DipoleShowerHandler::getGenerators(const DipoleIndex& ind, Ptr::tptr rw) { bool gotone = false; for ( auto & k : kernels ) { if ( k->canHandle(ind) ) { if ( verbosity > 0 ) { generator()->log() << "DipoleShowerHandler encountered the dipole configuration\n" << ind << " in event number " << eventHandler()->currentEvent()->number() << "\nwhich can be handled by the splitting kernel '" << k->name() << "'.\n" << flush; } gotone = true; Ptr::ptr nGenerator = new_ptr(DipoleSplittingGenerator()); nGenerator->doCompensate(theDoCompensate); nGenerator->splittingKernel(k); if ( renormalizationScaleFactor() != 1. ) nGenerator->splittingKernel()->renormalizationScaleFactor(renormalizationScaleFactor()); if ( factorizationScaleFactor() != 1. ) nGenerator->splittingKernel()->factorizationScaleFactor(factorizationScaleFactor()); if ( !nGenerator->splittingReweight() ) nGenerator->splittingReweight(rw); nGenerator->splittingKernel()->freezeGrid(theFreezeGrid); nGenerator->splittingKernel()->detuning(theDetuning); GeneratorMap::const_iterator equivalent = generators().end(); for ( GeneratorMap::const_iterator eq = generators().begin(); eq != generators().end(); ++eq ) { if ( !eq->second->wrapping() ) if ( k->canHandleEquivalent(ind,*(eq->second->splittingKernel()),eq->first) ) { equivalent = eq; if ( verbosity > 0 ) { generator()->log() << "The dipole configuration " << ind << " can equivalently be handled by the existing\n" << "generator for configuration " << eq->first << " using the kernel '" << eq->second->splittingKernel()->name() << "'\n" << flush; } break; } } if ( equivalent != generators().end() ) { nGenerator->wrap(equivalent->second); } DipoleSplittingInfo dummy; dummy.index(ind); nGenerator->prepare(dummy); generators().insert({ind,nGenerator}); } } if ( !gotone ) { throw Exception() << "DipoleShowerHandler could not " << "find a splitting kernel which is able " << "to handle splittings off the dipole " << ind << ".\n" << "Please check the input files." << Exception::runerror; } } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void DipoleShowerHandler::doinit() { ShowerHandler::doinit(); if ( theGlobalAlphaS ) resetAlphaS(theGlobalAlphaS); // copy off-shell particle ids before showering from input vector to the // set used in the simulation if ( theColouredOffShellInShower.empty() ) { for(unsigned int ix=0;ixsplittingKinematics()->openZBoundaries() == 0 ) zChoice0 = true; else if ( k->splittingKinematics()->openZBoundaries() == 1 ) zChoice1 = true; else zChoiceOther = true; // either inconsistent or other option which cannot be handled by the matching if ( zChoice0 && zChoice1 ) { zChoiceOther = true; break; } } if ( zChoiceOther ) theZBoundaries = 2; else if ( zChoice1 ) theZBoundaries = 1; else if ( zChoice0 ) theZBoundaries = 0; } void DipoleShowerHandler::dofinish() { ShowerHandler::dofinish(); } void DipoleShowerHandler::doinitrun() { ShowerHandler::doinitrun(); } void DipoleShowerHandler::persistentOutput(PersistentOStream & os) const { os << kernels << theEvolutionOrdering << constituentReshuffler << intrinsicPtGenerator << theGlobalAlphaS << chainOrderVetoScales << nEmissions << discardNoEmissions << firstMCatNLOEmission << thePowhegDecayEmission + //<< theAnalyseSpinCorrelations + << doSubleadingNc << subleadingNcEmissionsLimit + << densityOperatorEvolution << ounit(densityOperatorCutoff,GeV2) + << doPartialUnweightingAtEmission + << doPartialUnweighting << referenceWeight + << cmecReweightFactor << negCMECScaling << realignmentScheme << verbosity << printEvent << ounit(theRenormalizationScaleFreeze,GeV) << ounit(theFactorizationScaleFreeze,GeV) << theShowerApproximation << theDoCompensate << theFreezeGrid << theDetuning << theEventReweight << theSplittingReweight << ounit(maxPt,GeV) << ounit(muPt,GeV)<< theMergingHelper << theColouredOffShellInShower << theInputColouredOffShellInShower << _rearrange << _dipmax << _diplong << _rearrangeNEmissions << theZBoundaries; } void DipoleShowerHandler::persistentInput(PersistentIStream & is, int) { is >> kernels >> theEvolutionOrdering >> constituentReshuffler >> intrinsicPtGenerator >> theGlobalAlphaS >> chainOrderVetoScales >> nEmissions >> discardNoEmissions >> firstMCatNLOEmission >> thePowhegDecayEmission + //>> theAnalyseSpinCorrelations + >> doSubleadingNc >> subleadingNcEmissionsLimit + >> densityOperatorEvolution >> iunit(densityOperatorCutoff,GeV2) + >> doPartialUnweightingAtEmission + >> doPartialUnweighting >> referenceWeight + >> cmecReweightFactor >> negCMECScaling >> realignmentScheme >> verbosity >> printEvent >> iunit(theRenormalizationScaleFreeze,GeV) >> iunit(theFactorizationScaleFreeze,GeV) >> theShowerApproximation >> theDoCompensate >> theFreezeGrid >> theDetuning >> theEventReweight >> theSplittingReweight >> iunit(maxPt,GeV) >> iunit(muPt,GeV)>>theMergingHelper >> theColouredOffShellInShower >> theInputColouredOffShellInShower >> _rearrange >> _dipmax >> _diplong >> _rearrangeNEmissions >> theZBoundaries; } ClassDescription DipoleShowerHandler::initDipoleShowerHandler; // Definition of the static class description member. void DipoleShowerHandler::Init() { static ClassDocumentation documentation ("The DipoleShowerHandler class manages the showering using " "the dipole shower algorithm.", "The shower evolution was performed using the algorithm described in " "\\cite{Platzer:2009jq} and \\cite{Platzer:2011bc}.", "%\\cite{Platzer:2009jq}\n" "\\bibitem{Platzer:2009jq}\n" "S.~Platzer and S.~Gieseke,\n" "``Coherent Parton Showers with Local Recoils,''\n" " JHEP {\\bf 1101}, 024 (2011)\n" "arXiv:0909.5593 [hep-ph].\n" "%%CITATION = ARXIV:0909.5593;%%\n" "%\\cite{Platzer:2011bc}\n" "\\bibitem{Platzer:2011bc}\n" "S.~Platzer and S.~Gieseke,\n" "``Dipole Showers and Automated NLO Matching in Herwig,''\n" "arXiv:1109.6256 [hep-ph].\n" "%%CITATION = ARXIV:1109.6256;%%"); static RefVector interfaceKernels ("Kernels", "Set the splitting kernels to be used by the dipole shower.", &DipoleShowerHandler::kernels, -1, false, false, true, false, false); static Reference interfaceEvolutionOrdering ("EvolutionOrdering", "Set the evolution ordering to be used.", &DipoleShowerHandler::theEvolutionOrdering, false, false, true, false, false); static Reference interfaceConstituentReshuffler ("ConstituentReshuffler", "The object to be used to reshuffle partons to their constitutent mass shells.", &DipoleShowerHandler::constituentReshuffler, false, false, true, true, false); static Reference interfaceIntrinsicPtGenerator ("IntrinsicPtGenerator", "Set the object in charge to generate intrinsic pt for incoming partons.", &DipoleShowerHandler::intrinsicPtGenerator, false, false, true, true, false); static Reference interfaceGlobalAlphaS - ("GlobalAlphaS", - "Set a global strong coupling for all splitting kernels.", - &DipoleShowerHandler::theGlobalAlphaS, false, false, true, true, false); - + ("GlobalAlphaS", + "Set a global strong coupling for all splitting kernels.", + &DipoleShowerHandler::theGlobalAlphaS, false, false, true, true, false); +// Start: Trying to add interface for the subleading Nc + static Switch interfaceDoSubleadingNc + ("DoSubleadingNc", + "Switch on or off subleading Nc corrections.", + &DipoleShowerHandler::doSubleadingNc, true, false, false); + static SwitchOption interfaceDoSubleadingNcOn + (interfaceDoSubleadingNc, + "On", + "Switch on subleading Nc corrections.", + true); + static SwitchOption interfaceDoSubleadingNcOff + (interfaceDoSubleadingNc, + "Off", + "Switch off subleading Nc corrections.", + false); + // Limit for how many subleading Nc emissions should be calculated + static Parameter interfaceSubleadingNcEmissionsLimit + ("SubleadingNcEmissionsLimit", + "Number of emissions to calculate subleading Nc corrections for.", + &DipoleShowerHandler::subleadingNcEmissionsLimit,0,0,0, + false, false, Interface::lowerlim); + static Parameter interfaceDensityOperatorEvolution + ("DensityOperatorEvolution", + "Scheme for evolving the density operator.", + &DipoleShowerHandler::densityOperatorEvolution,0,0,0, + false, false, Interface::lowerlim); + static Parameter interfaceDensityOperatorCutoff + ("DensityOperatorCutoff", + "Cutoff for momentum invariants for the density operator evolution.", + &DipoleShowerHandler::densityOperatorCutoff,GeV2,1.0*GeV2,0.0*GeV2,0*GeV2, + false, false, Interface::lowerlim); + static Switch interfaceDoPartialUnweightingAtEmission + ("DoPartialUnweightingAtEmission", + "Switch on or off partial unweighting at the emission level.", + &DipoleShowerHandler::doPartialUnweightingAtEmission,true,false,false); + static SwitchOption interfaceDoPartialUnweightingAtEmissionOn + (interfaceDoPartialUnweightingAtEmission, + "On", + "Switch on partial unweighting.", + true); + static SwitchOption interfaceDoPartialUnweightingAtEmissionOff + (interfaceDoPartialUnweightingAtEmission, + "Off", + "Switch off partial unweighting.", + false); + static Switch interfaceDoPartialUnweighting + ("DoPartialUnweighting", + "Switch on or off partial unweighting at the dipole splitting level.", + &DipoleShowerHandler::doPartialUnweighting,true,false,false); + static SwitchOption interfaceDoPartialUnweightingOn + (interfaceDoPartialUnweighting, + "On", + "Switch on partial unweighting.", + true); + static SwitchOption interfaceDoPartialUnweightingOff + (interfaceDoPartialUnweighting, + "Off", + "Switch off partial unweighting.", + false); + static Parameter interfaceReferenceWeight + ("ReferenceWeight", + "Reference weight for the partial unweighting.", + &DipoleShowerHandler::referenceWeight,0.1,0.0,0, + false, false, Interface::lowerlim); + static Parameter interfaceCMECReweightFactor + ("CMECReweightFactor", + "Factor used in the reweighting algorithm.", + &DipoleShowerHandler::cmecReweightFactor,1.0,0.0,0, + false, false, Interface::lowerlim); + static Parameter interfaceNegCMECScaling + ("NegCMECScaling", + "Scaling factor for the negative colour matrix element corrections (CMECs).", + &DipoleShowerHandler::negCMECScaling,0.0,0.0,0, + false, false, Interface::lowerlim); static Switch interfaceRealignmentScheme ("RealignmentScheme", "The realignment scheme to use.", &DipoleShowerHandler::realignmentScheme, 0, false, false); static SwitchOption interfaceRealignmentSchemePreserveRapidity (interfaceRealignmentScheme, "PreserveRapidity", "Preserve the rapidity of non-coloured outgoing system.", 0); static SwitchOption interfaceRealignmentSchemeEvolutionFractions (interfaceRealignmentScheme, "EvolutionFractions", "Use momentum fractions as generated by the evolution.", 1); static SwitchOption interfaceRealignmentSchemeCollisionFrame (interfaceRealignmentScheme, "CollisionFrame", "Determine realignment from collision frame.", 2); static Switch interfaceChainOrderVetoScales ("ChainOrderVetoScales", "[experimental] Switch the chain ordering for veto scales on or off.", &DipoleShowerHandler::chainOrderVetoScales, true, false, false); static SwitchOption interfaceChainOrderVetoScalesYes (interfaceChainOrderVetoScales, "Yes", "Switch on chain ordering for veto scales.", true); static SwitchOption interfaceChainOrderVetoScalesNo (interfaceChainOrderVetoScales, "No", "Switch off chain ordering for veto scales.", false); interfaceChainOrderVetoScales.rank(-1); static Parameter interfaceNEmissions ("NEmissions", "[debug option] Limit the number of emissions to be generated. Zero does not limit the number of emissions.", &DipoleShowerHandler::nEmissions, 0, 0, 0, false, false, Interface::lowerlim); interfaceNEmissions.rank(-1); static Switch interfaceDiscardNoEmissions ("DiscardNoEmissions", "[debug option] Discard events without radiation.", &DipoleShowerHandler::discardNoEmissions, false, false, false); static SwitchOption interfaceDiscardNoEmissionsYes (interfaceDiscardNoEmissions, "Yes", "Discard events without radiation.", true); static SwitchOption interfaceDiscardNoEmissionsNo (interfaceDiscardNoEmissions, "No", "Do not discard events without radiation.", false); interfaceDiscardNoEmissions.rank(-1); static Switch interfaceFirstMCatNLOEmission ("FirstMCatNLOEmission", "[debug option] Only perform the first MC@NLO emission.", &DipoleShowerHandler::firstMCatNLOEmission, false, false, false); static SwitchOption interfaceFirstMCatNLOEmissionYes (interfaceFirstMCatNLOEmission, "Yes", "Perform only the first MC@NLO emission.", true); static SwitchOption interfaceFirstMCatNLOEmissionNo (interfaceFirstMCatNLOEmission, "No", "Produce all emissions.", false); interfaceFirstMCatNLOEmission.rank(-1); static Parameter interfaceVerbosity ("Verbosity", "[debug option] Set the level of debug information provided.", &DipoleShowerHandler::verbosity, 0, 0, 0, false, false, Interface::lowerlim); interfaceVerbosity.rank(-1); static Parameter interfacePrintEvent ("PrintEvent", "[debug option] The number of events for which debugging information should be provided.", &DipoleShowerHandler::printEvent, 0, 0, 0, false, false, Interface::lowerlim); interfacePrintEvent.rank(-1); static Parameter interfaceRenormalizationScaleFreeze ("RenormalizationScaleFreeze", "The freezing scale for the renormalization scale.", &DipoleShowerHandler::theRenormalizationScaleFreeze, GeV, 1.0*GeV, 0.0*GeV, 0*GeV, false, false, Interface::lowerlim); static Parameter interfaceFactorizationScaleFreeze ("FactorizationScaleFreeze", "The freezing scale for the factorization scale.", &DipoleShowerHandler::theFactorizationScaleFreeze, GeV, 2.0*GeV, 0.0*GeV, 0*GeV, false, false, Interface::lowerlim); static Switch interfaceDoCompensate ("DoCompensate", "", &DipoleShowerHandler::theDoCompensate, false, false, false); static SwitchOption interfaceDoCompensateYes (interfaceDoCompensate, "Yes", "", true); static SwitchOption interfaceDoCompensateNo (interfaceDoCompensate, "No", "", false); static Parameter interfaceFreezeGrid ("FreezeGrid", "", &DipoleShowerHandler::theFreezeGrid, 500000, 1, 0, false, false, Interface::lowerlim); static Parameter interfaceDetuning ("Detuning", "A value to detune the overestimate kernel.", &DipoleShowerHandler::theDetuning, 1.0, 1.0, 0, false, false, Interface::lowerlim); static Reference interfaceEventReweight ("EventReweight", "", &DipoleShowerHandler::theEventReweight, false, false, true, true, false); static Reference interfaceSplittingReweight ("SplittingReweight", "Set the splitting reweight.", &DipoleShowerHandler::theSplittingReweight, false, false, true, true, false); static Switch interfacePowhegDecayEmission ("PowhegDecayEmission", "Use Powheg style emission for the decays", &DipoleShowerHandler::thePowhegDecayEmission, true, false, false); static SwitchOption interfacePowhegDecayEmissionYes (interfacePowhegDecayEmission,"Yes","Powheg decay emission on", true); static SwitchOption interfacePowhegDecayEmissionNo (interfacePowhegDecayEmission,"No","Powheg decay emission off", false); static ParVector interfaceOffShellInShower ("OffShellInShower", "PDG codes of the coloured particles that can be off-shell in the process.", &DipoleShowerHandler::theInputColouredOffShellInShower, -1, 0l, -10000000l, 10000000l, false, false, Interface::limited); + /* + static Switch interfaceAnalyseSpinCorrelations + ("AnalyseSpinCorrelations", + "Record the information required for the spin correlation analyis.", + &DipoleShowerHandler::theAnalyseSpinCorrelations, false, false, false); + + static SwitchOption interfaceAnalyseSpinCorrelationsYes + (interfaceAnalyseSpinCorrelations,"Yes","Record the information for analysing the spin correlations.", true); + + static SwitchOption interfaceAnalyseSpinCorrelationsNo + (interfaceAnalyseSpinCorrelations,"No","Do not record extra information.", false); + */ + static Switch interfacerearrange ("Rearrange", "Allow rearranging of dipole chains according to arXiv:1801.06113", &DipoleShowerHandler::_rearrange, false, false, false); static SwitchOption interfacerearrangeYes (interfacerearrange,"Yes","_rearrange on", true); static SwitchOption interfacerearrangeNo (interfacerearrange,"No","_rearrange off", false); static Parameter interfacedipmax ("DipMax", "Allow rearrangment of color chains with ME including dipmax dipoles.", &DipoleShowerHandler::_dipmax, 0, 0, 0, false, false, Interface::lowerlim); static Parameter interfacediplong ("DipLong", "Dipole chains with more than dipmax dipoles are treated as long. \ diplong=3 rearranges these chains with eeuugg MEs, \ diplong=4 rearranges these chains with eeuuggg MEs (slower), \ diplong=5 rearranges these chains with eeuugggg MEs (slow).\ Note: Numerically there is no difference between the options. ", &DipoleShowerHandler::_diplong, 0, 0, 0, false, false, Interface::lowerlim); static Parameter interfacedcorrectNemissions ("RearrangeNEmissions", "Allow rearrangment of color chains up to the nth emission.", &DipoleShowerHandler::_rearrangeNEmissions, 0, 0, 0, false, false, Interface::lowerlim); } + diff --git a/Shower/Dipole/DipoleShowerHandler.h b/Shower/Dipole/DipoleShowerHandler.h --- a/Shower/Dipole/DipoleShowerHandler.h +++ b/Shower/Dipole/DipoleShowerHandler.h @@ -1,603 +1,777 @@ // -*- C++ -*- // // DipoleShowerHandler.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_DipoleShowerHandler_H #define HERWIG_DipoleShowerHandler_H // // This is the declaration of the DipoleShowerHandler class. // #include "Herwig/Shower/ShowerHandler.h" #include "Herwig/Shower/Dipole/DipoleShowerHandler.fh" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingReweight.h" #include "Herwig/Shower/Dipole/Kernels/DipoleSplittingKernel.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleEventRecord.h" #include "Herwig/Shower/Dipole/Base/DipoleEvolutionOrdering.h" #include "Herwig/Shower/Dipole/Base/DipoleEventReweight.h" #include "Herwig/Shower/Dipole/Utility/ConstituentReshuffler.h" #include "Herwig/Shower/Dipole/Utility/IntrinsicPtGenerator.h" #include "Herwig/MatrixElement/Matchbox/Base/MergerBase.h" #include "Herwig/MatrixElement/Matchbox/Matching/ShowerApproximation.h" +#include "Herwig/Shower/Dipole/SpinCorrelations/DipoleVertexRecord.h" +#include "Herwig/MatrixElement/Matchbox/Utility/DensityOperator.h" + +#include + namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Stephen Webster * * \brief The DipoleShowerHandler class manages the showering using * the dipole shower algorithm. * * @see \ref DipoleShowerHandlerInterfaces "The interfaces" * defined for DipoleShowerHandler. */ class DipoleShowerHandler: public ShowerHandler { friend class Merger; public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ DipoleShowerHandler(); /** * The destructor. */ virtual ~DipoleShowerHandler(); //@} public: inline void colourPrint(); /** * Indicate a problem in the shower. */ struct RedoShower {}; /** * Insert an additional splitting kernel. */ void addSplitting(Ptr::ptr sp) { kernels.push_back(sp); } /** * Reset the alpha_s for all splitting kernels. */ void resetAlphaS(Ptr::tptr); virtual void cascade(tPVector); /** * Reset the splitting reweight for all splitting kernels. */ void resetReweight(Ptr::tptr); /** * Return true, if the shower handler can generate a truncated * shower for POWHEG style events generated using Matchbox */ virtual bool canHandleMatchboxTrunc() const { return false; } /** * Return true, if this cascade handler will perform reshuffling from hard * process masses. */ virtual bool isReshuffling() const { return false; } /** * Return the relevant hard scale to be used in the profile scales */ virtual Energy hardScale() const { return muPt; } /** * Calculate the alpha_s value the shower uses for Q. */ double as(Energy Q)const{return theGlobalAlphaS->value(sqr(Q));} /** * Return the number of scale dependent active flavours from * the alpha_s object. */ double Nf(Energy Q)const{return theGlobalAlphaS->Nf(sqr(Q));} + /* + * Access the vertex record + */ + DipoleVertexRecord& vertexRecord() { return theVertexRecord; } + + /** + * Return the event record + */ + const DipoleVertexRecord& vertexRecord() const { return theVertexRecord; } + /** * Set the pointer to the Merging Helper. * Used by the merging factory. */ void setMerger(Ptr::ptr mh){theMergingHelper=mh;} +public: + + /** + * Return the dictionary for the incoming particles and outgoing partons + * in the event, will be empty if subleading Nc is turned off. + */ + const map& particleIndices() const { return eventRecord().particleIndices(); } + + /** + * Return the particle data vector of the particles in the event, will + * be empty if subleading Nc is turned off. + */ + const cPDVector& particlesAfter() const { return eventRecord().particlesAfter(); } + + /** + * Return the density operator from the event record. + */ + DensityOperator& densityOperator() { return eventRecord().densityOperator(); } + + /** + * Return the colour matrix element correction map, will be empty if + * subleading Nc is turned off. NOT USED, REMOVE + */ + const map,pair >,double>& correlatorMap() const { + return eventRecord().densityOperator().correlatorMap(); + } + + /** + * Return the colour basis used in the density operator. NOT USED, REMOVE + */ + Ptr::tptr colourBasis() { + return eventRecord().densityOperator().colourBasis(); + } + + /** + * Return the continue subleading Nc flag from the event record. + */ + bool continueSubleadingNc() const { return eventRecord().getContinueSubleadingNc(); } + +protected: + + /** + * Add the possible splitting candidates given a pair of emitting particles + */ + void addCandidates(PPair particles, list& clist) const; + + /** + * Get all possible radiating dipoles + */ + void getCandidates(list& clist) const; + + /** + * Perform a splitting independent of any chains + */ + void performSplitting(DipoleSplittingInfo&) const; + + /** + * Generate the next subleading Nc improved splitting and return the scale + */ + Energy nextSubleadingSplitting(Energy hardPt, + Energy optHardPt, Energy optCutoff, + const bool decay); + protected: typedef multimap::ptr> GeneratorMap; /** * The main method which manages the showering of a subprocess. */ virtual tPPair cascade(tSubProPtr sub, XCombPtr xcomb) { return cascade(sub,xcomb,ZERO,ZERO); } /** * The main method which manages the showering of a subprocess. */ tPPair cascade(tSubProPtr sub, XCombPtr xcomb, Energy optHardPt, Energy optCutoff); /** * Build splitting generators for the given * dipole index. */ void getGenerators(const DipoleIndex&, Ptr::tptr rw = Ptr::tptr()); /** * Setup the hard scales. */ void hardScales(Energy2 scale); /** + * Setup the hard scales of the dipoles after subleading emissions.//debug + */ + void hardScalesSubleading(list candidates,Energy hardPt); + + /** * Return the evolution ordering */ Ptr::tptr evolutionOrdering() const { return theEvolutionOrdering; } /** * Reshuffle to constituent mass shells */ void constituentReshuffle(); /** * Reshuffle to constituent mass shells */ void decayConstituentReshuffle( PerturbativeProcessPtr decayProc); /** * Access the generator map */ GeneratorMap& generators() { return theGenerators; } /** * Access the event record */ DipoleEventRecord& eventRecord() { return theEventRecord; } /** * Return the event record */ const DipoleEventRecord& eventRecord() const { return theEventRecord; } /** * Return the splitting kernels. */ const vector::ptr>& splittingKernels() const { return kernels; } /** * Return the set of offshell parton ids. **/ const set& offShellPartons() { return theColouredOffShellInShower; } /** * Realign the event such as to have the incoming partons along thre * beam axes. */ bool realign(); /** * The choice of z boundaries; 0 = restricted, 1 = open, 2 = mixed/other */ virtual int showerPhaseSpaceOption() const { return theZBoundaries; } protected: /** * Perform the cascade. */ void doCascade(unsigned int& emDone, Energy optHardPt = ZERO, Energy optCutoff = ZERO, const bool decay = false); /** * Set the number of emissions **/ void setNEmissions(unsigned int n){nEmissions=n;} /** * Get the winning splitting for the * given dipole and configuration. */ Energy getWinner(DipoleSplittingInfo& winner, const Dipole& dip, pair conf, Energy optHardPt = ZERO, Energy optCutoff = ZERO); /** * Get the winning splitting for the * given dipole and configuration. */ + Energy getWinner(DipoleSplittingInfo& winner, + Energy optHardPt = ZERO, + Energy optCutoff = ZERO); + + /** + * Get the winning splitting for the + * given dipole and configuration. + */ Energy getWinner(SubleadingSplittingInfo& winner, Energy optHardPt = ZERO, Energy optCutoff = ZERO); /** * Get the winning splitting for the * given dipole and configuration. */ Energy getWinner(DipoleSplittingInfo& winner, const DipoleIndex& index, double emitterX, double spectatorX, pair conf, tPPtr emitter, tPPtr spectator, Energy startScale, Energy optHardPt = ZERO, Energy optCutoff = ZERO); public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). protected: /** @name Standard Interfaced functions. */ //@{ /** * Initialize this object after the setup phase before saving an * EventGenerator to disk. * @throws InitException if object could not be initialized properly. */ virtual void doinit(); /** * Initialize this object. Called in the run phase just before * a run begins. */ virtual void doinitrun(); /** * Finalize this object. Called in the run phase just after a * run has ended. Used eg. to write out statistics. */ virtual void dofinish(); //@} private: /** * The splitting kernels to be used. */ vector::ptr> kernels; /** * The evolution ordering considered */ Ptr::ptr theEvolutionOrdering; /** * The ConstituentReshuffler to be used */ Ptr::ptr constituentReshuffler; /** * The intrinsic pt generator to be used. */ Ptr::ptr intrinsicPtGenerator; /** * A global alpha_s to be used for all splitting kernels. */ Ptr::ptr theGlobalAlphaS; /** * Apply chain ordering to events from matrix * element corrections. */ bool chainOrderVetoScales; /** * Limit the number of emissions. * Limit applied if > 0. */ unsigned int nEmissions; /** * Discard events which did not radiate. */ bool discardNoEmissions; /** * Perform the first MC@NLO emission only. */ bool firstMCatNLOEmission; /** * True if powheg style emissions are to be used in the decays */ bool thePowhegDecayEmission; + /** + * Switch to record information required for the + * nearest neighbour analysis. + */ + //bool theAnalyseSpinCorrelations; + /** * The realignment scheme */ int realignmentScheme; + /** + * Switch on or off subleading Nc corrections + */ + bool doSubleadingNc; + + /** + * Number of emissions to do subleading Nc corrections to. + */ + size_t subleadingNcEmissionsLimit; + + /** + * Current reference weight used for partial unweighting of the + * subleading colour shower (updated each emission). + */ + int currentReferenceWeight; + + /** + * Integer used to set which method of evolving the density operator + * to use: + * 0 - Vijk is Eikonal but there is a cutoff. + * 1 - Vijk is Eikonal. + * 2 - Vijk=1 for all i,j,k. + * 3 - Semi-leading Nc, Vijk=0 for all + */ + int densityOperatorEvolution; + + /** + * Cutoff scale for the invariants (e.g. pEmitter*pEmission) in the + * Eikonal dipole kernel, Vijk. + */ + Energy2 densityOperatorCutoff; + + /** + * Switch on or off partial unweighting in after each subleading emission. + */ + bool doPartialUnweightingAtEmission; + + /** + * Switch on or off partial unweighting in the splitting generator. + */ + bool doPartialUnweighting; + + /** + * Reference weight for the partial unweighting. + */ + double referenceWeight; + + /** + * Factor changing the acceptance probability for the veto algorithm. + */ + double cmecReweightFactor; + + /** + * Scaling factor for the negative colour matrix element corrections. + */ + double negCMECScaling; + private: /** * The verbosity level. * 0 - print no info * 1 - print diagnostic information on setting up * splitting generators etc. * 2 - print detailed event information for up to * printEvent events. * 3 - print dipole chains after each splitting. */ int verbosity; /** * See verbosity. */ int printEvent; private: /** * The splitting generators indexed by the dipole * indices they can work on. */ GeneratorMap theGenerators; /** * The evnt record used. */ DipoleEventRecord theEventRecord; /** + * The vertex record. + **/ + DipoleVertexRecord theVertexRecord; + + /** * The number of shoer tries so far. */ unsigned int nTries; /** * Whether or not we did radiate anything */ bool didRadiate; /** * Whether or not we did realign the event */ bool didRealign; + /** + * Vector of candidate splittings containing a vector of the + * weights, scale and a bool for every step in the reweighted + * veto algorithm. The bool is true for an accept step. + */ + vector > > theWeightsVector; + + /** + * Winning candidate index. + */ + size_t winnerIndex;//debug + size_t kernelIndex;//debug + size_t winningKernelIndex;//debug + vector scales;//debug + private: /** * A freezing value for the renormalization scale */ Energy theRenormalizationScaleFreeze; /** * A freezing value for the factorization scale */ Energy theFactorizationScaleFreeze; /** * The matching subtraction, if appropriate */ Ptr::tptr theShowerApproximation; /** * True, if sampler should apply compensation */ bool theDoCompensate; /** * Return the number of accepted points after which the grid should * be frozen */ unsigned long theFreezeGrid; /** * The detuning factor applied to the sampling overestimate kernel */ double theDetuning; /** * A pointer to the dipole event reweight object */ Ptr::ptr theEventReweight; /** * A pointer to a global dipole splitting reweight */ Ptr::ptr theSplittingReweight; /** * True if no warnings have been issued yet */ static bool firstWarn; /** * The shower starting scale for the last event encountered */ Energy maxPt; /** * The shower hard scale for the last event encountered */ Energy muPt; /** * The merging helper takes care of merging multiple LO and NLO * cross sections. Here we need to check if an emission would * radiate in the matrix element region of an other multipicity. * If so, the emission is vetoed. */ Ptr::ptr theMergingHelper; /** * PDG codes of the partons which can have an off-shell mass, * this is fast storage for use during running */ set theColouredOffShellInShower; /** * PDG codes of the partons which can have an off-shell mass, * this is a vector that is interfaced so they can be changed */ vector theInputColouredOffShellInShower; /** * Allow the dipole chains to be rearranged */ bool _rearrange=false; /** * number of maximal ME dipoles in the rearrangement. */ unsigned int _dipmax=3; /** * If a chain is considered long (more than dipmax dipoles) * ME with diplong dipoles are used to test for rearrangement. */ unsigned int _diplong=3; /** * Number of emissions to be rearranged. */ int _rearrangeNEmissions=-1; /** * The choice of z boundaries; 0 = restricted, 1 = open, 2 = mixed/other */ int theZBoundaries; private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initDipoleShowerHandler; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ DipoleShowerHandler & operator=(const DipoleShowerHandler &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of DipoleShowerHandler. */ template <> struct BaseClassTrait { /** Typedef of the first base class of DipoleShowerHandler. */ typedef Herwig::ShowerHandler NthBase; }; /** This template specialization informs ThePEG about the name of * the DipoleShowerHandler class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::DipoleShowerHandler"; } /** * The name of a file containing the dynamic library where the class * DipoleShowerHandler is implemented. It may also include several, space-separated, * libraries if the class DipoleShowerHandler depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_DipoleShowerHandler_H */ diff --git a/Shower/Dipole/Kernels/ColourMatrixElementCorrection.cc b/Shower/Dipole/Kernels/ColourMatrixElementCorrection.cc new file mode 100644 --- /dev/null +++ b/Shower/Dipole/Kernels/ColourMatrixElementCorrection.cc @@ -0,0 +1,91 @@ +// -*- C++ -*- +// +// ColourMatrixElementCorrection.cc is a part of Herwig - A multi-purpose Monte Carlo event generator +// Copyright (C) 2002-2007 The Herwig Collaboration +// +// Herwig is licenced under version 2 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 ColourMatrixElementCorrection class. +// + +#include "ColourMatrixElementCorrection.h" +#include "ThePEG/Interface/ClassDocumentation.h" +#include "ThePEG/EventRecord/Particle.h" +#include "ThePEG/Repository/UseRandom.h" +#include "ThePEG/Repository/EventGenerator.h" +#include "ThePEG/Utilities/DescribeClass.h" + +#include "ThePEG/Persistency/PersistentOStream.h" +#include "ThePEG/Persistency/PersistentIStream.h" + +#include "Herwig/Shower/Dipole/DipoleShowerHandler.h" + +using namespace Herwig; + +ColourMatrixElementCorrection::ColourMatrixElementCorrection():lambda(1.0),negCMECScaling(1.0) {} + +ColourMatrixElementCorrection::~ColourMatrixElementCorrection() {} + +IBPtr ColourMatrixElementCorrection::clone() const { + return new_ptr(*this); +} + +IBPtr ColourMatrixElementCorrection::fullclone() const { + return new_ptr(*this); +} + +double ColourMatrixElementCorrection::cmec(const DipoleSplittingInfo& dsplit) const { + // Do not calculate CMECs if the subleading Nc shower has stopped + if ( !(currentHandler()->continueSubleadingNc()) ) { + return 1.; + } + const PPtr em = dsplit.emitter(); + const PPtr sp = dsplit.spectator(); + const tcPDPtr emis = dsplit.emissionData(); + // Get the dictionary from particle pointers to herwig particle numbers + const map& theDictionary = currentHandler()->particleIndices(); + const cPDVector& theParticles = currentHandler()->particlesAfter(); + // Use the dictionary to find + // - emitter index + // - spectator index + // - emission ParticleID + const std::tuple ikemission = std::make_tuple(theDictionary.at(em), + theDictionary.at(sp), + emis->id()); + double factor = currentHandler()->densityOperator().colourMatrixElementCorrection(ikemission,theParticles); + + return factor > 0.0 ? factor : negCMECScaling*factor; +} + +// If needed, insert default implementations of virtual function defined +// in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). + + +void ColourMatrixElementCorrection::persistentOutput(PersistentOStream &) const { + // *** ATTENTION *** os << ; // Add all member variable which should be written persistently here. +} + +void ColourMatrixElementCorrection::persistentInput(PersistentIStream &, int) { + // *** ATTENTION *** is >> ; // Add all member variable which should be read persistently here. +} + + +// *** Attention *** The following static variable is needed for the type +// description system in ThePEG. Please check that the template arguments +// are correct (the class and its base class), and that the constructor +// arguments are correct (the class name and the name of the dynamically +// loadable library where the class implementation can be found). +DescribeClass + describeHerwigColourMatrixElementCorrection("Herwig::ColourMatrixElementCorrection", + "HwDipoleShower.so"); + +void ColourMatrixElementCorrection::Init() { + + static ClassDocumentation documentation + ("There is no documentation for the ColourMatrixElementCorrection class"); + +} + diff --git a/Shower/Dipole/Kernels/ColourMatrixElementCorrection.h b/Shower/Dipole/Kernels/ColourMatrixElementCorrection.h new file mode 100644 --- /dev/null +++ b/Shower/Dipole/Kernels/ColourMatrixElementCorrection.h @@ -0,0 +1,171 @@ +// -*- C++ -*- +// +// ColourMatrixElementCorrection.h is a part of Herwig - A multi-purpose Monte Carlo event generator +// Copyright (C) 2002-2007 The Herwig Collaboration +// +// Herwig is licenced under version 2 of the GPL, see COPYING for details. +// Please respect the MCnet academic guidelines, see GUIDELINES for details. +// +#ifndef Herwig_ColourMatrixElementCorrection_H +#define Herwig_ColourMatrixElementCorrection_H +// +// This is the declaration of the ColourMatrixElementCorrection class. +// + +#include "Herwig/Shower/Dipole/Base/DipoleSplittingReweight.h" + +#include + +namespace Herwig { + +using namespace ThePEG; + +/** + * \ingroup DipoleShower + * \author Johan Thoren, Simon Platzer + * + * \brief ColourMatrixElementCorrection is implementing colour matrix element + * corrections through the weighted Sudakov algorithm + * + * @see \ref ColourMatrixElementCorrectionInterfaces "The interfaces" + * defined for ColourMatrixElementCorrection. + */ +class ColourMatrixElementCorrection: public DipoleSplittingReweight { + +public: + + /** @name Standard constructors and destructors. */ + //@{ + /** + * The default constructor. + */ + ColourMatrixElementCorrection(); + + /** + * The destructor. + */ + virtual ~ColourMatrixElementCorrection(); + //@} + +public: + + /** + * Calculate and cache the colour matrix element correction factor + * for the given splitting type. + */ + double cmec(const DipoleSplittingInfo&) const; + + /** + * Return the reweighting factor for the given splitting type. + */ + virtual double evaluate(const DipoleSplittingInfo& s) const { + return cmec(s); + } + + /** + * Return the absolute value of the colour matrix element correction + * as an enhancement hint for the sampling of the un-reweighted + * splitting kernel. + */ + virtual double hint(const DipoleSplittingInfo& s) const { + if ( hintOnly(s) ) + return cmec(s); + return abs(cmec(s))*lambda; + } + + /** + * Return true, if the reweight can be entirely absorbed into the hint. A + * possible detuning will be switched off. + */ + virtual bool hintOnly(const DipoleSplittingInfo& s) const { + return cmec(s) > 0.; + } + + /** + * Set the factor in front of enhance used by the veto algorithm. + */ + virtual void reweightFactor(const double c) { + assert(c > 0.0); + lambda = c; + } + + /** + * Set the factor in front of enhance used by the veto algorithm. + */ + virtual void negativeScaling(const double c) { + assert(c >= 0.0); + negCMECScaling = c; + } + +public: + + /** @name Functions used by the persistent I/O system. */ + //@{ + /** + * Function used to write out object persistently. + * @param os the persistent output stream written to. + */ + void persistentOutput(PersistentOStream & os) const; + + /** + * Function used to read in object persistently. + * @param is the persistent input stream read from. + * @param version the version number of the object when written. + */ + void persistentInput(PersistentIStream & is, int version); + //@} + + /** + * The standard Init function used to initialize the interfaces. + * Called exactly once for each class by the class description system + * before the main function starts or + * when this class is dynamically loaded. + */ + static void Init(); + +protected: + + /** @name Clone Methods. */ + //@{ + /** + * Make a simple clone of this object. + * @return a pointer to the new object. + */ + virtual IBPtr clone() const; + + /** Make a clone of this object, possibly modifying the cloned object + * to make it sane. + * @return a pointer to the new object. + */ + virtual IBPtr fullclone() const; + //@} + +// If needed, insert declarations of virtual function defined in the +// InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). + +private: + + /** + * Factor to shuffle the magnitude of the CMEC between the splitting kernel + * and the weight in the reweighted veto algorithm. + */ + double lambda; + + /** + * Scaling factor multiplying all of the negative colour matrix element + * corrections. The physically sensible value is 1.0, but this factor can + * be used to examine the effects of the negative contributions. + */ + double negCMECScaling; + + /** + * The assignment operator is private and must never be called. + * In fact, it should not even be implemented. + */ + ColourMatrixElementCorrection & operator=(const ColourMatrixElementCorrection &); + +}; + +} + +#endif /* Herwig_ColourMatrixElementCorrection_H */ diff --git a/Shower/Dipole/Kernels/DipoleSplittingKernel.h b/Shower/Dipole/Kernels/DipoleSplittingKernel.h --- a/Shower/Dipole/Kernels/DipoleSplittingKernel.h +++ b/Shower/Dipole/Kernels/DipoleSplittingKernel.h @@ -1,520 +1,535 @@ // -*- C++ -*- // // DipoleSplittingKernel.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_DipoleSplittingKernel_H #define HERWIG_DipoleSplittingKernel_H // // This is the declaration of the DipoleSplittingKernel class. // #include "ThePEG/Handlers/HandlerBase.h" #include "ThePEG/StandardModel/AlphaSBase.h" #include "ThePEG/PDF/PDF.h" #include "Herwig/Shower/Dipole/Utility/PDFRatio.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" #include "Herwig/Shower/Dipole/Kinematics/DipoleSplittingKinematics.h" +#include "ThePEG/EventRecord/RhoDMatrix.h" +#include "Herwig/Decay/DecayMatrixElement.h" +#include "Herwig/Decay/TwoBodyDecayMatrixElement.h" + namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief DipoleSplittingKernel is the base class for all kernels * used within the dipole shower. * * @see \ref DipoleSplittingKernelInterfaces "The interfaces" * defined for DipoleSplittingKernel. */ class DipoleSplittingKernel: public HandlerBase { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ DipoleSplittingKernel(); /** * The destructor. */ virtual ~DipoleSplittingKernel(); //@} public: /** * Return the alpha_s to be used */ Ptr::tptr alphaS() const { return theAlphaS; } /** * Set the alpha_s to be used */ void alphaS(Ptr::tptr ap) { theAlphaS = ap; } /** * Return the splitting kinematics object */ Ptr::tptr splittingKinematics() const { return theSplittingKinematics; } /** * Return the mc check object */ Ptr::ptr mcCheck() const { return theMCCheck; } /** * Set the splitting kinematics object */ void splittingKinematics(Ptr::tptr sp) { theSplittingKinematics = sp; } /** * Return the PDFRatio object */ Ptr::tptr pdfRatio() const { return thePDFRatio; } /** * Set the PDFRatio object */ void pdfRatio(Ptr::tptr sp) { thePDFRatio = sp; } /** * Return the number of additional parameter * random variables needed to evaluate this kernel * except the momentum fractions of incoming partons. * These will be accessible through the * lastSplittingParameters() container of the splitting * info object. */ virtual int nDimAdditional() const { return 0; } /** * Set the freezing value for the renormalization scale */ void renormalizationScaleFreeze(Energy s) { theRenormalizationScaleFreeze = s; } /** * Set the freezing value for the factorization scale */ void factorizationScaleFreeze(Energy s) { theFactorizationScaleFreeze = s; } /** * Get the freezing value for the renormalization scale */ Energy renormalizationScaleFreeze() const { return theRenormalizationScaleFreeze; } /** * Get the freezing value for the factorization scale */ Energy factorizationScaleFreeze() const { return theFactorizationScaleFreeze; } public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const = 0; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const = 0; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const = 0; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const = 0; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const = 0; /** * Return the flavour produced, if this cannot * be determined from the dipole. */ PDPtr flavour() const { return theFlavour; } /** * Return true, if this splitting kernel is supposed to work in a * strict large-N limit, i.e. replacing C_F by C_A/2 */ bool strictLargeN() const { return theStrictLargeN; } public: /** * Inform this splitting kernel, that it is being * presampled until a call to stopPresampling */ virtual void startPresampling(const DipoleIndex&) { presampling = true; } /** * Inform this splitting kernel, that it is not being * presampled until a call to startPresampling */ virtual void stopPresampling(const DipoleIndex&) { presampling = false; } /** * Return the number of points to presample this * splitting generator. */ unsigned long presamplingPoints() const { return thePresamplingPoints; } /** * Return the maximum number of trials * to generate a splitting. */ unsigned long maxtry() const { return theMaxtry; } /** * Return the number of accepted points after which the grid should * be frozen */ unsigned long freezeGrid() const { return theFreezeGrid; } /** * Set the number of accepted points after which the grid should * be frozen */ void freezeGrid(unsigned long n) { theFreezeGrid = n; } /** * Set a detuning factor to be applied to the sampling overestimate kernel */ void detuning(double d) { theDetuning = d; } /** * Return the detuning factor applied to the sampling overestimate kernel */ double detuning() const { return theDetuning; } /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const = 0; + + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const = 0; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const = 0; /** * Clear the alphaPDF cache */ void clearAlphaPDFCache() const { theAlphaSCache.clear(); thePDFCache.clear(); } /** * Update the variations vector at the given splitting using the indicated * kernel and overestimate values. */ virtual void accept(const DipoleSplittingInfo&, double, double, map&) const; /** * Update the variations vector at the given splitting using the indicated * kernel and overestimate values. */ virtual void veto(const DipoleSplittingInfo&, double, double, map&) const; /** * Return true, if this kernel is capable of * delivering an overestimate to the kernel, and * of inverting the integral over the overestimate * w.r.t. the phasepsace provided by the given * DipoleSplittingInfo object. */ virtual bool haveOverestimate(const DipoleSplittingInfo&) const { return false; } /** * Return the overestimate to this splitting kernel * for the given dipole splitting. */ virtual double overestimate(const DipoleSplittingInfo&) const { return -1.; } /** * Invert the integral over the overestimate * w.r.t. the phasepsace provided by the given * DipoleSplittingInfo object to equal * the given value. */ virtual double invertOverestimateIntegral(const DipoleSplittingInfo&, double) const { return -1.; } /** * . */ bool useThisKernel() const { return theUseThisKernel; } public: /** * Get the factorization scale factor */ double factorizationScaleFactor() const { return theFactorizationScaleFactor; } /** * Set the factorization scale factor */ void factorizationScaleFactor(double f) { theFactorizationScaleFactor = f; } /** * Get the renormalization scale factor */ double renormalizationScaleFactor() const { return theRenormalizationScaleFactor; } /** * Set the renormalization scale factor */ void renormalizationScaleFactor(double f) { theRenormalizationScaleFactor = f; } protected: /** * Return the common factor of (alphas/2pi)*(pdf ratio) */ double alphaPDF(const DipoleSplittingInfo&, Energy optScale = ZERO, double rScaleFactor = 1.0, double fScaleFactor = 1.0) const; /** * Return true, if the virtuality of the splitting should be used as the * argument of alphas rather than the pt */ bool virtualitySplittingScale() const { return theVirtualitySplittingScale; } 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(); // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The alpha_s to be used. */ Ptr::ptr theAlphaS; /** * An optional 'colour screening' scale * for alternative intrinsic pt generation. */ Energy theScreeningScale; /** * The splitting kinematics to be used. */ Ptr::ptr theSplittingKinematics; /** * An optional PDF ratio object to be used * when evaluating this kernel. */ Ptr::ptr thePDFRatio; /** * The number of points to presample this * splitting generator. */ unsigned long thePresamplingPoints; /** * The maximum number of trials * to generate a splitting. */ unsigned long theMaxtry; /** * Return the number of accepted points after which the grid should * be frozen */ unsigned long theFreezeGrid; /** * The detuning factor applied to the sampling overestimate kernel */ double theDetuning; /** * The flavour produced, if this cannot * be determined from the dipole. */ PDPtr theFlavour; /** * Pointer to a check histogram object */ Ptr::ptr theMCCheck; /** * True, if this splitting kernel is supposed to work in a * strict large-N limit, i.e. replacing C_F by C_A/2 */ bool theStrictLargeN; /** * The factorization scale factor. */ double theFactorizationScaleFactor; /** * The renormalization scale factor. */ double theRenormalizationScaleFactor; /** * A freezing value for the renormalization scale */ Energy theRenormalizationScaleFreeze; /** * A freezing value for the factorization scale */ Energy theFactorizationScaleFreeze; /** * True, if the virtuality of the splitting should be used as the * argument of alphas rather than the pt */ bool theVirtualitySplittingScale; /** * Implementing CMW in the kernels. **/ unsigned int theCMWScheme=0; /** * Cache for alphas evaluations */ mutable map theAlphaSCache; /** * Cache for PDF evaluations */ mutable map thePDFCache; /** * True, if we are presampling */ bool presampling; /** * True, if the kernel should be used */ bool theUseThisKernel = true; private: /** * The static object used to initialize the description of this class. * Indicates that this is an abstract class with persistent data. */ static AbstractClassDescription initDipoleSplittingKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ DipoleSplittingKernel & operator=(const DipoleSplittingKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of DipoleSplittingKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of DipoleSplittingKernel. */ typedef HandlerBase NthBase; }; /** This template specialization informs ThePEG about the name of * the DipoleSplittingKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::DipoleSplittingKernel"; } /** * The name of a file containing the dynamic library where the class * DipoleSplittingKernel is implemented. It may also include several, space-separated, * libraries if the class DipoleSplittingKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_DipoleSplittingKernel_H */ diff --git a/Shower/Dipole/Kernels/FFMgx2ggxDipoleKernel.cc b/Shower/Dipole/Kernels/FFMgx2ggxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FFMgx2ggxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FFMgx2ggxDipoleKernel.cc @@ -1,142 +1,196 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FFMgx2ggxDipoleKernel class. // #include "FFMgx2ggxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FFMgx2ggxDipoleKernel::FFMgx2ggxDipoleKernel() : DipoleSplittingKernel(){} FFMgx2ggxDipoleKernel::~FFMgx2ggxDipoleKernel() {} IBPtr FFMgx2ggxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FFMgx2ggxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FFMgx2ggxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() != ZERO && !ind.initialStateEmitter() && !ind.initialStateSpectator() && !ind.incomingDecayEmitter() && !ind.incomingDecaySpectator(); } bool FFMgx2ggxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() == ParticleID::g && sk.emission(b)->id() == ParticleID::g && abs(spectator(a)->mass()) == abs(sk.spectator(b)->mass()); } tcPDPtr FFMgx2ggxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FFMgx2ggxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FFMgx2ggxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FFMgx2ggxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); - // These are the physical variables as used in the - // standard form of the kernel (i.e. do not redefine variables or kernel) - const double z = split.lastZ(); - const Energy pt = split.lastPt(); - - // Need zPrime to calculate y, - // TODO: Should just store y in the dipole splitting info everywhere anyway!!! - // The only value stored in dInfo.lastSplittingParameters() should be zPrime - - const double zPrime = split.lastSplittingParameters()[0]; + // Sudakov parameterisation variables, + // needed to calculate y. + double z = split.lastZ(); + Energy pt = split.lastPt(); // Construct mass squared variables - - // Construct mass squared variables - double muj2 = sqr(split.spectatorMass() / split.scale()); - double bar = 1. - muj2; + Energy2 Qijk = sqr(split.scale()); + Energy2 mk2 = sqr(split.spectatorMass()); + Energy2 sbar = Qijk - mk2; // Calculate y - double y = sqr(pt)/sqr(split.scale()) / (bar*zPrime*(1.-zPrime)); + double y = sqr(pt) / sbar / z / (1.-z); - double vijk = sqrt( sqr(2.*muj2+bar*(1.-y))-4.*muj2 ) / (bar*(1.-y)); + double vijk = sqrt( sqr(2.*mk2 + sbar*(1.-y)) - 4.*mk2*Qijk ) / sbar / (1.-y); double viji = 1.; - double zp = 0.5*(1.+viji*vijk); - double zm = 0.5*(1.-viji*vijk); + // zi, used in dipole splitting kernel + double zi = split.lastSplittingParameters()[0]; + double zip = 0.5*(1.+viji*vijk); + double zim = 0.5*(1.-viji*vijk); + // how to choose kappa? double kappa = 0.; - double S1=1./(1.-z*(1.-y)); - double S2=1./(1.-(1.-z)*(1.-y)); - double NS=(z*(1.-z)-(1.-kappa)*zp*zm-2.)/vijk; + double S1=1./(1.-zi*(1.-y)); + double S2=1./(1.-(1.-zi)*(1.-y)); + double NS=(zi*(1.-zi)-(1.-kappa)*zip*zim-2.)/vijk; if( theAsymmetryOption == 0 ){ ret *= 3.*( S1 + 0.5 * NS); }else if ( theAsymmetryOption == 1 ){ - ret *= 3.*z*( S1 +S2 + NS ); + ret *= 3.*zi*( S1 +S2 + NS ); }else{ ret *= 3.*0.5*( S1 + S2 + NS ); } return ret > 0. ? ret : 0.; } +vector< pair > +FFMgx2ggxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + // Need variables for the AP kernels + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_pmp = (1.-z)*sqrt( (1.-z)/z ); + + //double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp)) - 2.*abs(rho(0,2))*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp))/max ) ); + distPhiDep.push_back( make_pair(-2, rho(0,2)*(v_AP_mpm*v_AP_ppm + v_AP_mmp*v_AP_pmp)/max ) ); + distPhiDep.push_back( make_pair(2, rho(2,0)*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp)/max) ); + + return distPhiDep; +} + +DecayMEPtr FFMgx2ggxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + // Need variables for the AP kernels + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_pmp = (1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1,PDT::Spin1,PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(2,2,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(2,2,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,2) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,2) = 0; + (*kernelPhiDep)(2,0,0) = 0; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FFMgx2ggxDipoleKernel::persistentOutput(PersistentOStream & os) const { os << theAsymmetryOption; } void FFMgx2ggxDipoleKernel::persistentInput(PersistentIStream & is, int) { is >> theAsymmetryOption; } ClassDescription FFMgx2ggxDipoleKernel::initFFMgx2ggxDipoleKernel; // Definition of the static class description member. void FFMgx2ggxDipoleKernel::Init() { static ClassDocumentation documentation ("FFMgx2ggxDipoleKernel"); static Parameter interfacetheAsymmetryOption ("AsymmetryOption", "The asymmetry option for final state gluon spliitings.", &FFMgx2ggxDipoleKernel::theAsymmetryOption, 1, 0, 0, false, false, Interface::lowerlim); } diff --git a/Shower/Dipole/Kernels/FFMgx2ggxDipoleKernel.h b/Shower/Dipole/Kernels/FFMgx2ggxDipoleKernel.h --- a/Shower/Dipole/Kernels/FFMgx2ggxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FFMgx2ggxDipoleKernel.h @@ -1,187 +1,198 @@ // -*- C++ -*- #ifndef HERWIG_FFMgx2ggxDipoleKernel_H #define HERWIG_FFMgx2ggxDipoleKernel_H // // This is the declaration of the FFMgx2ggxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll, Stephen Webster * * \brief FFMgx2ggxDipoleKernel implements the g -> gg * splitting off a final-final dipole * */ class FFMgx2ggxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FFMgx2ggxDipoleKernel(); /** * The destructor. */ virtual ~FFMgx2ggxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * Asymmetry option for final state gluon splittings. */ int theAsymmetryOption=1; /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFFMgx2ggxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FFMgx2ggxDipoleKernel & operator=(const FFMgx2ggxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FFMgx2ggxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FFMgx2ggxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FFMgx2ggxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FFMgx2ggxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FFMgx2ggxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FFMgx2ggxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FFMgx2ggxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FFMgx2qqxDipoleKernel.cc b/Shower/Dipole/Kernels/FFMgx2qqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FFMgx2qqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FFMgx2qqxDipoleKernel.cc @@ -1,133 +1,195 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FFMgx2qqxDipoleKernel class. // #include "FFMgx2qqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FFMgx2qqxDipoleKernel::FFMgx2qqxDipoleKernel() : DipoleSplittingKernel() {} FFMgx2qqxDipoleKernel::~FFMgx2qqxDipoleKernel() {} IBPtr FFMgx2qqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FFMgx2qqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FFMgx2qqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && !( ind.spectatorData()->mass() == ZERO && flavour()->mass() == ZERO ) && !ind.initialStateEmitter() && !ind.initialStateSpectator() && !ind.incomingDecayEmitter() && !ind.incomingDecaySpectator(); } bool FFMgx2qqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() + sk.emission(b)->id() == 0 && abs(sk.emitter(b)->id()) < 6 && emitter(a)->id() == sk.emitter(b)->id() && abs(sk.spectator(b)->mass()) == abs(spectator(a)->mass()); } tcPDPtr FFMgx2qqxDipoleKernel::emitter(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6); return flavour(); } tcPDPtr FFMgx2qqxDipoleKernel::emission(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6); return flavour()->CC(); } tcPDPtr FFMgx2qqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FFMgx2qqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); - // These are the physical variables as used in the - //standard form of the kernel (i.e. do not redefine variables or kernel) + // Sudakov parameterisation variables, + // needed to calculate y. double z = split.lastZ(); Energy pt = split.lastPt(); - // Need zPrime to calculate y, - // TODO: Should just store y in the dipole splitting info everywhere anyway!!! - // The only value stored in dInfo.lastSplittingParameters() should be zPrime - //assert(split.lastSplittingParameters().size() == 1 ); - double zPrime = split.lastSplittingParameters()[0]; - // Construct mass squared variables - double mui2 = sqr(split.emitterData()->mass() / split.scale()); - double mu2 = mui2; - double muj2 = sqr(split.spectatorMass() / split.scale()); - double bar = 1. - mui2 - mu2 - muj2; + Energy2 Qijk = sqr(split.scale()); + Energy2 mi2 = sqr(split.emitterData()->mass()); + Energy2 mj2 = mi2; + Energy2 mk2 = sqr(split.spectatorMass()); + Energy2 sbar = Qijk - mi2 - mj2 - mk2; // Calculate y - double y = (sqr(pt)/sqr(split.scale()) - + sqr(1.-zPrime)*mui2 + sqr(zPrime)*mu2) - / (bar*zPrime*(1.-zPrime)); + double y = (sqr(pt) + sqr(1.-z)*mi2 + sqr(z)*mj2) / sbar / z / (1.-z); + + // zi, used in dipole splitting kernel + double zi = split.lastSplittingParameters()[0]; + + double vijk = sqrt( sqr(2.*mk2 + sbar*(1.-y)) - 4.*mk2*Qijk ) / sbar / (1.-y); + double viji = sqrt( sqr(sbar*y) - 4.*sqr(mi2) ) / (sbar*y + 2.*mi2); - double vijk = sqrt( sqr(2.*muj2+bar*(1.-y))-4.*muj2 ) / (bar*(1.-y)); - double viji = sqrt( sqr(bar*y)-4.*sqr(mui2) ) / (bar*y+2.*mui2); - - double zp = 0.5*(1.+viji*vijk); - double zm = 0.5*(1.-viji*vijk); + double zip = 0.5*(1.+viji*vijk); + double zim = 0.5*(1.-viji*vijk); // how to choose kappa?? double kappa = 0.; ret *= 0.25 / vijk * - ( 1. - 2.*( z*(1.-z) - (1.-kappa)*zp*zm - - kappa*mui2/(2.*mui2+(1.-2.*mui2-muj2)*y) ) ); + ( 1. - 2.*( zi*(1.-zi) - (1.-kappa)*zip*zim + - kappa*mi2 / ( 2.*mi2 + (Qijk - 2.*mi2 - mk2)*y) ) ); return ret > 0. ? ret : 0.; } +vector< pair > +FFMgx2qqxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + // Need variables for the AP kernels + double z = dInfo.lastZ(); + Energy pt = dInfo.lastPt(); + Energy2 mi2 = sqr(dInfo.emitterData()->mass()); + + // Altarelli-Parisi spin-indexed kernels: + double ratio = mi2 / ( mi2 + sqr(pt) ); + double root = sqrt(1.-ratio); + double v_AP_ppp = sqrt(ratio); + double v_AP_ppm = z*root; + double v_AP_pmp = -(1.-z)*root; + + //double v_AP_mmm = v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp)) + 2.*abs(rho(0,2))*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp) )/max ) ); + distPhiDep.push_back( make_pair(-2, rho(0,2)*(v_AP_mpm*v_AP_ppm + v_AP_mmp*v_AP_pmp)/max ) ); + distPhiDep.push_back( make_pair(2, rho(2,0)*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp)/max) ); + + return distPhiDep; +} + +DecayMEPtr FFMgx2qqxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + // Need variables for the AP kernels + double z = dInfo.lastZ(); + Energy pt = dInfo.lastPt(); + Energy2 mi2 = sqr(dInfo.emitterData()->mass()); + + // Altarelli-Parisi spin-indexed kernels: + double ratio = mi2 / ( mi2 + sqr(pt) ); + double root = sqrt(1.-ratio); + double v_AP_ppp = sqrt(ratio); + double v_AP_ppm = z*root; + double v_AP_pmp = -(1.-z)*root; + + double v_AP_mmm = v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm; + (*kernelPhiDep)(2,1,1) = v_AP_ppp; + (*kernelPhiDep)(0,0,1) = v_AP_mmp/phase; + (*kernelPhiDep)(2,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,1,1) = 0.; + (*kernelPhiDep)(2,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FFMgx2qqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FFMgx2qqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FFMgx2qqxDipoleKernel::initFFMgx2qqxDipoleKernel; // Definition of the static class description member. void FFMgx2qqxDipoleKernel::Init() { static ClassDocumentation documentation ("FFMgx2qqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FFMgx2qqxDipoleKernel.h b/Shower/Dipole/Kernels/FFMgx2qqxDipoleKernel.h --- a/Shower/Dipole/Kernels/FFMgx2qqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FFMgx2qqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FFMgx2qqxDipoleKernel_H #define HERWIG_FFMgx2qqxDipoleKernel_H // // This is the declaration of the FFMgx2qqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll, Stephen Webster * * \brief FFMgx2qqxDipoleKernel implements the g -> qqbar * splitting off a final-final dipole * */ class FFMgx2qqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FFMgx2qqxDipoleKernel(); /** * The destructor. */ virtual ~FFMgx2qqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFFMgx2qqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FFMgx2qqxDipoleKernel & operator=(const FFMgx2qqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FFMgx2qqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FFMgx2qqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FFMgx2qqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FFMgx2qqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FFMgx2qqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FFMgx2qqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FFMgx2qqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FFMqx2qgxDipoleKernel.cc b/Shower/Dipole/Kernels/FFMqx2qgxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FFMqx2qgxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FFMqx2qgxDipoleKernel.cc @@ -1,126 +1,169 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FFMqx2qgxDipoleKernel class. // #include "FFMqx2qgxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FFMqx2qgxDipoleKernel::FFMqx2qgxDipoleKernel() : DipoleSplittingKernel() {} FFMqx2qgxDipoleKernel::~FFMqx2qgxDipoleKernel() {} IBPtr FFMqx2qgxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FFMqx2qgxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FFMqx2qgxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 7 && // 2012-05-01 abs(ind.emitterData()->id()) == abs(flavour()->id()) && !( ind.emitterData()->mass() == ZERO && ind.spectatorData()->mass() == ZERO ) && !ind.initialStateEmitter() && !ind.initialStateSpectator() && !ind.incomingDecayEmitter() && !ind.incomingDecaySpectator(); } bool FFMqx2qgxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emission(b)->id() == ParticleID::g && abs(sk.emitter(b)->id()) < 7 && abs(sk.emitter(b)->mass()) == abs(emitter(a)->mass()) && abs(sk.spectator(b)->mass()) == abs(spectator(a)->mass()); } // 2012-05-01 tcPDPtr FFMqx2qgxDipoleKernel::emitter(const DipoleIndex& ind) const { assert(flavour()); assert(abs(flavour()->id())<7); return ind.emitterData()->id() > 0 ? (tcPDPtr) flavour() : (tcPDPtr) flavour()->CC(); } tcPDPtr FFMqx2qgxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FFMqx2qgxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FFMqx2qgxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); - // These are the physical variables as used in the standard - // form of the kernel (i.e. do not redefine variables or kernel) + // Sudakov parameterisation variables, + // needed to calculate y. double z = split.lastZ(); Energy pt = split.lastPt(); - // Need zPrime to calculate y, - // TODO: Should just store y in the dipole splitting info everywhere anyway!!! - // The only value stored in dInfo.lastSplittingParameters() should be zPrime - //assert(split.lastSplittingParameters().size() == 1 ); - double zPrime = split.lastSplittingParameters()[0]; - // Construct mass squared variables // Note for q->qg can use the emitterMass // (i.e. mass of emitter before splitting = mass of emitter after) - double mui2 = sqr(split.emitterMass() / split.scale()); - double muj2 = sqr(split.spectatorMass() / split.scale()); - double bar = 1. - mui2 - muj2; + Energy2 Qijk = sqr(split.scale()); + Energy2 mi2 = sqr(split.emitterMass()); + Energy2 mk2 = sqr(split.spectatorMass()); + Energy2 sbar = Qijk - mi2 - mk2; // Calculate y - double y = (sqr(pt)/sqr(split.scale()) + sqr(1.-zPrime)*mui2) / (bar*zPrime*(1.-zPrime)); + double y = (sqr(pt) + sqr(1.-z)*mi2) / sbar / z / (1.-z); - double vijk = sqrt( sqr(2.*muj2 + bar*(1.-y))-4.*muj2 ) / (bar*(1.-y)); - double vbar = sqrt( 1.+sqr(mui2)+sqr(muj2)-2.*(mui2+muj2+mui2*muj2) ) / bar; + // zi, used in dipole splitting kernel + double zi = split.lastSplittingParameters()[0]; - ret *= (!strictLargeN() ? 4./3. : 3./2.)*( 2./(1.-z*(1.-y)) - vbar/vijk*( 1.+z + mui2*2./(y*(1.-mui2-muj2)) ) ); + double vijk = sqrt( sqr(2.*mk2 + sbar*(1.-y)) - 4.*mk2*Qijk ) / sbar / (1.-y); + double vtilde = sqrt( sqr(Qijk) + sqr(mi2) + sqr(mk2) + - 2.*(mi2*Qijk + mk2*Qijk + mi2*mk2) ) / sbar; + + ret *= (!strictLargeN() ? 4./3. : 3./2.)* + ( 2./(1.-zi*(1.-y)) - vtilde/vijk*( 1. + zi + 2.*mi2/sbar/y ) ); return ret > 0. ? ret : 0.; } +vector< pair > +FFMqx2qgxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr FFMqx2qgxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + // Need variables for the AP kernels + double z = dInfo.lastZ(); + Energy pt = dInfo.lastPt(); + Energy mi = dInfo.emitterMass(); + + // Altarelli-Parisi spin-indexed kernels: + Energy den = sqrt(sqr(mi)*sqr(1.-z) + sqr(pt)); + double v_AP_ppp = pt / den / sqrt(1.-z); + double v_AP_ppm = - z * v_AP_ppp ; + double v_AP_pmp = mi*(1.-z)*sqrt(1.-z) / den ; + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -1 or -1/2, 1=+1/2, 2 = +1 + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,1,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(1,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm; + (*kernelPhiDep)(1,0,2) = v_AP_pmp; + (*kernelPhiDep)(0,1,2) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FFMqx2qgxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FFMqx2qgxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FFMqx2qgxDipoleKernel::initFFMqx2qgxDipoleKernel; // Definition of the static class description member. void FFMqx2qgxDipoleKernel::Init() { static ClassDocumentation documentation ("FFMqx2qgxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FFMqx2qgxDipoleKernel.h b/Shower/Dipole/Kernels/FFMqx2qgxDipoleKernel.h --- a/Shower/Dipole/Kernels/FFMqx2qgxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FFMqx2qgxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FFMqx2qgxDipoleKernel_H #define HERWIG_FFMqx2qgxDipoleKernel_H // // This is the declaration of the FFMqx2qgxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll, Stephen Webster * * \brief FFMqx2qgxDipoleKernel implements the q -> qg * splitting off a final-final dipole * */ class FFMqx2qgxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FFMqx2qgxDipoleKernel(); /** * The destructor. */ virtual ~FFMqx2qgxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFFMqx2qgxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FFMqx2qgxDipoleKernel & operator=(const FFMqx2qgxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FFMqx2qgxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FFMqx2qgxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FFMqx2qgxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FFMqx2qgxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FFMqx2qgxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FFMqx2qgxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FFMqx2qgxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FFgx2ggxDipoleKernel.cc b/Shower/Dipole/Kernels/FFgx2ggxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FFgx2ggxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FFgx2ggxDipoleKernel.cc @@ -1,113 +1,169 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FFgx2ggxDipoleKernel class. // #include "FFgx2ggxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FFgx2ggxDipoleKernel::FFgx2ggxDipoleKernel() : DipoleSplittingKernel(){} FFgx2ggxDipoleKernel::~FFgx2ggxDipoleKernel() {} IBPtr FFgx2ggxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FFgx2ggxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FFgx2ggxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() == ZERO && !ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool FFgx2ggxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() == ParticleID::g && sk.emission(b)->id() == ParticleID::g; } tcPDPtr FFgx2ggxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FFgx2ggxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FFgx2ggxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FFgx2ggxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double y = sqr(split.lastPt() / split.scale()) / (z*(1.-z)); double S1=1./(1.-z*(1.-y)); double S2=1./(1.-(1.-z)*(1.-y)); double NS=(-2 + z*(1.-z)); if( theAsymmetryOption == 0 ){ ret *= 3.*( S1 + 0.5 * NS); }else if ( theAsymmetryOption == 1 ){ ret *= 3.*z*( S1 +S2 + NS ); }else{ ret *= 3.*0.5*( S1 + S2 + NS ); } return ret > 0. ? ret : 0.; } +vector< pair > +FFgx2ggxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_pmp = (1.-z)*sqrt( (1.-z)/z ); + + //double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp)) - 2.*abs(rho(0,2))*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp))/max ) ); + distPhiDep.push_back( make_pair(-2, rho(0,2)*(v_AP_mpm*v_AP_ppm + v_AP_mmp*v_AP_pmp)/max ) ); + distPhiDep.push_back( make_pair(2, rho(2,0)*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp)/max) ); + + return distPhiDep; +} + +DecayMEPtr FFgx2ggxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_pmp = (1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1,PDT::Spin1,PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(2,2,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(2,2,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,2) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,2) = 0; + (*kernelPhiDep)(2,0,0) = 0; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FFgx2ggxDipoleKernel::persistentOutput(PersistentOStream & os) const { os << theAsymmetryOption; } void FFgx2ggxDipoleKernel::persistentInput(PersistentIStream & is, int) { is >> theAsymmetryOption; } ClassDescription FFgx2ggxDipoleKernel::initFFgx2ggxDipoleKernel; // Definition of the static class description member. void FFgx2ggxDipoleKernel::Init() { static ClassDocumentation documentation ("FFgx2ggxDipoleKernel"); static Parameter interfacetheAsymmetryOption ("AsymmetryOption", "The asymmetry option for final state gluon spliitings.", &FFgx2ggxDipoleKernel::theAsymmetryOption, 1, 0, 0, false, false, Interface::lowerlim); } diff --git a/Shower/Dipole/Kernels/FFgx2ggxDipoleKernel.h b/Shower/Dipole/Kernels/FFgx2ggxDipoleKernel.h --- a/Shower/Dipole/Kernels/FFgx2ggxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FFgx2ggxDipoleKernel.h @@ -1,187 +1,198 @@ // -*- C++ -*- #ifndef HERWIG_FFgx2ggxDipoleKernel_H #define HERWIG_FFgx2ggxDipoleKernel_H // // This is the declaration of the FFgx2ggxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief FFgx2ggxDipoleKernel implements the g -> gg * splitting off a final-final dipole * */ class FFgx2ggxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FFgx2ggxDipoleKernel(); /** * The destructor. */ virtual ~FFgx2ggxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFFgx2ggxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FFgx2ggxDipoleKernel & operator=(const FFgx2ggxDipoleKernel &) = delete; /** * Asymmetry option for final state gluon splittings. */ int theAsymmetryOption=1; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FFgx2ggxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FFgx2ggxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FFgx2ggxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FFgx2ggxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FFgx2ggxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FFgx2ggxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FFgx2ggxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FFgx2qqxDipoleKernel.cc b/Shower/Dipole/Kernels/FFgx2qqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FFgx2qqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FFgx2qqxDipoleKernel.cc @@ -1,102 +1,154 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FFgx2qqxDipoleKernel class. // #include "FFgx2qqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FFgx2qqxDipoleKernel::FFgx2qqxDipoleKernel() : DipoleSplittingKernel() {} FFgx2qqxDipoleKernel::~FFgx2qqxDipoleKernel() {} IBPtr FFgx2qqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FFgx2qqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FFgx2qqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() == ZERO && flavour()->mass() == ZERO && !ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool FFgx2qqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() + sk.emission(b)->id() == 0 && abs(sk.emitter(b)->id()) < 6 && sk.emitter(b)->mass() == ZERO; } tcPDPtr FFgx2qqxDipoleKernel::emitter(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour(); } tcPDPtr FFgx2qqxDipoleKernel::emission(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour()->CC(); } tcPDPtr FFgx2qqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FFgx2qqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); ret *= .25 * ( 1. - 2.*z*(1.-z) ); return ret > 0. ? ret : 0.; } +vector< pair > +FFgx2qqxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppm = z; + double v_AP_pmp = -(1.-z); + + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppm) + sqr(v_AP_pmp)) + 2.*abs(rho(0,2))*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*( sqr(v_AP_ppm) + sqr(v_AP_pmp) )/max ) ); + distPhiDep.push_back( make_pair(-2, rho(0,2)*(v_AP_mpm*v_AP_ppm + v_AP_mmp*v_AP_pmp)/max ) ); + distPhiDep.push_back( make_pair(2, rho(2,0)*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp)/max) ); + + return distPhiDep; +} + +DecayMEPtr FFgx2qqxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppm = z; + double v_AP_pmp = -(1.-z); + + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = 0.; + (*kernelPhiDep)(2,1,1) = 0.; + (*kernelPhiDep)(0,0,1) = v_AP_mmp/phase; + (*kernelPhiDep)(2,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,1,1) = 0.; + (*kernelPhiDep)(2,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FFgx2qqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FFgx2qqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FFgx2qqxDipoleKernel::initFFgx2qqxDipoleKernel; // Definition of the static class description member. void FFgx2qqxDipoleKernel::Init() { static ClassDocumentation documentation ("FFgx2qqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FFgx2qqxDipoleKernel.h b/Shower/Dipole/Kernels/FFgx2qqxDipoleKernel.h --- a/Shower/Dipole/Kernels/FFgx2qqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FFgx2qqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FFgx2qqxDipoleKernel_H #define HERWIG_FFgx2qqxDipoleKernel_H // // This is the declaration of the FFgx2qqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief FFgx2qqxDipoleKernel implements the g -> qqbar * splitting off a final-final dipole * */ class FFgx2qqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FFgx2qqxDipoleKernel(); /** * The destructor. */ virtual ~FFgx2qqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFFgx2qqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FFgx2qqxDipoleKernel & operator=(const FFgx2qqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FFgx2qqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FFgx2qqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FFgx2qqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FFgx2qqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FFgx2qqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FFgx2qqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FFgx2qqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FFqx2qgxDipoleKernel.cc b/Shower/Dipole/Kernels/FFqx2qgxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FFqx2qgxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FFqx2qgxDipoleKernel.cc @@ -1,99 +1,136 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FFqx2qgxDipoleKernel class. // #include "FFqx2qgxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FFqx2qgxDipoleKernel::FFqx2qgxDipoleKernel() : DipoleSplittingKernel() {} FFqx2qgxDipoleKernel::~FFqx2qgxDipoleKernel() {} IBPtr FFqx2qgxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FFqx2qgxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FFqx2qgxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 6 && ind.emitterData()->mass() == ZERO && ind.spectatorData()->mass() == ZERO && !ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool FFqx2qgxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emission(b)->id() == ParticleID::g && abs(sk.emitter(b)->id()) < 6 && sk.emitter(b)->mass() == ZERO; } tcPDPtr FFqx2qgxDipoleKernel::emitter(const DipoleIndex& ind) const { return ind.emitterData(); } tcPDPtr FFqx2qgxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FFqx2qgxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FFqx2qgxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double y = sqr(split.lastPt() / split.scale()) / (z*(1.-z)); ret *= (!strictLargeN() ? 4./3. : 3./2.)*( 2./(1.-z*(1.-y)) - (1.+z) ); return ret > 0. ? ret : 0.; } +vector< pair > +FFqx2qgxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr FFqx2qgxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt( 1./(1.-z) ); + double v_AP_ppm = -z/sqrt(1.-z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -1 or -1/2, 1=+1/2, 2 = +1 + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,1,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(1,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = 0.; + (*kernelPhiDep)(1,0,2) = 0.; + (*kernelPhiDep)(0,1,2) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FFqx2qgxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FFqx2qgxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FFqx2qgxDipoleKernel::initFFqx2qgxDipoleKernel; // Definition of the static class description member. void FFqx2qgxDipoleKernel::Init() { static ClassDocumentation documentation ("FFqx2qgxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FFqx2qgxDipoleKernel.h b/Shower/Dipole/Kernels/FFqx2qgxDipoleKernel.h --- a/Shower/Dipole/Kernels/FFqx2qgxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FFqx2qgxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FFqx2qgxDipoleKernel_H #define HERWIG_FFqx2qgxDipoleKernel_H // // This is the declaration of the FFqx2qgxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief FFqx2qgxDipoleKernel implements the q -> qg * splitting off a final-final dipole * */ class FFqx2qgxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FFqx2qgxDipoleKernel(); /** * The destructor. */ virtual ~FFqx2qgxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFFqx2qgxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FFqx2qgxDipoleKernel & operator=(const FFqx2qgxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FFqx2qgxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FFqx2qgxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FFqx2qgxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FFqx2qgxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FFqx2qgxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FFqx2qgxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FFqx2qgxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FIMDecaygx2ggxDipoleKernel.cc b/Shower/Dipole/Kernels/FIMDecaygx2ggxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FIMDecaygx2ggxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FIMDecaygx2ggxDipoleKernel.cc @@ -1,159 +1,220 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FIMDecaygx2ggxDipoleKernel class. // #include "FIMDecaygx2ggxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FIMDecaygx2ggxDipoleKernel::FIMDecaygx2ggxDipoleKernel() : DipoleSplittingKernel(){} FIMDecaygx2ggxDipoleKernel::~FIMDecaygx2ggxDipoleKernel() {} IBPtr FIMDecaygx2ggxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FIMDecaygx2ggxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FIMDecaygx2ggxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.incomingDecaySpectator() && !ind.incomingDecayEmitter() && ind.emitterData()->id() == ParticleID::g && !(ind.spectatorData()->mass() == ZERO) && // Initial state here refers to the entire event !ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool FIMDecaygx2ggxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emission(b)->id() == ParticleID::g && sk.emitter(b)->id() == ParticleID::g && abs(sk.spectator(b)->mass()) == abs(spectator(a)->mass()); } tcPDPtr FIMDecaygx2ggxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FIMDecaygx2ggxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FIMDecaygx2ggxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FIMDecaygx2ggxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { + + double ret = alphaPDF(split); - double ret = alphaPDF(split); - - // These are the physical variables as used in the standard form of the kernel (i.e. do not redefine variables or kernel) + // Sudakov parameterisation variables, + // needed to calculate y. double z = split.lastZ(); Energy pt = split.lastPt(); - // Need zPrime to calculate y, - // TODO: Should just store y in the dipole splitting info everywhere anyway!!! - // The only value stored in dInfo.lastSplittingParameters() should be zPrime - //assert(split.lastSplittingParameters().size() == 1 ); - double zPrime = split.lastSplittingParameters()[0]; + // Construct mass squared variables + // Note for q->qg can use the emitterMass + // (i.e. mass of emitter before splitting = mass of emitter after) + Energy2 Qijk = sqr(split.scale()); + Energy2 mk2 = sqr(split.recoilMass()); + Energy2 sbar = Qijk - mk2; - // Construct mass squared variables - double mua2 = sqr( split.spectatorMass() / split.scale() ); - // Recoil system mass - double muj2 = sqr(split.recoilMass() / split.scale()); - double bar = 1. - muj2; + // Note this should be the same as Qijk + Energy2 ma2 = sqr(split.spectatorMass()); -// Calculate y - double y = (sqr(pt)/sqr(split.scale())) / (bar*zPrime*(1.-zPrime)); - if( sqr(2.*muj2+bar*(1.-y))-4.*muj2 < 0. ){ + // Calculate y + double y = sqr(pt) / sbar / z / (1.-z); + + if( sqr(2.*mk2+sbar*(1.-y)) - 4.*mk2*Qijk < ZERO ){ generator()->logWarning( Exception() - << "error in FIMDecaygx2ggxDipoleKernel::evaluate -- " << - "muj2 " << muj2 << " y " << y << Exception::warning ); - + << "error in FIMDecayqx2qgxDipoleKernel::evaluate -- " << + "mk2 " << mk2/GeV2 << " y " << y << Exception::warning ); return 0.0; } - double vijk = sqrt( sqr(2.*muj2+bar*(1.-y))-4.*muj2 ) / (bar*(1.-y)); + // zi, used in dipole splitting kernel + double zi = split.lastSplittingParameters()[0]; + + double vijk = sqrt( sqr(2.*mk2 + sbar*(1.-y)) - 4.*mk2*Qijk ) / sbar / (1.-y); + double vtilde = 1.; double viji = 1.; - double vbar = 1.; - double zp = 0.5*(1.+viji*vijk); - double zm = 0.5*(1.-viji*vijk); + double zip = 0.5*(1.+viji*vijk); + double zim = 0.5*(1.-viji*vijk); // how to choose kappa? double kappa = 0.; - double S1 = 0.5*3.*(2.*y + 1.)/((1.+y)-z*(1.-y)) + + double S1 = 0.5*3.*(2.*y + 1.)/((1.+y)-zi*(1.-y)) + (!strictLargeN() ? 4./3. : 3./2.)* - y/(1.-z*(1.-y)) * ( 2.*(2.*y + 1.)/((1.+y)-z*(1.-y)) - - (vbar/vijk)*(2. + 2.*mua2/((1.-z*(1.-y))*bar)) ); - double S2 = 0.5*3.*(2.*y + 1.)/((1.+y)-(1.-z)*(1.-y)) + + y/(1.-zi*(1.-y)) * ( 2.*(2.*y + 1.)/((1.+y)-zi*(1.-y)) + - (vtilde/vijk)*(2. + 2.*ma2/((1.-zi*(1.-y))*sbar)) ); + double S2 = 0.5*3.*(2.*y + 1.)/((1.+y)-(1.-zi)*(1.-y)) + (!strictLargeN() ? 4./3. : 3./2.)* - y/(1.-(1.-z)*(1.-y)) * ( 2.*(2.*y + 1.)/((1.+y)-(1.-z)*(1.-y)) - - (vbar/vijk)*(2. + 2.*mua2/((1.-(1.-z)*(1.-y))*bar)) ); - double NS = 0.5*3.*(z*(1.-z)-(1.-kappa)*zp*zm - 2.)/vijk; - + y/(1.-(1.-zi)*(1.-y)) * ( 2.*(2.*y + 1.)/((1.+y)-(1.-zi)*(1.-y)) + - (vtilde/vijk)*(2. + 2.*ma2/((1.-(1.-zi)*(1.-y))*sbar)) ); + double NS = 0.5*3.*(zi*(1.-zi)-(1.-kappa)*zip*zim - 2.)/vijk; if( theAsymmetryOption == 0 ){ ret *= 2.*S1 + NS; }else if ( theAsymmetryOption == 1 ){ - ret *= 2.*z*( S1 + S2 + NS ); + ret *= 2.*zi*( S1 + S2 + NS ); }else{ ret *= S1 + S2 + NS; } return ret > 0. ? ret : 0.; } +vector< pair > +FIMDecaygx2ggxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, + const RhoDMatrix& rho) const { + + // Need variables for the AP kernels + double z = dInfo.lastSplittingParameters()[0]; + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_pmp = (1.-z)*sqrt( (1.-z)/z ); + + //double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp)) - 2.*abs(rho(0,2))*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp))/max ) ); + distPhiDep.push_back( make_pair(-2, rho(0,2)*(v_AP_mpm*v_AP_ppm + v_AP_mmp*v_AP_pmp)/max ) ); + distPhiDep.push_back( make_pair(2, rho(2,0)*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp)/max) ); + + return distPhiDep; +} + + +DecayMEPtr FIMDecaygx2ggxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + // Need variables for the AP kernels + double z = dInfo.lastSplittingParameters()[0]; + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_pmp = (1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1,PDT::Spin1,PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(2,2,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(2,2,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,2) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,2) = 0; + (*kernelPhiDep)(2,0,0) = 0; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FIMDecaygx2ggxDipoleKernel::persistentOutput(PersistentOStream & os) const { os<>theAsymmetryOption; } ClassDescription FIMDecaygx2ggxDipoleKernel::initFIMDecaygx2ggxDipoleKernel; // Definition of the static class description member. void FIMDecaygx2ggxDipoleKernel::Init() { static ClassDocumentation documentation ("FIMDecaygx2ggxDipoleKernel"); static Parameter interfacetheAsymmetryOption ("AsymmetryOption", "The asymmetry option for final state gluon spliitings.", &FIMDecaygx2ggxDipoleKernel::theAsymmetryOption, 0, 0, 0, false, false, Interface::lowerlim); } diff --git a/Shower/Dipole/Kernels/FIMDecaygx2ggxDipoleKernel.h b/Shower/Dipole/Kernels/FIMDecaygx2ggxDipoleKernel.h --- a/Shower/Dipole/Kernels/FIMDecaygx2ggxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FIMDecaygx2ggxDipoleKernel.h @@ -1,186 +1,197 @@ // -*- C++ -*- #ifndef HERWIG_FIMDecaygx2ggxDipoleKernel_H #define HERWIG_FIMDecaygx2ggxDipoleKernel_H // // This is the declaration of the FIMDecaygx2ggxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Stephen Webster * * \brief FIMDecaygx2ggxDipoleKernel implements the g -> gg * splitting off a final-initial decay dipole and includes the * contribution from the splitting of the intial / decay particle * */ class FIMDecaygx2ggxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FIMDecaygx2ggxDipoleKernel(); /** * The destructor. */ virtual ~FIMDecaygx2ggxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFIMDecaygx2ggxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FIMDecaygx2ggxDipoleKernel & operator=(const FIMDecaygx2ggxDipoleKernel &) = delete; /** * Asymmetry option for final state gluon splittings. */ int theAsymmetryOption=0; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FIMDecaygx2ggxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FIMDecaygx2ggxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FIMDecaygx2ggxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FIMDecaygx2ggxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FIMDecaygx2ggxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FIMDecaygx2ggxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FIMDecaygx2ggxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FIMDecaygx2qqxDipoleKernel.cc b/Shower/Dipole/Kernels/FIMDecaygx2qqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FIMDecaygx2qqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FIMDecaygx2qqxDipoleKernel.cc @@ -1,142 +1,207 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FIMDecaygx2qqxDipoleKernel class. // #include "FIMDecaygx2qqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FIMDecaygx2qqxDipoleKernel::FIMDecaygx2qqxDipoleKernel() : DipoleSplittingKernel() {} FIMDecaygx2qqxDipoleKernel::~FIMDecaygx2qqxDipoleKernel() {} IBPtr FIMDecaygx2qqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FIMDecaygx2qqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FIMDecaygx2qqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.incomingDecaySpectator() && !ind.incomingDecayEmitter() && ind.emitterData()->id() == ParticleID::g && !(ind.spectatorData()->mass() == ZERO) && // Initial state here refers to the entire event !ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool FIMDecaygx2qqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() + sk.emission(b)->id() == 0 && abs(sk.emitter(b)->id()) < 6 && emitter(a)->id() == sk.emitter(b)->id() && abs(sk.spectator(b)->mass()) == abs(spectator(a)->mass()); } tcPDPtr FIMDecaygx2qqxDipoleKernel::emitter(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6); return flavour(); } tcPDPtr FIMDecaygx2qqxDipoleKernel::emission(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6); return flavour()->CC(); } tcPDPtr FIMDecaygx2qqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FIMDecaygx2qqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); - // These are the physical variables as used in the standard form of the kernel (i.e. do not redefine variables or kernel) + // Sudakov parameterisation variables, + // needed to calculate y. double z = split.lastZ(); Energy pt = split.lastPt(); - // Need zPrime to calculate y, - // TODO: Should just store y in the dipole splitting info everywhere anyway!!! - // The only value stored in dInfo.lastSplittingParameters() should be zPrime - //assert(split.lastSplittingParameters().size() == 1 ); - double zPrime = split.lastSplittingParameters()[0]; - - // Construct mass squared variables - double mui2 = sqr(split.emitterData()->mass() / split.scale()); - double mu2 = mui2; - //double mua2 = sqr( split.spectatorMass() / split.scale() ); - // Recoil system mass - double muj2 = sqr(split.recoilMass() / split.scale()); - double bar = 1. - mui2 - mu2 - muj2; + // Note for q->qg can use the emitterMass + // (i.e. mass of emitter before splitting = mass of emitter after) + Energy2 Qijk = sqr(split.scale()); + Energy2 mi2 = sqr(split.emitterData()->mass()); + Energy2 mj2 = mi2; + Energy2 mk2 = sqr(split.recoilMass()); + Energy2 sbar = Qijk - mi2 - mj2 - mk2; // Calculate y - double y = (sqr(pt)/sqr(split.scale()) + sqr(1.-zPrime)*mui2 + sqr(zPrime)*mu2) / (bar*zPrime*(1.-zPrime)); + double y = (sqr(pt) + sqr(1.-z)*mi2 + sqr(z)*mj2) / sbar / z / (1.-z); - if( sqr(2.*muj2+bar*(1.-y))-4.*muj2 < 0. ){ + if( sqr(2.*mk2+sbar*(1.-y)) - 4.*mk2*Qijk < ZERO ){ generator()->logWarning( Exception() - << "error in FIMDecaygx2qqxDipoleKernel::evaluate -- " << - "muj2 " << muj2 << " mui2 " << mui2 << " y " << y << Exception::warning ); - + << "error in FIMDecayqx2qgxDipoleKernel::evaluate -- " << + "mk2 " << mk2/GeV2 << " mi2 " << mi2/GeV2 << " y " << y << Exception::warning ); return 0.0; } - double vijk = sqrt( sqr(2.*muj2+bar*(1.-y))-4.*muj2 ) / (bar*(1.-y)); - double viji = sqrt( sqr(bar*y)-4.*sqr(mui2) ) / (bar*y+2.*mui2); - //double vbar = sqrt( 1.+sqr(mui2)+sqr(muj2)-2.*(mui2+muj2+mui2*muj2) ) / bar; + // zi, used in dipole splitting kernel + double zi = split.lastSplittingParameters()[0]; + + double vijk = sqrt( sqr(2.*mk2 + sbar*(1.-y)) - 4.*mk2*Qijk ) / sbar / (1.-y); + double viji = sqrt( sqr(sbar*y) - 4.*sqr(mi2) ) / (sbar*y + 2.*mi2); - double zp = 0.5*(1.+viji*vijk); - double zm = 0.5*(1.-viji*vijk); + double zip = 0.5*(1.+viji*vijk); + double zim = 0.5*(1.-viji*vijk); // how to choose kappa? double kappa = 0.; - ret *= 0.25 / vijk * ( 1. - 2.*( z*(1.-z) - (1.-kappa)*zp*zm - kappa*mui2/(2*mui2+bar*y) ) ); + ret *= 0.25 / vijk + * ( 1. - 2.*( zi*(1.-zi) - (1.-kappa)*zip*zim - kappa*mi2/(2.*mi2 + sbar*y) ) ); return ret > 0. ? ret : 0.; } +vector< pair > +FIMDecaygx2qqxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, + const RhoDMatrix& rho) const { + + // Need variables for the AP kernels + double z = dInfo.lastSplittingParameters()[0]; + Energy pt = dInfo.lastPt(); + Energy2 mi2 = sqr(dInfo.emitterData()->mass()); + + // Altarelli-Parisi spin-indexed kernels: + double ratio = mi2 / ( mi2 + sqr(pt) ); + double root = sqrt(1.-ratio); + double v_AP_ppp = sqrt(ratio); + double v_AP_ppm = z*root; + double v_AP_pmp = -(1.-z)*root; + + // double v_AP_mmm = v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp)) + 2.*abs(rho(0,2))*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp) )/max ) ); + distPhiDep.push_back( make_pair(-2, rho(0,2)*(v_AP_mpm*v_AP_ppm + v_AP_mmp*v_AP_pmp)/max ) ); + distPhiDep.push_back( make_pair(2, rho(2,0)*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp)/max) ); + + return distPhiDep; +} + + +DecayMEPtr FIMDecaygx2qqxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + // Need variables for the AP kernels + double z = dInfo.lastSplittingParameters()[0]; + Energy pt = dInfo.lastPt(); + Energy2 mi2 = sqr(dInfo.emitterData()->mass()); + + // Altarelli-Parisi spin-indexed kernels: + double ratio = mi2 / ( mi2 + sqr(pt) ); + double root = sqrt(1.-ratio); + double v_AP_ppp = sqrt(ratio); + double v_AP_ppm = z*root; + double v_AP_pmp = -(1.-z)*root; + + double v_AP_mmm = v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm; + (*kernelPhiDep)(2,1,1) = v_AP_ppp;; + (*kernelPhiDep)(0,0,1) = v_AP_mmp/phase; + (*kernelPhiDep)(2,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,1,1) = 0.; + (*kernelPhiDep)(2,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FIMDecaygx2qqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FIMDecaygx2qqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FIMDecaygx2qqxDipoleKernel::initFIMDecaygx2qqxDipoleKernel; // Definition of the static class description member. void FIMDecaygx2qqxDipoleKernel::Init() { static ClassDocumentation documentation ("FIMDecaygx2qqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FIMDecaygx2qqxDipoleKernel.h b/Shower/Dipole/Kernels/FIMDecaygx2qqxDipoleKernel.h --- a/Shower/Dipole/Kernels/FIMDecaygx2qqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FIMDecaygx2qqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FIMDecaygx2qqxDipoleKernel_H #define HERWIG_FIMDecaygx2qqxDipoleKernel_H // // This is the declaration of the FIMDecaygx2qqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Stephen Webster * * \brief FIMDecaygx2qqxDipoleKernel implements the g -> qq * splitting off a final-initial decay dipole * */ class FIMDecaygx2qqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FIMDecaygx2qqxDipoleKernel(); /** * The destructor. */ virtual ~FIMDecaygx2qqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFIMDecaygx2qqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FIMDecaygx2qqxDipoleKernel & operator=(const FIMDecaygx2qqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FIMDecaygx2qqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FIMDecaygx2qqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FIMDecaygx2qqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FIMDecaygx2qqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FIMDecaygx2qqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FIMDecaygx2qqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FIMDecaygx2qqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FIMDecayqx2qgxDipoleKernel.cc b/Shower/Dipole/Kernels/FIMDecayqx2qgxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FIMDecayqx2qgxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FIMDecayqx2qgxDipoleKernel.cc @@ -1,144 +1,187 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FIMDecayqx2qgxDipoleKernel class. // #include "FIMDecayqx2qgxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FIMDecayqx2qgxDipoleKernel::FIMDecayqx2qgxDipoleKernel() : DipoleSplittingKernel() {} FIMDecayqx2qgxDipoleKernel::~FIMDecayqx2qgxDipoleKernel() {} IBPtr FIMDecayqx2qgxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FIMDecayqx2qgxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FIMDecayqx2qgxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.incomingDecaySpectator() && !ind.incomingDecayEmitter() && abs(ind.emitterData()->id()) < 7 && // This line matches to the kernel declared in a .in file for the given emitter flavour abs(ind.emitterData()->id()) == abs(flavour()->id()) && !(ind.spectatorData()->mass() == ZERO) && // Initial state here refers to the entire event !ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool FIMDecayqx2qgxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emission(b)->id() == ParticleID::g && abs(sk.emitter(b)->id()) < 7 && abs(sk.emitter(b)->mass()) == abs(emitter(a)->mass()) && abs(sk.spectator(b)->mass()) == abs(spectator(a)->mass()); } tcPDPtr FIMDecayqx2qgxDipoleKernel::emitter(const DipoleIndex& ind) const { assert(flavour()); assert(abs(flavour()->id()) < 7); return ind.emitterData()->id() > 0 ? (tcPDPtr) flavour() : (tcPDPtr) flavour()->CC(); } tcPDPtr FIMDecayqx2qgxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FIMDecayqx2qgxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FIMDecayqx2qgxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); - // These are the physical variables as used in the standard form of the kernel (i.e. do not redefine variables or kernel) + // Sudakov parameterisation variables, + // needed to calculate y. double z = split.lastZ(); Energy pt = split.lastPt(); - // Need zPrime to calculate y, - // TODO: Should just store y in the dipole splitting info everywhere anyway!!! - // The only value stored in dInfo.lastSplittingParameters() should be zPrime - //assert(split.lastSplittingParameters().size() == 1 ); - double zPrime = split.lastSplittingParameters()[0]; - // Construct mass squared variables // Note for q->qg can use the emitterMass // (i.e. mass of emitter before splitting = mass of emitter after) - double mui2 = sqr(split.emitterMass() / split.scale()); - // Recoil system mass - double muj2 = sqr(split.recoilMass() / split.scale()); - // This should be equal to one - double mua2 = sqr( split.spectatorMass() / split.scale() ); - double bar = 1. - mui2 - muj2; - + Energy2 Qijk = sqr(split.scale()); + Energy2 mi2 = sqr(split.emitterMass()); + Energy2 mk2 = sqr(split.recoilMass()); + Energy2 sbar = Qijk - mi2 - mk2; + + // Note this should be the same as Qijk + Energy2 ma2 = sqr(split.spectatorMass()); + // Calculate y - double y = (sqr(pt)/sqr(split.scale()) + sqr(1.-zPrime)*mui2) / (bar*zPrime*(1.-zPrime)); + double y = (sqr(pt) + sqr(1.-z)*mi2) / sbar / z / (1.-z); - if( sqr(2.*muj2+bar*(1.-y))-4.*muj2 < 0. ){ + if( sqr(2.*mk2+sbar*(1.-y)) - 4.*mk2*Qijk < ZERO ){ generator()->logWarning( Exception() << "error in FIMDecayqx2qgxDipoleKernel::evaluate -- " << - "muj2 " << muj2 << " mui2 " << mui2 << " y " << y << Exception::warning ); + "mk2 " << mk2/GeV2 << " mi2 " << mi2/GeV2 << " y " << y << Exception::warning ); return 0.0; } - double vijk = sqrt( sqr(2.*muj2 + bar*(1.-y))-4.*muj2 ) / (bar*(1.-y)); - double vbar = sqrt( 1.+sqr(mui2)+sqr(muj2)-2.*(mui2+muj2+mui2*muj2) ) / bar; + // zi, used in dipole splitting kernel + double zi = split.lastSplittingParameters()[0]; + + double vijk = sqrt( sqr(2.*mk2 + sbar*(1.-y)) - 4.*mk2*Qijk ) / sbar / (1.-y); + double vtilde = sqrt( sqr(Qijk) + sqr(mi2) + sqr(mk2) + - 2.*(mi2*Qijk + mk2*Qijk + mi2*mk2) ) / sbar; ret *= (!strictLargeN() ? 4./3. : 3./2.) - * ( ( 2.*(2.*mui2/bar + 2.*y + 1.)/((1.+y)-z*(1.-y)) - - (vbar/vijk)*((1.+z) + 2.*mui2/(y*bar)) ) - + y/(1.-z*(1.-y)) * ( 2.*(2.*mui2/bar + 2.*y + 1.)/((1.+y)-z*(1.-y)) - - (vbar/vijk)*(2. + 2.*mua2/((1.-z*(1.-y))*bar)) ) ); + * ( ( 2.*( 2.*mi2/sbar + 2.*y + 1. ) / ((1.+y) - zi*(1.-y)) + - (vtilde/vijk) * ( (1.+zi) + 2.*mi2/y/sbar ) ) + + y/(1. - zi*(1.-y)) * ( 2.*(2.*mi2/sbar + 2.*y + 1. ) / ((1.+y) - zi*(1.-y)) + - (vtilde/vijk) * ( 2. + 2.*ma2/((1. - zi*(1.-y))*sbar) ) ) ); return ret > 0. ? ret : 0.; } +vector< pair > +FIMDecayqx2qgxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr FIMDecayqx2qgxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + // Need variables for the AP kernels + double z = dInfo.lastSplittingParameters()[0]; + Energy pt = dInfo.lastPt(); + Energy mi = dInfo.emitterMass(); + + // Altarelli-Parisi spin-indexed kernels: + Energy den = sqrt(sqr(mi)*sqr(1.-z) + sqr(pt)); + double v_AP_ppp = pt / den / sqrt(1.-z); + double v_AP_ppm = - z * v_AP_ppp ; + double v_AP_pmp = mi*(1.-z)*sqrt(1.-z) / den ; + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -1 or -1/2, 1=+1/2, 2 = +1 + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,1,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(1,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm; + (*kernelPhiDep)(1,0,2) = v_AP_pmp; + (*kernelPhiDep)(0,1,2) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FIMDecayqx2qgxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FIMDecayqx2qgxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FIMDecayqx2qgxDipoleKernel::initFIMDecayqx2qgxDipoleKernel; // Definition of the static class description member. void FIMDecayqx2qgxDipoleKernel::Init() { static ClassDocumentation documentation ("FIMDecayqx2qgxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FIMDecayqx2qgxDipoleKernel.h b/Shower/Dipole/Kernels/FIMDecayqx2qgxDipoleKernel.h --- a/Shower/Dipole/Kernels/FIMDecayqx2qgxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FIMDecayqx2qgxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FIMDecayqx2qgxDipoleKernel_H #define HERWIG_FIMDecayqx2qgxDipoleKernel_H // // This is the declaration of the FIMDecayqx2qgxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Stephen Webster * * \brief FIMDecayqx2qgxDipoleKernel implements the q -> qg * splitting off a final-initial decay dipole and includes the * contribution from the splitting of the intial / decay particle * */ class FIMDecayqx2qgxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FIMDecayqx2qgxDipoleKernel(); /** * The destructor. */ virtual ~FIMDecayqx2qgxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex& ind) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex& ind) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo& split) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFIMDecayqx2qgxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FIMDecayqx2qgxDipoleKernel & operator=(const FIMDecayqx2qgxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FIMDecayqx2qgxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FIMDecayqx2qgxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FIMDecayqx2qgxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FIMDecayqx2qgxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FIMDecayqx2qgxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FIMDecayqx2qgxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FIMDecayqx2qgxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FIMgx2qqxDipoleKernel.cc b/Shower/Dipole/Kernels/FIMgx2qqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FIMgx2qqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FIMgx2qqxDipoleKernel.cc @@ -1,116 +1,180 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FIMgx2qqxDipoleKernel class. // #include "FIMgx2qqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FIMgx2qqxDipoleKernel::FIMgx2qqxDipoleKernel() : DipoleSplittingKernel() {} FIMgx2qqxDipoleKernel::~FIMgx2qqxDipoleKernel() {} IBPtr FIMgx2qqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FIMgx2qqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FIMgx2qqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() == ZERO && flavour()->mass() != ZERO && !ind.initialStateEmitter() && ind.initialStateSpectator(); } bool FIMgx2qqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() + sk.emission(b)->id() == 0 && abs(sk.emitter(b)->id()) < 6 && emitter(a)->mass() == sk.emitter(b)->mass() && a.spectatorPDF() == b.spectatorPDF(); } tcPDPtr FIMgx2qqxDipoleKernel::emitter(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() != ZERO); return flavour(); } tcPDPtr FIMgx2qqxDipoleKernel::emission(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() != ZERO); return flavour()->CC(); } tcPDPtr FIMgx2qqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } // TODO // assure split.scale() is sqrt(sbar) double FIMgx2qqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); // mi=m=mQ, Mi=0, mj=Mj=0 Energy2 mQ2 = sqr(split.emitterData()->mass()); double z = split.lastZ(); double x = 1./ ( 1. + ( sqr(split.lastPt()) + mQ2 ) / ( z*(1.-z) * sqr(split.scale()) ) ); double muQ2 = x * mQ2/sqr(split.scale()); double zm = .5 * ( 1. - sqrt( 1. - 4.*muQ2/(1.-x) ) ); double zp = .5 * ( 1. + sqrt( 1. - 4.*muQ2/(1.-x) ) ); ret *= .25 * (1.-2.*(zp-z)*(z-zm)); return ret > 0. ? ret : 0.; } +vector< pair > +FIMgx2qqxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + Energy pt = dInfo.lastPt(); + Energy2 mi2 = sqr(dInfo.emitterData()->mass()); + + // Altarelli-Parisi spin-indexed kernels: + double ratio = mi2 / ( mi2 + sqr(pt) ); + double root = sqrt(1.-ratio); + double v_AP_ppp = sqrt(ratio); + double v_AP_ppm = z*root; + double v_AP_pmp = -(1.-z)*root; + + //double v_AP_mmm = v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp)) + 2.*abs(rho(0,2))*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp) )/max ) ); + distPhiDep.push_back( make_pair(-2, rho(0,2)*(v_AP_mpm*v_AP_ppm + v_AP_mmp*v_AP_pmp)/max ) ); + distPhiDep.push_back( make_pair(2, rho(2,0)*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp)/max) ); + + return distPhiDep; +} + +DecayMEPtr FIMgx2qqxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + double z = dInfo.lastZ(); + Energy pt = dInfo.lastPt(); + Energy2 mi2 = sqr(dInfo.emitterData()->mass()); + + // Altarelli-Parisi spin-indexed kernels: + double ratio = mi2 / ( mi2 + sqr(pt) ); + double root = sqrt(1.-ratio); + double v_AP_ppp = sqrt(ratio); + double v_AP_ppm = z*root; + double v_AP_pmp = -(1.-z)*root; + + double v_AP_mmm = v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm; + (*kernelPhiDep)(2,1,1) = v_AP_ppp; + (*kernelPhiDep)(0,0,1) = v_AP_mmp/phase; + (*kernelPhiDep)(2,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,1,1) = 0.; + (*kernelPhiDep)(2,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FIMgx2qqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FIMgx2qqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FIMgx2qqxDipoleKernel::initFIMgx2qqxDipoleKernel; // Definition of the static class description member. void FIMgx2qqxDipoleKernel::Init() { static ClassDocumentation documentation ("FIMgx2qqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FIMgx2qqxDipoleKernel.h b/Shower/Dipole/Kernels/FIMgx2qqxDipoleKernel.h --- a/Shower/Dipole/Kernels/FIMgx2qqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FIMgx2qqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FIMgx2qqxDipoleKernel_H #define HERWIG_FIMgx2qqxDipoleKernel_H // // This is the declaration of the FIMgx2qqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll * * \brief FIMgx2qqxDipoleKernel implements the g -> qqbar * splitting off a final-initial dipole * */ class FIMgx2qqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FIMgx2qqxDipoleKernel(); /** * The destructor. */ virtual ~FIMgx2qqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFIMgx2qqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FIMgx2qqxDipoleKernel & operator=(const FIMgx2qqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FIMgx2qqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FIMgx2qqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FIMgx2qqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FIMgx2qqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FIMgx2qqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FIMgx2qqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FIMgx2qqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FIMqx2qgxDipoleKernel.cc b/Shower/Dipole/Kernels/FIMqx2qgxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FIMqx2qgxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FIMqx2qgxDipoleKernel.cc @@ -1,113 +1,155 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FIMqx2qgxDipoleKernel class. // #include "FIMqx2qgxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FIMqx2qgxDipoleKernel::FIMqx2qgxDipoleKernel() : DipoleSplittingKernel() {} FIMqx2qgxDipoleKernel::~FIMqx2qgxDipoleKernel() {} IBPtr FIMqx2qgxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FIMqx2qgxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FIMqx2qgxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 7 && abs(ind.emitterData()->id())==abs(flavour()->id()) && ind.emitterData()->mass() != ZERO && ind.spectatorData()->mass() == ZERO && !ind.initialStateEmitter() && ind.initialStateSpectator(); } bool FIMqx2qgxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emission(b)->id() == ParticleID::g && abs(sk.emitter(b)->id()) < 7 && sk.emitter(b)->mass() == emitter(a)->mass() && a.spectatorPDF() == b.spectatorPDF(); } tcPDPtr FIMqx2qgxDipoleKernel::emitter(const DipoleIndex& ind) const { assert(flavour()); assert(abs(flavour()->id())<7 && flavour()->mass() != ZERO); return ind.emitterData()->id() > 0 ? (tcPDPtr) flavour() : (tcPDPtr) flavour()->CC(); } tcPDPtr FIMqx2qgxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FIMqx2qgxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } // TODO // split.scale() should be sqrt(sbar) = sqrt( Mi2 - Q2 ) !!! double FIMqx2qgxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); // Mi=mi=mQ, m=0, Mj=mj=0 Energy2 mQ2 = sqr(split.emitterMass()); double z = split.lastZ(); double x = 1. / ( 1. + ( sqr(split.lastPt()) + sqr(1.-z)*mQ2 ) / ( z*(1.-z) * sqr(split.scale()) ) ); // Simon has extra terms ret *= (!strictLargeN() ? 4./3. : 3./2.) * ( 2./(1.-z+(1.-x)) -(1.+z) - mQ2/sqr(split.scale()) * 2.*x/(1.-x) ); return ret > 0. ? ret : 0.; } +vector< pair > +FIMqx2qgxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr FIMqx2qgxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + double z = dInfo.lastZ(); + Energy pt = dInfo.lastPt(); + Energy mi = dInfo.emitterMass(); + + // Altarelli-Parisi spin-indexed kernels: + Energy den = sqrt(sqr(mi)*sqr(1.-z) + sqr(pt)); + double v_AP_ppp = pt / den / sqrt(1.-z); + double v_AP_ppm = - z * v_AP_ppp ; + double v_AP_pmp = mi*(1.-z)*sqrt(1.-z) / den ; + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -1 or -1/2, 1=+1/2, 2 = +1 + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,1,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(1,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm; + (*kernelPhiDep)(1,0,2) = v_AP_pmp; + (*kernelPhiDep)(0,1,2) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FIMqx2qgxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FIMqx2qgxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FIMqx2qgxDipoleKernel::initFIMqx2qgxDipoleKernel; // Definition of the static class description member. void FIMqx2qgxDipoleKernel::Init() { static ClassDocumentation documentation ("FIMqx2qgxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FIMqx2qgxDipoleKernel.h b/Shower/Dipole/Kernels/FIMqx2qgxDipoleKernel.h --- a/Shower/Dipole/Kernels/FIMqx2qgxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FIMqx2qgxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FIMqx2qgxDipoleKernel_H #define HERWIG_FIMqx2qgxDipoleKernel_H // // This is the declaration of the FIMqx2qgxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll * * \brief FIMqx2qgxDipoleKernel implements the q -> qg * splitting off a final-initial dipole * */ class FIMqx2qgxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FIMqx2qgxDipoleKernel(); /** * The destructor. */ virtual ~FIMqx2qgxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFIMqx2qgxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FIMqx2qgxDipoleKernel & operator=(const FIMqx2qgxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FIMqx2qgxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FIMqx2qgxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FIMqx2qgxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FIMqx2qgxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FIMqx2qgxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FIMqx2qgxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FIMqx2qgxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FIgx2ggxDipoleKernel.cc b/Shower/Dipole/Kernels/FIgx2ggxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FIgx2ggxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FIgx2ggxDipoleKernel.cc @@ -1,116 +1,172 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FIgx2ggxDipoleKernel class. // #include "FIgx2ggxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FIgx2ggxDipoleKernel::FIgx2ggxDipoleKernel() : DipoleSplittingKernel() {} FIgx2ggxDipoleKernel::~FIgx2ggxDipoleKernel() {} IBPtr FIgx2ggxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FIgx2ggxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FIgx2ggxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() == ZERO && !ind.initialStateEmitter() && ind.initialStateSpectator(); } bool FIgx2ggxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() == ParticleID::g && sk.emission(b)->id() == ParticleID::g && a.spectatorPDF() == b.spectatorPDF(); } tcPDPtr FIgx2ggxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FIgx2ggxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FIgx2ggxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FIgx2ggxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double x = 1. / ( 1. + sqr(split.lastPt()/split.scale()) / (z*(1.-z)) ); double S1=1./(1.-z+(1.-x)); double S2=1./(1.-(1.-z)+(1.-x)); double NS=(-2 + z*(1.-z)+(1.-x)*(1.+x*z*(1.-z))); if( theAsymmetryOption == 0 ){ ret *= 3.*( S1 + 0.5 * NS); }else if ( theAsymmetryOption == 1 ){ ret *= 3.*z*( S1 +S2 + NS ); }else{ ret *= 3.*0.5*( S1 + S2 + NS ); } return ret > 0. ? ret : 0.; } +vector< pair > +FIgx2ggxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_pmp = (1.-z)*sqrt( (1.-z)/z ); + + //double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp)) - 2.*abs(rho(0,2))*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_pmp))/max ) ); + distPhiDep.push_back( make_pair(-2, rho(0,2)*(v_AP_mpm*v_AP_ppm + v_AP_mmp*v_AP_pmp)/max ) ); + distPhiDep.push_back( make_pair(2, rho(2,0)*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp)/max) ); + + return distPhiDep; +} + +DecayMEPtr FIgx2ggxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_pmp = (1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1,PDT::Spin1,PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(2,2,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(2,2,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,2) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,2) = 0; + (*kernelPhiDep)(2,0,0) = 0; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FIgx2ggxDipoleKernel::persistentOutput(PersistentOStream & os) const { os << theAsymmetryOption; } void FIgx2ggxDipoleKernel::persistentInput(PersistentIStream & is, int) { is >> theAsymmetryOption; } ClassDescription FIgx2ggxDipoleKernel::initFIgx2ggxDipoleKernel; // Definition of the static class description member. void FIgx2ggxDipoleKernel::Init() { static ClassDocumentation documentation ("FIgx2ggxDipoleKernel"); static Parameter interfacetheAsymmetryOption ("AsymmetryOption", "The asymmetry option for final state gluon spliitings.", &FIgx2ggxDipoleKernel::theAsymmetryOption, 1, 0, 0, false, false, Interface::lowerlim); } diff --git a/Shower/Dipole/Kernels/FIgx2ggxDipoleKernel.h b/Shower/Dipole/Kernels/FIgx2ggxDipoleKernel.h --- a/Shower/Dipole/Kernels/FIgx2ggxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FIgx2ggxDipoleKernel.h @@ -1,186 +1,197 @@ // -*- C++ -*- #ifndef HERWIG_FIgx2ggxDipoleKernel_H #define HERWIG_FIgx2ggxDipoleKernel_H // // This is the declaration of the FIgx2ggxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief FIgx2ggxDipoleKernel implements the g -> gg * splitting off a final-initial dipole * */ class FIgx2ggxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FIgx2ggxDipoleKernel(); /** * The destructor. */ virtual ~FIgx2ggxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * Asymmetry option for final state gluon splittings. */ int theAsymmetryOption=1; /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFIgx2ggxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FIgx2ggxDipoleKernel & operator=(const FIgx2ggxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FIgx2ggxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FIgx2ggxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FIgx2ggxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FIgx2ggxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FIgx2ggxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FIgx2ggxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FIgx2ggxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FIgx2qqxDipoleKernel.cc b/Shower/Dipole/Kernels/FIgx2qqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FIgx2qqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FIgx2qqxDipoleKernel.cc @@ -1,103 +1,155 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FIgx2qqxDipoleKernel class. // #include "FIgx2qqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FIgx2qqxDipoleKernel::FIgx2qqxDipoleKernel() : DipoleSplittingKernel() {} FIgx2qqxDipoleKernel::~FIgx2qqxDipoleKernel() {} IBPtr FIgx2qqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FIgx2qqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FIgx2qqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() == ZERO && flavour()->mass() == ZERO && !ind.initialStateEmitter() && ind.initialStateSpectator(); } bool FIgx2qqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() + sk.emission(b)->id() == 0 && abs(sk.emitter(b)->id()) < 6 && // sk.emitter(b)->mass() == ZERO && a.spectatorPDF() == b.spectatorPDF(); } tcPDPtr FIgx2qqxDipoleKernel::emitter(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour(); } tcPDPtr FIgx2qqxDipoleKernel::emission(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour()->CC(); } tcPDPtr FIgx2qqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FIgx2qqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); ret *= .25 * (1.-2.*z*(1.-z)); return ret > 0. ? ret : 0.; } +vector< pair > +FIgx2qqxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppm = z; + double v_AP_pmp = -(1.-z); + + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppm) + sqr(v_AP_pmp)) + 2.*abs(rho(0,2))*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*( sqr(v_AP_ppm) + sqr(v_AP_pmp) )/max ) ); + distPhiDep.push_back( make_pair(-2, rho(0,2)*(v_AP_mpm*v_AP_ppm + v_AP_mmp*v_AP_pmp)/max ) ); + distPhiDep.push_back( make_pair(2, rho(2,0)*(v_AP_ppm*v_AP_mpm + v_AP_pmp*v_AP_mmp)/max) ); + + return distPhiDep; +} + +DecayMEPtr FIgx2qqxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppm = z; + double v_AP_pmp = -(1.-z); + + double v_AP_mmp = -v_AP_ppm; + double v_AP_mpm = -v_AP_pmp; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1,PDT::Spin1Half,PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = 0.; + (*kernelPhiDep)(2,1,1) = 0.; + (*kernelPhiDep)(0,0,1) = v_AP_mmp/phase; + (*kernelPhiDep)(2,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,1,1) = 0.; + (*kernelPhiDep)(2,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FIgx2qqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FIgx2qqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FIgx2qqxDipoleKernel::initFIgx2qqxDipoleKernel; // Definition of the static class description member. void FIgx2qqxDipoleKernel::Init() { static ClassDocumentation documentation ("FIgx2qqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FIgx2qqxDipoleKernel.h b/Shower/Dipole/Kernels/FIgx2qqxDipoleKernel.h --- a/Shower/Dipole/Kernels/FIgx2qqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FIgx2qqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FIgx2qqxDipoleKernel_H #define HERWIG_FIgx2qqxDipoleKernel_H // // This is the declaration of the FIgx2qqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief FIgx2qqxDipoleKernel implements the g -> qqbar * splitting off a final-initial dipole * */ class FIgx2qqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FIgx2qqxDipoleKernel(); /** * The destructor. */ virtual ~FIgx2qqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFIgx2qqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FIgx2qqxDipoleKernel & operator=(const FIgx2qqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FIgx2qqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FIgx2qqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FIgx2qqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FIgx2qqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FIgx2qqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FIgx2qqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FIgx2qqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/FIqx2qgxDipoleKernel.cc b/Shower/Dipole/Kernels/FIqx2qgxDipoleKernel.cc --- a/Shower/Dipole/Kernels/FIqx2qgxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/FIqx2qgxDipoleKernel.cc @@ -1,101 +1,138 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the FIqx2qgxDipoleKernel class. // #include "FIqx2qgxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; FIqx2qgxDipoleKernel::FIqx2qgxDipoleKernel() : DipoleSplittingKernel() {} FIqx2qgxDipoleKernel::~FIqx2qgxDipoleKernel() {} IBPtr FIqx2qgxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr FIqx2qgxDipoleKernel::fullclone() const { return new_ptr(*this); } bool FIqx2qgxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 6 && ind.emitterData()->mass() == ZERO && ind.spectatorData()->mass() == ZERO && !ind.initialStateEmitter() && ind.initialStateSpectator(); } bool FIqx2qgxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emission(b)->id() == ParticleID::g && abs(sk.emitter(b)->id()) < 6 && sk.emitter(b)->mass() == ZERO && a.spectatorPDF() == b.spectatorPDF(); } tcPDPtr FIqx2qgxDipoleKernel::emitter(const DipoleIndex& ind) const { return ind.emitterData(); } tcPDPtr FIqx2qgxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr FIqx2qgxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double FIqx2qgxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double x = 1. / ( 1. + sqr(split.lastPt()/split.scale()) / (z*(1.-z)) ); ret *= (!strictLargeN() ? 4./3. : 3./2.) * ( 2./(1.-z+(1.-x)) -(1.+z) + (1.-x)*(1.+3.*x*z) ); return ret > 0. ? ret : 0.; } +vector< pair > +FIqx2qgxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr FIqx2qgxDipoleKernel::matrixElement( const DipoleSplittingInfo& dInfo ) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt( 1./(1.-z) ); + double v_AP_ppm = -z/sqrt(1.-z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half,PDT::Spin1Half,PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -1 or -1/2, 1=+1/2, 2 = +1 + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,1,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(1,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = 0.; + (*kernelPhiDep)(1,0,2) = 0.; + (*kernelPhiDep)(0,1,2) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FIqx2qgxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void FIqx2qgxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription FIqx2qgxDipoleKernel::initFIqx2qgxDipoleKernel; // Definition of the static class description member. void FIqx2qgxDipoleKernel::Init() { static ClassDocumentation documentation ("FIqx2qgxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/FIqx2qgxDipoleKernel.h b/Shower/Dipole/Kernels/FIqx2qgxDipoleKernel.h --- a/Shower/Dipole/Kernels/FIqx2qgxDipoleKernel.h +++ b/Shower/Dipole/Kernels/FIqx2qgxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_FIqx2qgxDipoleKernel_H #define HERWIG_FIqx2qgxDipoleKernel_H // // This is the declaration of the FIqx2qgxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief FIqx2qgxDipoleKernel implements the q -> qg * splitting off a final-initial dipole * */ class FIqx2qgxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FIqx2qgxDipoleKernel(); /** * The destructor. */ virtual ~FIqx2qgxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFIqx2qgxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FIqx2qgxDipoleKernel & operator=(const FIqx2qgxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FIqx2qgxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FIqx2qgxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the FIqx2qgxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FIqx2qgxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * FIqx2qgxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class FIqx2qgxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FIqx2qgxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IFMgx2ggxDipoleKernel.cc b/Shower/Dipole/Kernels/IFMgx2ggxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IFMgx2ggxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IFMgx2ggxDipoleKernel.cc @@ -1,109 +1,165 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IFMgx2ggxDipoleKernel class. // #include "IFMgx2ggxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IFMgx2ggxDipoleKernel::IFMgx2ggxDipoleKernel() : DipoleSplittingKernel() {} IFMgx2ggxDipoleKernel::~IFMgx2ggxDipoleKernel() {} IBPtr IFMgx2ggxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IFMgx2ggxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IFMgx2ggxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() != ZERO && ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool IFMgx2ggxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() == ParticleID::g && sk.emission(b)->id() == ParticleID::g && a.emitterPDF() == b.emitterPDF() && sk.spectator(b)->mass() == spectator(a)->mass(); } tcPDPtr IFMgx2ggxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IFMgx2ggxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IFMgx2ggxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IFMgx2ggxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); Energy pt = split.lastPt(); double ratio = sqr(pt/split.scale()); double muk2 = sqr(split.spectatorMass()/split.scale()); // Calculate x and u double rho = 1. - 4.*ratio*(1.-muk2)*z*(1.-z)/sqr(1.-z+ratio); double x = 0.5*((1.-z+ratio)/(ratio*(1.-muk2))) * (1. - sqrt(rho)); double u = x*ratio / (1.-z); // NOTE - The definition of muk used in the kinematics differs from that in CS double muk2CS = x*muk2; ret *= 3. * ( 1./(1.-x+u) - 1. + x*(1.-x) + (1.-x)/x - muk2CS*u/(x*(1.-u)) ); return ret > 0. ? ret : 0.; } +vector< pair > +IFMgx2ggxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_mpm = -(1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + //double v_AP_mmp = -v_AP_ppm; + double v_AP_pmp = -v_AP_mpm; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_mpm)) - 2.*abs(rho(0,2))*(v_AP_ppp*v_AP_pmp + v_AP_mmm*v_AP_mpm); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_mpm))/max ) ); + distPhiDep.push_back( make_pair(2, rho(0,2)*(v_AP_mmm*v_AP_mpm + v_AP_pmp*v_AP_ppp)/max ) ); + distPhiDep.push_back( make_pair(-2, rho(2,0)*(v_AP_ppp*v_AP_pmp + v_AP_mpm*v_AP_mmm)/max) ); + + return distPhiDep; +} + +DecayMEPtr IFMgx2ggxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_mpm = -(1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_pmp = -v_AP_mpm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1, PDT::Spin1, PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(2,2,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(2,2,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,2) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,2) = 0; + (*kernelPhiDep)(2,0,0) = 0; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFMgx2ggxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IFMgx2ggxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IFMgx2ggxDipoleKernel::initIFMgx2ggxDipoleKernel; // Definition of the static class description member. void IFMgx2ggxDipoleKernel::Init() { static ClassDocumentation documentation ("IFMgx2ggxDipoleKernelv"); } diff --git a/Shower/Dipole/Kernels/IFMgx2ggxDipoleKernel.h b/Shower/Dipole/Kernels/IFMgx2ggxDipoleKernel.h --- a/Shower/Dipole/Kernels/IFMgx2ggxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IFMgx2ggxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IFMgx2ggxDipoleKernel_H #define HERWIG_IFMgx2ggxDipoleKernel_H // // This is the declaration of the IFMgx2ggxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll * * \brief IFMgx2ggxDipoleKernel implements the g -> gg * splitting off an initial-final dipole * */ class IFMgx2ggxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IFMgx2ggxDipoleKernel(); /** * The destructor. */ virtual ~IFMgx2ggxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIFMgx2ggxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IFMgx2ggxDipoleKernel & operator=(const IFMgx2ggxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IFMgx2ggxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IFMgx2ggxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IFMgx2ggxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IFMgx2ggxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IFMgx2ggxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IFMgx2ggxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IFMgx2ggxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IFMgx2qqxDipoleKernel.cc b/Shower/Dipole/Kernels/IFMgx2qqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IFMgx2qqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IFMgx2qqxDipoleKernel.cc @@ -1,115 +1,168 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IFMgx2qqxDipoleKernel class. // #include "IFMgx2qqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IFMgx2qqxDipoleKernel::IFMgx2qqxDipoleKernel() : DipoleSplittingKernel() {} IFMgx2qqxDipoleKernel::~IFMgx2qqxDipoleKernel() {} IBPtr IFMgx2qqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IFMgx2qqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IFMgx2qqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() != ZERO && flavour()->mass() == ZERO && ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool IFMgx2qqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return flavour() == sk.flavour() && abs(flavour()->id()) < 6 && flavour()->mass() == ZERO && spectator(a)->mass() == sk.spectator(b)->mass() && a.emitterPDF() == b.emitterPDF(); } tcPDPtr IFMgx2qqxDipoleKernel::emitter(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour(); } tcPDPtr IFMgx2qqxDipoleKernel::emission(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour(); } tcPDPtr IFMgx2qqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IFMgx2qqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); Energy pt = split.lastPt(); double ratio = sqr(pt/split.scale()); double muk2 = sqr(split.spectatorMass()/split.scale()); // Calculate x and u double rho = 1. - 4.*ratio*(1.-muk2)*z*(1.-z)/sqr(1.-z+ratio); double x = 0.5*((1.-z+ratio)/(ratio*(1.-muk2))) * (1. - sqrt(rho)); double u = x*ratio / (1.-z); // NOTE - The definition of muk used in the kinematics differs from that in CS double muk2CS = x*muk2; ret *= 0.5 * (!strictLargeN() ? 4./3. : 3./2.) * ( x + 2.*(1.-x)/x - 2.*muk2CS/x*u/(1.-u) ); return ret > 0. ? ret : 0.; } +vector< pair > +IFMgx2qqxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt(1./z); + double v_AP_mpm = (1.-z)/sqrt(z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_pmp = -v_AP_mpm; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = sqr(v_AP_ppp) + sqr(v_AP_mpm) + - 2.*abs(rho(0,2))*(v_AP_pmp*v_AP_ppp + v_AP_mmm*v_AP_mpm); + + distPhiDep.push_back(make_pair( 0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_mpm))/max ) ); + distPhiDep.push_back(make_pair( 2, rho(0,2)*(v_AP_pmp*v_AP_ppp + v_AP_mmm*v_AP_mpm )/max ) ); + distPhiDep.push_back(make_pair( -2, rho(2,0)*(v_AP_ppp*v_AP_pmp + v_AP_mpm*v_AP_mmm )/max ) ); + + return distPhiDep; +} + +DecayMEPtr IFMgx2qqxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt(1./z); + double v_AP_mpm = (1.-z)/sqrt(z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_pmp = -v_AP_mpm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half, PDT::Spin1, PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -1 or -1/2, 1=+1/2, 2 = +1 + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,2,1) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,1) = 0.; + (*kernelPhiDep)(1,2,0) = 0.; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(1,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,1) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFMgx2qqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IFMgx2qqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IFMgx2qqxDipoleKernel::initIFMgx2qqxDipoleKernel; // Definition of the static class description member. void IFMgx2qqxDipoleKernel::Init() { static ClassDocumentation documentation ("IFMgx2qqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IFMgx2qqxDipoleKernel.h b/Shower/Dipole/Kernels/IFMgx2qqxDipoleKernel.h --- a/Shower/Dipole/Kernels/IFMgx2qqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IFMgx2qqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IFMgx2qqxDipoleKernel_H #define HERWIG_IFMgx2qqxDipoleKernel_H // // This is the declaration of the IFgx2qqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll * * \brief IFMgx2qqxDipoleKernel implements the g -> qq * splitting off an initial-final dipole * */ class IFMgx2qqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IFMgx2qqxDipoleKernel(); /** * The destructor. */ virtual ~IFMgx2qqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIFMgx2qqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IFMgx2qqxDipoleKernel & operator=(const IFMgx2qqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IFMgx2qqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IFMgx2qqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IFMgx2qqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IFMgx2qqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IFMgx2qqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IFMgx2qqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IFMgx2qqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IFMqx2gqxDipoleKernel.cc b/Shower/Dipole/Kernels/IFMqx2gqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IFMqx2gqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IFMqx2gqxDipoleKernel.cc @@ -1,107 +1,144 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IFMqx2gqxDipoleKernel class. // #include "IFMqx2gqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IFMqx2gqxDipoleKernel::IFMqx2gqxDipoleKernel() : DipoleSplittingKernel() {} IFMqx2gqxDipoleKernel::~IFMqx2gqxDipoleKernel() {} IBPtr IFMqx2gqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IFMqx2gqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IFMqx2gqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 6 && ind.emitterData()->mass() == ZERO && ind.spectatorData()->mass() != ZERO && ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool IFMqx2gqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return a.emitterData() == b.emitterData() && emitter(a) == sk.emitter(b) && spectator(a)->mass() == sk.spectator(b)->mass() && a.emitterPDF() == b.emitterPDF(); } tcPDPtr IFMqx2gqxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IFMqx2gqxDipoleKernel::emission(const DipoleIndex& ind) const { return ind.emitterData()->CC(); } tcPDPtr IFMqx2gqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IFMqx2gqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); Energy pt = split.lastPt(); double ratio = sqr(pt/split.scale()); double muk2 = sqr(split.spectatorMass()/split.scale()); -// Calculate x - double rho = 1. - 4.*ratio*(1.-muk2)*z*(1.-z)/sqr(1.-z+ratio); - double x = 0.5*((1.-z+ratio)/(ratio*(1.-muk2))) * (1. - sqrt(rho)); - + // Calculate x + double rho = 1. - 4.*ratio*(1.-muk2)*z*(1.-z)/sqr(1.-z+ratio); + double x = 0.5*((1.-z+ratio)/(ratio*(1.-muk2))) * (1. - sqrt(rho)); ret *= .5 * ( 1.-2.*x*(1.-x) ); return ret > 0. ? ret : 0.; } +vector< pair > +IFMqx2gqxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr IFMqx2gqxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppm = z; + double v_AP_mpm = (1.-z); + + double v_AP_mmp = -v_AP_ppm; + double v_AP_pmp = -v_AP_mpm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1, PDT::Spin1Half, PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = 0.; + (*kernelPhiDep)(2,1,1) = 0.; + (*kernelPhiDep)(0,0,1) = v_AP_mmp/phase; + (*kernelPhiDep)(2,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,1,1) = 0.; + (*kernelPhiDep)(2,0,0) = 0.; + + return kernelPhiDep; +} + + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFMqx2gqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IFMqx2gqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IFMqx2gqxDipoleKernel::initIFMqx2gqxDipoleKernel; // Definition of the static class description member. void IFMqx2gqxDipoleKernel::Init() { static ClassDocumentation documentation ("IFMqx2gqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IFMqx2gqxDipoleKernel.h b/Shower/Dipole/Kernels/IFMqx2gqxDipoleKernel.h --- a/Shower/Dipole/Kernels/IFMqx2gqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IFMqx2gqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IFMqx2gqxDipoleKernel_H #define HERWIG_IFMqx2gqxDipoleKernel_H // // This is the declaration of the IFqx2gqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll * * \brief IFMqx2gqxDipoleKernel implements the q -> gqbar * splitting off an initial-final dipole * */ class IFMqx2gqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IFMqx2gqxDipoleKernel(); /** * The destructor. */ virtual ~IFMqx2gqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIFMqx2gqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IFMqx2gqxDipoleKernel & operator=(const IFMqx2gqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IFMqx2gqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IFMqx2gqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IFMqx2gqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IFMqx2gqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IFMqx2gqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IFMqx2gqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IFMqx2gqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IFMqx2qgxDipoleKernel.cc b/Shower/Dipole/Kernels/IFMqx2qgxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IFMqx2qgxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IFMqx2qgxDipoleKernel.cc @@ -1,109 +1,147 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IFMqx2qgxDipoleKernel class. // #include "IFMqx2qgxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IFMqx2qgxDipoleKernel::IFMqx2qgxDipoleKernel() : DipoleSplittingKernel() {} IFMqx2qgxDipoleKernel::~IFMqx2qgxDipoleKernel() {} IBPtr IFMqx2qgxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IFMqx2qgxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IFMqx2qgxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 6 && ind.emitterData()->mass() == ZERO && ind.spectatorData()->mass() != ZERO && ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool IFMqx2qgxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return emitter(a) == sk.emitter(b) && emission(a) == sk.emission(b) && spectator(a)->mass() == sk.spectator(b)->mass() && a.emitterPDF() == b.emitterPDF(); } tcPDPtr IFMqx2qgxDipoleKernel::emitter(const DipoleIndex& ind) const { return ind.emitterData(); } tcPDPtr IFMqx2qgxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IFMqx2qgxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IFMqx2qgxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); Energy pt = split.lastPt(); double ratio = sqr(pt/split.scale()); double muk2 = sqr(split.spectatorMass()/split.scale()); // Calculate x and u double rho = 1. - 4.*ratio*(1.-muk2)*z*(1.-z)/sqr(1.-z+ratio); double x = 0.5*((1.-z+ratio)/(ratio*(1.-muk2))) * (1. - sqrt(rho)); double u = x*ratio / (1.-z); // 19/01/2017 - SW: Removed finite term as its effect on // the ratio of -ve/+ve kernels is small ret *= (!strictLargeN() ? 4./3. : 3./2.) * ( 2./(1.-x+u) - (1.+x) ); return ret > 0. ? ret : 0.; } +vector< pair > +IFMqx2qgxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr IFMqx2qgxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt( 1./(1.-z) ); + double v_AP_ppm = -z/sqrt(1.-z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half, PDT::Spin1Half, PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,1,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(1,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = 0.; + (*kernelPhiDep)(1,0,2) = 0.; + (*kernelPhiDep)(0,1,2) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFMqx2qgxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IFMqx2qgxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IFMqx2qgxDipoleKernel::initIFMqx2qgxDipoleKernel; // Definition of the static class description member. void IFMqx2qgxDipoleKernel::Init() { static ClassDocumentation documentation ("IFMqx2qgxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IFMqx2qgxDipoleKernel.h b/Shower/Dipole/Kernels/IFMqx2qgxDipoleKernel.h --- a/Shower/Dipole/Kernels/IFMqx2qgxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IFMqx2qgxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IFMqx2qgxDipoleKernel_H #define HERWIG_IFMqx2qgxDipoleKernel_H // // This is the declaration of the IFqx2qgxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll * * \brief IFMqx2qgxDipoleKernel implements the q -> qg * splitting off an initial-final dipole * */ class IFMqx2qgxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IFMqx2qgxDipoleKernel(); /** * The destructor. */ virtual ~IFMqx2qgxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIFMqx2qgxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IFMqx2qgxDipoleKernel & operator=(const IFMqx2qgxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IFMqx2qgxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IFMqx2qgxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IFMqx2qgxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IFMqx2qgxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IFMqx2qgxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IFMqx2qgxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IFMqx2qgxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IFgx2ggxDipoleKernel.cc b/Shower/Dipole/Kernels/IFgx2ggxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IFgx2ggxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IFgx2ggxDipoleKernel.cc @@ -1,102 +1,158 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IFgx2ggxDipoleKernel class. // #include "IFgx2ggxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IFgx2ggxDipoleKernel::IFgx2ggxDipoleKernel() : DipoleSplittingKernel() {} IFgx2ggxDipoleKernel::~IFgx2ggxDipoleKernel() {} IBPtr IFgx2ggxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IFgx2ggxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IFgx2ggxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() == ZERO && ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool IFgx2ggxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() == ParticleID::g && sk.emission(b)->id() == ParticleID::g && a.emitterPDF() == b.emitterPDF(); } tcPDPtr IFgx2ggxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IFgx2ggxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IFgx2ggxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IFgx2ggxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double ratio = sqr(split.lastPt()/split.scale()); double rho = 1. - 4.*ratio*z*(1.-z)/sqr(1.-z+ratio); double x = 0.5*((1.-z+ratio)/ratio)*(1.-sqrt(rho)); double u = 0.5*((1.-z+ratio)/(1.-z))*(1.-sqrt(rho)); ret *= 3. * ( 1./(1.-x+u) + (1.-x)/x - 1. + x*(1.-x) ); return ret > 0. ? ret : 0.; } +vector< pair > +IFgx2ggxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_mpm = -(1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + //double v_AP_mmp = -v_AP_ppm; + double v_AP_pmp = -v_AP_mpm; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_mpm)) - 2.*abs(rho(0,2))*(v_AP_ppp*v_AP_pmp + v_AP_mmm*v_AP_mpm); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_mpm))/max ) ); + distPhiDep.push_back( make_pair(2, rho(0,2)*(v_AP_mmm*v_AP_mpm + v_AP_pmp*v_AP_ppp)/max ) ); + distPhiDep.push_back( make_pair(-2, rho(2,0)*(v_AP_ppp*v_AP_pmp + v_AP_mpm*v_AP_mmm)/max) ); + + return distPhiDep; +} + +DecayMEPtr IFgx2ggxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_mpm = -(1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_pmp = -v_AP_mpm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1, PDT::Spin1, PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(2,2,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(2,2,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,2) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,2) = 0; + (*kernelPhiDep)(2,0,0) = 0; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFgx2ggxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IFgx2ggxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IFgx2ggxDipoleKernel::initIFgx2ggxDipoleKernel; // Definition of the static class description member. void IFgx2ggxDipoleKernel::Init() { static ClassDocumentation documentation ("IFgx2ggxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IFgx2ggxDipoleKernel.h b/Shower/Dipole/Kernels/IFgx2ggxDipoleKernel.h --- a/Shower/Dipole/Kernels/IFgx2ggxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IFgx2ggxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IFgx2ggxDipoleKernel_H #define HERWIG_IFgx2ggxDipoleKernel_H // // This is the declaration of the IFgx2ggxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief IFgx2ggxDipoleKernel implements the g -> gg * splitting off an initial-final dipole * */ class IFgx2ggxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IFgx2ggxDipoleKernel(); /** * The destructor. */ virtual ~IFgx2ggxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIFgx2ggxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IFgx2ggxDipoleKernel & operator=(const IFgx2ggxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IFgx2ggxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IFgx2ggxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IFgx2ggxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IFgx2ggxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IFgx2ggxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IFgx2ggxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IFgx2ggxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IFgx2qqxDipoleKernel.cc b/Shower/Dipole/Kernels/IFgx2qqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IFgx2qqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IFgx2qqxDipoleKernel.cc @@ -1,107 +1,160 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IFgx2qqxDipoleKernel class. // #include "IFgx2qqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IFgx2qqxDipoleKernel::IFgx2qqxDipoleKernel() : DipoleSplittingKernel() {} IFgx2qqxDipoleKernel::~IFgx2qqxDipoleKernel() {} IBPtr IFgx2qqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IFgx2qqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IFgx2qqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() == ZERO && flavour()->mass() == ZERO && ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool IFgx2qqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return flavour() == sk.flavour() && abs(flavour()->id()) < 6 && flavour()->mass() == ZERO && a.emitterPDF() == b.emitterPDF(); } tcPDPtr IFgx2qqxDipoleKernel::emitter(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour(); } tcPDPtr IFgx2qqxDipoleKernel::emission(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour(); } tcPDPtr IFgx2qqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IFgx2qqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double ratio = sqr(split.lastPt()/split.scale()); double rho = 1. - 4.*ratio*z*(1.-z)/sqr(1.-z+ratio); double x = 0.5*((1.-z+ratio)/ratio)*(1.-sqrt(rho)); ret *= 0.5 * (!strictLargeN() ? 4./3. : 3./2.) * ( x + 2.*(1.-x)/x ); return ret > 0. ? ret : 0.; } +vector< pair > +IFgx2qqxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt(1./z); + double v_AP_mpm = (1.-z)/sqrt(z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_pmp = -v_AP_mpm; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = sqr(v_AP_ppp) + sqr(v_AP_mpm) + - 2.*abs(rho(0,2))*(v_AP_pmp*v_AP_ppp + v_AP_mmm*v_AP_mpm); + + distPhiDep.push_back(make_pair( 0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_mpm))/max ) ); + distPhiDep.push_back(make_pair( 2, rho(0,2)*(v_AP_pmp*v_AP_ppp + v_AP_mmm*v_AP_mpm )/max ) ); + distPhiDep.push_back(make_pair( -2, rho(2,0)*(v_AP_ppp*v_AP_pmp + v_AP_mpm*v_AP_mmm )/max ) ); + + return distPhiDep; +} + +DecayMEPtr IFgx2qqxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt(1./z); + double v_AP_mpm = (1.-z)/sqrt(z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_pmp = -v_AP_mpm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half, PDT::Spin1, PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -1 or -1/2, 1=+1/2, 2 = +1 + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,2,1) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,1) = 0.; + (*kernelPhiDep)(1,2,0) = 0.; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(1,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,1) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFgx2qqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IFgx2qqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IFgx2qqxDipoleKernel::initIFgx2qqxDipoleKernel; // Definition of the static class description member. void IFgx2qqxDipoleKernel::Init() { static ClassDocumentation documentation ("IFgx2qqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IFgx2qqxDipoleKernel.h b/Shower/Dipole/Kernels/IFgx2qqxDipoleKernel.h --- a/Shower/Dipole/Kernels/IFgx2qqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IFgx2qqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IFgx2qqxDipoleKernel_H #define HERWIG_IFgx2qqxDipoleKernel_H // // This is the declaration of the IFgx2qqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief IFgx2qqxDipoleKernel implements the g -> qq * splitting off an initial-final dipole * */ class IFgx2qqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IFgx2qqxDipoleKernel(); /** * The destructor. */ virtual ~IFgx2qqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIFgx2qqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IFgx2qqxDipoleKernel & operator=(const IFgx2qqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IFgx2qqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IFgx2qqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IFgx2qqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IFgx2qqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IFgx2qqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IFgx2qqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IFgx2qqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IFqx2gqxDipoleKernel.cc b/Shower/Dipole/Kernels/IFqx2gqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IFqx2gqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IFqx2gqxDipoleKernel.cc @@ -1,102 +1,139 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IFqx2gqxDipoleKernel class. // #include "IFqx2gqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IFqx2gqxDipoleKernel::IFqx2gqxDipoleKernel() : DipoleSplittingKernel() {} IFqx2gqxDipoleKernel::~IFqx2gqxDipoleKernel() {} IBPtr IFqx2gqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IFqx2gqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IFqx2gqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 6 && ind.emitterData()->mass() == ZERO && ind.spectatorData()->mass() == ZERO && ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool IFqx2gqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return a.emitterData() == b.emitterData() && emitter(a) == sk.emitter(b) && a.emitterPDF() == b.emitterPDF(); } tcPDPtr IFqx2gqxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IFqx2gqxDipoleKernel::emission(const DipoleIndex& ind) const { return ind.emitterData()->CC(); } tcPDPtr IFqx2gqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IFqx2gqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double ratio = sqr(split.lastPt()/split.scale()); double rho = 1. - 4.*ratio*z*(1.-z)/sqr(1.-z+ratio); double x = 0.5*((1.-z+ratio)/ratio)*(1.-sqrt(rho)); ret *= .5 * ( 1.-2.*x*(1.-x) ); return ret > 0. ? ret : 0.; } +vector< pair > +IFqx2gqxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr IFqx2gqxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppm = z; + double v_AP_mpm = (1.-z); + + double v_AP_mmp = -v_AP_ppm; + double v_AP_pmp = -v_AP_mpm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1, PDT::Spin1Half, PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = 0.; + (*kernelPhiDep)(2,1,1) = 0.; + (*kernelPhiDep)(0,0,1) = v_AP_mmp/phase; + (*kernelPhiDep)(2,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,1,1) = 0.; + (*kernelPhiDep)(2,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFqx2gqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IFqx2gqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IFqx2gqxDipoleKernel::initIFqx2gqxDipoleKernel; // Definition of the static class description member. void IFqx2gqxDipoleKernel::Init() { static ClassDocumentation documentation ("IFqx2gqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IFqx2gqxDipoleKernel.h b/Shower/Dipole/Kernels/IFqx2gqxDipoleKernel.h --- a/Shower/Dipole/Kernels/IFqx2gqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IFqx2gqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IFqx2gqxDipoleKernel_H #define HERWIG_IFqx2gqxDipoleKernel_H // // This is the declaration of the IFqx2gqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief IFqx2gqxDipoleKernel implements the q -> gqbar * splitting off an initial-final dipole * */ class IFqx2gqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IFqx2gqxDipoleKernel(); /** * The destructor. */ virtual ~IFqx2gqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIFqx2gqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IFqx2gqxDipoleKernel & operator=(const IFqx2gqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IFqx2gqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IFqx2gqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IFqx2gqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IFqx2gqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IFqx2gqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IFqx2gqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IFqx2gqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IFqx2qgxDipoleKernel.cc b/Shower/Dipole/Kernels/IFqx2qgxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IFqx2qgxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IFqx2qgxDipoleKernel.cc @@ -1,103 +1,140 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IFqx2qgxDipoleKernel class. // #include "IFqx2qgxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IFqx2qgxDipoleKernel::IFqx2qgxDipoleKernel() : DipoleSplittingKernel() {} IFqx2qgxDipoleKernel::~IFqx2qgxDipoleKernel() {} IBPtr IFqx2qgxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IFqx2qgxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IFqx2qgxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 6 && ind.emitterData()->mass() == ZERO && ind.spectatorData()->mass() == ZERO && ind.initialStateEmitter() && !ind.initialStateSpectator(); } bool IFqx2qgxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return emitter(a) == sk.emitter(b) && emission(a) == sk.emission(b) && a.emitterPDF() == b.emitterPDF(); } tcPDPtr IFqx2qgxDipoleKernel::emitter(const DipoleIndex& ind) const { return ind.emitterData(); } tcPDPtr IFqx2qgxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IFqx2qgxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IFqx2qgxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double ratio = sqr(split.lastPt()/split.scale()); double rho = 1. - 4.*ratio*z*(1.-z)/sqr(1.-z+ratio); double x = 0.5*((1.-z+ratio)/ratio)*(1.-sqrt(rho)); double u = 0.5*((1.-z+ratio)/(1.-z))*(1.-sqrt(rho)); ret *= (!strictLargeN() ? 4./3. : 3./2.) * ( 2./(1.-x+u) - (1.+x) + u*(1.+3.*x*(1.-u) ) ); return ret > 0. ? ret : 0.; } +vector< pair > +IFqx2qgxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr IFqx2qgxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt( 1./(1.-z) ); + double v_AP_ppm = -z/sqrt(1.-z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half, PDT::Spin1Half, PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,1,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(1,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = 0.; + (*kernelPhiDep)(1,0,2) = 0.; + (*kernelPhiDep)(0,1,2) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFqx2qgxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IFqx2qgxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IFqx2qgxDipoleKernel::initIFqx2qgxDipoleKernel; // Definition of the static class description member. void IFqx2qgxDipoleKernel::Init() { static ClassDocumentation documentation ("IFqx2qgxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IFqx2qgxDipoleKernel.h b/Shower/Dipole/Kernels/IFqx2qgxDipoleKernel.h --- a/Shower/Dipole/Kernels/IFqx2qgxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IFqx2qgxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IFqx2qgxDipoleKernel_H #define HERWIG_IFqx2qgxDipoleKernel_H // // This is the declaration of the IFqx2qgxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief IFqx2qgxDipoleKernel implements the q -> qg * splitting off an initial-final dipole * */ class IFqx2qgxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IFqx2qgxDipoleKernel(); /** * The destructor. */ virtual ~IFqx2qgxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIFqx2qgxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IFqx2qgxDipoleKernel & operator=(const IFqx2qgxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IFqx2qgxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IFqx2qgxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IFqx2qgxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IFqx2qgxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IFqx2qgxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IFqx2qgxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IFqx2qgxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IIgx2ggxDipoleKernel.cc b/Shower/Dipole/Kernels/IIgx2ggxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IIgx2ggxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IIgx2ggxDipoleKernel.cc @@ -1,101 +1,160 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IIgx2ggxDipoleKernel class. // #include "IIgx2ggxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IIgx2ggxDipoleKernel::IIgx2ggxDipoleKernel() : DipoleSplittingKernel() {} IIgx2ggxDipoleKernel::~IIgx2ggxDipoleKernel() {} IBPtr IIgx2ggxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IIgx2ggxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IIgx2ggxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() == ZERO && ind.initialStateEmitter() && ind.initialStateSpectator(); } bool IIgx2ggxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return sk.emitter(b)->id() == ParticleID::g && sk.emission(b)->id() == ParticleID::g && a.emitterPDF() == b.emitterPDF() && a.spectatorData() == b.spectatorData() && a.spectatorPDF() == b.spectatorPDF(); } tcPDPtr IIgx2ggxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IIgx2ggxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IIgx2ggxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IIgx2ggxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double ratio = sqr(split.lastPt()/split.scale()); + double x = z*(1.-z)/(1.-z+ratio); ret *= 3. * ( x/(1.-x) + (1.-x)/x +x*(1.-x) ); return ret > 0. ? ret : 0.; } +vector< pair > +IIgx2ggxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_mpm = -(1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + //double v_AP_mmp = -v_AP_ppm; + double v_AP_pmp = -v_AP_mpm; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_mpm)) + - 2.*abs(rho(0,2))*(v_AP_ppp*v_AP_pmp + v_AP_mmm*v_AP_mpm); + + distPhiDep.push_back( make_pair(0, (rho(0,0)+rho(2,2))* + (sqr(v_AP_ppp) + sqr(v_AP_ppm) + sqr(v_AP_mpm))/max ) ); + distPhiDep.push_back( make_pair(2, rho(0,2)*(v_AP_mmm*v_AP_mpm + v_AP_pmp*v_AP_ppp)/max ) ); + distPhiDep.push_back( make_pair(-2, rho(2,0)*(v_AP_ppp*v_AP_pmp + v_AP_mpm*v_AP_mmm)/max) ); + + return distPhiDep; +} + +DecayMEPtr IIgx2ggxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = -sqrt( 1./(z*(1.-z)) ); + double v_AP_ppm = z*sqrt( z / (1.-z) ); + double v_AP_mpm = -(1.-z)*sqrt( (1.-z)/z ); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + double v_AP_pmp = -v_AP_mpm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1, PDT::Spin1, PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(2,2,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(2,2,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,2) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,2) = 0; + (*kernelPhiDep)(2,0,0) = 0; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IIgx2ggxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IIgx2ggxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IIgx2ggxDipoleKernel::initIIgx2ggxDipoleKernel; // Definition of the static class description member. void IIgx2ggxDipoleKernel::Init() { static ClassDocumentation documentation ("IIgx2ggxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IIgx2ggxDipoleKernel.h b/Shower/Dipole/Kernels/IIgx2ggxDipoleKernel.h --- a/Shower/Dipole/Kernels/IIgx2ggxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IIgx2ggxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IIgx2ggxDipoleKernel_H #define HERWIG_IIgx2ggxDipoleKernel_H // // This is the declaration of the IIgx2ggxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief IIgx2ggxDipoleKernel implements the g -> gg * splitting off an initial-initial dipole * */ class IIgx2ggxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IIgx2ggxDipoleKernel(); /** * The destructor. */ virtual ~IIgx2ggxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; - + + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIIgx2ggxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IIgx2ggxDipoleKernel & operator=(const IIgx2ggxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IIgx2ggxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IIgx2ggxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IIgx2ggxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IIgx2ggxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IIgx2ggxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IIgx2ggxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IIgx2ggxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IIgx2qqxDipoleKernel.cc b/Shower/Dipole/Kernels/IIgx2qqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IIgx2qqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IIgx2qqxDipoleKernel.cc @@ -1,107 +1,161 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IIgx2qqxDipoleKernel class. // #include "IIgx2qqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IIgx2qqxDipoleKernel::IIgx2qqxDipoleKernel() : DipoleSplittingKernel() {} IIgx2qqxDipoleKernel::~IIgx2qqxDipoleKernel() {} IBPtr IIgx2qqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IIgx2qqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IIgx2qqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && ind.emitterData()->id() == ParticleID::g && ind.spectatorData()->mass() == ZERO && flavour()->mass() == ZERO && ind.initialStateEmitter() && ind.initialStateSpectator(); } bool IIgx2qqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return flavour() == sk.flavour() && abs(flavour()->id()) < 6 && flavour()->mass() == ZERO && a.emitterPDF() == b.emitterPDF() && a.spectatorData() == b.spectatorData() && a.spectatorPDF() == b.spectatorPDF(); } tcPDPtr IIgx2qqxDipoleKernel::emitter(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour(); } tcPDPtr IIgx2qqxDipoleKernel::emission(const DipoleIndex&) const { assert(flavour()); assert(abs(flavour()->id()) < 6 && flavour()->mass() == ZERO); return flavour(); } tcPDPtr IIgx2qqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IIgx2qqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double ratio = sqr(split.lastPt()/split.scale()); + double x = z*(1.-z)/(1.-z+ratio); ret *= 0.5 * (!strictLargeN() ? 4./3. : 3./2.) * ( 1./x +sqr(1.-x)/x ); return ret > 0. ? ret : 0.; } +vector< pair > +IIgx2qqxDipoleKernel::generatePhi(const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt(1./z); + double v_AP_mpm = (1.-z)/sqrt(z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_pmp = -v_AP_mpm; + + // Initialise variables for the distributions + vector< pair > distPhiDep; + double max = sqr(v_AP_ppp) + sqr(v_AP_mpm) + - 2.*abs(rho(0,2))*(v_AP_pmp*v_AP_ppp + v_AP_mmm*v_AP_mpm); + + distPhiDep.push_back(make_pair( 0, (rho(0,0)+rho(2,2))*(sqr(v_AP_ppp) + sqr(v_AP_mpm))/max ) ); + distPhiDep.push_back(make_pair( 2, rho(0,2)*(v_AP_pmp*v_AP_ppp + v_AP_mmm*v_AP_mpm )/max ) ); + distPhiDep.push_back(make_pair( -2, rho(2,0)*(v_AP_ppp*v_AP_pmp + v_AP_mpm*v_AP_mmm )/max ) ); + + return distPhiDep; +} + +DecayMEPtr IIgx2qqxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt(1./z); + double v_AP_mpm = (1.-z)/sqrt(z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_pmp = -v_AP_mpm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half, PDT::Spin1, PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -1 or -1/2, 1=+1/2, 2 = +1 + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,2,1) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,1) = 0.; + (*kernelPhiDep)(1,2,0) = 0.; + (*kernelPhiDep)(0,2,0) = v_AP_mpm/phase; + (*kernelPhiDep)(1,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,2,1) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IIgx2qqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IIgx2qqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IIgx2qqxDipoleKernel::initIIgx2qqxDipoleKernel; // Definition of the static class description member. void IIgx2qqxDipoleKernel::Init() { static ClassDocumentation documentation ("IIgx2qqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IIgx2qqxDipoleKernel.h b/Shower/Dipole/Kernels/IIgx2qqxDipoleKernel.h --- a/Shower/Dipole/Kernels/IIgx2qqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IIgx2qqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IIgx2qqxDipoleKernel_H #define HERWIG_IIgx2qqxDipoleKernel_H // // This is the declaration of the IIgx2qqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief IIgx2qqxDipoleKernel implements the g -> qq * splitting off an initial-initial dipole * */ class IIgx2qqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IIgx2qqxDipoleKernel(); /** * The destructor. */ virtual ~IIgx2qqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIIgx2qqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IIgx2qqxDipoleKernel & operator=(const IIgx2qqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IIgx2qqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IIgx2qqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IIgx2qqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IIgx2qqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IIgx2qqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IIgx2qqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IIgx2qqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IIqx2gqxDipoleKernel.cc b/Shower/Dipole/Kernels/IIqx2gqxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IIqx2gqxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IIqx2gqxDipoleKernel.cc @@ -1,102 +1,140 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IIqx2gqxDipoleKernel class. // #include "IIqx2gqxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IIqx2gqxDipoleKernel::IIqx2gqxDipoleKernel() : DipoleSplittingKernel() {} IIqx2gqxDipoleKernel::~IIqx2gqxDipoleKernel() {} IBPtr IIqx2gqxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IIqx2gqxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IIqx2gqxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 6 && ind.emitterData()->mass() == ZERO && ind.spectatorData()->mass() == ZERO && ind.initialStateEmitter() && ind.initialStateSpectator(); } bool IIqx2gqxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return a.emitterData() == b.emitterData() && emitter(a) == sk.emitter(b) && a.emitterPDF() == b.emitterPDF() && a.spectatorData() == b.spectatorData() && a.spectatorPDF() == b.spectatorPDF(); } - + tcPDPtr IIqx2gqxDipoleKernel::emitter(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IIqx2gqxDipoleKernel::emission(const DipoleIndex& ind) const { return ind.emitterData()->CC(); } tcPDPtr IIqx2gqxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IIqx2gqxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double ratio = sqr(split.lastPt()/split.scale()); + double x = z*(1.-z)/(1.-z+ratio); ret *= .5 * ( 1.-2.*x*(1.-x) ); return ret > 0. ? ret : 0.; } +vector< pair > +IIqx2gqxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr IIqx2gqxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppm = z; + double v_AP_mpm = (1.-z); + + double v_AP_mmp = -v_AP_ppm; + double v_AP_pmp = -v_AP_mpm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1, PDT::Spin1Half, PDT::Spin1Half))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = 0.; + (*kernelPhiDep)(2,1,1) = 0.; + (*kernelPhiDep)(0,0,1) = v_AP_mmp/phase; + (*kernelPhiDep)(2,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = v_AP_mpm/phase; + (*kernelPhiDep)(2,0,1) = v_AP_pmp*phase; + (*kernelPhiDep)(0,1,1) = 0.; + (*kernelPhiDep)(2,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IIqx2gqxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IIqx2gqxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IIqx2gqxDipoleKernel::initIIqx2gqxDipoleKernel; // Definition of the static class description member. void IIqx2gqxDipoleKernel::Init() { static ClassDocumentation documentation ("IIqx2gqxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IIqx2gqxDipoleKernel.h b/Shower/Dipole/Kernels/IIqx2gqxDipoleKernel.h --- a/Shower/Dipole/Kernels/IIqx2gqxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IIqx2gqxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IIqx2gqxDipoleKernel_H #define HERWIG_IIqx2gqxDipoleKernel_H // // This is the declaration of the IIqx2gqxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief IIqx2gqxDipoleKernel implements the q -> gqbar * splitting off an initial-initial dipole * */ class IIqx2gqxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IIqx2gqxDipoleKernel(); /** * The destructor. */ virtual ~IIqx2gqxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIIqx2gqxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IIqx2gqxDipoleKernel & operator=(const IIqx2gqxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IIqx2gqxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IIqx2gqxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IIqx2gqxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IIqx2gqxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IIqx2gqxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IIqx2gqxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IIqx2gqxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/IIqx2qgxDipoleKernel.cc b/Shower/Dipole/Kernels/IIqx2qgxDipoleKernel.cc --- a/Shower/Dipole/Kernels/IIqx2qgxDipoleKernel.cc +++ b/Shower/Dipole/Kernels/IIqx2qgxDipoleKernel.cc @@ -1,102 +1,139 @@ // -*- C++ -*- // // This is the implementation of the non-inlined, non-templated member // functions of the IIqx2qgxDipoleKernel class. // #include "IIqx2qgxDipoleKernel.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" using namespace Herwig; IIqx2qgxDipoleKernel::IIqx2qgxDipoleKernel() : DipoleSplittingKernel() {} IIqx2qgxDipoleKernel::~IIqx2qgxDipoleKernel() {} IBPtr IIqx2qgxDipoleKernel::clone() const { return new_ptr(*this); } IBPtr IIqx2qgxDipoleKernel::fullclone() const { return new_ptr(*this); } bool IIqx2qgxDipoleKernel::canHandle(const DipoleIndex& ind) const { return useThisKernel() && abs(ind.emitterData()->id()) < 6 && ind.emitterData()->mass() == ZERO && ind.spectatorData()->mass() == ZERO && ind.initialStateEmitter() && ind.initialStateSpectator(); } bool IIqx2qgxDipoleKernel::canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const { assert(canHandle(a)); if ( !canHandle(b) ) return false; return emitter(a) == sk.emitter(b) && emission(a) == sk.emission(b) && a.emitterPDF() == b.emitterPDF() && a.spectatorData() == b.spectatorData() && a.spectatorPDF() == b.spectatorPDF(); } tcPDPtr IIqx2qgxDipoleKernel::emitter(const DipoleIndex& ind) const { return ind.emitterData(); } tcPDPtr IIqx2qgxDipoleKernel::emission(const DipoleIndex&) const { return getParticleData(ParticleID::g); } tcPDPtr IIqx2qgxDipoleKernel::spectator(const DipoleIndex& ind) const { return ind.spectatorData(); } double IIqx2qgxDipoleKernel::evaluate(const DipoleSplittingInfo& split) const { double ret = alphaPDF(split); double z = split.lastZ(); double ratio = sqr(split.lastPt()/split.scale()); double x = z*(1.-z)/(1.-z+ratio); ret *= (!strictLargeN() ? 4./3. : 3./2.) * ( (1.+sqr(x))/(1.-x) ); return ret > 0. ? ret : 0.; } +vector< pair > +IIqx2qgxDipoleKernel::generatePhi(const DipoleSplittingInfo&, const RhoDMatrix&) const { + + // No dependence on the spin density matrix, + // dependence on off-diagonal terms cancels. + return {{ {0, 1.} }}; +} + +DecayMEPtr IIqx2qgxDipoleKernel::matrixElement(const DipoleSplittingInfo& dInfo) const { + + double z = dInfo.lastZ(); + + // Altarelli-Parisi spin-indexed kernels: + double v_AP_ppp = sqrt( 1./(1.-z) ); + double v_AP_ppm = -z/sqrt(1.-z); + + double v_AP_mmm = -v_AP_ppp; + double v_AP_mmp = -v_AP_ppm; + + // Construct the (phi-dependent) spin-unaveraged splitting kernel + DecayMEPtr kernelPhiDep + (new_ptr(TwoBodyDecayMatrixElement(PDT::Spin1Half, PDT::Spin1Half, PDT::Spin1))); + Complex phase = exp(Complex(0.,1.)*dInfo.lastPhi()); + + // 0 = -, 2 = + + (*kernelPhiDep)(0,0,0) = v_AP_mmm*phase; + (*kernelPhiDep)(1,1,2) = v_AP_ppp/phase; + (*kernelPhiDep)(0,0,2) = v_AP_mmp/phase; + (*kernelPhiDep)(1,1,0) = v_AP_ppm*phase; + (*kernelPhiDep)(0,1,0) = 0.; + (*kernelPhiDep)(1,0,2) = 0.; + (*kernelPhiDep)(0,1,2) = 0.; + (*kernelPhiDep)(1,0,0) = 0.; + + return kernelPhiDep; +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IIqx2qgxDipoleKernel::persistentOutput(PersistentOStream & ) const { } void IIqx2qgxDipoleKernel::persistentInput(PersistentIStream & , int) { } ClassDescription IIqx2qgxDipoleKernel::initIIqx2qgxDipoleKernel; // Definition of the static class description member. void IIqx2qgxDipoleKernel::Init() { static ClassDocumentation documentation ("IIqx2qgxDipoleKernel"); } diff --git a/Shower/Dipole/Kernels/IIqx2qgxDipoleKernel.h b/Shower/Dipole/Kernels/IIqx2qgxDipoleKernel.h --- a/Shower/Dipole/Kernels/IIqx2qgxDipoleKernel.h +++ b/Shower/Dipole/Kernels/IIqx2qgxDipoleKernel.h @@ -1,181 +1,192 @@ // -*- C++ -*- #ifndef HERWIG_IIqx2qgxDipoleKernel_H #define HERWIG_IIqx2qgxDipoleKernel_H // // This is the declaration of the IIqx2qgxDipoleKernel class. // #include "DipoleSplittingKernel.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief IIqx2qgxDipoleKernel implements the q -> qg * splitting off an initial-initial dipole * */ class IIqx2qgxDipoleKernel: public DipoleSplittingKernel { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IIqx2qgxDipoleKernel(); /** * The destructor. */ virtual ~IIqx2qgxDipoleKernel(); //@} public: /** * Return true, if this splitting kernel * applies to the given dipole index. */ virtual bool canHandle(const DipoleIndex&) const; /** * Return true, if this splitting kernel is * the same for the given index a, as the given * splitting kernel for index b. */ virtual bool canHandleEquivalent(const DipoleIndex& a, const DipoleSplittingKernel& sk, const DipoleIndex& b) const; /** * Return the emitter data after splitting, given * a dipole index. */ virtual tcPDPtr emitter(const DipoleIndex&) const; /** * Return the emission data after splitting, given * a dipole index. */ virtual tcPDPtr emission(const DipoleIndex&) const; /** * Return the spectator data after splitting, given * a dipole index. */ virtual tcPDPtr spectator(const DipoleIndex&) const; /** * Evaluate this splitting kernel for the given * dipole splitting. */ virtual double evaluate(const DipoleSplittingInfo&) const; + /** + * Evaluate rho_ii' V_ijk V*_i'jk / equivalent for initial-state splitting, + * required for generating spin-correlated azimuthal angles. + **/ + virtual vector< pair > generatePhi( const DipoleSplittingInfo& dInfo, const RhoDMatrix& rho) const; + + /** + * Return the completely spin-unaveraged (i.e. spin-indexed) splitting kernel. + **/ + virtual DecayMEPtr matrixElement(const DipoleSplittingInfo& dInfo) const; + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIIqx2qgxDipoleKernel; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IIqx2qgxDipoleKernel & operator=(const IIqx2qgxDipoleKernel &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IIqx2qgxDipoleKernel. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IIqx2qgxDipoleKernel. */ typedef Herwig::DipoleSplittingKernel NthBase; }; /** This template specialization informs ThePEG about the name of * the IIqx2qgxDipoleKernel class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IIqx2qgxDipoleKernel"; } /** * The name of a file containing the dynamic library where the class * IIqx2qgxDipoleKernel is implemented. It may also include several, space-separated, * libraries if the class IIqx2qgxDipoleKernel depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IIqx2qgxDipoleKernel_H */ diff --git a/Shower/Dipole/Kernels/Makefile.am b/Shower/Dipole/Kernels/Makefile.am --- a/Shower/Dipole/Kernels/Makefile.am +++ b/Shower/Dipole/Kernels/Makefile.am @@ -1,74 +1,76 @@ noinst_LTLIBRARIES = libHwDipoleShowerKernels.la nodist_libHwDipoleShowerKernels_la_SOURCES = \ DipoleKernels__all.cc BUILT_SOURCES = DipoleKernels__all.cc CLEANFILES = DipoleKernels__all.cc DipoleKernels__all.cc : $(DIR_H_FILES) $(DIR_CC_FILES) Makefile @echo "Concatenating .cc files into $@" @$(top_srcdir)/cat_with_cpplines $(DIR_CC_FILES) > $@ EXTRA_DIST = $(ALL_H_FILES) $(ALL_CC_FILES) DIR_H_FILES = $(addprefix $(srcdir)/,$(ALL_H_FILES)) ALL_H_FILES = \ DipoleSplittingKernel.h \ + ColourMatrixElementCorrection.h \ FFMqx2qgxDipoleKernel.h \ FFMgx2ggxDipoleKernel.h \ FFMgx2qqxDipoleKernel.h \ FFqx2qgxDipoleKernel.h \ FFgx2ggxDipoleKernel.h \ FFgx2qqxDipoleKernel.h \ FIqx2qgxDipoleKernel.h \ FIgx2ggxDipoleKernel.h \ FIgx2qqxDipoleKernel.h \ IFqx2qgxDipoleKernel.h \ IFqx2gqxDipoleKernel.h \ IFgx2ggxDipoleKernel.h \ IFgx2qqxDipoleKernel.h \ IIqx2qgxDipoleKernel.h \ IIqx2gqxDipoleKernel.h \ IIgx2ggxDipoleKernel.h \ IIgx2qqxDipoleKernel.h \ FIMqx2qgxDipoleKernel.h \ FIMgx2qqxDipoleKernel.h \ IFMqx2qgxDipoleKernel.h \ IFMqx2gqxDipoleKernel.h \ IFMgx2ggxDipoleKernel.h \ IFMgx2qqxDipoleKernel.h \ FIMDecaygx2qqxDipoleKernel.h \ FIMDecayqx2qgxDipoleKernel.h \ FIMDecaygx2ggxDipoleKernel.h DIR_CC_FILES = $(addprefix $(srcdir)/,$(ALL_CC_FILES)) ALL_CC_FILES = \ DipoleSplittingKernel.cc \ + ColourMatrixElementCorrection.cc \ FFMqx2qgxDipoleKernel.cc \ FFMgx2ggxDipoleKernel.cc \ FFMgx2qqxDipoleKernel.cc \ FFqx2qgxDipoleKernel.cc \ FFgx2ggxDipoleKernel.cc \ FFgx2qqxDipoleKernel.cc \ FIqx2qgxDipoleKernel.cc \ FIgx2ggxDipoleKernel.cc \ FIgx2qqxDipoleKernel.cc \ IFqx2qgxDipoleKernel.cc \ IFqx2gqxDipoleKernel.cc \ IFgx2ggxDipoleKernel.cc \ IFgx2qqxDipoleKernel.cc \ IIqx2qgxDipoleKernel.cc \ IIqx2gqxDipoleKernel.cc \ IIgx2ggxDipoleKernel.cc \ IIgx2qqxDipoleKernel.cc \ FIMqx2qgxDipoleKernel.cc \ FIMgx2qqxDipoleKernel.cc \ IFMqx2qgxDipoleKernel.cc \ IFMqx2gqxDipoleKernel.cc \ IFMgx2ggxDipoleKernel.cc \ IFMgx2qqxDipoleKernel.cc \ FIMDecaygx2qqxDipoleKernel.cc \ FIMDecayqx2qgxDipoleKernel.cc \ FIMDecaygx2ggxDipoleKernel.cc diff --git a/Shower/Dipole/Kinematics/DipoleSplittingKinematics.cc b/Shower/Dipole/Kinematics/DipoleSplittingKinematics.cc --- a/Shower/Dipole/Kinematics/DipoleSplittingKinematics.cc +++ b/Shower/Dipole/Kinematics/DipoleSplittingKinematics.cc @@ -1,327 +1,389 @@ // -*- C++ -*- // // DipoleSplittingKinematics.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. // // // This is the implementation of the non-inlined, non-templated member // functions of the DipoleSplittingKinematics class. // #include "DipoleSplittingKinematics.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Reference.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "Herwig/MatrixElement/Matchbox/Phasespace/RandomHelpers.h" #include using namespace Herwig; DipoleSplittingKinematics::DipoleSplittingKinematics() : HandlerBase(), theIRCutoff(1.0*GeV), theXMin(1.e-5), theJacobian(0.0), theLastPt(0.0*GeV), theLastZ(0.0), theLastPhi(0.0), theLastEmitterZ(1.0), theLastSpectatorZ(1.0), theLastSplittingParameters(),theOpenZBoundaries(1) {} DipoleSplittingKinematics::~DipoleSplittingKinematics() {} void DipoleSplittingKinematics::persistentOutput(PersistentOStream & os) const { os << ounit(theIRCutoff,GeV) << theXMin << theMCCheck<> iunit(theIRCutoff,GeV) >> theXMin >> theMCCheck>>theOpenZBoundaries; } void DipoleSplittingKinematics::prepareSplitting(DipoleSplittingInfo& dInfo) { dInfo.splittingKinematics(this); if ( lastPt() > IRCutoff() ) dInfo.lastPt(lastPt()); else { dInfo.lastPt(0.0*GeV); dInfo.didStopEvolving(); } dInfo.lastZ(lastZ()); dInfo.lastPhi(lastPhi()); dInfo.lastEmitterZ(lastEmitterZ()); dInfo.lastSpectatorZ(lastSpectatorZ()); dInfo.splittingParameters().resize(lastSplittingParameters().size()); copy(lastSplittingParameters().begin(),lastSplittingParameters().end(), dInfo.splittingParameters().begin()); } Energy DipoleSplittingKinematics::ptMax(Energy dScale, - double emX, double specX, - const DipoleSplittingInfo& dInfo, - const DipoleSplittingKernel& split) const { + double emX, double specX, + const DipoleSplittingInfo& dInfo, + const DipoleSplittingKernel& split) const { return ptMax(dScale, emX, specX, dInfo.index(), split); } Energy DipoleSplittingKinematics::ptMax(Energy dScale, - double emX, double specX, - const DipoleIndex& dIndex, - const DipoleSplittingKernel& split, - tPPtr, tPPtr) const { + double emX, double specX, + const DipoleIndex& dIndex, + const DipoleSplittingKernel& split, + tPPtr, tPPtr) const { return ptMax(dScale, emX, specX, dIndex, split); } Energy DipoleSplittingKinematics::QMax(Energy dScale, - double emX, double specX, - const DipoleSplittingInfo& dInfo, - const DipoleSplittingKernel& split) const { + double emX, double specX, + const DipoleSplittingInfo& dInfo, + const DipoleSplittingKernel& split) const { return QMax(dScale, emX, specX, dInfo.index(), split); } Energy DipoleSplittingKinematics::QMax(Energy dScale, - double emX, double specX, - const DipoleIndex& dIndex, - const DipoleSplittingKernel& split, - tPPtr, tPPtr) const { + double emX, double specX, + const DipoleIndex& dIndex, + const DipoleSplittingKernel& split, + tPPtr, tPPtr) const { return QMax(dScale, emX, specX, dIndex, split); } Energy DipoleSplittingKinematics::generatePt(double r, Energy dScale, - double emX, double specX, - const DipoleIndex& dIndex, - const DipoleSplittingKernel& split, - double& weight) const { + double emX, double specX, + const DipoleIndex& dIndex, + const DipoleSplittingKernel& split, + double& weight) const { Energy maxPt = ptMax(dScale,emX,specX,dIndex,split); if ( maxPt <= IRCutoff() ) { weight = 0.0; return ZERO; } weight *= log(sqr(maxPt/IRCutoff())); return IRCutoff()*pow(maxPt/IRCutoff(),r); } double DipoleSplittingKinematics::ptToRandom(Energy pt, Energy dScale, - double emX, double specX, - const DipoleIndex& dIndex, - const DipoleSplittingKernel& split) const { + double emX, double specX, + const DipoleIndex& dIndex, + const DipoleSplittingKernel& split) const { Energy maxPt = ptMax(dScale,emX,specX,dIndex,split); assert(pt >= IRCutoff() && pt <= maxPt); return log(pt/IRCutoff())/log(maxPt/IRCutoff()); } double DipoleSplittingKinematics::generateZ(double r, Energy pt, int sampling, - const DipoleSplittingInfo& dInfo, - const DipoleSplittingKernel& split, - double& weight) const { + const DipoleSplittingInfo& dInfo, + const DipoleSplittingKernel& split, + double& weight) const { pair zLims = zBoundaries(pt,dInfo,split); if(zLims.first==zLims.second){ weight = 0.0; - return 0.0; + return 0.0; } using namespace RandomHelpers; if ( sampling == FlatZ ) { pair kw = generate(flat(zLims.first,zLims.second),r); if ( kw.second != 0. ) { weight *= kw.second; return kw.first; } else { assert( kw.first < zLims.first || kw.first > zLims.second ); weight *= kw.second; return -1.; } } if ( sampling == OneOverZ ) { pair kw = generate(inverse(0.0,zLims.first,zLims.second),r); if ( kw.second != 0. ) { weight *= kw.second; return kw.first; } else { assert( kw.first < zLims.first || kw.first > zLims.second ); weight *= kw.second; return -1.; } } if ( sampling == OneOverOneMinusZ ) { pair kw = generate(inverse(1.0,zLims.first,zLims.second),r); if ( kw.second != 0. ) { weight *= kw.second; return kw.first; } else { assert( kw.first < zLims.first || kw.first > zLims.second ); weight *= kw.second; return -1.; } } if ( sampling == OneOverZOneMinusZ ) { pair kw = generate(inverse(0.0,zLims.first,zLims.second) + - inverse(1.0,zLims.first,zLims.second),r); + inverse(1.0,zLims.first,zLims.second),r); if ( kw.second != 0. ) { weight *= kw.second; return kw.first; } else { assert( kw.first < zLims.first || kw.first > zLims.second ); weight *= kw.second; return -1.; } } weight = 0.0; return 0.0; } Lorentz5Momentum DipoleSplittingKinematics::getKt(const Lorentz5Momentum& p1, - const Lorentz5Momentum& p2, - Energy pt, - double phi, - bool spacelike) const { + const Lorentz5Momentum& p2, + Energy pt, + double phi, + bool spacelike) const { Lorentz5Momentum P; + // CoM frame if ( !spacelike ) P = p1 + p2; + // Breit frame else P = p1 - p2; + + Energy mag = sqrt(abs(P.m2())); - Energy2 Q2 = abs(P.m2()); - + // Define Q. The 'boost' part of this transforms from the current + // frame into a frame (') in which P' = Q Lorentz5Momentum Q = !spacelike ? - Lorentz5Momentum(ZERO,ZERO,ZERO,sqrt(Q2),sqrt(Q2)) : - Lorentz5Momentum(ZERO,ZERO,sqrt(Q2),ZERO,-sqrt(Q2)); + Lorentz5Momentum(ZERO,ZERO,ZERO,mag,mag) : + Lorentz5Momentum(ZERO,ZERO,mag,ZERO,-mag); - if ( spacelike && Q.z() < P.z() ) + // This is required to make the boost parameter + // gamma positive (construct the 00 term of the + // transformtion below to see this) + //if ( spacelike && P.z() < -mag ) + //Q.setZ(-Q.z()); + // Below is safer than above as it avoids + // cases of very small positive (P*Q + Q2) + if ( spacelike && P.z() < ZERO ) Q.setZ(-Q.z()); + Energy2 Q2 = Q.m2(); + + // Establish if we need to boost bool boost = abs((P-Q).vect().mag2()/GeV2) > 1e-10 || abs((P-Q).t()/GeV) > 1e-5; - boost &= (P*Q-Q.mass2())/GeV2 > 1e-8; + + // Initialise copy of p1 to transform in the following + Lorentz5Momentum inFrame1(p1); + if ( boost ) + inFrame1 = inFrame1 - ((P*inFrame1+Q*inFrame1)/(Q2+P*Q))*(P+Q) + 2.*((P*inFrame1)/Q2)*Q; - Lorentz5Momentum inFrame1; - if ( boost ) - inFrame1 = p1 + ((P*p1-Q*p1)/(P*Q-Q.mass2()))*(P-Q); - else - inFrame1 = p1; - - Energy ptx = inFrame1.x(); - Energy pty = inFrame1.y(); - Energy q = 2.*inFrame1.z(); - - Energy Qp = sqrt(4.*(sqr(ptx)+sqr(pty))+sqr(q)); - Energy Qy = sqrt(4.*sqr(pty)+sqr(q)); - + // Compute components of kt double cPhi = cos(phi); double sPhi = sqrt(1.-sqr(cPhi)); if ( phi > Constants::pi ) sPhi = -sPhi; + + // Initialise kt + Lorentz5Momentum kt; + + // By 'timelike' case we mean we work in the centre-of-momentum frame + // The boost to the com frame is defined upto some rotation, + // here we do the rotation to/from the frame with boosted p1 along the +ve z-axis + if ( !spacelike ) { - Lorentz5Momentum kt; + Axis inFrame1Unit = inFrame1.vect().unit(); + if ( inFrame1Unit.perp2() > 1e-12 ) { + // 'n' indicates normalised momenta components + double pxn = inFrame1Unit.x(); + double pyn = inFrame1Unit.y(); + double pzn = inFrame1Unit.z(); + double den = 1./(1.+pzn); + + kt.setT(ZERO); + kt.setX( pt * ( (sqr(pyn)*den + pzn)*cPhi - pxn*pyn*den*sPhi ) ); + kt.setY( pt * ( -pxn*pyn*den*cPhi + (sqr(pxn)*den + pzn)*sPhi) ); + kt.setZ( -pt * ( pxn*cPhi + pyn*sPhi ) ); + } - if ( !spacelike ) { - kt.setT(ZERO); - kt.setX(pt*Qy*cPhi/Qp); - kt.setY(-pt*(4*ptx*pty*cPhi/Qp+q*sPhi)/Qy); - kt.setZ(2.*pt*(-ptx*q*cPhi/Qp + pty*sPhi)/Qy); - } else { - kt.setT(2.*pt*(ptx*q*cPhi+pty*Qp*sPhi)/(q*Qy)); - kt.setX(pt*(Qp*q*cPhi+4.*ptx*pty*sPhi)/(q*Qy)); - kt.setY(pt*Qy*sPhi/q); + // If boosted p1 already lies along the z-axis, construct the pt + // in this frame, rotating to put boosted p1 along the *+ve* z-axis + // if required + else { + + // Note pzn will simply be +1 or -1 in this case + double pzn = inFrame1Unit.z(); + + // Multiply y component by pzn: + // In the case of pzn = -1, this corresponds to a rotation + // about the x-axis as done in boostToSplitting + kt.setT(ZERO); + kt.setX( pt * cPhi ); + kt.setY( pt * pzn*sPhi ); + kt.setZ(ZERO); + } + } + + // By 'spacelike' we mean we work in the Breit frame. + // The transformation to the breit frame above is + // defined up to boosts in the x- and y-directions, + // here we do the boosts to put the momenta along the z-axis + else { + Energy ptx = inFrame1.x(); + Energy pty = inFrame1.y(); + + // q/2 = energy component of inFrame1 AFTER applying + // boosts to eliminate the x and y components. Therefore + // we calculate q from the mass and the z-component as + // these will not change due to these boosts. + Energy q = 2.*sqrt(sqr(inFrame1) + sqr(inFrame1.z())); + + Energy Qp = sqrt(4.*(sqr(ptx)+sqr(pty))+sqr(q)); + Energy Qy = sqrt(4.*sqr(pty)+sqr(q)); + + // Most straightforward way to construct kt in frame + // where p1 lies along the positive z-axis + double pzn = inFrame1.z()/abs(inFrame1.z()); + + kt.setT(2.*pt*(ptx*q*cPhi+pty*Qp*pzn*sPhi)/(q*Qy)); + kt.setX(pt*(Qp*q*cPhi+4.*ptx*pty*pzn*sPhi)/(q*Qy)); + kt.setY(pt*Qy*pzn*sPhi/q); kt.setZ(ZERO); } + // Transform back to the lab frame + // Note Q*kt = 0 if ( boost ) - kt = kt + ((P*kt-Q*kt)/(P*Q-Q.mass2()))*(P-Q); + kt = kt - ((P*kt+Q*kt)/(Q2+P*Q))*(P+Q);// + 2.*((Q*kt)/Q2)*P; + kt.setMass(-pt); kt.rescaleRho(); - return kt; + return kt; +} -} // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). AbstractClassDescription DipoleSplittingKinematics::initDipoleSplittingKinematics; // Definition of the static class description member. void DipoleSplittingKinematics::Init() { static ClassDocumentation documentation ("DipoleSplittingKinematics is the base class for dipole splittings " "as performed in the dipole shower."); static Parameter interfaceIRCutoff ("IRCutoff", "The IR cutoff to be used by this splitting kinematics.", &DipoleSplittingKinematics::theIRCutoff, GeV, 1.0*GeV, 0.0*GeV, 0*GeV, false, false, Interface::lowerlim); static Parameter interfaceXMin ("XMin", "The minimum momentum fraction for incoming partons", &DipoleSplittingKinematics::theXMin, 1.0e-5, 0.0, 1.0, false, false, Interface::limited); static Reference interfaceMCCheck ("MCCheck", "[debug option] MCCheck", &DipoleSplittingKinematics::theMCCheck, false, false, true, true, false); interfaceMCCheck.rank(-1); static Switch interfaceOpenZBoundaries - ("OpenZBoundaries", "", - &DipoleSplittingKinematics::theOpenZBoundaries, 0, false, false); + ("OpenZBoundaries", "", + &DipoleSplittingKinematics::theOpenZBoundaries, 0, false, false); static SwitchOption interfaceOpenZBoundarieshardScale - (interfaceOpenZBoundaries, "Hard", "", 0); + (interfaceOpenZBoundaries, "Hard", "", 0); static SwitchOption interfaceOpenZBoundariesfull - (interfaceOpenZBoundaries, "Full", "", 1); + (interfaceOpenZBoundaries, "Full", "", 1); static SwitchOption interfaceOpenZBoundariesDipoleScale - (interfaceOpenZBoundaries, "DipoleScale", "", 2); + (interfaceOpenZBoundaries, "DipoleScale", "", 2); } diff --git a/Shower/Dipole/Kinematics/DipoleSplittingKinematics.h b/Shower/Dipole/Kinematics/DipoleSplittingKinematics.h --- a/Shower/Dipole/Kinematics/DipoleSplittingKinematics.h +++ b/Shower/Dipole/Kinematics/DipoleSplittingKinematics.h @@ -1,643 +1,672 @@ // -*- C++ -*- // // DipoleSplittingKinematics.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_DipoleSplittingKinematics_H #define HERWIG_DipoleSplittingKinematics_H // // This is the declaration of the DipoleSplittingKinematics class. // #include "ThePEG/Handlers/HandlerBase.h" #include "ThePEG/Vectors/Lorentz5Vector.h" #include "ThePEG/EventRecord/Particle.h" #include "ThePEG/Utilities/UtilityBase.h" #include "Herwig/Shower/Dipole/Utility/DipoleMCCheck.h" namespace Herwig { using namespace ThePEG; class DipoleIndex; class DipoleSplittingInfo; class DipoleSplittingKernel; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief DipoleSplittingKinematics is the base class for dipole splittings * as performed in the dipole shower. * * @see \ref DipoleSplittingKinematicsInterfaces "The interfaces" * defined for DipoleSplittingKinematics. */ class DipoleSplittingKinematics: public HandlerBase { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ DipoleSplittingKinematics(); /** * The destructor. */ virtual ~DipoleSplittingKinematics(); //@} public: /** * Return the boundaries in between the evolution * variable random number is to be sampled; the lower * cuoff is assumed to correspond to the infrared cutoff. */ virtual pair kappaSupport(const DipoleSplittingInfo&) const { return {0.0,1.0}; } /** * Return the boundaries in between the momentum * fraction random number is to be sampled. */ virtual pair xiSupport(const DipoleSplittingInfo&) const { return {0.0,1.0}; } /** * Return the dipole scale associated to the * given pair of emitter and spectator. This * should be the invariant mass or absolute value * final/final or initial/initial and the absolute * value of the momentum transfer for intial/final or * final/initial dipoles. */ virtual Energy dipoleScale(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator) const { // MEMinBias produces non-zero zeros. if(abs(pEmitter*pSpectator)<0.0000001*GeV2)return ZERO; assert(pEmitter*pSpectator >= ZERO); return sqrt(2.*pEmitter*pSpectator); } /** * Return the mass of the system absorbing * the recoil in the dipole splitting. * This is overloaded in the decay dipoles. */ virtual Energy recoilMassKin(const Lorentz5Momentum&, const Lorentz5Momentum& pSpectator) const { return pSpectator.m(); } /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy dScale, double emX, double specX, const DipoleIndex& dIndex, const DipoleSplittingKernel& split) const =0; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy dScale, double emX, double specX, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy dScale, double emX, double specX, const DipoleIndex& dIndex, const DipoleSplittingKernel& split, tPPtr emitter, tPPtr spectator) const; /** * Return the maximum virtuality for the given dipole scale. */ virtual Energy QMax(Energy dScale, double emX, double specX, const DipoleIndex& dIndex, const DipoleSplittingKernel& split) const =0; /** * Return the maximum virtuality for the given dipole scale. */ virtual Energy QMax(Energy dScale, double emX, double specX, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const; /** * Return the maximum virtuality for the given dipole scale. */ virtual Energy QMax(Energy dScale, double emX, double specX, const DipoleIndex& dIndex, const DipoleSplittingKernel& split, tPPtr emitter, tPPtr spectator) const; /** * Return the pt given a virtuality. */ virtual Energy PtFromQ(Energy scale, const DipoleSplittingInfo&) const = 0; /** * Return the virtuality given a pt. */ virtual Energy QFromPt(Energy scale, const DipoleSplittingInfo&) const = 0; /** * Return the infrared cutoff. */ virtual Energy IRCutoff() const { return theIRCutoff; } /** * Return the minimum momentum fraction for * incoming partons */ double xMin() const { return theXMin; } /** * Generate a pt */ Energy generatePt(double r, Energy dScale, double emX, double specX, const DipoleIndex& dIndex, const DipoleSplittingKernel& split, double& weight) const; /** * Return the random number associated to * the given pt. */ virtual double ptToRandom(Energy pt, Energy dScale, double emX, double specX, const DipoleIndex& dIndex, const DipoleSplittingKernel& split) const; /** * Return the boundaries on the momentum fraction */ virtual pair zBoundaries(Energy pt, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const = 0; /** * Enumerate the variants of sampling z */ enum ZSamplingOptions { FlatZ = 0, OneOverZ, OneOverOneMinusZ, OneOverZOneMinusZ }; /** * Generate a z value flat */ double generateZ(double r, Energy pt, int sampling, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split, double& weight) const; /** * Generate splitting variables given three random numbers * and the momentum fractions of the emitter and spectator. * Return true on success. */ virtual bool generateSplitting(double kappa, double xi, double phi, DipoleSplittingInfo& info, const DipoleSplittingKernel&) = 0; /** * Get the splitting phasespace weight associated to * the last call to generateSplitting. This is taken to * be the single particle phasespace times 16 \pi^2 divided * by the relevant propagator invariant. */ double jacobian() const { return theJacobian; } /** * Return true, if this splitting kinematics * class is capable of delivering an overestimate * to the jacobian. */ virtual bool haveOverestimate() const { return false; } /** * Return the overestimated jacobian for the * last generated parameters. */ virtual double jacobianOverestimate() const { return -1.; } /** * Return the last generated pt */ Energy lastPt() const { return theLastPt; } /** * Return the last generated momentum fraction. */ double lastZ() const { return theLastZ; } /** * Return the last calculated zPrime for massive FF and decay dipoles. */ // Do not need in current implementation, // using lastSplittingParameters instead. //double lastZPrime() const { return theLastZPrime; } /** * Return the last generated azimuthal angle. */ double lastPhi() const { return theLastPhi; } /** * Return the momentum fraction, by which the emitter's * momentum fraction should be divided after the splitting. */ double lastEmitterZ() const { return theLastEmitterZ; } /** * Return the momentum fraction, by which the spectator's * momentum fraction should be divided after the splitting. */ double lastSpectatorZ() const { return theLastSpectatorZ; } /** * Return any additional parameters needed to * evaluate the splitting kernel or to generate the * full splitting. */ const vector& lastSplittingParameters() const { return theLastSplittingParameters; } /** * Complete a DipoleSplittingInfo object with * the parameters generated by the last call to * generateSplitting() */ void prepareSplitting(DipoleSplittingInfo& dInfo); public: /** * Generate the full kinematics given emitter and * spectator momentum and a previously completeted * DipoleSplittingInfo object. */ virtual void generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) = 0; /** * Return the emitter's momentum after the splitting. */ const Lorentz5Momentum& lastEmitterMomentum() const { return theEmitterMomentum; } /** * Return the spectator's momentum after the splitting. */ const Lorentz5Momentum& lastSpectatorMomentum() const { return theSpectatorMomentum; } /** * Return the emission's momentum. */ const Lorentz5Momentum& lastEmissionMomentum() const { return theEmissionMomentum; } /* * Return true, if there is a transformation which should * be applied to all other final state particles except the ones * involved in the splitting after having performed the splitting. */ virtual bool doesTransform () const { return false; } + /** + * Calculate and store a required Lorentz transformation + **/ + virtual void setTransformation () {}; + /* * Use the Dipole scale instead of hardpt for z-boundaries. */ int openZBoundaries() const { return theOpenZBoundaries; } /* * perform the transformation if required. */ - virtual Lorentz5Momentum transform (const Lorentz5Momentum& p) const { return p; } + virtual void transform (PPtr&) {}; /* + * SW 30/01/2019: Test feature only, not for release. + * Return true to only apply the transformation to non-coloured particles. + * Note this requires careful handling in DipoleEventRecord + */ + //virtual bool transformHardOnly() const { return false; } + + /** + * SW 30/01/2019: Test feature only, not for release. + * In II case use colourless particles only to absorb recoil + */ + //virtual void transformHard ( PPtr& ) {}; + + /** + * SW 30/01/2019: Used in DipoleEventRecord to prepare for + * transformHard, test feature only, not for release. + * Add to splitRecoilMomentum for transformation + */ + // void addToRecoilMom( const Lorentz5Momentum& mom ) { + // Lorentz5Momentum newRecoilMom = splitRecoilMomentum() + mom; + // splitRecoilMomentum(newRecoilMom); + // } + + /* * Return true if this splitting is of a dipole which contains * a decayed parton and requires the remnant to absorb the recoil. */ virtual bool isDecay() const { return false; } /** * Perform the recoil in the case of a decayed parton */ //virtual Lorentz5Momentum decayRecoil ( const Lorentz5Momentum& p, const int) { return p; } /** * Perform the recoil in the case of a decayed parton */ virtual void decayRecoil ( PList& ) {}; + /** + * Return the pVector, required for spin correlations. + */ + virtual Lorentz5Momentum pVector(const Lorentz5Momentum& pEmitter, + const Lorentz5Momentum&, + const DipoleSplittingInfo&) const { + return pEmitter; + } + + /** + * Return the nVector, required for spin correlations. + */ + virtual Lorentz5Momentum nVector(const Lorentz5Momentum&, + const Lorentz5Momentum& pSpectator, + const DipoleSplittingInfo&) const { + return pSpectator; + } + // {;} protected: /** * Calculate a transverse momentum for the given momenta, * invariant pt and azimuth. */ Lorentz5Momentum getKt(const Lorentz5Momentum& p1, const Lorentz5Momentum& p2, Energy pt, double phi, bool spacelike = false) const; /** * Set the splitting phasespace weight associated to * the last call to generateSplitting. This is taken to * be the single particle phasespace times 16 \pi^2 divided * by the relevant propagator invariant. */ void jacobian(double w) { theJacobian = w; } /** * Set the last generated pt */ void lastPt(Energy p) { theLastPt = p; } /** * Set the last generated momentum fraction. */ void lastZ(double z) { theLastZ = z; } /** * Set the last calculated zPrime for massive FF and decay dipoles. */ // Do not need in current implementation, // using lastSplittingParameters instead. //void lastZPrime(double zPrime) { theLastZPrime = zPrime; } /** * Set the last generated azimuthal angle. */ void lastPhi(double p) { theLastPhi = p; } /** * Set the momentum fraction, by which the emitter's * momentum fraction should be divided after the splitting. */ void lastEmitterZ(double z) { theLastEmitterZ = z; } /** * Set the momentum fraction, by which the spectator's * momentum fraction should be divided after the splitting. */ void lastSpectatorZ(double z) { theLastSpectatorZ = z; } /** * Access any additional parameters needed to * evaluate the splitting kernel or to generate the * full splitting. */ vector& splittingParameters() { return theLastSplittingParameters; } /** * Set the emitter's momentum after the splitting. */ void emitterMomentum(const Lorentz5Momentum& p) { theEmitterMomentum = p; } /** * Set the spectator's momentum after the splitting. */ void spectatorMomentum(const Lorentz5Momentum& p) { theSpectatorMomentum = p; } /** * Set the emission's momentum. */ void emissionMomentum(const Lorentz5Momentum& p) { theEmissionMomentum = p; } /** - * Set the momentum of the recoil system before the splitting. - * 25/05/2016 - Not currently used. - */ - //void recoilMomentum( const Lorentz5Momentum& mom ) { theRecoilMomentum = mom; } - - /** * Set the momentum of the recoil system after the splitting. */ void splitRecoilMomentum( const Lorentz5Momentum& mom ) { theSplitRecoilMomentum = mom; } - - /** - * Return the momentum of the recoil system before splitting. - * 25/05/2016 - Not currently used. - */ - //const Lorentz5Momentum& recoilMomentum() const { return theRecoilMomentum; } - + /** * Return the momentum of the recoil system after splitting. */ const Lorentz5Momentum& splitRecoilMomentum() const { return theSplitRecoilMomentum; } public: /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); public: /** @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); //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The infrared cutoff associated to this * splitting kinematics. */ Energy theIRCutoff; /** * The minimum momentum fraction for * incoming partons */ double theXMin; /** * The last calculated splitting phase space weight. */ double theJacobian; /** * The last generated pt */ Energy theLastPt; /** * The last generated momentum fraction. */ double theLastZ; /** * The last calculated zPrime required for massive FF * and decay kinematics dipoles. * zPrime := qi.nk / (qi+qj).nk (qj = emission momentum) */ // Do not need in current implementation, // using lastSplittingParameters instead. //double theLastZPrime; /** * The last generated azimuthal angle. */ double theLastPhi; /** * The momentum fraction, by which the emitter's * momentum fraction should be divided after the splitting. */ double theLastEmitterZ; /** * The momentum fraction, by which the spectator's * momentum fraction should be divided after the splitting. */ double theLastSpectatorZ; /** * Any additional parameters needed to * evaluate the splitting kernel or to generate the * full splitting. */ vector theLastSplittingParameters; /** * The emitter's momentum after the splitting. */ Lorentz5Momentum theEmitterMomentum; /** * The emission's momentum after the splitting. */ Lorentz5Momentum theEmissionMomentum; /** * The spectator's momentum after the splitting. */ Lorentz5Momentum theSpectatorMomentum; /** - * The momentum of the recoil system used in decay dipole kinematics. - * 25/05/2016 - Not currently used - */ - //Lorentz5Momentum theRecoilMomentum; - - /** - * The momentum of the recoil system after the splitting, used in decay dipole kinematics. + * The momentum of the recoil system after the splitting, + * used in decay dipole kinematics. */ Lorentz5Momentum theSplitRecoilMomentum; int theOpenZBoundaries; protected: /** * Pointer to a check histogram object */ Ptr::ptr theMCCheck; private: /** * The static object used to initialize the description of this class. * Indicates that this is an abstract class. */ static AbstractClassDescription initDipoleSplittingKinematics; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ DipoleSplittingKinematics & operator=(const DipoleSplittingKinematics &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of DipoleSplittingKinematics. */ template <> struct BaseClassTrait { /** Typedef of the first base class of DipoleSplittingKinematics. */ typedef HandlerBase NthBase; }; /** This template specialization informs ThePEG about the name of * the DipoleSplittingKinematics class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::DipoleSplittingKinematics"; } /** * The name of a file containing the dynamic library where the class * DipoleSplittingKinematics is implemented. It may also include several, space-separated, * libraries if the class DipoleSplittingKinematics depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_DipoleSplittingKinematics_H */ diff --git a/Shower/Dipole/Kinematics/FFLightKinematics.cc b/Shower/Dipole/Kinematics/FFLightKinematics.cc --- a/Shower/Dipole/Kinematics/FFLightKinematics.cc +++ b/Shower/Dipole/Kinematics/FFLightKinematics.cc @@ -1,176 +1,175 @@ // -*- C++ -*- // // FFLightKinematics.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 FFLightKinematics class. // #include "FFLightKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" using namespace Herwig; FFLightKinematics::FFLightKinematics() : DipoleSplittingKinematics() {} FFLightKinematics::~FFLightKinematics() {} IBPtr FFLightKinematics::clone() const { return new_ptr(*this); } IBPtr FFLightKinematics::fullclone() const { return new_ptr(*this); } Energy FFLightKinematics::ptMax(Energy dScale, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { return dScale/2.; } Energy FFLightKinematics::QMax(Energy dScale, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { return dScale; } Energy FFLightKinematics::PtFromQ(Energy scale, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return scale*sqrt(z*(1.-z)); } Energy FFLightKinematics::QFromPt(Energy scale, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return scale/sqrt(z*(1.-z)); } pair FFLightKinematics::zBoundaries(Energy pt, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel&) const { Energy hard=dInfo.hardPt(); if(openZBoundaries()>0)hard=dInfo.scale()/2.; const double s = sqrt(1.-sqr(pt/hard)); return {0.5*(1.-s),0.5*(1.+s)}; } bool FFLightKinematics::generateSplitting(double kappa, double xi, double rphi, DipoleSplittingInfo& info, const DipoleSplittingKernel& split) { double weight = 1.0; Energy pt = generatePt(kappa,info.scale(), info.emitterX(),info.spectatorX(), info.index(),split, weight); if ( pt < IRCutoff() || pt > info.hardPt() ) { jacobian(0.0); return false; } double z = 0.0; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emissionData()->id() != ParticleID::g ) { z = generateZ(xi,pt,FlatZ, info,split,weight); } else { z = generateZ(xi,pt,OneOverZOneMinusZ, info,split,weight); } } else { z = generateZ(xi,pt,OneOverOneMinusZ, info,split,weight); } double y = sqr(pt/info.scale())/(z*(1.-z)); if ( z < 0.0 || z > 1.0 || y < 0.0 || y > 1.0 ) { jacobian(0.0); return false; } double phi = 2.*Constants::pi*rphi; jacobian(weight*(1.-y)); lastPt(pt); lastZ(z); lastPhi(phi); if ( theMCCheck ) theMCCheck->book(1.,1.,info.scale(),info.hardPt(),pt,z,jacobian()); return true; - } void FFLightKinematics::generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) { double z = dInfo.lastZ(); Energy pt = dInfo.lastPt(); double y = sqr(pt / (pEmitter+pSpectator).m()) / (z*(1.-z)); - + Lorentz5Momentum kt = getKt(pEmitter, pSpectator, pt, dInfo.lastPhi()); - + Lorentz5Momentum em = z*pEmitter + y*(1.-z)*pSpectator + kt; Lorentz5Momentum emm = (1.-z)*pEmitter + z*y*pSpectator - kt; Lorentz5Momentum spe = (1.-y)*pSpectator; em.setMass(ZERO); em.rescaleEnergy(); emm.setMass(ZERO); emm.rescaleEnergy(); spe.setMass(ZERO); spe.rescaleEnergy(); emitterMomentum(em); emissionMomentum(emm); spectatorMomentum(spe); - + } // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FFLightKinematics::persistentOutput(PersistentOStream & ) const { } void FFLightKinematics::persistentInput(PersistentIStream & , int) { } ClassDescription FFLightKinematics::initFFLightKinematics; // Definition of the static class description member. void FFLightKinematics::Init() { static ClassDocumentation documentation ("FFLightKinematics implements massless splittings " "off a final-final dipole."); } diff --git a/Shower/Dipole/Kinematics/FFMassiveKinematics.cc b/Shower/Dipole/Kinematics/FFMassiveKinematics.cc --- a/Shower/Dipole/Kinematics/FFMassiveKinematics.cc +++ b/Shower/Dipole/Kinematics/FFMassiveKinematics.cc @@ -1,503 +1,436 @@ // -*- C++ -*- // // FFMassiveKinematics.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 FFMassiveKinematics class. // #include "FFMassiveKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" #include "Herwig/Shower/Dipole/Kernels/DipoleSplittingKernel.h" #include "ThePEG/Interface/Switch.h" // TODO: remove after verification // only for checking for NaN or inf #include using namespace Herwig; FFMassiveKinematics::FFMassiveKinematics() - : DipoleSplittingKinematics(), - theFullJacobian(true) {} + : DipoleSplittingKinematics() {} FFMassiveKinematics::~FFMassiveKinematics() {} IBPtr FFMassiveKinematics::clone() const { return new_ptr(*this); } IBPtr FFMassiveKinematics::fullclone() const { return new_ptr(*this); } pair FFMassiveKinematics::kappaSupport(const DipoleSplittingInfo&) const { return {0.0,1.0}; } pair FFMassiveKinematics::xiSupport(const DipoleSplittingInfo& split) const { double c = sqrt(1.-4.*sqr(IRCutoff()/generator()->maximumCMEnergy())); if ( split.index().emitterData()->id() == ParticleID::g ) { if ( split.emissionData()->id() != ParticleID::g ) return {0.5*(1.-c),0.5*(1.+c)}; double b = log((1.+c)/(1.-c)); return {-b,b}; } return {-log(0.5*(1.+c)),-log(0.5*(1.-c))}; } Energy FFMassiveKinematics::dipoleScale(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator) const { return (pEmitter+pSpectator).m(); } Energy FFMassiveKinematics::ptMax(Energy dScale, double, double, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const { DipoleIndex ind = dInfo.index(); double mui2 = 0.; // g->gg and g->qqbar if ( abs(split.emitter(ind)->id()) == abs(split.emission(ind)->id()) ) { mui2 = sqr(split.emitter(ind)->mass() / dScale); } // Otherwise have X->Xg (should work for SUSY) else { mui2 = sqr(dInfo.emitterMass()/dScale); } - double mu2 = sqr(split.emission(ind)->mass() / dScale); - double muj = dInfo.spectatorMass()/dScale; + double muj2 = sqr(split.emission(ind)->mass() / dScale); + double muk = dInfo.spectatorMass()/dScale; - return rootOfKallen( mui2, mu2, sqr(1.-muj) ) / ( 2.-2.*muj ) * dScale; + return rootOfKallen(mui2, muj2, sqr(1.-muk)) / ( 2.-2.*muk ) * dScale; } Energy FFMassiveKinematics::ptMax(Energy dScale, double, double, const DipoleIndex& ind, const DipoleSplittingKernel& split, tPPtr emitter, tPPtr spectator) const { double mui2 = 0.; // g->gg and g->qqbar if ( abs(split.emitter(ind)->id()) == abs(split.emission(ind)->id()) ) { mui2 = sqr(split.emitter(ind)->mass() / dScale); } // Otherwise have X->Xg (should work for SUSY) else { mui2 = sqr(emitter->mass()/dScale); } - double mu2 = sqr(split.emission(ind)->mass() / dScale); - double muj = spectator->mass()/dScale; + double muj2 = sqr(split.emission(ind)->mass() / dScale); + double muk = spectator->mass()/dScale; - return rootOfKallen( mui2, mu2, sqr(1.-muj) ) / ( 2.-2.*muj ) * dScale; + return rootOfKallen(mui2, muj2, sqr(1.-muk)) / ( 2.-2.*muk ) * dScale; } Energy FFMassiveKinematics::QMax(Energy dScale, double, double, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel&) const { assert(false && "implementation missing"); - double Muj = dInfo.spectatorMass() / dScale; - return dScale * ( 1.-2.*Muj+sqr(Muj) ); + double Muk = dInfo.spectatorMass() / dScale; + return dScale * ( 1.-2.*Muk+sqr(Muk) ); } // The name of this function is misleading // scale here is defined as sqr(scale) = sqr(qi+qj) // Here, scale is Q Energy FFMassiveKinematics::PtFromQ(Energy scale, const DipoleSplittingInfo& split) const { - double zPrime=split.lastSplittingParameters()[0]; + double z=split.lastSplittingParameters()[0]; // masses Energy2 mi2 = ZERO; // g->gg and g->qqbar if ( abs(split.emitterData()->id()) == abs(split.emissionData()->id()) ) { mi2 = sqr(split.emitterData()->mass()); } // Otherwise have X->Xg (should work for SUSY) else { mi2 = sqr(split.emitterMass()); } Energy2 m2 = sqr(split.emissionData()->mass()); - Energy2 pt2 = zPrime*(1.-zPrime)*sqr(scale) - (1-zPrime)*mi2 - zPrime*m2; + Energy2 pt2 = z*(1.-z)*sqr(scale) - (1.-z)*mi2 - z*m2; assert(pt2 >= ZERO); return sqrt(pt2); } // This is simply the inverse of PtFromQ Energy FFMassiveKinematics::QFromPt(Energy pt, const DipoleSplittingInfo& split) const { - double zPrime=split.lastSplittingParameters()[0]; + double z=split.lastSplittingParameters()[0]; // masses Energy2 mi2 = ZERO; // g->gg and g->qqbar if ( abs(split.emitterData()->id()) == abs(split.emissionData()->id()) ) { mi2 = sqr( split.emitterData()->mass()); } // Otherwise have X->Xg (should work for SUSY) else { mi2 = sqr(split.emitterMass()); } Energy2 m2 = sqr(split.emissionData()->mass()); - Energy2 Q2 = (sqr(pt) + (1-zPrime)*mi2 + zPrime*m2)/(zPrime*(1.-zPrime)); + Energy2 Q2 = (sqr(pt) + (1.-z)*mi2 + z*m2)/(z*(1.-z)); return sqrt(Q2); } double FFMassiveKinematics::ptToRandom(Energy pt, Energy, double,double, const DipoleIndex&, const DipoleSplittingKernel&) const { return log(pt/IRCutoff()) / log(0.5 * generator()->maximumCMEnergy()/IRCutoff()); } -// TODO - SW: Implement if statement to check -// for spectator mass when tidying up the notation. +// SW, 14/02/2019: Tidied to match thesis bool FFMassiveKinematics::generateSplitting(double kappa, double xi, double rphi, DipoleSplittingInfo& info, const DipoleSplittingKernel&) { - // scaled masses - double Mui2 = sqr(info.emitterMass() / info.scale()); - double Muj2 = sqr(info.spectatorMass() / info.scale()); - double muj2 = Muj2; + // Scale 's' and masses + Energy2 Qijk = sqr(info.scale()); + Energy2 mij2 = sqr(info.emitterMass()); + Energy2 Mk2 = sqr(info.spectatorMass()); - double mui2 = 0.; - // g->gg and g->qqbar - if ( abs(info.emitterData()->id()) == abs(info.emissionData()->id()) ) { - mui2 = sqr(info.emitterData()->mass()/info.scale()); - } - // Otherwise have X->Xg (should work for SUSY) - else { - mui2 = Mui2; - } - double mu2 = sqr(info.emissionData()->mass()/info.scale() ); - // To solve issue with scale during presampling - // need to enforce that Qijk-mij2-mk2 = 2*pij.pk > 0, - // so combine checks by comparing against square root. - if ( 1.-Mui2-Muj2 < sqrt(4.*Mui2*Muj2) ) { + // need to enforce that Qijk-mij2-mk2 = 2*pij.pk > 0. + // Combine checks by comparing against square root + if ( Qijk-mij2-Mk2 < sqrt(4.*mij2*Mk2) ) { jacobian(0.0); return false; } - Energy2 Qijk = sqr(info.scale()); - double suijk = 0.5*( 1. - Mui2 - Muj2 + sqrt( sqr(1.-Mui2-Muj2) - 4.*Mui2*Muj2 ) ); - + Energy2 mk2 = Mk2; + Energy2 mi2 = ZERO; + // g->gg and g->qqbar + if ( abs(info.emitterData()->id()) == abs(info.emissionData()->id()) ) { + mi2 = sqr(info.emitterData()->mass()); + } + // Otherwise have X->Xg (should work for SUSY) + else { + mi2 = mij2; + } + Energy2 mj2 = sqr(info.emissionData()->mass()); + // Calculate pt Energy pt = IRCutoff() * pow(0.5 * generator()->maximumCMEnergy()/IRCutoff(),kappa); Energy2 pt2 = sqr(pt); - + if ( pt > info.hardPt() || pt < IRCutoff() ) { jacobian(0.0); return false; } - // Generate zPrime (i.e. the new definition of z specific to massive FF) - double zPrime; + // Generate z + double z; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emissionData()->id() != ParticleID::g ) { - zPrime = xi; + z = xi; } else { - zPrime = exp(xi)/(1.+exp(xi)); + z = exp(xi)/(1.+exp(xi)); } } else { - zPrime = 1.-exp(-xi); + z = 1.-exp(-xi); } // new: 2011-08-31 // 2011-11-08: this does happen - if( sqrt(mui2)+sqrt(mu2)+sqrt(muj2) > 1. ){ + if( (sqrt(mi2)+sqrt(mj2)+sqrt(mk2))/ sqrt(Qijk) > 1. ){ jacobian(0.0); return false; } - // These apply to zPrime - // phasespace constraint to incorporate ptMax + // Limits on z. + // Phasespace constraint to incorporate ptMax. Energy hard = info.hardPt(); + Energy2 sqrRootQijkMk = sqr(sqrt(Qijk)-sqrt(mk2)); if(openZBoundaries()>0){ // From ptMax(..) - hard = rootOfKallen( mui2, mu2, sqr(1.-sqrt(muj2)) ) / - ( 2.-2.*sqrt(muj2) ) * info.scale(); + hard = rootOfKallen(mi2, mj2, sqrRootQijkMk) / ( 2.*sqrt(sqrRootQijkMk) ); assert(pt<=hard); } double ptRatio = sqrt(1.-sqr(pt/hard)); - double zp1 = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) + - rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * ptRatio) / - ( 2.*sqr(1.-sqrt(muj2)) ); - double zm1 = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) - - rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * ptRatio) / - ( 2.*sqr(1.-sqrt(muj2)) ); - - if ( zPrime > zp1 || zPrime < zm1 ) { + double zp1 = ( mi2 - mj2 + sqrRootQijkMk + + rootOfKallen(mi2,mj2,sqrRootQijkMk) * ptRatio ) + / 2. / sqrRootQijkMk ; + double zm1 = ( mi2 - mj2 + sqrRootQijkMk - + rootOfKallen(mi2,mj2,sqrRootQijkMk) * ptRatio ) + / 2. / sqrRootQijkMk ; + + if ( z > zp1 || z < zm1 ) { jacobian(0.0); return false; } - - // Calculate A:=xij*w - double A = (1./(suijk*zPrime*(1.-zPrime))) * ( pt2/Qijk + zPrime*mu2 + (1.-zPrime)*mui2 - zPrime*(1.-zPrime)*Mui2 ); - // Calculate y from A (can also write explicitly in terms of qt, zPrime and masses however we need A anyway) - double bar = 1.-mui2-mu2-muj2; - double y = (1./bar) * (A*suijk + Mui2 - mui2 - mu2 ); + // Calculate y + Energy2 sbar = Qijk - mi2 - mj2 - mk2; + double y = (pt2 + sqr(1.-z)*mi2 + sqr(z)*mj2) + / sbar / z / (1.-z); - // kinematic phasespace boundaries for y - // same as in Dittmaier hep-ph/9904440v2 (equivalent to CS) - double ym = 2.*sqrt(mui2)*sqrt(mu2)/bar; - double yp = 1. - 2.*sqrt(muj2)*(1.-sqrt(muj2))/bar; + // Kinematic phasespace boundaries for y. + // Same as in Dittmaier hep-ph/9904440v2 (equivalent to CS). + double ym = 2.*sqrt(mi2)*sqrt(mj2)/sbar; + double yp = 1. - 2.*sqrt(mk2)*sqrt(sqrRootQijkMk) / sbar; if ( y < ym || y > yp ) { jacobian(0.0); return false; } + // Virtuality of emitted pair and other invariant scale + Energy2 Qij2 = (pt2 + (1.-z)*mi2 + z*mj2) + / z / (1.-z); + Energy2 sijk = 0.5*( Qijk - mij2 - Mk2 + rootOfKallen(Qijk,mij2,mk2) ); + // Calculate xk and xij - double lambdaK = 1. + (Muj2/suijk); - double lambdaIJ = 1. + (Mui2/suijk); - double xk = (1./(2.*lambdaK)) * ( (lambdaK + (Muj2/suijk)*lambdaIJ - A) + sqrt( sqr(lambdaK + (Muj2/suijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*Muj2/suijk) ); - double xij = 1. - ( (Muj2/suijk) * (1.-xk) / xk ); + double lambdaIJ = 1. + (mij2/sijk); + double lambdaK = 1. + (mk2/sijk); + double fac1 = lambdaIJ*lambdaK + (mk2 - Qij2)/sijk; + double xk = + ( fac1 + sqrt( sqr(fac1) - 4.*lambdaIJ*lambdaK*mk2/sijk ) ) + / 2. / lambdaK ; + double xij = 1. - mk2*(1.-xk) / xk / sijk; - // Transform to standard z definition as used in the kernels (i.e. that used in CS and standard sudakov parametrisations) - double z = ( (zPrime*xij*xk*suijk/2.) + (Muj2/ ( 2.*xk*xij*suijk*zPrime))*(pt2/Qijk + mui2) ) / - ( (xij*xk*suijk/2.) + (Muj2/(2.*xk*xij))*(Mui2/suijk + A) ); + // Calculate zi + double zi = + ( z*xij*xk*sijk + mk2*(pt2+mi2) / (z*xij*xk*sijk) ) + / (1.-y) / sbar; - // These apply to z, not zPrime - double zm = ( (2.*mui2+bar*y)*(1.-y) - sqrt(y*y-ym*ym)*sqrt(sqr(2.*muj2+bar-bar*y)-4.*muj2) ) / - ( 2.*(1.-y)*(mui2+mu2+bar*y) ); - double zp = ( (2.*mui2+bar*y)*(1.-y) + sqrt(y*y-ym*ym)*sqrt(sqr(2.*muj2+bar-bar*y)-4.*muj2) ) / - ( 2.*(1.-y)*(mui2+mu2+bar*y) ); + // Limits on zi + double facA = (2.*mi2 + sbar*y) / 2. / (mi2 + mj2 + sbar*y); + // viji*vijk + double facB = + sqrt( (sqr(2.*mk2 + sbar*(1.-y)) - 4.*mk2*Qijk) * + (sqr(sbar)*sqr(y) - 4.*mi2*mj2)) + / sbar / (1.-y) / (sbar*y + 2.*mi2); + double zim = facA * (1. - facB); + double zip = facA * (1. + facB); - if ( z < zm || z > zp ) { + if ( zi < zim || zi > zip ) { jacobian(0.0); return false; } - double phi = 2.*Constants::pi*rphi; - double mapZJacobian; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emissionData()->id() != ParticleID::g ) { mapZJacobian = 1.; } else { mapZJacobian = z*(1.-z); } } else { mapZJacobian = 1.-z; } // Compute and store the jacobian double jac = 0.0; - double propCntrb = 1./ ( 1. + (mui2+mu2-Mui2)/(bar*y) ); - - // TODO - SW - Tidy up notation everywhere alongside writing for the manual - // The full jacobian including the z->zprime jacobian - if ( theFullJacobian && info.spectatorData()->mass() != ZERO ) { - - // Sort out variables - Energy2 sbar = Qijk*bar; - Energy2 sijk = Qijk*suijk; - Energy2 mi2 = Qijk*mui2; - Energy2 mj2 = Qijk*mu2; - Energy2 mk2 = Qijk*muj2; - - // Compute dy/dzPrime and pt2* dy/dpt2 - double dyBydzPrime = (1./sbar) * ( -pt2*(1.-2.*zPrime)/sqr(zPrime*(1.-zPrime)) - mi2/sqr(zPrime) + mj2/sqr(1.-zPrime) ); - InvEnergy2 dyBydpt2 = 1./(sbar*zPrime*(1.-zPrime)); - - // Compute dA/dzPrime and dA/dpt2 - double dABydzPrime = (sbar/sijk) * dyBydzPrime; - InvEnergy2 dABydpt2 = (sbar/sijk) * dyBydpt2; - - // Compute dxk/dzPrime, dxk/dpt2, dxij/dzPrime and dxij/dpt2 - double factor = (0.5/lambdaK) * (-1. - (1./sqrt( sqr(lambdaK + (mk2/sijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*mk2/sijk)) * (lambdaK + (mk2/sijk)*lambdaIJ - A)); - - double dxkBydzPrime = factor * dABydzPrime; - InvEnergy2 dxkBydpt2 = factor * dABydpt2; - - double dxijBydzPrime = (mk2/sijk) * (1./sqr(xk)) * dxkBydzPrime; - InvEnergy2 dxijBydpt2 = (mk2/sijk) * (1./sqr(xk)) * dxkBydpt2; - - Energy2 dqiDotqkBydzPrime = xij*xk*0.5*sijk + zPrime*dxijBydzPrime*xk*0.5*sijk + zPrime*xij*dxkBydzPrime*0.5*sijk - + 0.5*(mk2/sijk)*(pt2 + mi2) * (-1./(xk*xij*sqr(zPrime)) - dxkBydzPrime/(zPrime*xij*sqr(xk)) - dxijBydzPrime/(zPrime*xk*sqr(xij))); - - double dqiDotqkBydpt2 = dxijBydpt2*zPrime*xk*0.5*sijk + zPrime*xij*dxkBydpt2*0.5*sijk - + (0.5*mk2/sijk) * (1./(zPrime*xk*xij)) * (1. + (pt2+mi2)*(-dxkBydpt2/xk - dxijBydpt2/xij) ); - - - // Compute dzBydzPrime and dzBydpt2 - Energy2 qiDotqk = (zPrime*xij*xk*sijk*0.5) + (mk2/ ( 2.*xk*xij*sijk*zPrime))*(pt2 + mi2); - - double dzBydzPrime = (1./sbar) * ( 2.*qiDotqk*dyBydzPrime/sqr(1.-y) + (1./(1.-y)) * 2.*dqiDotqkBydzPrime ); - InvEnergy2 dzBydpt2 = (1./sbar) * ( 2.*qiDotqk*dyBydpt2/sqr(1.-y) + (1./(1.-y)) * 2.*dqiDotqkBydpt2 ); - - double pt2Jac = pt2*abs(dzBydpt2*dyBydzPrime - dzBydzPrime*dyBydpt2); - - // Include the other terms and calculate the jacobian - jac = propCntrb * bar / rootOfKallen(1.,Mui2,Muj2) * (1.-y)/y * pt2Jac; - } - - - // the exact result when the spectator is massless. - else { - double jacPt2 = 1. / ( 1. + sqr(1.-zPrime)*Qijk*mui2/pt2 + zPrime*zPrime*Qijk*mu2/pt2 ); - jac = propCntrb * bar / rootOfKallen(1.,Mui2,Muj2) * (1.-y) * jacPt2; - } + jac = sbar / rootOfKallen(Qijk,mij2,mk2) * (1.-y) / ( 1. + (mi2 + mj2 - mij2)/sbar/y ) + * (pt2 / (pt2 + sqr(1.-z)*mi2+sqr(z)*mj2)) + * abs(1. - 2.*mk2*Qij2 / (sbar*(1.-y)*xij*xk*sijk)); jacobian(jac * mapZJacobian * 2. * log(0.5 * generator()->maximumCMEnergy()/IRCutoff()) ); // Record the physical variables, as used by the CS kernel definitions + double phi = 2.*Constants::pi*rphi; lastPt(pt); lastZ(z); lastPhi(phi); - // Record zPrime for use in kinematics generation and kernel evaluation + // Record zi for use in kinematics generation and kernel evaluation splittingParameters().clear(); - splittingParameters().push_back(zPrime); - + splittingParameters().push_back(zi); + if ( theMCCheck ) { theMCCheck->book(1.,1.,info.scale(),info.hardPt(),pt,z,jacobian()); } return true; } // revised 2011-08-22 // revised 2011-11-06 // revised with new FF kinematics in 2016 - SW +// SW, 14/02/2019: Tidied to match thesis void FFMassiveKinematics::generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) { - // The only value stored in dInfo.lastSplittingParameters() should be zPrime - assert(dInfo.lastSplittingParameters().size() == 1 ); - double zPrime = dInfo.lastSplittingParameters()[0]; + double z = dInfo.lastZ(); Energy pt = dInfo.lastPt(); Energy2 pt2 = sqr(pt); - // scaled masses - double Mui2 = sqr(dInfo.emitterMass() / dInfo.scale()); - double Muk2 = sqr(dInfo.spectatorMass() / dInfo.scale()); - //double muk2 = Muk2; - - Energy mi = ZERO; + // Masses + Energy2 mij2 = sqr(dInfo.emitterMass()); + Energy2 Mk2 = sqr(dInfo.spectatorMass()); + Energy2 mk2 = Mk2; + + Energy2 mi2 = ZERO; // g->gg and g->qqbar if ( abs(dInfo.emitterData()->id()) == abs(dInfo.emissionData()->id()) ) { - mi = dInfo.emitterData()->mass(); + mi2 = sqr(dInfo.emitterData()->mass()); } // Otherwise have X->Xg (should work for SUSY) else { - mi = dInfo.emitterMass(); + mi2 = mij2; } - double mui2 = sqr(mi/dInfo.scale()); - double mu2 = sqr(dInfo.emissionData()->mass() / dInfo.scale() ); + Energy2 mj2 = sqr(dInfo.emissionData()->mass()); + // Scales Energy2 Qijk = sqr(dInfo.scale()); - double suijk = 0.5*( 1. - Mui2 - Muk2 + sqrt( sqr(1.-Mui2-Muk2) - 4.*Mui2*Muk2 ) ); - double suijk2 = sqr(suijk); + Energy2 Qij2 = (pt2 + (1.-z)*mi2 + z*mj2) / z / (1.-z); + Energy2 sijk = 0.5*( Qijk - mij2 - Mk2 + rootOfKallen(Qijk,mij2,Mk2) ); + Energy4 sijk2 = sqr(sijk); + + // Calculate xk and xij + double lambdaIJ = 1. + (mij2/sijk); + double lambdaK = 1. + (mk2/sijk); + double fac1 = lambdaIJ*lambdaK + (mk2 - Qij2)/sijk; + double xk = + ( fac1 + sqrt( sqr(fac1) - 4.*lambdaIJ*lambdaK*mk2/sijk ) ) + / 2. / lambdaK ; + double xij = 1. - mk2*(1.-xk) / xk / sijk; - // Calculate A:=xij*w - double A = (1./(suijk*zPrime*(1.-zPrime))) * ( pt2/Qijk + zPrime*mu2 + (1.-zPrime)*mui2 - zPrime*(1.-zPrime)*Mui2 ); - - // Calculate the scaling factors, xk and xij - double lambdaK = 1. + (Muk2/suijk); - double lambdaIJ = 1. + (Mui2/suijk); - double xk = (1./(2.*lambdaK)) * ( (lambdaK + (Muk2/suijk)*lambdaIJ - A) + sqrt( sqr(lambdaK + (Muk2/suijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*Muk2/suijk) ); - double xij = 1. - ( (Muk2/suijk) * (1.-xk) / xk ); - - // Construct reference momenta nk, nij, nt - Lorentz5Momentum nij = ( suijk2 / (suijk2-Mui2*Muk2) ) * (pEmitter - (Mui2/suijk)*pSpectator); - Lorentz5Momentum nk = ( suijk2 / (suijk2-Mui2*Muk2) ) * (pSpectator - (Muk2/suijk)*pEmitter); - - // Following notation in notes, qt = sqrt(wt)*nt - Lorentz5Momentum qt = getKt(nij,nk,pt,dInfo.lastPhi()); + // Construct reference momenta nk and nij + Lorentz5Momentum nij = ( sijk2 / (sijk2-mij2*Mk2) ) * (pEmitter - (mij2/sijk)*pSpectator); + Lorentz5Momentum nk = ( sijk2 / (sijk2-mij2*Mk2) ) * (pSpectator - (Mk2/sijk)*pEmitter); // Construct qij, qk, qi and qj - Lorentz5Momentum qij = xij*nij + (Mui2/(xij*suijk))*nk; - Lorentz5Momentum qk = xk*nk + (Muk2/(xk*suijk))*nij; + Lorentz5Momentum qij = xij*nij + (mij2/(xij*sijk))*nk; + Lorentz5Momentum qk = xk*nk + (Mk2/(xk*sijk))*nij; + + Lorentz5Momentum qt = getKt(pEmitter, pSpectator, pt, dInfo.lastPhi()); // For clarity, following notation in notes: - //Lorentz5Momentum qi = zPrime*qij + ((pt2 + mi2 - zPrime*zPrime*mij2)/(xij*sijk*zPrime))*nk + sqrt(wt)*nt; - //Lorentz5Momentum qj = (1.-zPrime)*qij + ((pt2 + mj2 - sqr(1.-zPrime)*mij2)/(xij*sijk*(1.-zPrime)))*nk - sqrt(wt)*nt; + Lorentz5Momentum qi = z*qij + ((pt2 + mi2 - z*z*mij2)/(xij*sijk*z))*nk + qt; + Lorentz5Momentum qj = (1.-z)*qij + ((pt2 + mj2 - sqr(1.-z)*mij2)/(xij*sijk*(1.-z)))*nk - qt; - // No need to actually calculate nt and wt: - Lorentz5Momentum qi = zPrime*qij + ((pt2/Qijk + mui2 - zPrime*zPrime*Mui2)/(xij*suijk*zPrime))*nk + qt; - Lorentz5Momentum qj = (1.-zPrime)*qij + ((pt2/Qijk + mu2 - sqr(1.-zPrime)*Mui2)/(xij*suijk*(1.-zPrime)))*nk - qt; - - qi.setMass(mi); + qi.setMass(sqrt(mi2)); qi.rescaleEnergy(); - qj.setMass(dInfo.emissionData()->mass()); + qj.setMass(sqrt(mj2)); qj.rescaleEnergy(); - qk.setMass(dInfo.spectatorMass()); + qk.setMass(sqrt(mk2)); qk.rescaleEnergy(); emitterMomentum(qi); emissionMomentum(qj); spectatorMomentum(qk); } // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). -void FFMassiveKinematics::persistentOutput(PersistentOStream & os) const { - os << theFullJacobian; +void FFMassiveKinematics::persistentOutput(PersistentOStream &) const { + //os << ; } -void FFMassiveKinematics::persistentInput(PersistentIStream & is, int) { - is >> theFullJacobian; +void FFMassiveKinematics::persistentInput(PersistentIStream &, int) { + //is >> ; } ClassDescription FFMassiveKinematics::initFFMassiveKinematics; // Definition of the static class description member. void FFMassiveKinematics::Init() { static ClassDocumentation documentation ("FFMassiveKinematics implements massive splittings " "off a final-final dipole."); - - static Switch interfaceFullJacobian - ("FullJacobian", - "Use the full jacobian expression for the FF kinematics.", - &FFMassiveKinematics::theFullJacobian, true, false, false); - static SwitchOption interfaceFullJacobianYes - (interfaceFullJacobian, - "Yes", - "Use the full jacobian.", - true); - static SwitchOption interfaceFullJacobianNo - (interfaceFullJacobian, - "No", - "Do not use the full jacobian.", - false); - interfaceFullJacobian.rank(-1); } diff --git a/Shower/Dipole/Kinematics/FFMassiveKinematics.h b/Shower/Dipole/Kinematics/FFMassiveKinematics.h --- a/Shower/Dipole/Kinematics/FFMassiveKinematics.h +++ b/Shower/Dipole/Kinematics/FFMassiveKinematics.h @@ -1,303 +1,305 @@ // -*- C++ -*- // // FFMassiveKinematics.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_FFMassiveKinematics_H #define HERWIG_FFMassiveKinematics_H // // This is the declaration of the FFMassiveKinematics class. // #include "DipoleSplittingKinematics.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Stephen Webster * * \brief FFMassiveKinematics implements massive splittings * off a final-final dipole. * */ class FFMassiveKinematics: public DipoleSplittingKinematics { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FFMassiveKinematics(); /** * The destructor. */ virtual ~FFMassiveKinematics(); //@} public: /** * Return the boundaries in between the evolution * variable random number is to be sampled; the lower * cuoff is assumed to correspond to the infrared cutoff. */ virtual pair kappaSupport(const DipoleSplittingInfo& dIndex) const; /** * Return the boundaries in between the momentum * fraction random number is to be sampled. */ virtual pair xiSupport(const DipoleSplittingInfo& dIndex) const; /** * Return the boundaries on the momentum fraction */ virtual pair zBoundaries(Energy, const DipoleSplittingInfo&, const DipoleSplittingKernel&) const { return {0.0,1.0}; } /** * Return the dipole scale associated to the * given pair of emitter and spectator. This * should be the invariant mass or absolute value * final/final or initial/initial and the absolute * value of the momentum transfer for intial/final or * final/initial dipoles. */ virtual Energy dipoleScale(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy dScale, double emX, double specX, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy dScale, double, double, const DipoleIndex& dIndex, const DipoleSplittingKernel& split, tPPtr emitter, tPPtr spectator) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { // Only the DipoleSplittingInfo version should be used for massive // dipoles, for now anyway. assert(false); return ZERO; } /** * Return the maximum virtuality for the given dipole scale. */ virtual Energy QMax(Energy dScale, double emX, double specX, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const; /** * Return the maximum virtuality for the given dipole scale. */ virtual Energy QMax(Energy, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { // Only the DipoleSplittingInfo version should be used for massive // dipoles, for now anyway. assert(false); return ZERO; } /** * Return the pt given a virtuality. */ virtual Energy PtFromQ(Energy scale, const DipoleSplittingInfo&) const; /** * Return the virtuality given a pt. */ virtual Energy QFromPt(Energy scale, const DipoleSplittingInfo&) const; /** * Return the random number associated to * the given pt. */ virtual double ptToRandom(Energy pt, Energy dScale, double emX, double specX, const DipoleIndex& dIndex, const DipoleSplittingKernel& split) const; /** * Generate splitting variables given three random numbers * and the momentum fractions of the emitter and spectator. * Return true on success. */ virtual bool generateSplitting(double kappa, double xi, double phi, DipoleSplittingInfo& dIndex, const DipoleSplittingKernel& split); /** * Generate the full kinematics given emitter and * spectator momentum and a previously completeted * DipoleSplittingInfo object. */ virtual void generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo); public: /** * Triangular / Kallen function */ template - inline double rootOfKallen (T a, T b, T c) const { - double sres=a*a + b*b + c*c - 2.*( a*b+a*c+b*c ); - return sres>0.?sqrt( sres ):0.; } + inline T rootOfKallen (T a, T b, T c) const { + if ( a*a + b*b + c*c - 2.*(a*b + a*c + b*c) > ZERO ) + return sqrt(a*a + b*b + c*c - 2.*(a*b + a*c + b*c) ) ; + else + return ZERO; } /** * Perform a rotation on both momenta such that the first one will * point along the (positive) z axis. Rotate back to the original * reference frame by applying rotateUz(returnedVector) to each momentum. */ ThreeVector rotateToZ (Lorentz5Momentum& pTarget, Lorentz5Momentum& p1){ ThreeVector oldAxis = pTarget.vect().unit(); double ct = oldAxis.z(); double st = sqrt( 1.-sqr(ct) ); // cos,sin(theta) double cp = oldAxis.x()/st; double sp = oldAxis.y()/st; // cos,sin(phi) pTarget.setZ( pTarget.vect().mag() ); pTarget.setX( 0.*GeV ); pTarget.setY( 0.*GeV ); Lorentz5Momentum p1old = p1; p1.setX( sp*p1old.x() - cp*p1old.y() ); p1.setY( ct*cp*p1old.x() + ct*sp*p1old.y() - st*p1old.z() ); p1.setZ( st*cp*p1old.x() + st*sp*p1old.y() + ct*p1old.z() ); return oldAxis; } public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFFMassiveKinematics; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FFMassiveKinematics & operator=(const FFMassiveKinematics &) = delete; /** * Option to use the full jacobian, including the z->zprime jacobian. **/ bool theFullJacobian; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FFMassiveKinematics. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FFMassiveKinematics. */ typedef Herwig::DipoleSplittingKinematics NthBase; }; /** This template specialization informs ThePEG about the name of * the FFMassiveKinematics class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FFMassiveKinematics"; } /** * The name of a file containing the dynamic library where the class * FFMassiveKinematics is implemented. It may also include several, space-separated, * libraries if the class FFMassiveKinematics depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FFMassiveKinematics_H */ diff --git a/Shower/Dipole/Kinematics/FILightKinematics.cc b/Shower/Dipole/Kinematics/FILightKinematics.cc --- a/Shower/Dipole/Kinematics/FILightKinematics.cc +++ b/Shower/Dipole/Kinematics/FILightKinematics.cc @@ -1,187 +1,187 @@ // -*- C++ -*- // // FILightKinematics.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 FILightKinematics class. // #include "FILightKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" using namespace Herwig; FILightKinematics::FILightKinematics() : DipoleSplittingKinematics() {} FILightKinematics::~FILightKinematics() {} IBPtr FILightKinematics::clone() const { return new_ptr(*this); } IBPtr FILightKinematics::fullclone() const { return new_ptr(*this); } Energy FILightKinematics::ptMax(Energy dScale, double, double specX, const DipoleIndex&, const DipoleSplittingKernel&) const { return dScale * sqrt((1.-specX)/specX) /2.; } Energy FILightKinematics::QMax(Energy dScale, double, double specX, const DipoleIndex&, const DipoleSplittingKernel&) const { return dScale * sqrt((1.-specX)/specX); } Energy FILightKinematics::PtFromQ(Energy scale, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return scale*sqrt(z*(1.-z)); } Energy FILightKinematics::QFromPt(Energy scale, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return scale/sqrt(z*(1.-z)); } pair FILightKinematics::zBoundaries(Energy pt, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel&) const { Energy hard=dInfo.hardPt(); if(openZBoundaries()==1) hard=dInfo.scale()*sqrt((1.-dInfo.spectatorX())/dInfo.spectatorX())/2.; if(openZBoundaries()==2) hard=dInfo.scale()*min(1.,sqrt((1.-dInfo.spectatorX())/dInfo.spectatorX())/2.); if(hard info.hardPt() ) { jacobian(0.0); return false; } double z = 0.0; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emissionData()->id() != ParticleID::g ) { z = generateZ(xi,pt,FlatZ, info,split,weight); } else { z = generateZ(xi,pt,OneOverZOneMinusZ, info,split,weight); } } else { z = generateZ(xi,pt,OneOverOneMinusZ, info,split,weight); } double x = 1./(1.+sqr(pt/info.scale())/(z*(1.-z))); if ( z < 0.0 || z > 1.0 || x < info.spectatorX() || x > 1.0 ) { jacobian(0.0); return false; } double phi = 2.*Constants::pi*rphi; jacobian(weight); lastPt(pt); lastZ(z); lastPhi(phi); lastSpectatorZ(x); if ( theMCCheck ) theMCCheck->book(1.,info.spectatorX(),info.scale(),info.hardPt(),pt,z,jacobian()); return true; } void FILightKinematics::generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) { Energy pt = dInfo.lastPt(); double z = dInfo.lastZ(); double x = 1./(1.+sqr(pt/dInfo.scale())/(z*(1.-z))); - + Lorentz5Momentum kt = - getKt (pSpectator, pEmitter, pt, dInfo.lastPhi(),true); + getKt(pEmitter, pSpectator, pt, dInfo.lastPhi(), true); Lorentz5Momentum em = z*pEmitter + (1.-z)*((1.-x)/x)*pSpectator + kt; Lorentz5Momentum emm = (1.-z)*pEmitter + z*((1.-x)/x)*pSpectator - kt; Lorentz5Momentum spe = (1./x)*pSpectator; em.setMass(ZERO); em.rescaleEnergy(); emm.setMass(ZERO); emm.rescaleEnergy(); spe.setMass(ZERO); spe.rescaleEnergy(); emitterMomentum(em); emissionMomentum(emm); spectatorMomentum(spe); } // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FILightKinematics::persistentOutput(PersistentOStream & ) const { } void FILightKinematics::persistentInput(PersistentIStream & , int) { } ClassDescription FILightKinematics::initFILightKinematics; // Definition of the static class description member. void FILightKinematics::Init() { static ClassDocumentation documentation ("FILightKinematics implements massless splittings " "off a final-initial dipole."); } diff --git a/Shower/Dipole/Kinematics/FIMassiveDecayKinematics.cc b/Shower/Dipole/Kinematics/FIMassiveDecayKinematics.cc --- a/Shower/Dipole/Kinematics/FIMassiveDecayKinematics.cc +++ b/Shower/Dipole/Kinematics/FIMassiveDecayKinematics.cc @@ -1,512 +1,452 @@ // -*- C++ -*- // // FIMassiveDecayKinematics.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 FIMassiveDecayKinematics class. // #include "FIMassiveDecayKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" #include "Herwig/Shower/Dipole/Kernels/DipoleSplittingKernel.h" #include "ThePEG/Interface/Switch.h" using namespace Herwig; FIMassiveDecayKinematics::FIMassiveDecayKinematics() - : DipoleSplittingKinematics(), theFullJacobian(true) {} + : DipoleSplittingKinematics() {} FIMassiveDecayKinematics::~FIMassiveDecayKinematics() {} IBPtr FIMassiveDecayKinematics::clone() const { return new_ptr(*this); } IBPtr FIMassiveDecayKinematics::fullclone() const { return new_ptr(*this); } pair FIMassiveDecayKinematics::kappaSupport(const DipoleSplittingInfo&) const { return {0.0,1.0}; } pair FIMassiveDecayKinematics::xiSupport(const DipoleSplittingInfo& split) const { double c = sqrt(1.-4.*sqr(IRCutoff()/generator()->maximumCMEnergy())); if ( split.index().emitterData()->id() == ParticleID::g ) { if ( split.emissionData()->id() != ParticleID::g ){ return {0.5*(1.-c),0.5*(1.+c)}; } double b = log((1.+c)/(1.-c)); return {-b,b}; } return {-log(0.5*(1.+c)),-log(0.5*(1.-c))}; } Energy FIMassiveDecayKinematics::dipoleScale(const Lorentz5Momentum&, - const Lorentz5Momentum& pSpectator) const { + const Lorentz5Momentum& pSpectator) const { return pSpectator.m(); } Energy FIMassiveDecayKinematics::recoilMassKin(const Lorentz5Momentum& pEmitter, - const Lorentz5Momentum& pSpectator) const { + const Lorentz5Momentum& pSpectator) const { Lorentz5Momentum pk = pSpectator - pEmitter; Energy pkmass = pk.m(); return pkmass; } Energy FIMassiveDecayKinematics::ptMax(Energy dScale, - double, double, - const DipoleSplittingInfo& dInfo, - const DipoleSplittingKernel& split) const { + double, double, + const DipoleSplittingInfo& dInfo, + const DipoleSplittingKernel& split) const { DipoleIndex ind = dInfo.index(); double mui2 = 0.; // g->gg and g->qqbar if ( abs(split.emitter(ind)->id()) == abs(split.emission(ind)->id()) ) { mui2 = sqr(split.emitter(ind)->mass() / dScale); } // Otherwise have X->Xg (should work for SUSY) else { mui2 = sqr(dInfo.emitterMass()/dScale); } - double mu2 = sqr(split.emission(ind)->mass() / dScale); + double muj2 = sqr( split.emission(ind)->mass() / dScale ); // Mass of recoil system - double muj = dInfo.recoilMass() / dScale; + double muk = dInfo.recoilMass() / dScale; - return rootOfKallen( mui2, mu2, sqr(1.-muj) ) / ( 2.-2.*muj ) * dScale; + return rootOfKallen( mui2, muj2, sqr(1.-muk) ) / ( 2.-2.*muk ) * dScale; } Energy FIMassiveDecayKinematics::ptMax(Energy dScale, - double, double, - const DipoleIndex& ind, - const DipoleSplittingKernel& split, - tPPtr emitter, tPPtr spectator) const { + double, double, + const DipoleIndex& ind, + const DipoleSplittingKernel& split, + tPPtr emitter, tPPtr spectator) const { double mui2 = 0.; // g->gg and g->qqbar if ( abs(split.emitter(ind)->id()) == abs(split.emission(ind)->id()) ) { mui2 = sqr(split.emitter(ind)->mass() / dScale); } // Otherwise have X->Xg (should work for SUSY) else { mui2 = sqr(emitter->mass()/dScale); } - double mu2 = sqr(split.emission(ind)->mass() / dScale); + double muj2 = sqr(split.emission(ind)->mass() / dScale); // Mass of recoil system - double muj = recoilMassKin(emitter->momentum(),spectator->momentum()) / dScale; + double muk = recoilMassKin(emitter->momentum(),spectator->momentum()) / dScale; - return rootOfKallen( mui2, mu2, sqr(1.-muj) ) / ( 2.-2.*muj ) * dScale; + return rootOfKallen( mui2, muj2, sqr(1.-muk) ) / ( 2.-2.*muk ) * dScale; } Energy FIMassiveDecayKinematics::QMax(Energy dScale, - double, double, - const DipoleSplittingInfo& dInfo, - const DipoleSplittingKernel&) const { + double, double, + const DipoleSplittingInfo& dInfo, + const DipoleSplittingKernel&) const { assert(false && "implementation missing"); // Mass of recoil system - double Muj2 = sqr( dInfo.recoilMass() / dScale ); - double Muj = sqrt( Muj2 ); + double Muk2 = sqr( dInfo.recoilMass() / dScale ); + double Muk = sqrt( Muk2 ); - return dScale * ( 1.-2.*Muj+Muj2 ); + return dScale * ( 1.-2.*Muk+Muk2 ); } // The name of this function is misleading // scale here is defined as sqr(scale) = sqr(qi+qj) // Here, scale is Q Energy FIMassiveDecayKinematics::PtFromQ(Energy scale, const DipoleSplittingInfo& split) const { double zPrime = split.lastSplittingParameters()[0]; // masses Energy2 mi2 = ZERO; // g->gg and g->qqbar if ( abs(split.emitterData()->id()) == abs(split.emissionData()->id()) ) { mi2 = sqr( split.emitterData()->mass()); } // Otherwise have X->Xg (should work for SUSY) else { mi2 = sqr(split.emitterMass()); } Energy2 m2 = sqr(split.emissionData()->mass()); Energy2 pt2 = zPrime*(1.-zPrime)*sqr(scale) - (1-zPrime)*mi2 - zPrime*m2; assert(pt2 >= ZERO); return sqrt(pt2); } // This is simply the inverse of PtFromQ Energy FIMassiveDecayKinematics::QFromPt(Energy pt, const DipoleSplittingInfo& split) const { double zPrime = split.lastSplittingParameters()[0]; // masses Energy2 mi2 = ZERO; // g->gg and g->qqbar if ( abs(split.emitterData()->id()) == abs(split.emissionData()->id()) ) { mi2 = sqr( split.emitterData()->mass()); } // Otherwise have X->Xg (should work for SUSY) else { mi2 = sqr(split.emitterMass()); } Energy2 m2 = sqr(split.emissionData()->mass()); Energy2 Q2 = (sqr(pt) + (1-zPrime)*mi2 + zPrime*m2)/(zPrime*(1.-zPrime)); return sqrt(Q2); } double FIMassiveDecayKinematics::ptToRandom(Energy pt, Energy, - double,double, - const DipoleIndex&, - const DipoleSplittingKernel&) const { + double,double, + const DipoleIndex&, + const DipoleSplittingKernel&) const { return log(pt/IRCutoff()) / log(0.5 * generator()->maximumCMEnergy()/IRCutoff()); } +// SW, 14/02/2019: Tidied to match thesis +bool FIMassiveDecayKinematics::generateSplitting(double kappa, double xi, double rphi, + DipoleSplittingInfo& info, + const DipoleSplittingKernel&) { + + // Scale 's' and masses + Energy2 Qijk = sqr(info.scale()); + Energy2 mij2 = sqr(info.emitterMass()); + Energy2 Mk2 = sqr(info.recoilMass()); -bool FIMassiveDecayKinematics::generateSplitting(double kappa, double xi, double rphi, - DipoleSplittingInfo& info, - const DipoleSplittingKernel&) { - - // scaled masses - double Mui2 = sqr(info.emitterMass() / info.scale()); - double Muj2 = sqr(info.recoilMass() / info.scale()); - double muj2 = Muj2; - - double mui2 = 0.; - // g->gg and g->qqbar - if ( abs(info.emitterData()->id()) == abs(info.emissionData()->id()) ) { - mui2 = sqr(info.emitterData()->mass()/info.scale()); - } - // Otherwise have X->Xg (should work for SUSY) - else { - mui2 = Mui2; - } - double mu2 = sqr(info.emissionData()->mass()/info.scale() ); - - // To solve issue with scale during presampling - // need to enforce that Qijk-mij2-mk2 = 2*pij.pk > 0, - // so combine checks by comparing against square root. - if ( 1.-Mui2-Muj2 < sqrt(4.*Mui2*Muj2) ) { + // need to enforce that Qijk-mij2-mk2 = 2*pij.pk > 0. + // Combine checks by comparing against square root + if ( Qijk-mij2-Mk2 < sqrt(4.*mij2*Mk2) ) { jacobian(0.0); return false; } - Energy2 Qijk = sqr(info.scale()); - double suijk = 0.5*( 1. - Mui2 - Muj2 + sqrt( sqr(1.-Mui2-Muj2) - 4.*Mui2*Muj2 ) ); + Energy2 mk2 = Mk2; + Energy2 mi2 = ZERO; + // g->gg and g->qqbar + if ( abs(info.emitterData()->id()) == abs(info.emissionData()->id()) ) { + mi2 = sqr(info.emitterData()->mass()); + } + // Otherwise have X->Xg (should work for SUSY) + else { + mi2 = mij2; + } + Energy2 mj2 = sqr(info.emissionData()->mass()); - // Calculate pt + // Calculate pt Energy pt = IRCutoff() * pow(0.5 * generator()->maximumCMEnergy()/IRCutoff(),kappa); Energy2 pt2 = sqr(pt); - + if ( pt > info.hardPt() || pt < IRCutoff() ) { jacobian(0.0); return false; } - // Generate zPrime (i.e. the new definition of z specific to massive FF and decays) - double zPrime; + // Generate z + double z; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emissionData()->id() != ParticleID::g ) { - zPrime = xi; + z = xi; } else { - zPrime = exp(xi)/(1.+exp(xi)); + z = exp(xi)/(1.+exp(xi)); } } else { - zPrime = 1.-exp(-xi); + z = 1.-exp(-xi); } // new: 2011-08-31 // 2011-11-08: this does happen - if( sqrt(mui2)+sqrt(mu2)+sqrt(muj2) > 1. ){ - jacobian(0.0); - return false; - } - - // Have derived and checked the equations for zp1 and zm1, these apply to zPrime - // phasespace constraint to incorporate ptMax - Energy hard = info.hardPt(); - - if(openZBoundaries()>0){ - // From ptMax(..) - hard = rootOfKallen( mui2, mu2, sqr(1.-sqrt(muj2)) ) / - ( 2.-2.*sqrt(muj2) ) * info.scale(); - assert(pt<=hard); - } - - double ptRatio = sqrt(1.-sqr(pt/hard)); - double zp1 = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) + - rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * ptRatio) / - ( 2.*sqr(1.-sqrt(muj2)) ); - double zm1 = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) - - rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * ptRatio) / - ( 2.*sqr(1.-sqrt(muj2)) ); - - if ( zPrime > zp1 || zPrime < zm1 ) { + if( (sqrt(mi2)+sqrt(mj2)+sqrt(mk2))/ sqrt(Qijk) > 1. ){ jacobian(0.0); return false; } - // Calculate A:=xij*w - double A = (1./(suijk*zPrime*(1.-zPrime))) * ( pt2/Qijk + zPrime*mu2 + (1.-zPrime)*mui2 - zPrime*(1.-zPrime)*Mui2 ); + // Limits on z. + // Phasespace constraint to incorporate ptMax. + Energy hard = info.hardPt(); + Energy2 sqrRootQijkMk = sqr(sqrt(Qijk)-sqrt(mk2)); - // Calculate y from A (can also write explicitly in terms of qt, zPrime and masses however we need A anyway) - double bar = 1.-mui2-mu2-muj2; - double y = (1./bar) * (A*suijk + Mui2 - mui2 - mu2 ); + if(openZBoundaries()>0){ + // From ptMax(..) + hard = rootOfKallen(mi2, mj2, sqrRootQijkMk) / ( 2.*sqrt(sqrRootQijkMk) ); + assert(pt<=hard); + } - // kinematic phasespace boundaries for y - // same as in Dittmaier hep-ph/9904440v2 (equivalent to CS) - double ym = 2.*sqrt(mui2)*sqrt(mu2)/bar; - double yp = 1. - 2.*sqrt(muj2)*(1.-sqrt(muj2))/bar; + double ptRatio = sqrt(1.-sqr(pt/hard)); + double zp1 = ( mi2 - mj2 + sqrRootQijkMk + + rootOfKallen(mi2,mj2,sqrRootQijkMk) * ptRatio ) + / 2. / sqrRootQijkMk ; + double zm1 = ( mi2 - mj2 + sqrRootQijkMk - + rootOfKallen(mi2,mj2,sqrRootQijkMk) * ptRatio ) + / 2. / sqrRootQijkMk ; + + if ( z > zp1 || z < zm1 ) { + jacobian(0.0); + return false; + } + + // Calculate y + Energy2 sbar = Qijk - mi2 - mj2 - mk2; + double y = (pt2 + sqr(1.-z)*mi2 + sqr(z)*mj2) + / sbar / z / (1.-z); + + // Kinematic phasespace boundaries for y. + // Same as in Dittmaier hep-ph/9904440v2 (equivalent to CS). + double ym = 2.*sqrt(mi2)*sqrt(mj2)/sbar; + double yp = 1. - 2.*sqrt(mk2)*sqrt(sqrRootQijkMk) / sbar; if ( y < ym || y > yp ) { jacobian(0.0); return false; } + // Virtuality of emitted pair and other invariant scale + Energy2 Qij2 = (pt2 + (1.-z)*mi2 + z*mj2) / z / (1.-z); + Energy2 sijk = 0.5*( Qijk - mij2 - Mk2 + rootOfKallen(Qijk,mij2,mk2) ); + // Calculate xk and xij - double lambdaK = 1. + (Muj2/suijk); - double lambdaIJ = 1. + (Mui2/suijk); - double xk = (1./(2.*lambdaK)) * ( (lambdaK + (Muj2/suijk)*lambdaIJ - A) + sqrt( sqr(lambdaK + (Muj2/suijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*Muj2/suijk) ); - double xij = 1. - ( (Muj2/suijk) * (1.-xk) / xk ); + double lambdaIJ = 1. + (mij2/sijk); + double lambdaK = 1. + (mk2/sijk); + double fac1 = lambdaIJ*lambdaK + (mk2 - Qij2)/sijk; + double xk = + ( fac1 + sqrt( sqr(fac1) - 4.*lambdaIJ*lambdaK*mk2/sijk ) ) + / 2. / lambdaK ; + double xij = 1. - mk2*(1.-xk) / xk / sijk; - // Transform to standard z definition as used in the kernels (i.e. that used in CS and standard sudakov parametrisations) - double z = ( (zPrime*xij*xk*suijk/2.) + (Muj2/ ( 2.*xk*xij*suijk*zPrime))*(pt2/Qijk + mui2) ) / - ( (xij*xk*suijk/2.) + (Muj2/(2.*xk*xij))*(Mui2/suijk + A) ); + // Calculate zi + double zi = + ( z*xij*xk*sijk + mk2*(pt2+mi2) / (z*xij*xk*sijk) ) + / (1.-y) / sbar; - // These apply to z, not zPrime - double zm = ( (2.*mui2+bar*y)*(1.-y) - sqrt(y*y-ym*ym)*sqrt(sqr(2.*muj2+bar-bar*y)-4.*muj2) ) / - ( 2.*(1.-y)*(mui2+mu2+bar*y) ); - double zp = ( (2.*mui2+bar*y)*(1.-y) + sqrt(y*y-ym*ym)*sqrt(sqr(2.*muj2+bar-bar*y)-4.*muj2) ) / - ( 2.*(1.-y)*(mui2+mu2+bar*y) ); + // Limits on zi + double facA = (2.*mi2 + sbar*y) / 2. / (mi2 + mj2 + sbar*y); + // viji*vijk + double facB = + sqrt( (sqr(2.*mk2 + sbar*(1.-y)) - 4.*mk2*Qijk) * + (sqr(sbar)*sqr(y) - 4.*mi2*mj2)) + / sbar / (1.-y) / (sbar*y + 2.*mi2); + double zim = facA * (1. - facB); + double zip = facA * (1. + facB); - if ( z < zm || z > zp ) { + if ( zi < zim || zi > zip ) { jacobian(0.0); return false; } - - double phi = 2.*Constants::pi*rphi; - + double mapZJacobian; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emissionData()->id() != ParticleID::g ) { mapZJacobian = 1.; } else { mapZJacobian = z*(1.-z); } } else { mapZJacobian = 1.-z; } - // Compute and store the jacobian double jac = 0.0; - double propCntrb = 1./ ( 1. + (mui2+mu2-Mui2)/(bar*y) ); - - // TODO - SW - Tidy up notation everywhere alongside writing for the manual - // The full jacobian including the z->zprime jacobian - if ( theFullJacobian ) { - - // Sort out variables - Energy2 sbar = Qijk*bar; - Energy2 sijk = Qijk*suijk; - Energy2 mi2 = Qijk*mui2; - Energy2 mj2 = Qijk*mu2; - Energy2 mk2 = Qijk*muj2; - - // Compute dy/dzPrime and pt2* dy/dpt2 - double dyBydzPrime = (1./sbar) * ( -pt2*(1.-2.*zPrime)/sqr(zPrime*(1.-zPrime)) - mi2/sqr(zPrime) + mj2/sqr(1.-zPrime) ); - InvEnergy2 dyBydpt2 = 1./(sbar*zPrime*(1.-zPrime)); - - // Compute dA/dzPrime and dA/dpt2 - double dABydzPrime = (sbar/sijk) * dyBydzPrime; - InvEnergy2 dABydpt2 = (sbar/sijk) * dyBydpt2; - - // Compute dxk/dzPrime, dxk/dpt2, dxij/dzPrime and dxij/dpt2 - double factor = (0.5/lambdaK) * (-1. - (1./sqrt( sqr(lambdaK + (mk2/sijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*mk2/sijk)) * (lambdaK + (mk2/sijk)*lambdaIJ - A)); - - double dxkBydzPrime = factor * dABydzPrime; - InvEnergy2 dxkBydpt2 = factor * dABydpt2; - - double dxijBydzPrime = (mk2/sijk) * (1./sqr(xk)) * dxkBydzPrime; - InvEnergy2 dxijBydpt2 = (mk2/sijk) * (1./sqr(xk)) * dxkBydpt2; - - Energy2 dqiDotqkBydzPrime = xij*xk*0.5*sijk + zPrime*dxijBydzPrime*xk*0.5*sijk + zPrime*xij*dxkBydzPrime*0.5*sijk - + 0.5*(mk2/sijk)*(pt2 + mi2) * (-1./(xk*xij*sqr(zPrime)) - dxkBydzPrime/(zPrime*xij*sqr(xk)) - dxijBydzPrime/(zPrime*xk*sqr(xij))); - - double dqiDotqkBydpt2 = dxijBydpt2*zPrime*xk*0.5*sijk + zPrime*xij*dxkBydpt2*0.5*sijk - + (0.5*mk2/sijk) * (1./(zPrime*xk*xij)) * (1. + (pt2+mi2)*(-dxkBydpt2/xk - dxijBydpt2/xij) ); - - - // Compute dzBydzPrime and dzBydpt2 - Energy2 qiDotqk = (zPrime*xij*xk*sijk*0.5) + (mk2/ ( 2.*xk*xij*sijk*zPrime))*(pt2 + mi2); - - double dzBydzPrime = (1./sbar) * ( 2.*qiDotqk*dyBydzPrime/sqr(1.-y) + (1./(1.-y)) * 2.*dqiDotqkBydzPrime ); - InvEnergy2 dzBydpt2 = (1./sbar) * ( 2.*qiDotqk*dyBydpt2/sqr(1.-y) + (1./(1.-y)) * 2.*dqiDotqkBydpt2 ); - - double pt2Jac = pt2*abs(dzBydpt2*dyBydzPrime - dzBydzPrime*dyBydpt2); - - // Include the other terms and calculate the jacobian - jac = propCntrb * bar / rootOfKallen(1.,Mui2,Muj2) * (1.-y)/y * pt2Jac; - } - - else { - double jacPt2 = 1. / ( 1. + sqr(1.-zPrime)*Qijk*mui2/pt2 + zPrime*zPrime*Qijk*mu2/pt2 ); - jac = propCntrb * bar / rootOfKallen(1.,Mui2,Muj2) * (1.-y) * jacPt2; - } + jac = sbar / rootOfKallen(Qijk,mij2,mk2) * (1.-y) / ( 1. + (mi2 + mj2 - mij2)/sbar/y ) + * (pt2 / (pt2 + sqr(1.-z)*mi2+sqr(z)*mj2)) + * abs(1. - 2.*mk2*Qij2 / (sbar*(1.-y)*xij*xk*sijk)); jacobian(jac * mapZJacobian * 2. * log(0.5 * generator()->maximumCMEnergy()/IRCutoff()) ); - - + // Record the physical variables, as used by the CS kernel definitions + double phi = 2.*Constants::pi*rphi; + lastPt(pt); lastZ(z); lastPhi(phi); - // Record zPrime for use in kinematics generation + // Record zi for use in kinematics generation and kernel evaluation splittingParameters().clear(); - splittingParameters().push_back(zPrime); - + splittingParameters().push_back(zi); + if ( theMCCheck ) { theMCCheck->book(1.,1.,info.scale(),info.hardPt(),pt,z,jacobian()); } return true; - + } +// SW, 14/02/2019: Tidied to match thesis void FIMassiveDecayKinematics::generateKinematics(const Lorentz5Momentum& pEmitter, - const Lorentz5Momentum& pSpectator, - const DipoleSplittingInfo& dInfo) { + const Lorentz5Momentum& pSpectator, + const DipoleSplittingInfo& dInfo) { - // The only value stored in dInfo.lastSplittingParameters() should be zPrime - assert(dInfo.lastSplittingParameters().size() == 1 ); - double zPrime = dInfo.lastSplittingParameters()[0]; + double z = dInfo.lastZ(); Energy pt = dInfo.lastPt(); Energy2 pt2 = sqr(pt); // Momentum of the recoil system Lorentz5Momentum pk = pSpectator - pEmitter; Lorentz5Momentum pij = pEmitter; - + // scaled masses - double Mui2 = sqr(dInfo.emitterMass() / dInfo.scale()); - double Muk2 = sqr(dInfo.recoilMass() / dInfo.scale()); - //double muk2 = Muk2; + Energy2 mij2 = sqr(dInfo.emitterMass()); + Energy2 Mk2 = sqr(dInfo.recoilMass()); + Energy2 mk2 = Mk2; - Energy mi = ZERO; + Energy2 mi2 = ZERO; // g->gg and g->qqbar if ( abs(dInfo.emitterData()->id()) == abs(dInfo.emissionData()->id()) ) { - mi = dInfo.emitterData()->mass(); + mi2 = sqr(dInfo.emitterData()->mass()); } // Otherwise have X->Xg (should work for SUSY) else { - mi = dInfo.emitterMass(); + mi2 = mij2; } - double mui2 = sqr(mi/dInfo.scale()); - double mu2 = sqr(dInfo.emissionData()->mass()/dInfo.scale() ); - + Energy2 mj2 = sqr(dInfo.emissionData()->mass()); + + // Scales Energy2 Qijk = sqr(dInfo.scale()); - double suijk = 0.5*( 1. - Mui2 - Muk2 + sqrt( sqr(1.-Mui2-Muk2) - 4.*Mui2*Muk2 ) ); - double suijk2 = sqr(suijk); + Energy2 Qij2 = (pt2 + (1.-z)*mi2 + z*mj2) / z / (1.-z); + Energy2 sijk = 0.5*( Qijk - mij2 - mk2 + rootOfKallen(Qijk,mij2,mk2) ); + Energy4 sijk2 = sqr(sijk); - // Calculate A:=xij*w - double A = (1./(suijk*zPrime*(1.-zPrime))) * ( pt2/Qijk + zPrime*mu2 + (1.-zPrime)*mui2 - zPrime*(1.-zPrime)*Mui2 ); - - // Calculate the scaling factors, xk and xij - double lambdaK = 1. + (Muk2/suijk); - double lambdaIJ = 1. + (Mui2/suijk); - double xk = (1./(2.*lambdaK)) * ( (lambdaK + (Muk2/suijk)*lambdaIJ - A) + sqrt( sqr(lambdaK + (Muk2/suijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*Muk2/suijk) ); - double xij = 1. - ( (Muk2/suijk) * (1.-xk) / xk ); - - // Construct reference momenta nk, nij, nt - Lorentz5Momentum nij = ( suijk2 / (suijk2-Mui2*Muk2) ) * (pij - (Mui2/suijk)*pk); - Lorentz5Momentum nk = ( suijk2 / (suijk2-Mui2*Muk2) ) * (pk - (Muk2/suijk)*pij); - - // Following notation in notes, qt = sqrt(wt)*nt - Lorentz5Momentum qt = getKt(nij,nk,pt,dInfo.lastPhi()); + // Calculate xk and xij + double lambdaIJ = 1. + (mij2/sijk); + double lambdaK = 1. + (mk2/sijk); + double fac1 = lambdaIJ*lambdaK + (mk2 - Qij2)/sijk; + double xk = + ( fac1 + sqrt( sqr(fac1) - 4.*lambdaIJ*lambdaK*mk2/sijk ) ) + / 2. / lambdaK ; + double xij = 1. - mk2*(1.-xk) / xk / sijk; + + // Construct reference momenta nk and nij + Lorentz5Momentum nij = ( sijk2 / (sijk2-mij2*Mk2) ) * (pij - (mij2/sijk)*pk); + Lorentz5Momentum nk = ( sijk2 / (sijk2-mij2*Mk2) ) * (pk - (Mk2/sijk)*pij); // Construct qij, qk, qi and qj - Lorentz5Momentum qij = xij*nij + (Mui2/(xij*suijk))*nk; - Lorentz5Momentum qk = xk*nk + (Muk2/(xk*suijk))*nij; + Lorentz5Momentum qij = xij*nij + (mij2/(xij*sijk))*nk; + Lorentz5Momentum qk = xk*nk + (Mk2/(xk*sijk))*nij; + + Lorentz5Momentum qt = getKt(pij, pk, pt, dInfo.lastPhi()); // No need to actually calculate nt and wt: - Lorentz5Momentum qi = zPrime*qij + ((pt2/Qijk + mui2 - zPrime*zPrime*Mui2)/(xij*suijk*zPrime))*nk + qt; - Lorentz5Momentum qj = (1.-zPrime)*qij + ((pt2/Qijk + mu2 - sqr(1.-zPrime)*Mui2)/(xij*suijk*(1.-zPrime)))*nk - qt; + Lorentz5Momentum qi = z*qij + ((pt2 + mi2 - z*z*mij2)/(xij*sijk*z))*nk + qt; + Lorentz5Momentum qj = (1.-z)*qij + ((pt2 + mj2 - sqr(1.-z)*mij2)/(xij*sijk*(1.-z)))*nk - qt; - - qi.setMass(mi); + qi.setMass(sqrt(mi2)); qi.rescaleEnergy(); - qj.setMass(dInfo.emissionData()->mass()); + qj.setMass(sqrt(mj2)); qj.rescaleEnergy(); - + emitterMomentum(qi); emissionMomentum(qj); spectatorMomentum(pSpectator); // Required for absorbing recoil in DipoleEventRecord::update splitRecoilMomentum(qk); } - +Lorentz5Momentum FIMassiveDecayKinematics::nVector(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo&) const { + return (pSpectator-pEmitter); +} + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). -void FIMassiveDecayKinematics::persistentOutput(PersistentOStream & os) const { - os << theFullJacobian; +void FIMassiveDecayKinematics::persistentOutput(PersistentOStream &) const { + //os << ; } -void FIMassiveDecayKinematics::persistentInput(PersistentIStream & is, int) { - is >> theFullJacobian; +void FIMassiveDecayKinematics::persistentInput(PersistentIStream &, int) { + //is >> ; } ClassDescription FIMassiveDecayKinematics::initFIMassiveDecayKinematics; // Definition of the static class description member. void FIMassiveDecayKinematics::Init() { static ClassDocumentation documentation ("FIMassiveDecayKinematics implements implements massive splittings " "off a final-initial decay dipole."); - - static Switch interfaceFullJacobian - ("FullJacobian", - "Use the full jacobian expression for the FI Decay kinematics.", - &FIMassiveDecayKinematics::theFullJacobian, true, false, false); - static SwitchOption interfaceFullJacobianYes - (interfaceFullJacobian, - "Yes", - "Use the full jacobian.", - true); - static SwitchOption interfaceFullJacobianNo - (interfaceFullJacobian, - "No", - "Do not use the full jacobian.", - false); - interfaceFullJacobian.rank(-1); } diff --git a/Shower/Dipole/Kinematics/FIMassiveDecayKinematics.h b/Shower/Dipole/Kinematics/FIMassiveDecayKinematics.h --- a/Shower/Dipole/Kinematics/FIMassiveDecayKinematics.h +++ b/Shower/Dipole/Kinematics/FIMassiveDecayKinematics.h @@ -1,309 +1,331 @@ // -*- C++ -*- // // FIMassiveDecayKinematics.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_FIMassiveDecayKinematics_H #define HERWIG_FIMassiveDecayKinematics_H // // This is the declaration of the FIMassiveDecayKinematics class. // #include "DipoleSplittingKinematics.h" #include "ThePEG/EventRecord/Particle.h" #include "ThePEG/Utilities/UtilityBase.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Stephen Webster * * \brief FIMassiveDecayKinematics implements massive splittings * off a final-initial decay dipole. * */ class FIMassiveDecayKinematics: public DipoleSplittingKinematics { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ FIMassiveDecayKinematics(); /** * The destructor. */ virtual ~FIMassiveDecayKinematics(); //@} public: /** * Return the boundaries in between the evolution * variable random number is to be sampled; the lower * cuoff is assumed to correspond to the infrared cutoff. */ virtual pair kappaSupport(const DipoleSplittingInfo& dIndex) const; /** * Return the boundaries in between the momentum * fraction random number is to be sampled. */ virtual pair xiSupport(const DipoleSplittingInfo& dIndex) const; /** * Return the boundaries on the momentum fraction */ virtual pair zBoundaries(Energy, const DipoleSplittingInfo&, const DipoleSplittingKernel&) const { return {0.0,1.0}; } /** * Return the dipole scale associated to the * given pair of emitter and spectator. This * should be the invariant mass or absolute value * final/final or initial/initial and the absolute * value of the momentum transfer for intial/final or * final/initial dipoles. */ virtual Energy dipoleScale(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator) const; /** * Return the mass of the system absorbing * the recoil in the dipole splitting. * This is only used in decay dipoles. */ virtual Energy recoilMassKin(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy dScale, double emX, double specX, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy dScale, double, double, const DipoleIndex& dIndex, const DipoleSplittingKernel& split, tPPtr emitter, tPPtr spectator) const; /** * Return the maximum virtuality for the given dipole scale. */ virtual Energy QMax(Energy dScale, double emX, double specX, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { // Only the DipoleSplittingInfo version should be used for the decays. assert(false); return ZERO; } /** * Return the maximum virtuality for the given dipole scale. */ virtual Energy QMax(Energy, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { // Only the DipoleSplittingInfo version should be used for the decays. assert(false); return ZERO; } /** * Return the pt given a virtuality. */ virtual Energy PtFromQ(Energy scale, const DipoleSplittingInfo&) const; /** * Return the virtuality given a pt. */ virtual Energy QFromPt(Energy scale, const DipoleSplittingInfo&) const; /** * Return the random number associated to * the given pt. */ virtual double ptToRandom(Energy pt, Energy dScale, double emX, double specX, const DipoleIndex& dIndex, const DipoleSplittingKernel& split) const; /** * Generate splitting variables given three random numbers * and the momentum fractions of the emitter and spectator. * Return true on success. */ virtual bool generateSplitting(double kappa, double xi, double phi, DipoleSplittingInfo& info, const DipoleSplittingKernel& split); /** * Generate the full kinematics given emitter and * spectator momentum and a previously completeted * DipoleSplittingInfo object. */ virtual void generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo); - + + /** + * Return the nVector as required for spin correlations. + */ + virtual Lorentz5Momentum nVector(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) const; + /* * Return true if this splitting is of a dipole which contains * a decayed parton and requires the remnant to absorb the recoil. */ virtual bool isDecay() const { return true; } /** * Perform the recoil in the case of a decayed parton */ virtual void decayRecoil ( PList& recoilSystem ) { PList::iterator beginRecoil = recoilSystem.begin(); PList::iterator endRecoil = recoilSystem.end(); - const ThreeVector transformMom = splitRecoilMomentum().vect(); - ThePEG::UtilityBase::setMomentum(beginRecoil, endRecoil, transformMom ); + + // This is the final momentum that we must transform the system to + const Momentum3 transformMom = splitRecoilMomentum().vect(); + + // Calculate required Lorentz rotation + Lorentz5Momentum sum = ThePEG::UtilityBase::sumMomentum(beginRecoil, endRecoil); + LorentzRotation rot = ThePEG::UtilityBase::transformToCMS(sum); + rot = ThePEG::UtilityBase::transformFromCMS + (Lorentz5Momentum(transformMom, sqrt(transformMom.mag2() + sum.m2()))) * rot; + + // Transform the particle spinInfo if required + for ( const auto& p : recoilSystem ) { + if ( p->spinInfo() ) + p->spinInfo()->transform(p->momentum(),rot); + } + + ThePEG::UtilityBase::transform(beginRecoil, endRecoil, rot ); } public: /** * Triangular / Kallen function */ - template - inline double rootOfKallen (T a, T b, T c) const { - double sres=a*a + b*b + c*c - 2.*( a*b+a*c+b*c ); - return sres>0.?sqrt( sres ):0.; } - + template + inline T rootOfKallen (T a, T b, T c) const { + if ( a*a + b*b + c*c - 2.*(a*b + a*c + b*c) > ZERO ) + return sqrt(a*a + b*b + c*c - 2.*(a*b + a*c + b*c) ) ; + else + return ZERO; } + public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initFIMassiveDecayKinematics; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ FIMassiveDecayKinematics & operator=(const FIMassiveDecayKinematics &) = delete; /** * Option to use the full jacobian, including the z->zprime jacobian. **/ bool theFullJacobian; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of FIMassiveDecayKinematics. */ template <> struct BaseClassTrait { /** Typedef of the first base class of FIMassiveDecayKinematics. */ typedef Herwig::DipoleSplittingKinematics NthBase; }; /** This template specialization informs ThePEG about the name of * the FIMassiveDecayKinematics class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::FIMassiveDecayKinematics"; } /** * The name of a file containing the dynamic library where the class * FIMassiveDecayKinematics is implemented. It may also include several, space-separated, * libraries if the class FIMassiveDecayKinematics depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_FIMassiveDecayKinematics_H */ diff --git a/Shower/Dipole/Kinematics/FIMassiveKinematics.cc b/Shower/Dipole/Kinematics/FIMassiveKinematics.cc --- a/Shower/Dipole/Kinematics/FIMassiveKinematics.cc +++ b/Shower/Dipole/Kinematics/FIMassiveKinematics.cc @@ -1,385 +1,385 @@ // -*- C++ -*- // // FIMassiveKinematics.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 FIMassiveKinematics class. // #include "FIMassiveKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" #include "Herwig/Shower/Dipole/Kernels/DipoleSplittingKernel.h" using namespace Herwig; FIMassiveKinematics::FIMassiveKinematics() : DipoleSplittingKinematics() {} FIMassiveKinematics::~FIMassiveKinematics() {} IBPtr FIMassiveKinematics::clone() const { return new_ptr(*this); } IBPtr FIMassiveKinematics::fullclone() const { return new_ptr(*this); } pair FIMassiveKinematics::kappaSupport(const DipoleSplittingInfo&) const { return {0.0,1.0}; } pair FIMassiveKinematics::xiSupport(const DipoleSplittingInfo& split) const { double c = sqrt(1.-4.*sqr(IRCutoff()/generator()->maximumCMEnergy())); if ( split.index().emitterData()->id() == ParticleID::g ) { if ( split.emissionData()->id() != ParticleID::g ) return {0.5*(1.-c),0.5*(1.+c)}; double b = log((1.+c)/(1.-c)); return {-b,b}; } return {-log(0.5*(1.+c)),-log(0.5*(1.-c))}; } // sbar Energy FIMassiveKinematics::dipoleScale(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator) const { return sqrt(2.*(pEmitter*pSpectator)); } Energy FIMassiveKinematics::ptMax(Energy dScale, - double, double specX, + double, double specX, const DipoleSplittingInfo& dInfo, - const DipoleSplittingKernel& split) const { + const DipoleSplittingKernel& split) const { DipoleIndex ind = dInfo.index(); Energy2 mij2 = sqr(dInfo.emitterMass()); Energy2 mi2 = ZERO; // g->gg and g->qqbar if ( abs(split.emitter(ind)->id()) == abs(split.emission(ind)->id()) ) { mi2 = sqr(split.emitter(ind)->mass()); } // Otherwise have X->Xg (should work for SUSY) else { mi2 = mij2; } Energy2 mj2 = sqr(split.emission(ind)->mass()); Energy2 sPrime = sqr(dScale) * (1.-specX)/specX + mij2; return .5 * sqrt(sPrime) * rootOfKallen( sPrime/sPrime, mi2/sPrime, mj2/sPrime ); } Energy FIMassiveKinematics::ptMax(Energy dScale, double, double specX, const DipoleIndex& ind, const DipoleSplittingKernel& split, tPPtr emitter, tPPtr) const { Energy2 mij2 = sqr(emitter->mass()); Energy2 mi2 = ZERO; // g->gg and g->qqbar if ( abs(split.emitter(ind)->id()) == abs(split.emission(ind)->id()) ) { mi2 = sqr(split.emitter(ind)->mass()); } // Otherwise have X->Xg (should work for SUSY) else { mi2 = mij2; } Energy2 mj2 = sqr(split.emission(ind)->mass()); Energy2 sPrime = sqr(dScale) * (1.-specX)/specX + mij2; return .5 * sqrt(sPrime) * rootOfKallen( sPrime/sPrime, mi2/sPrime, mj2/sPrime ); } Energy FIMassiveKinematics::QMax(Energy dScale, - double, double specX, + double, double specX, const DipoleSplittingInfo&, - const DipoleSplittingKernel&) const { + const DipoleSplittingKernel&) const { generator()->log() << "FIMassiveKinematics::QMax called.\n" << flush; assert(false && "implementation missing"); // this is sqrt( 2qi*q ) -> max; return dScale * sqrt((1.-specX)/specX); } Energy FIMassiveKinematics::PtFromQ(Energy scale, const DipoleSplittingInfo& split) const { // from Martin's thesis double z = split.lastZ(); // masses Energy2 mi2 = ZERO;; if ( abs(split.emitterData()->id()) == abs(split.emissionData()->id()) ) { mi2 = sqr(split.emitterData()->mass()); } // Otherwise have X->Xg (should work for SUSY) else { mi2 = sqr(split.emitterMass()); } Energy2 mj2 = sqr(split.emissionData()->mass()); - Energy2 pt2 = z*(1.-z)*sqr(scale) - (1-z)*mi2 - z*mj2; + Energy2 pt2 = z*(1.-z)*sqr(scale) - (1.-z)*mi2 - z*mj2; assert(pt2 >= ZERO); return sqrt(pt2); } Energy FIMassiveKinematics::QFromPt(Energy pt, const DipoleSplittingInfo& split) const { // from Martin's thesis double z = split.lastZ(); // masses Energy2 mi2 = ZERO;; if ( abs(split.emitterData()->id()) == abs(split.emissionData()->id()) ) { mi2 = sqr(split.emitterData()->mass()); } // Otherwise have X->Xg (should work for SUSY) else { mi2 = sqr(split.emitterMass()); } Energy2 mj2 = sqr(split.emissionData()->mass()); - Energy2 Q2 = (sqr(pt) + (1-z)*mi2 + z*mj2)/(z*(1.-z)); + Energy2 Q2 = (sqr(pt) + (1.-z)*mi2 + z*mj2)/(z*(1.-z)); return sqrt(Q2); } double FIMassiveKinematics::ptToRandom(Energy pt, Energy, double,double, const DipoleIndex&, const DipoleSplittingKernel&) const { return log(pt/IRCutoff()) / log(0.5 * generator()->maximumCMEnergy()/IRCutoff()); } bool FIMassiveKinematics::generateSplitting(double kappa, double xi, double rphi, DipoleSplittingInfo& info, const DipoleSplittingKernel&) { if ( info.spectatorX() < xMin() ) { jacobian(0.0); return false; } Energy pt = IRCutoff() * pow(0.5 * generator()->maximumCMEnergy()/IRCutoff(),kappa); if ( pt > info.hardPt() || pt < IRCutoff() ) { jacobian(0.0); return false; } double z; double mapZJacobian; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emissionData()->id() != ParticleID::g ) { z = xi; mapZJacobian = 1.; } else { z = exp(xi)/(1.+exp(xi)); mapZJacobian = z*(1.-z); } } else { z = 1.-exp(-xi); mapZJacobian = 1.-z; } // Construct mass squared variables Energy2 mij2 = sqr(info.emitterMass()); Energy2 mi2 = ZERO; // g->gg and g->qqbar if ( abs(info.emitterData()->id()) == abs(info.emissionData()->id()) ) { mi2 = sqr(info.emitterData()->mass()); } // Otherwise have X->Xg (should work for SUSY) else { mi2 = mij2; } Energy2 mj2 = sqr(info.emissionData()->mass()); Energy2 pt2 = sqr(pt); // 2 pij.pb Energy2 sbar = sqr(info.scale()); // Compute x double x = 1. / ( 1. + ( pt2 + (1.-z)*mi2 + z*mj2 - z*(1.-z)*mij2 ) / ( z*(1.-z)*sbar ) ); // Check the limit on x double xs = info.spectatorX(); if ( x < xs ) { jacobian(0.0); return false; } // Compute and check the z limits Energy2 sPrime = sbar * (1.-xs)/xs + mij2; Energy hard=info.hardPt(); if(openZBoundaries()==1){ hard=.5 * sqrt(sPrime) * rootOfKallen( sPrime/sPrime, mi2/sPrime, mj2/sPrime ); } if(openZBoundaries()==2){ Energy2 s = mij2 - sbar; hard=min(0.5*sqrt(sPrime) * rootOfKallen( sPrime/sPrime, mi2/sPrime, mj2/sPrime ) , 0.5*sqrt(s) * rootOfKallen( s/s, mi2/s, mj2/s )); } double ptRatio = sqrt(1.-sqr(pt/hard)); double zm1 = .5*( 1.+(mi2-mj2)/sPrime - rootOfKallen(sPrime/sPrime,mi2/sPrime,mj2/sPrime) * ptRatio); double zp1 = .5*( 1.+(mi2-mj2)/sPrime + rootOfKallen(sPrime/sPrime,mi2/sPrime,mj2/sPrime) * ptRatio); if ( z > zp1 || z < zm1 ) { jacobian(0.0); return false; } // additional purely kinematic constraints from // the integration limits in Catani-Seymour double mui2CS = x*mi2/sbar; double muj2CS = x*mj2/sbar; double muij2CS = x*mij2/sbar; // Limit on x double xp = 1. + muij2CS - sqr(sqrt(mui2CS)+sqrt(muj2CS)); if (x > xp ) { jacobian(0.0); return false; } // Limit on z double root = sqr(1.-x+muij2CS-mui2CS-muj2CS)-4.*mui2CS*muj2CS; if( root < 0. && root>-1e-10 ) { // assert(false); root = 0.; } else if (root <0. ) { jacobian(0.0); return false; } root = sqrt(root); double zm2 = .5*( 1.-x+muij2CS+mui2CS-muj2CS - root ) / (1.-x+muij2CS); double zp2 = .5*( 1.-x+muij2CS+mui2CS-muj2CS + root ) / (1.-x+muij2CS); if ( z > zp2 || z < zm2 ) { jacobian(0.0); return false; } // Store the splitting variables double phi = 2.*Constants::pi*rphi; // Compute and store the jacobian double jacPt2 = 1. / ( 1. + (1.-z)*mi2/pt2 + z*mj2/pt2 - z*(1.-z)*mij2/pt2 ); jacobian( jacPt2 * mapZJacobian * 2.*log(0.5 * generator()->maximumCMEnergy()/IRCutoff())); lastPt(pt); lastZ(z); lastPhi(phi); lastSpectatorZ(x); if ( theMCCheck ) theMCCheck->book(1.,info.spectatorX(),info.scale(),info.hardPt(),pt,z,jacobian()); return true; } void FIMassiveKinematics::generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) { - + // Get splitting variables Energy pt = dInfo.lastPt(); double z = dInfo.lastZ(); // Compute sqr scales Energy2 pt2 = sqr(pt); Energy2 sbar = sqr(dInfo.scale()); - Lorentz5Momentum kt = - getKt (pSpectator, pEmitter, pt, dInfo.lastPhi(),true); - // Construct mass squared variables Energy2 mij2 = sqr(dInfo.emitterMass()); Energy mi = ZERO; // g->gg and g->qqbar if ( abs(dInfo.emitterData()->id()) == abs(dInfo.emissionData()->id()) ) { mi = dInfo.emitterData()->mass(); } // Otherwise have X->Xg (should work for SUSY) else { mi = dInfo.emitterMass(); } Energy2 mi2 = sqr(mi); Energy2 mj2 = sqr(dInfo.emissionData()->mass()); - + double xInv = ( 1. + (pt2+(1.-z)*mi2+z*mj2-z*(1.-z)*mij2) / (z*(1.-z)*sbar) ); + Lorentz5Momentum kt = getKt(pEmitter, pSpectator, pt, dInfo.lastPhi(), true); + Lorentz5Momentum em = z*pEmitter + (pt2+mi2-z*z*mij2)/(z*sbar)*pSpectator + kt; Lorentz5Momentum emm = (1.-z)*pEmitter + (pt2+mj2-sqr(1.-z)*mij2)/((1.-z)*sbar)*pSpectator - kt; Lorentz5Momentum spe = xInv*pSpectator; em.setMass(mi); em.rescaleEnergy(); emm.setMass(dInfo.emissionData()->mass()); emm.rescaleEnergy(); - + spe.setMass(ZERO); spe.rescaleEnergy(); - + emitterMomentum(em); emissionMomentum(emm); spectatorMomentum(spe); } + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void FIMassiveKinematics::persistentOutput(PersistentOStream & ) const { } void FIMassiveKinematics::persistentInput(PersistentIStream & , int) { } ClassDescription FIMassiveKinematics::initFIMassiveKinematics; // Definition of the static class description member. void FIMassiveKinematics::Init() { static ClassDocumentation documentation ("FIMassiveKinematics implements massless splittings " "off a final-initial dipole."); } diff --git a/Shower/Dipole/Kinematics/IFLightKinematics.cc b/Shower/Dipole/Kinematics/IFLightKinematics.cc --- a/Shower/Dipole/Kinematics/IFLightKinematics.cc +++ b/Shower/Dipole/Kinematics/IFLightKinematics.cc @@ -1,255 +1,255 @@ // -*- C++ -*- // // IFLightKinematics.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 IFLightKinematics class. // #include "IFLightKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" using namespace Herwig; IFLightKinematics::IFLightKinematics() : DipoleSplittingKinematics(), theCollinearScheme(true) {} IFLightKinematics::~IFLightKinematics() {} IBPtr IFLightKinematics::clone() const { return new_ptr(*this); } IBPtr IFLightKinematics::fullclone() const { return new_ptr(*this); } Energy IFLightKinematics::ptMax(Energy dScale, double emX, double, const DipoleIndex&, const DipoleSplittingKernel&) const { return dScale * sqrt((1.-emX)/emX) /2.; } Energy IFLightKinematics::QMax(Energy, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { assert(false && "add this"); return 0.0*GeV; } Energy IFLightKinematics::PtFromQ(Energy scale, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return scale*sqrt(1.-z); } Energy IFLightKinematics::QFromPt(Energy scale, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return scale/sqrt(1.-z); } pair IFLightKinematics::zBoundaries(Energy pt, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel&) const { double x = dInfo.emitterX(); Energy hard=dInfo.hardPt(); if(openZBoundaries()==1)hard=dInfo.scale() * sqrt((1.-x)/x) /2.; if(openZBoundaries()==2)hard=dInfo.scale() * min(1.,sqrt((1.-x)/x) /2.); if(hard info.hardPt() ) { jacobian(0.0); return false; } double z = 0.0; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emitterData()->id() == ParticleID::g ) { z = generateZ(xi,pt,OneOverZOneMinusZ, info,split,weight); } else { z = generateZ(xi,pt,OneOverZ, info,split,weight); } } if ( info.index().emitterData()->id() != ParticleID::g ) { if ( info.emitterData()->id() != ParticleID::g ) { z = generateZ(xi,pt,OneOverOneMinusZ, info,split,weight); } else { z = generateZ(xi,pt,FlatZ, info,split,weight); } } if ( weight == 0. && z == -1. ) { jacobian(0.0); return false; } double ratio = sqr(pt/info.scale()); - + double rho = 1. - 4.*ratio*z*(1.-z)/sqr(1.-z+ratio); if ( rho < 0.0 ) { jacobian(0.0); return false; } double x = 0.5*((1.-z+ratio)/ratio)*(1.-sqrt(rho)); double u = 0.5*((1.-z+ratio)/(1.-z))*(1.-sqrt(rho)); if ( x < info.emitterX() || x > 1. || u < 0. || u > 1. ) { jacobian(0.0); return false; } double phi = 2.*Constants::pi*rphi; - jacobian(weight*(1./(u+x-2.*u*x))); + jacobian(weight*(1./(u+x-2.*u*x))); lastPt(pt); lastZ(z); lastPhi(phi); lastEmitterZ(x); if ( theMCCheck ) theMCCheck->book(info.emitterX(),1.,info.scale(),info.hardPt(),pt,z,jacobian()); return true; } void IFLightKinematics::generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) { Energy pt = dInfo.lastPt(); double z = dInfo.lastZ(); double ratio = sqr(pt)/(2.*pEmitter*pSpectator); double rho = 1. - 4.*ratio*z*(1.-z)/sqr(1.-z+ratio); - + double x = 0.5*((1.-z+ratio)/ratio)*(1.-sqrt(rho)); double u = 0.5*((1.-z+ratio)/(1.-z))*(1.-sqrt(rho)); Lorentz5Momentum kt = - getKt (pEmitter, pSpectator, pt, dInfo.lastPhi(),true); + getKt(pEmitter, pSpectator, pt, dInfo.lastPhi(), true); // Initialise the momenta Lorentz5Momentum em; Lorentz5Momentum emm; Lorentz5Momentum spe; if ( !theCollinearScheme && x > u && (1.-x)/(x-u) < 1. ) { - + assert(false); em = ((1.-u)/(x-u))*pEmitter + ((u/x)*(1.-x)/(x-u))*pSpectator - kt/(x-u); emm = ((1.-x)/(x-u))*pEmitter + ((u/x)*(1.-u)/(x-u))*pSpectator - kt/(x-u); spe = (1.-u/x)*pSpectator; } else { em = (1./x)*pEmitter; emm = ((1.-x)*(1.-u)/x)*pEmitter + u*pSpectator + kt; spe = ((1.-x)*u/x)*pEmitter + (1.-u)*pSpectator - kt; } em.setMass(ZERO); em.rescaleEnergy(); emm.setMass(ZERO); emm.rescaleEnergy(); spe.setMass(ZERO); spe.rescaleEnergy(); emitterMomentum(em); emissionMomentum(emm); spectatorMomentum(spe); } // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFLightKinematics::persistentOutput(PersistentOStream &) const { //os << theCollinearScheme; } void IFLightKinematics::persistentInput(PersistentIStream &, int) { //is >> theCollinearScheme; } ClassDescription IFLightKinematics::initIFLightKinematics; // Definition of the static class description member. void IFLightKinematics::Init() { static ClassDocumentation documentation ("IFLightKinematics implements massless splittings " "off a initial-final dipole."); /* static Switch interfaceCollinearScheme ("CollinearScheme", "[experimental] Switch on or off the collinear scheme", &IFLightKinematics::theCollinearScheme, false, false, false); static SwitchOption interfaceCollinearSchemeYes (interfaceCollinearScheme, "Yes", "Switch on the collinear scheme.", true); static SwitchOption interfaceCollinearSchemeNo (interfaceCollinearScheme, "No", "Switch off the collinear scheme", false); - + interfaceCollinearScheme.rank(-1); */ } diff --git a/Shower/Dipole/Kinematics/IFMassiveDecayKinematics.cc b/Shower/Dipole/Kinematics/IFMassiveDecayKinematics.cc --- a/Shower/Dipole/Kinematics/IFMassiveDecayKinematics.cc +++ b/Shower/Dipole/Kinematics/IFMassiveDecayKinematics.cc @@ -1,390 +1,392 @@ // -*- C++ -*- // // IFMassiveDecayKinematics.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 IFMassiveDecayKinematics class. // #include "IFMassiveDecayKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" #include "Herwig/Shower/Dipole/Kernels/DipoleSplittingKernel.h" using namespace Herwig; IFMassiveDecayKinematics::IFMassiveDecayKinematics() : DipoleSplittingKinematics() {} IFMassiveDecayKinematics::~IFMassiveDecayKinematics() {} IBPtr IFMassiveDecayKinematics::clone() const { return new_ptr(*this); } IBPtr IFMassiveDecayKinematics::fullclone() const { return new_ptr(*this); } pair IFMassiveDecayKinematics::kappaSupport(const DipoleSplittingInfo&) const { return {0.0,1.0}; } pair IFMassiveDecayKinematics::xiSupport(const DipoleSplittingInfo& split) const { double c = sqrt(1.-4.*sqr(IRCutoff()/generator()->maximumCMEnergy())); if ( split.index().emitterData()->id() == ParticleID::g ) { if ( split.emissionData()->id() != ParticleID::g ){ return {0.5*(1.-c),0.5*(1.+c)};} double b = log((1.+c)/(1.-c)); return {-b,b}; } return {-log(0.5*(1.+c)),-log(0.5*(1.-c))}; } Energy IFMassiveDecayKinematics::dipoleScale(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum&) const { return pEmitter.m(); } Energy IFMassiveDecayKinematics::recoilMassKin(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator) const { Lorentz5Momentum pk = pEmitter - pSpectator; double pkmass = pk.m(); return pkmass; } Energy IFMassiveDecayKinematics::ptMax(Energy dScale, double, double, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const { DipoleIndex ind = dInfo.index(); double mui = split.spectator(ind)->mass() / dScale; double mu = split.emission(ind)->mass() / dScale; // Mass of recoil system // Use abs() due to generation of negative // recoilMass during sampling. double muj = abs(dInfo.recoilMass() / dScale); double mui2 = sqr( mui ), mu2 = sqr( mu ); return rootOfKallen( mui2, mu2, sqr(1.-muj) ) / ( 2.-2.*muj ) * dScale; } Energy IFMassiveDecayKinematics::QMax(Energy dScale, double, double, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel&) const { assert(false && "implementation missing"); // Mass of recoil system double Muj = abs(dInfo.recoilMass() / dScale); return dScale * ( 1.-2.*Muj+sqr(Muj) ); } Energy IFMassiveDecayKinematics::PtFromQ(Energy scale, const DipoleSplittingInfo& split) const { // from Martin's thesis double zPrime = split.lastSplittingParameters()[0]; Energy mi = split.spectatorData()->mass(); Energy m = split.emissionData()->mass(); Energy2 pt2 = zPrime*(1.-zPrime)*sqr(scale) - (1-zPrime)*sqr(mi) - zPrime*sqr(m); assert(pt2 >= ZERO); return sqrt(pt2); } Energy IFMassiveDecayKinematics::QFromPt(Energy scale, const DipoleSplittingInfo& split) const { // from Martin's thesis double zPrime = split.lastSplittingParameters()[0]; Energy mi = split.spectatorData()->mass(); Energy m = split.emissionData()->mass(); Energy2 Q2 = (sqr(scale) + (1-zPrime)*sqr(mi) + zPrime*sqr(m))/(zPrime*(1.-zPrime)); return sqrt(Q2); } double IFMassiveDecayKinematics::ptToRandom(Energy pt, Energy, double,double, const DipoleIndex&, const DipoleSplittingKernel&) const { return log(pt/IRCutoff()) / log(0.5 * generator()->maximumCMEnergy()/IRCutoff()); } bool IFMassiveDecayKinematics::generateSplitting(double kappa, double xi, double rphi, DipoleSplittingInfo& info, const DipoleSplittingKernel&) { // Construct mass squared variables Energy2 mi2 = sqr( info.spectatorData()->mass() ); Energy2 mj2 = sqr( info.emissionData()->mass() ); // Specific to the IFDecay kinematics Energy2 mij2 = mi2; Energy2 mk2 = sqr( info.recoilMass() ); Energy2 Qijk = sqr( info.scale()); // To solve issue with scale during presampling // need to enforce that Qijk-mij2-mk2 = 2*pij.pk > 0, // so combine checks by comparing against square root. if ( Qijk-mij2-mk2 < sqrt(4.*mij2*mk2) ) { jacobian(0.0); return false; } Energy2 sijk = 0.5*( Qijk - mij2 - mk2 + sqrt( sqr(Qijk-mij2-mk2) - 4.*mij2*mk2 ) ); // Calculate pt Energy pt = IRCutoff() * pow(0.5 * generator()->maximumCMEnergy()/IRCutoff(),kappa); Energy2 pt2 = sqr(pt); if ( pt > info.hardPt() || pt < IRCutoff() ) { jacobian(0.0); return false; } // Generate zPrime (i.e. the new definition of z specific to massive FF and decays) double zPrime; // TODO: This may need to change along with the emitter and spectator usage, if IFDecays are implemented again if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emissionData()->id() != ParticleID::g ) { zPrime = xi; } else { zPrime = exp(xi)/(1.+exp(xi)); } } else { zPrime = 1.-exp(-xi); } // scaled masses *** TODO: rewrite above in terms of mu to avoid this calculation ***, and mix up of notation double mui2 = mi2 / Qijk; double mu2 = mj2 / Qijk; double muj2 = mk2 / Qijk; double Mui2 = mui2; double Muj2 = muj2; // Check limit on pt Energy ptmax1 = rootOfKallen( mui2, mu2, sqr(1.-sqrt(muj2)) ) / ( 2.-2.*sqrt(muj2) ) * info.scale(); Energy auxHardPt = ptmax1 > info.hardPt() ? info.hardPt() : ptmax1; // 24/05/2015: Moved this check from the zPrime limit checks if ( pt > auxHardPt ){ jacobian(0.0); return false; } // 2011-11-09 //assert(ptmax1>info.hardPt()); // 24/05/2015: // The simple >= assert above is triggered // during sampling due to precision. // Have added a tolerance to deal with this. assert( abs(ptmax1 - info.hardPt()) <= 1e-8 || ptmax1>=info.hardPt() ); // new: 2011-08-31 // 2011-11-08: this does happen if( sqrt(mui2)+sqrt(mu2)+sqrt(muj2) > 1. ){ jacobian(0.0); return false; } // I have derived and checked the equations for zp1 and zm1, these apply to zPrime!!! // phasespace constraint to incorporate ptMax double zp1 = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) + rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * sqrt( 1.-sqr(pt/auxHardPt) ) ) / ( 2.*sqr(1.-sqrt(muj2)) ); double zm1 = ( 1.+mui2-mu2+muj2-2.*sqrt(muj2) - rootOfKallen(mui2,mu2,sqr(1-sqrt(muj2))) * sqrt( 1.-sqr(pt/auxHardPt) ) ) / ( 2.*sqr(1.-sqrt(muj2)) ); if ( zPrime > zp1 || zPrime < zm1 ) { jacobian(0.0); return false; } // Calculate A:=xij*w double A = (1./(sijk*zPrime*(1.-zPrime))) * ( pt2 + zPrime*mj2 + (1.-zPrime)*mi2 - zPrime*(1.-zPrime)*mij2 ); // Calculate y from A (can also write explicitly in terms of qt, zPrime and masses however we need A anyway) Energy2 sbar = Qijk - mi2 - mj2 - mk2; double y = (1./sbar) * (A*sijk + mij2 - mi2 - mj2); // kinematic phasespace boundaries for y // same as in Dittmaier hep-ph/9904440v2 (equivalent to CS) double bar = 1.-mui2-mu2-muj2; double ym = 2.*sqrt(mui2)*sqrt(mu2)/bar; double yp = 1. - 2.*sqrt(muj2)*(1.-sqrt(muj2))/bar; if ( y < ym || y > yp ) { jacobian(0.0); return false; } // Calculate xk and xij double lambdaK = 1. + (mk2/sijk); double lambdaIJ = 1. + (mij2/sijk); double xk = (1./(2.*lambdaK)) * ( (lambdaK + (mk2/sijk)*lambdaIJ - A) + sqrt( sqr(lambdaK + (mk2/sijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*mk2/sijk) ); double xij = 1. - ( (mk2/sijk) * (1.-xk) / xk ); // Transform to standard z definition as used in the kernels (i.e. that used in CS and standard sudakov parametrisations) double z = ( (zPrime*xij*xk*sijk/2.) + (mk2/ ( 2.*xk*xij*sijk*zPrime))*(pt2 + mi2) ) / ( (xij*xk*sijk/2.) + (mk2*mij2/(2.*xk*xij*sijk)) + (mk2/(2.*xk*xij))*A ); // I think these apply to z but need to double check double zm = ( (2.*mui2+bar*y)*(1.-y) - sqrt(y*y-ym*ym)*sqrt(sqr(2.*muj2+bar-bar*y)-4.*muj2) ) / ( 2.*(1.-y)*(mui2+mu2+bar*y) ); double zp = ( (2.*mui2+bar*y)*(1.-y) + sqrt(y*y-ym*ym)*sqrt(sqr(2.*muj2+bar-bar*y)-4.*muj2) ) / ( 2.*(1.-y)*(mui2+mu2+bar*y) ); if ( z < zm || z > zp ) { jacobian(0.0); return false; } double phi = 2.*Constants::pi*rphi; // TODO: This may need changing due to different definitions of z double mapZJacobian; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emissionData()->id() != ParticleID::g ) { mapZJacobian = 1.; } else { mapZJacobian = z*(1.-z); } } else { mapZJacobian = 1.-z; } // TODO: May need a redefinition due to different definitions of z jacobian( 2. * mapZJacobian * (1.-y) * log(0.5 * generator()->maximumCMEnergy()/IRCutoff()) * bar / rootOfKallen(1.,Mui2,Muj2) ); // Record the physical variables, as used by the CS kernel definitions lastPt(pt); lastZ(z); lastPhi(phi); // Record zPrime for use in kinematics generation and kernel evaluation splittingParameters().clear(); splittingParameters().push_back(zPrime); if ( theMCCheck ) { theMCCheck->book(1.,1.,info.scale(),info.hardPt(),pt,z,jacobian()); } return true; } // Check use of const void IFMassiveDecayKinematics::generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) { - + // There is no plan to implement IF-type decays, + // therefore these kinematics have not been kept up-to-date, + // have not had any bug fixes and have not been kept up-to-date + // with other developments since their creation. + assert(false && "The should be no initial-final type decay dipoles being showered, something is wrong."); // The only value stored in dInfo.lastSplittingParameters() should be zPrime assert(dInfo.lastSplittingParameters().size() == 1 ); double zPrime = dInfo.lastSplittingParameters()[0]; Energy pt = dInfo.lastPt(); Energy2 pt2 = sqr(pt); // Momentum of the recoil system Lorentz5Momentum pk = pEmitter-pSpectator; Lorentz5Momentum pij = pSpectator; // Masses - Currently not using the mu-ratio formalism and using a different notation to Simon Energy2 mi2 = sqr( dInfo.spectatorData()->mass() ); Energy2 mj2 = sqr( dInfo.emissionData()->mass() ); Energy2 mij2 = mi2; Energy2 mk2 = sqr(dInfo.recoilMass()); Energy2 Qijk = sqr(dInfo.scale()); Energy2 sijk = 0.5*( Qijk - mij2 - mk2 + sqrt( sqr(Qijk-mij2-mk2) - 4.*mij2*mk2 ) ); Energy4 sijk2 = sqr(sijk); // Calculate A:=xij*w double A = (1./(sijk*zPrime*(1.-zPrime))) * ( pt2 + zPrime*mj2 + (1.-zPrime)*mi2 - zPrime*(1.-zPrime)*mij2 ); // Calculate xk and xij double lambdaK = 1. + (mk2/sijk); double lambdaIJ = 1. + (mij2/sijk); double xk = (1./(2.*lambdaK)) * ( (lambdaK + (mk2/sijk)*lambdaIJ - A) + sqrt( sqr(lambdaK + (mk2/sijk)*lambdaIJ - A) - 4.*lambdaK*lambdaIJ*mk2/sijk) ); double xij = 1. - ( (mk2/sijk) * (1.-xk) / xk ); // Construct reference momenta nk, nij, nt Lorentz5Momentum nij = ( sijk2 / (sijk2-mij2*mk2) ) * (pij - (mij2/sijk)*pk); Lorentz5Momentum nk = ( sijk2 / (sijk2-mij2*mk2) ) * (pk - (mk2/sijk)*pij); - //Lorentz5Momentum nt = getKt(nij,nk,sqrt(sijk),dInfo.lastPhi()); // Following notation in notes, qt = sqrt(wt)*nt - Lorentz5Momentum qt = getKt(nij,nk,pt,dInfo.lastPhi()); + Lorentz5Momentum qt = getKt(nij, nk, pt, dInfo.lastPhi()); // Construct qij, qk, qi and qj Lorentz5Momentum qij = xij*nij + (mij2/(xij*sijk))*nk; Lorentz5Momentum qk = xk*nk + (mk2/(xk*sijk))*nij; // No need to actually calculate nt and wt: Lorentz5Momentum qi = zPrime*qij + ((pt2 + mi2 - zPrime*zPrime*mij2)/(xij*sijk*zPrime))*nk + qt; Lorentz5Momentum qj = (1.-zPrime)*qij + ((pt2 + mj2 - sqr(1.-zPrime)*mij2)/(xij*sijk*(1.-zPrime)))*nk - qt; // book spectatorMomentum(qi); emissionMomentum(qj); emitterMomentum(pEmitter); //recoilMomentum is not currently used //recoilMomentum(pk); splitRecoilMomentum(qk); } - - // If needed, insert default implementations of function defined +// If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFMassiveDecayKinematics::persistentOutput(PersistentOStream & ) const { } void IFMassiveDecayKinematics::persistentInput(PersistentIStream & , int) { } ClassDescription IFMassiveDecayKinematics::initIFMassiveDecayKinematics; // Definition of the static class description member. void IFMassiveDecayKinematics::Init() { static ClassDocumentation documentation ("IFMassiveDecayKinematics implements implements massive splittings " "off an initial-final decay dipole."); } diff --git a/Shower/Dipole/Kinematics/IFMassiveKinematics.cc b/Shower/Dipole/Kinematics/IFMassiveKinematics.cc --- a/Shower/Dipole/Kinematics/IFMassiveKinematics.cc +++ b/Shower/Dipole/Kinematics/IFMassiveKinematics.cc @@ -1,373 +1,373 @@ // -*- C++ -*- // // IFMassiveKinematics.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 IFMassiveKinematics class. // #include "IFMassiveKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" #include "Herwig/Shower/Dipole/Kernels/DipoleSplittingKernel.h" using namespace Herwig; IFMassiveKinematics::IFMassiveKinematics() : DipoleSplittingKinematics(), theCollinearScheme(true) {} IFMassiveKinematics::~IFMassiveKinematics() {} IBPtr IFMassiveKinematics::clone() const { return new_ptr(*this); } IBPtr IFMassiveKinematics::fullclone() const { return new_ptr(*this); } pair IFMassiveKinematics::kappaSupport(const DipoleSplittingInfo&) const { return {0.0,1.0}; } pair IFMassiveKinematics::xiSupport(const DipoleSplittingInfo& split) const { double c = sqrt(1.-4.*sqr(IRCutoff()/generator()->maximumCMEnergy())); if ( split.index().emitterData()->id() == ParticleID::g ) { if ( split.emitterData()->id() == ParticleID::g ) { double b = log((1.+c)/(1.-c)); return {-b,b}; } else { return {log(0.5*(1.-c)),log(0.5*(1.+c))}; } } if ( split.index().emitterData()->id() != ParticleID::g && split.emitterData()->id() != ParticleID::g ) { return {-log(0.5*(1.+c)),-log(0.5*(1.-c))}; } return {0.5*(1.-c),0.5*(1.+c)}; } // sbar Energy IFMassiveKinematics::dipoleScale(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator) const { return sqrt(2.*(pEmitter*pSpectator)); } Energy IFMassiveKinematics::ptMax(Energy dScale, double emX, double, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel&) const { Energy2 A = sqr(dScale) * (1.-emX)/emX; Energy2 mk2 = sqr(dInfo.spectatorMass()); Energy ptMax = 0.5*A/sqrt(mk2+A); return ptMax; } Energy IFMassiveKinematics::ptMax(Energy dScale, double emX, double, const DipoleIndex&, const DipoleSplittingKernel&, tPPtr, tPPtr spectator) const { Energy2 A = sqr(dScale) * (1.-emX)/emX; Energy2 mk2 = sqr(spectator->mass()); Energy ptMax = 0.5*A/sqrt(mk2+A); return ptMax; } Energy IFMassiveKinematics::QMax(Energy, double, double, const DipoleSplittingInfo&, const DipoleSplittingKernel&) const { assert(false && "add this"); return 0.0*GeV; } Energy IFMassiveKinematics::PtFromQ(Energy scale, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return scale*sqrt(1.-z); } Energy IFMassiveKinematics::QFromPt(Energy pt, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return pt/sqrt(1.-z); } double IFMassiveKinematics::ptToRandom(Energy pt, Energy, double,double, const DipoleIndex&, const DipoleSplittingKernel&) const { return log(pt/IRCutoff()) / log(0.5 * generator()->maximumCMEnergy()/IRCutoff()); } bool IFMassiveKinematics::generateSplitting(double kappa, double xi, double rphi, DipoleSplittingInfo& info, const DipoleSplittingKernel&) { // Check emitter x against xmin if ( info.emitterX() < xMin() ) { jacobian(0.0); return false; } // Generate pt and check it against max allowed Energy pt = IRCutoff() * pow(0.5 * generator()->maximumCMEnergy()/IRCutoff(),kappa); if ( pt < IRCutoff() || pt > info.hardPt() ) { jacobian(0.0); return false; } // Compute scales required Energy2 pt2 = sqr(pt); Energy2 saj = sqr(info.scale()); Energy2 mk2 = sqr(info.spectatorMass()); // Generate z double z = 0.; double mapZJacobian = 0.; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emitterData()->id() == ParticleID::g ) { z = exp(xi)/(1.+exp(xi)); mapZJacobian = z*(1.-z); } else { z = exp(xi); mapZJacobian = z; } } if ( info.index().emitterData()->id() != ParticleID::g ) { if ( info.emitterData()->id() != ParticleID::g ) { z = 1.-exp(-xi); mapZJacobian = 1.-z; } else { z = xi; mapZJacobian = 1.; } } // Check limits on z double xe = info.emitterX(); Energy hard = info.hardPt(); if(openZBoundaries()==1){ Energy2 A = saj*(1.-xe)/xe; hard = 0.5*A/sqrt(mk2+A); } if(openZBoundaries()==2){ Energy2 A = saj*min(1.,(1.-xe)/xe); hard= 0.5*A/sqrt(mk2+A); assert(pt2<=sqr(hard)); } double ptRatio = sqrt(1. - pt2/sqr(hard) ); double zp = 0.5*(1.+xe + (1.-xe)*ptRatio); double zm = 0.5*(1.+xe - (1.-xe)*ptRatio); if ( z < zm || z > zp ) { jacobian(0.0); return false; } // Calculate x and u in terms of z and pt double r = pt2/saj; double muk2 = mk2/saj; double rho = 1. - 4.*r*(1.-muk2)*z*(1.-z)/sqr(1.-z+r); if ( rho < 0.0 ) { // This has never happened jacobian(0.0); return false; } double x = 0.5*((1.-z+r)/(r*(1.-muk2))) * (1. - sqrt(rho)); double u = x*r / (1.-z); // Check limits on x and u // Following Catani-Seymour paper double muk2CS = x*muk2; double up = (1.-x) / ( 1.-x + muk2CS ); if ( x < xe || x > 1. || u < 0. || u > up ) { jacobian(0.0); return false; } - // Jacobian - // Jacobian for dpt2 dz -> dx du - Energy2 jac = saj*abs( (1.+x*(muk2-1.))*(-u*(1.-u)/sqr(x)) - (1.+u*(muk2-1.))*((1.-2.*u)*(1.-x)/x - 2.*u*muk2) ); - jacobian( (pt2/(x*u*jac)) * mapZJacobian * 2. * log(0.5 * generator()->maximumCMEnergy()/IRCutoff())); + // Compute the Jacobian + double jac = 1./(u + x - 2.*u*x*(1.-muk2)); + + jacobian( jac * mapZJacobian * 2. * log(0.5 * generator()->maximumCMEnergy()/IRCutoff())); // Log results double phi = 2.*Constants::pi*rphi; lastPt(pt); lastZ(z); lastPhi(phi); lastEmitterZ(x); if ( theMCCheck ) theMCCheck->book(info.emitterX(),1.,info.scale(),info.hardPt(),pt,z,jacobian()); return true; } void IFMassiveKinematics::generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) { // Initialise the momenta Lorentz5Momentum em; Lorentz5Momentum emm; Lorentz5Momentum spe; - // TODO: adjust phasespace boundary condition if (!theCollinearScheme) { assert(false); Energy2 sbar = 2.*pEmitter*pSpectator; Energy pt = dInfo.lastPt(); double ratio = pt*pt/sbar; double z = dInfo.lastZ(); double x = (z*(1.-z)-ratio)/(1.-z-ratio); double u = ratio / (1.-z); pt = sqrt(sbar*u*(1.-u)*(1.-x)); Energy magKt = - sqrt(sbar*u*(1.-u)*(1.-x)/x - sqr(u*dInfo.spectatorData()->mass())); + sqrt(sbar*u*(1.-u)*(1.-x)/x - sqr(u*dInfo.spectatorMass())); Lorentz5Momentum kt = - getKt (pEmitter, pSpectator, magKt, dInfo.lastPhi(),true); + getKt (pSpectator, pEmitter, magKt, dInfo.lastPhi(),true); Energy2 mj2 = sqr(dInfo.spectatorMass()); double alpha = 1. - 2.*mj2/sbar; if ( x > u && (1.-x)/(x-u) < 1. ) { double fkt = sqrt(sqr(x-u)+4.*x*u*mj2/sbar); // em = // ((1.-u)/(x-u))*pEmitter + ((u/x)*(1.-x)/(x-u))*pSpectator - kt/(x-u); Energy2 fa = (sbar*(x+u-2.*x*z)+2.*mj2*x*u) / sqrt(sqr(x-u)+4.*x*u*mj2/sbar); double a = (-sbar+fa) / (2.*x*(sbar-mj2)); double ap = (sbar+alpha*fa) / (2.*x*(sbar-mj2)); em = ap*pEmitter + a*pSpectator - fkt*kt; // emm = // ((1.-x)/(x-u))*pEmitter + ((u/x)*(1.-u)/(x-u))*pSpectator - kt/(x-u); Energy2 fb = abs(sbar*(u*(1.-u)-x*(1.-x))+2.*mj2*x*u) / sqrt(sqr(x-u)+4.*x*u*mj2/sbar); double b = (-sbar*(1.-x-u)+fb) / (2.*x*(sbar-mj2)); double bp = (sbar*(1.-x-u)+alpha*fb) / (2.*x*(sbar-mj2)); emm = bp*pEmitter + b*pSpectator + fkt*kt; // spe = // (1.-u/x)*pSpectator; Energy2 fc = sqrt(sqr(sbar*(x-u))+4.*sbar*mj2*x*u); double c = (sbar*(x-u)-2.*x*mj2+fc) / (2.*x*(sbar-mj2)); double cp = (-sbar*(x-u)+2.*x*mj2+alpha*fc) / (2.*x*(sbar-mj2)); spe = cp*pEmitter + c*pSpectator; } } else { // Get z, pt and the relevant scales double z = dInfo.lastZ(); Energy pt = dInfo.lastPt(); Energy2 pt2 = sqr(pt); Energy2 saj = 2.*pEmitter*pSpectator; double muk2 = sqr(dInfo.spectatorMass())/saj; double r = pt2/saj; // Calculate x and u double rho = 1. - 4.*r*(1.-muk2)*z*(1.-z)/sqr(1.-z+r); double x = 0.5*((1.-z+r)/(r*(1.-muk2))) * (1. - sqrt(rho)); double u = x*r / (1.-z); // Generate kt - Lorentz5Momentum kt = getKt (pEmitter, pSpectator, pt, dInfo.lastPhi(), true); + Lorentz5Momentum kt = getKt(pEmitter, pSpectator, pt, dInfo.lastPhi(), true); // Set the momenta em = (1./x)*pEmitter; emm = ((1.-x)*(1.-u)/x - 2.*u*muk2)*pEmitter + u*pSpectator + kt; spe = ((1.-x)*u/x + 2.*u*muk2)*pEmitter + (1.-u)*pSpectator - kt; } em.setMass(ZERO); em.rescaleEnergy(); emm.setMass(ZERO); emm.rescaleEnergy(); spe.setMass(dInfo.spectatorMass()); spe.rescaleEnergy(); emitterMomentum(em); emissionMomentum(emm); spectatorMomentum(spe); } + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IFMassiveKinematics::persistentOutput(PersistentOStream &) const { //os << theCollinearScheme; } void IFMassiveKinematics::persistentInput(PersistentIStream &, int) { //is >> theCollinearScheme; } ClassDescription IFMassiveKinematics::initIFMassiveKinematics; // Definition of the static class description member. void IFMassiveKinematics::Init() { static ClassDocumentation documentation ("IFMassiveKinematics implements massless splittings " "off a initial-final dipole."); /* static Switch interfaceCollinearScheme ("CollinearScheme", "[experimental] Switch on or off the collinear scheme", &IFMassiveKinematics::theCollinearScheme, false, false, false); static SwitchOption interfaceCollinearSchemeYes (interfaceCollinearScheme, "Yes", "Switch on the collinear scheme.", true); static SwitchOption interfaceCollinearSchemeNo (interfaceCollinearScheme, "No", "Switch off the collinear scheme", false); interfaceCollinearScheme.rank(-1); */ } diff --git a/Shower/Dipole/Kinematics/IFMassiveKinematics.h b/Shower/Dipole/Kinematics/IFMassiveKinematics.h --- a/Shower/Dipole/Kinematics/IFMassiveKinematics.h +++ b/Shower/Dipole/Kinematics/IFMassiveKinematics.h @@ -1,279 +1,279 @@ // -*- C++ -*- // // IFLightKinematics.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_IFLightKinematics_H #define HERWIG_IFLightKinematics_H // // This is the declaration of the IFLightKinematics class. // #include "DipoleSplittingKinematics.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Martin Stoll * * \brief IFMassiveKinematics implements massless splittings * off an initial-final dipole. * * @see \ref IFMassiveKinematicsInterfaces "The interfaces" * defined for IFMassiveKinematics. */ class IFMassiveKinematics: public DipoleSplittingKinematics { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IFMassiveKinematics(); /** * The destructor. */ virtual ~IFMassiveKinematics(); //@} public: /** * Return the boundaries in between the evolution * variable random number is to be sampled; the lower * cuoff is assumed to correspond to the infrared cutoff. */ virtual pair kappaSupport(const DipoleSplittingInfo& dIndex) const; /** * Return the boundaries in between the momentum * fraction random number is to be sampled. */ virtual pair xiSupport(const DipoleSplittingInfo& dIndex) const; /** * Return the boundaries on the momentum fraction */ virtual pair zBoundaries(Energy, const DipoleSplittingInfo&, const DipoleSplittingKernel&) const { return {0.0,1.0}; } /** * Return the dipole scale associated to the * given pair of emitter and spectator. This * should be the invariant mass or absolute value * final/final or initial/initial and the absolute * value of the momentum transfer for intial/final or * final/initial dipoles. */ virtual Energy dipoleScale(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy dScale, double emX, double specX, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy dScale, double, double, const DipoleIndex& dIndex, const DipoleSplittingKernel& split, tPPtr emitter, tPPtr) const; /** * Return the maximum pt for the given dipole scale. */ virtual Energy ptMax(Energy, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { // Only the DipoleSplittingInfo version should be used for massive // dipoles, for now anyway. assert(false); return ZERO; } /** * Return the maximum virtuality for the given dipole scale. */ virtual Energy QMax(Energy dScale, double emX, double specX, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel& split) const; /** * Return the maximum virtuality for the given dipole scale. */ virtual Energy QMax(Energy, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { // Only the DipoleSplittingInfo version should be used for massive // dipoles, for now anyway. assert(false); return ZERO; } /** * Return the pt given a virtuality. */ virtual Energy PtFromQ(Energy scale, const DipoleSplittingInfo&) const; /** * Return the virtuality given a pt. */ virtual Energy QFromPt(Energy scale, const DipoleSplittingInfo&) const; /** * Return the random number associated to * the given pt. */ virtual double ptToRandom(Energy pt, Energy dScale, double emX, double specX, const DipoleIndex& dIndex, const DipoleSplittingKernel&) const; /** * Generate splitting variables given three random numbers * and the momentum fractions of the emitter and spectator. * Return true on success. */ virtual bool generateSplitting(double kappa, double xi, double phi, DipoleSplittingInfo& dIndex, const DipoleSplittingKernel&); /** * Generate the full kinematics given emitter and * spectator momentum and a previously completeted * DipoleSplittingInfo object. */ virtual void generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo); public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIFMassiveKinematics; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ - IFMassiveKinematics & operator=(const IFMassiveKinematics &) = delete; + IFMassiveKinematics & operator=(const IFMassiveKinematics &); private: /** * Wether or not to choose the `collinear' scheme */ bool theCollinearScheme; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IFMassiveKinematics. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IFMassiveKinematics. */ typedef Herwig::DipoleSplittingKinematics NthBase; }; /** This template specialization informs ThePEG about the name of * the IFMassiveKinematics class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IFMassiveKinematics"; } /** * The name of a file containing the dynamic library where the class * IFMassiveKinematics is implemented. It may also include several, space-separated, * libraries if the class IFMassiveKinematics depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IFMassiveKinematics_H */ diff --git a/Shower/Dipole/Kinematics/IILightKinematics.cc b/Shower/Dipole/Kinematics/IILightKinematics.cc --- a/Shower/Dipole/Kinematics/IILightKinematics.cc +++ b/Shower/Dipole/Kinematics/IILightKinematics.cc @@ -1,290 +1,358 @@ // -*- C++ -*- // // IILightKinematics.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 IILightKinematics class. // #include "IILightKinematics.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Switch.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Repository/UseRandom.h" #include "ThePEG/Repository/EventGenerator.h" #include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" using namespace Herwig; IILightKinematics::IILightKinematics() - : DipoleSplittingKinematics(), theCollinearScheme(true), didCollinear(false) {} + : DipoleSplittingKinematics(), theCollinearScheme(true), didCollinear(false), + theTransformationCalculated(false) {} +//theTransformHardOnly(false) {} IILightKinematics::~IILightKinematics() {} IBPtr IILightKinematics::clone() const { return new_ptr(*this); } IBPtr IILightKinematics::fullclone() const { return new_ptr(*this); } Energy IILightKinematics::ptMax(Energy dScale, double emX, double specX, const DipoleIndex&, const DipoleSplittingKernel&) const { double tau = !theCollinearScheme ? emX*specX : emX; return (1.-tau) * dScale / (2.*sqrt(tau)); } Energy IILightKinematics::QMax(Energy, double, double, const DipoleIndex&, const DipoleSplittingKernel&) const { assert(false && "add this"); return 0.0*GeV; } Energy IILightKinematics::PtFromQ(Energy scale, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return scale*sqrt(1.-z); } Energy IILightKinematics::QFromPt(Energy scale, const DipoleSplittingInfo& split) const { double z = split.lastZ(); return scale/sqrt(1.-z); } pair IILightKinematics::zBoundaries(Energy pt, const DipoleSplittingInfo& dInfo, const DipoleSplittingKernel&) const { double x = !theCollinearScheme ? dInfo.emitterX()*dInfo.spectatorX() : dInfo.emitterX(); - Energy hard=dInfo.hardPt(); if(openZBoundaries()==1)hard=(1.-x) *dInfo.scale()/(2.*sqrt(x)); if(openZBoundaries()==2)hard=min(dInfo.scale(),(1.-x) *dInfo.scale()/(2.*sqrt(x))); if(hard info.hardPt() ) { jacobian(0.0); return false; } double z = 0.0; if ( info.index().emitterData()->id() == ParticleID::g ) { if ( info.emitterData()->id() == ParticleID::g ) { z = generateZ(xi,pt,OneOverZOneMinusZ, info,split,weight); } else { z = generateZ(xi,pt,OneOverZ, info,split,weight); } } - if ( info.index().emitterData()->id() != ParticleID::g ) { if ( info.emitterData()->id() != ParticleID::g ) { z = generateZ(xi,pt,OneOverOneMinusZ, info,split,weight); } else { z = generateZ(xi,pt,FlatZ, info,split,weight); } } if ( weight == 0. && z == -1. ) { jacobian(0.0); return false; } double ratio = sqr(pt/info.scale()); - double x = z*(1.-z)/(1.-z+ratio); double v = ratio*z /(1.-z+ratio); if ( x < 0. || x > 1. || v < 0. || v > 1.-x ) { jacobian(0.0); return false; } - + if ( !theCollinearScheme && (1.-v-x)/(v+x) < 1. ) { if ( (x+v) < info.emitterX() || - x/(x+v) < info.spectatorX() ) { + x/(x+v) < info.spectatorX() ) { jacobian(0.0); return false; } } else { if ( x < info.emitterX() ) { jacobian(0.0); return false; } } - + double phi = 2.*Constants::pi*rphi; jacobian(weight*(1./z)); lastPt(pt); lastZ(z); lastPhi(phi); if ( !theCollinearScheme && (1.-v-x)/(v+x) < 1. ) { lastEmitterZ(x+v); lastSpectatorZ(x/(x+v)); } else { lastEmitterZ(x); lastSpectatorZ(1.); } if ( theMCCheck ) theMCCheck->book(info.emitterX(),info.spectatorX(),info.scale(),info.hardPt(),pt,z,jacobian()); return true; } void IILightKinematics::generateKinematics(const Lorentz5Momentum& pEmitter, const Lorentz5Momentum& pSpectator, const DipoleSplittingInfo& dInfo) { - Energy pt = dInfo.lastPt(); + Energy pt = dInfo.lastPt(); double z = dInfo.lastZ(); double ratio = sqr(pt)/(2.*pEmitter*pSpectator); double x = z*(1.-z)/(1.-z+ratio); double v = ratio*z /(1.-z+ratio); - + Lorentz5Momentum kt = - getKt (pEmitter, pSpectator, pt, dInfo.lastPhi()); + getKt(pEmitter, pSpectator, pt, dInfo.lastPhi()); // Initialise the momenta Lorentz5Momentum em; Lorentz5Momentum emm; Lorentz5Momentum spe; if ( !theCollinearScheme && (1.-v-x)/(v+x) < 1. ) { assert(false); em = (1./(v+x))*pEmitter+(v*(1.-v-x)/(x*(x+v)))*pSpectator+kt/(x+v); emm = ((1.-v-x)/(v+x))*pEmitter+(v/(x*(x+v)))*pSpectator+kt/(x+v); spe = (1.+v/x)*pSpectator; didCollinear = false; } else { em = (1./x)*pEmitter; emm = ((1.-x-v)/x)*pEmitter+v*pSpectator+kt; spe = pSpectator; K = em + spe - emm; K2 = K.m2(); Ktilde = pEmitter + pSpectator; KplusKtilde = K + Ktilde; - KplusKtilde2 = KplusKtilde.m2(); didCollinear = true; + // Set indicator that the transformation, + // required for spin correlations, hasn't + // been calculated. + theTransformationCalculated = false; } - - + em.setMass(ZERO); em.rescaleEnergy(); emm.setMass(ZERO); emm.rescaleEnergy(); spe.setMass(ZERO); spe.rescaleEnergy(); emitterMomentum(em); emissionMomentum(emm); spectatorMomentum(spe); } +void IILightKinematics::setTransformation () { + + // Construct transformation for spin correlations + // Clear the rotation + theRecoilTransformation = LorentzRotation(); + + // Construct boost part + Energy KplusKtildeT = KplusKtilde.t(); + Energy KT = K.t(); + + double tt = 1. - 2.*sqr(KplusKtildeT)/KplusKtilde2 + 2.*KT*Ktilde.t()/K2; + double tx = 2.*KplusKtildeT*KplusKtilde.x()/KplusKtilde2 - 2.*KT*Ktilde.x()/K2; + double ty = 2.*KplusKtildeT*KplusKtilde.y()/KplusKtilde2 - 2.*KT*Ktilde.y()/K2; + double tz = 2.*KplusKtildeT*KplusKtilde.z()/KplusKtilde2 - 2.*KT*Ktilde.z()/K2; + + theRecoilTransformation.boost(tx/tt, ty/tt, tz/tt, tt); + + // Rotate KtildeSpinCorrTrans to z-axis + Lorentz5Momentum KtildeSpinCorrTrans = theRecoilTransformation*Lorentz5Momentum(Ktilde); + Axis axis(KtildeSpinCorrTrans.vect().unit()); + if( axis.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axis.z()))); + theRecoilTransformation.rotate(-acos(axis.z()),Axis(-axis.y()/sinth,axis.x()/sinth,0.)); + } + else if( axis.z() < 0. ) { + theRecoilTransformation.rotate(Constants::pi,Axis(1.,0.,0.)); + } + + // Rotate from z-axis to K + Axis axis2(K.vect().unit()); + if( axis2.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axis2.z()))); + theRecoilTransformation.rotate(acos(axis2.z()),Axis(-axis2.y()/sinth,axis2.x()/sinth,0.)); + } + else if( axis2.z() < 0. ) { + theRecoilTransformation.rotate(Constants::pi,Axis(1.,0.,0.)); + } + + // SW 30/01/2019: Test feature only, not for release. + // Required for absorbing recoil in hard particles only + //splitRecoilMomentum(K); + + // Set indicator that the transformation, + // has been calculated + theTransformationCalculated = true; + } + + // If needed, insert default implementations of function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IILightKinematics::persistentOutput(PersistentOStream &) const { + //os << theTransformHardOnly; //os << theCollinearScheme; } void IILightKinematics::persistentInput(PersistentIStream &, int) { + //is >> theTransformHardOnly; //is >> theCollinearScheme; } ClassDescription IILightKinematics::initIILightKinematics; // Definition of the static class description member. void IILightKinematics::Init() { static ClassDocumentation documentation ("IILightKinematics implements massless splittings " "off an initial-initial dipole."); /* + static Switch interfaceTransformHardOnly + ("TransformHardOnly", + "[Dev only] Apply recoil transformation to colourless particles only.", + &IILightKinematics::theTransformHardOnly, false, false, false); + static SwitchOption interfaceTransformHardOnlyYes + (interfaceTransformHardOnly, + "Yes", + "Transform only colourless particles.", + true); + static SwitchOption interfaceTransformHardOnlyNo + (interfaceTransformHardOnly, + "No", + "Transform all particles.", + false); + + interfaceTransformHardOnly.rank(-1); + */ + /* static Switch interfaceCollinearScheme ("CollinearScheme", "[experimental] Switch on or off the collinear scheme", &IILightKinematics::theCollinearScheme, false, false, false); static SwitchOption interfaceCollinearSchemeYes (interfaceCollinearScheme, "Yes", "Switch on the collinear scheme.", true); static SwitchOption interfaceCollinearSchemeNo (interfaceCollinearScheme, "No", "Switch off the collinear scheme", false); interfaceCollinearScheme.rank(-1); */ } diff --git a/Shower/Dipole/Kinematics/IILightKinematics.h b/Shower/Dipole/Kinematics/IILightKinematics.h --- a/Shower/Dipole/Kinematics/IILightKinematics.h +++ b/Shower/Dipole/Kinematics/IILightKinematics.h @@ -1,232 +1,295 @@ // -*- C++ -*- // // IILightKinematics.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_IILightKinematics_H #define HERWIG_IILightKinematics_H // // This is the declaration of the IILightKinematics class. // #include "DipoleSplittingKinematics.h" namespace Herwig { using namespace ThePEG; -/** - * \ingroup DipoleShower - * \author Simon Platzer - * - * \brief IILightKinematics implements massless splittings - * off an initial-initial dipole. - * - * @see \ref IILightKinematicsInterfaces "The interfaces" - * defined for IILightKinematics. - */ -class IILightKinematics: public DipoleSplittingKinematics { + /** + * \ingroup DipoleShower + * \author Simon Platzer + * + * \brief IILightKinematics implements massless splittings + * off an initial-initial dipole. + * + * @see \ref IILightKinematicsInterfaces "The interfaces" + * defined for IILightKinematics. + */ + class IILightKinematics: public DipoleSplittingKinematics { -public: + public: - /** @name Standard constructors and destructors. */ - //@{ - /** - * The default constructor. - */ - IILightKinematics(); + /** @name Standard constructors and destructors. */ + //@{ + /** + * The default constructor. + */ + IILightKinematics(); - /** - * The destructor. - */ - virtual ~IILightKinematics(); - //@} + /** + * The destructor. + */ + virtual ~IILightKinematics(); + //@} -public: + public: - /** - * Return the maximum pt for the given dipole scale. - */ - virtual Energy ptMax(Energy dScale, - double emX, double specX, - const DipoleIndex&, - const DipoleSplittingKernel&) const; + /** + * Return the maximum pt for the given dipole scale. + */ + virtual Energy ptMax(Energy dScale, + double emX, double specX, + const DipoleIndex&, + const DipoleSplittingKernel&) const; - /** - * Return the maximum virtuality for the given dipole scale. - */ - virtual Energy QMax(Energy dScale, - double emX, double specX, - const DipoleIndex& dIndex, - const DipoleSplittingKernel&) const; + /** + * Return the maximum virtuality for the given dipole scale. + */ + virtual Energy QMax(Energy dScale, + double emX, double specX, + const DipoleIndex& dIndex, + const DipoleSplittingKernel&) const; - /** - * Return the pt given a virtuality. - */ - virtual Energy PtFromQ(Energy scale, const DipoleSplittingInfo&) const; + /** + * Return the pt given a virtuality. + */ + virtual Energy PtFromQ(Energy scale, const DipoleSplittingInfo&) const; - /** - * Return the virtuality given a pt. - */ - virtual Energy QFromPt(Energy scale, const DipoleSplittingInfo&) const; + /** + * Return the virtuality given a pt. + */ + virtual Energy QFromPt(Energy scale, const DipoleSplittingInfo&) const; - /** - * Return the boundaries on the momentum fraction - */ - virtual pair zBoundaries(Energy pt, - const DipoleSplittingInfo& dInfo, - const DipoleSplittingKernel& split) const; + /** + * Return the boundaries on the momentum fraction + */ + virtual pair zBoundaries(Energy pt, + const DipoleSplittingInfo& dInfo, + const DipoleSplittingKernel& split) const; - /** - * Generate splitting variables given three random numbers - * and the momentum fractions of the emitter and spectator. - * Return true on success. - */ - virtual bool generateSplitting(double kappa, double xi, double phi, - DipoleSplittingInfo& dIndex, - const DipoleSplittingKernel&); + /** + * Generate splitting variables given three random numbers + * and the momentum fractions of the emitter and spectator. + * Return true on success. + */ + virtual bool generateSplitting(double kappa, double xi, double phi, + DipoleSplittingInfo& dIndex, + const DipoleSplittingKernel&); - /** - * Generate the full kinematics given emitter and - * spectator momentum and a previously completeted - * DipoleSplittingInfo object. - */ - virtual void generateKinematics(const Lorentz5Momentum& pEmitter, - const Lorentz5Momentum& pSpectator, - const DipoleSplittingInfo& dInfo); + /** + * Generate the full kinematics given emitter and + * spectator momentum and a previously completeted + * DipoleSplittingInfo object. + */ + virtual void generateKinematics(const Lorentz5Momentum& pEmitter, + const Lorentz5Momentum& pSpectator, + const DipoleSplittingInfo& dInfo); - /* - * Return true, if there is a transformation which should - * be applied to all other final state particles except the ones - * involved in the splitting after having performed the splitting - */ - virtual bool doesTransform () const { return theCollinearScheme || didCollinear; } + /** + * Return true, if there is a transformation which should + * be applied to all other final state particles except the ones + * involved in the splitting after having performed the splitting + */ + virtual bool doesTransform () const { return theCollinearScheme || didCollinear; } - /* - * perform the transformation, if existing - */ - virtual Lorentz5Momentum transform (const Lorentz5Momentum& p) const { - if ( !theCollinearScheme && !didCollinear ) return p; - return p-(2.*(KplusKtilde*p)/KplusKtilde2)*KplusKtilde+(2.*(Ktilde*p)/K2)*K; - } + /** + * Calculate and store a required Lorentz transformation + **/ + virtual void setTransformation () ; + + /* + * perform the transformation, if existing + */ + virtual void transform (PPtr& part) { + if ( !theCollinearScheme && !didCollinear ) return; -public: + Lorentz5Momentum mom = part->momentum(); - /** @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; + // If particle has SpinInfo check that we're + // not dealing with an intermediate spectator. + if ( part->spinInfo() + && !(part->spinInfo()->timelike() && part->children().size() == 1 ) + && !(!part->spinInfo()->timelike() && part->parents()[0]->children().size() == 1 ) ) { + + if ( !theTransformationCalculated ) + setTransformation(); + + part->spinInfo()->transform(mom,theRecoilTransformation); + } - /** - * 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); - //@} + part->set5Momentum(mom-(2.*(KplusKtilde*mom)/KplusKtilde2)*KplusKtilde+(2.*(Ktilde*mom)/K2)*K); + + } - /** - * 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(); + /* + * SW 30/01/2019: Test feature only, not for release. + * Return true to only apply the transformation to non-coloured particles. + * Note this requires careful handling in DipoleEventRecord + */ + //virtual bool transformHardOnly() const { return theTransformHardOnly; } -protected: - /** @name Clone Methods. */ - //@{ - /** - * Make a simple clone of this object. - * @return a pointer to the new object. - */ - virtual IBPtr clone() const; + /** + * SW 30/01/2019: Test feature only, not for release. + * Perform the recoil in the case of a decayed parton + */ + // virtual void transformHard ( PPtr& hard ) { + // if ( !theTransformationCalculated ) { + // setTransformation(); + // theTransformationCalculated = true; + // } + // hard->setMomentum(splitRecoilMomentum()); + // hard->rescaleMass(); + // } - /** 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; - //@} + + 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; -// If needed, insert declarations of virtual function defined in the -// InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). + /** + * 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(); -private: + protected: - /** - * The static object used to initialize the description of this class. - * Indicates that this is a concrete class with persistent data. - */ - static ClassDescription initIILightKinematics; + /** @name Clone Methods. */ + //@{ + /** + * Make a simple clone of this object. + * @return a pointer to the new object. + */ + virtual IBPtr clone() const; - /** - * The assignment operator is private and must never be called. - * In fact, it should not even be implemented. - */ - IILightKinematics & operator=(const IILightKinematics &) = delete; + /** Make a clone of this object, possibly modifying the cloned object + * to make it sane. + * @return a pointer to the new object. + */ + virtual IBPtr fullclone() const; + //@} -private: - /** - * Wether or not to choose the `collinear' scheme - */ - bool theCollinearScheme; + // If needed, insert declarations of virtual function defined in the + // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). - bool didCollinear; - Lorentz5Momentum K; - Energy2 K2; - Lorentz5Momentum Ktilde; - Lorentz5Momentum KplusKtilde; - Energy2 KplusKtilde2; + private: -}; + /** + * The static object used to initialize the description of this class. + * Indicates that this is a concrete class with persistent data. + */ + static ClassDescription initIILightKinematics; + + /** + * The assignment operator is private and must never be called. + * In fact, it should not even be implemented. + */ + IILightKinematics & operator=(const IILightKinematics &) = delete; + + private: + + /** + * Wether or not to choose the `collinear' scheme + */ + bool theCollinearScheme; + + bool didCollinear; + + /** + * SW 30/01/2019: Test feature only, not for release. + * Whether to transform only hard (i.e. non-coloured) particles. + */ + //bool theTransformHardOnly; + + Lorentz5Momentum K; + Energy2 K2; + Lorentz5Momentum Ktilde; + Lorentz5Momentum KplusKtilde; + Energy2 KplusKtilde2; + + /** + * Store the LorentzRotation object equivalent to the + * transformation applied to the outgoing particles. + * Need this to apply to the particle SpinInfo if spin + * correlations are included. + */ + LorentzRotation theRecoilTransformation; + + /** + * Bool to avoid unecessary recalculation of the + * Lorentz transformation. + */ + bool theTransformationCalculated; + + }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { -/** @cond TRAITSPECIALIZATIONS */ + /** @cond TRAITSPECIALIZATIONS */ -/** This template specialization informs ThePEG about the - * base classes of IILightKinematics. */ -template <> -struct BaseClassTrait { - /** Typedef of the first base class of IILightKinematics. */ - typedef Herwig::DipoleSplittingKinematics NthBase; -}; + /** This template specialization informs ThePEG about the + * base classes of IILightKinematics. */ + template <> + struct BaseClassTrait { + /** Typedef of the first base class of IILightKinematics. */ + typedef Herwig::DipoleSplittingKinematics NthBase; + }; -/** This template specialization informs ThePEG about the name of - * the IILightKinematics class and the shared object where it is defined. */ -template <> -struct ClassTraits - : public ClassTraitsBase { - /** Return a platform-independent class name */ - static string className() { return "Herwig::IILightKinematics"; } - /** - * The name of a file containing the dynamic library where the class - * IILightKinematics is implemented. It may also include several, space-separated, - * libraries if the class IILightKinematics depends on other classes (base classes - * excepted). In this case the listed libraries will be dynamically - * linked in the order they are specified. - */ - static string library() { return "HwDipoleShower.so"; } -}; + /** This template specialization informs ThePEG about the name of + * the IILightKinematics class and the shared object where it is defined. */ + template <> + struct ClassTraits + : public ClassTraitsBase { + /** Return a platform-independent class name */ + static string className() { return "Herwig::IILightKinematics"; } + /** + * The name of a file containing the dynamic library where the class + * IILightKinematics is implemented. It may also include several, space-separated, + * libraries if the class IILightKinematics depends on other classes (base classes + * excepted). In this case the listed libraries will be dynamically + * linked in the order they are specified. + */ + static string library() { return "HwDipoleShower.so"; } + }; -/** @endcond */ + /** @endcond */ } #endif /* HERWIG_IILightKinematics_H */ diff --git a/Shower/Dipole/Makefile.am b/Shower/Dipole/Makefile.am --- a/Shower/Dipole/Makefile.am +++ b/Shower/Dipole/Makefile.am @@ -1,23 +1,24 @@ -SUBDIRS = Base Kernels Kinematics Utility AlphaS Merging Colorea +SUBDIRS = Base Kernels Kinematics Utility AlphaS Merging SpinCorrelations Colorea pkglib_LTLIBRARIES = HwDipoleShower.la HwDipoleShower_la_LDFLAGS = $(AM_LDFLAGS) -module -version-info 10:0:0 HwDipoleShower_la_LIBADD = \ Base/libHwDipoleShowerBase.la \ Kernels/libHwDipoleShowerKernels.la \ Kinematics/libHwDipoleShowerKinematics.la \ Utility/libHwDipoleShowerUtility.la \ Merging/libHwDipoleShowerMerging.la \ + SpinCorrelations/libHwDipoleShowerSpinCorrelations.la \ Colorea/libHwDipoleShowerColorea.la HwDipoleShower_la_SOURCES = \ DipoleShowerHandler.h DipoleShowerHandler.fh DipoleShowerHandler.cc pkglib_LTLIBRARIES += HwKrknloEventReweight.la HwKrknloEventReweight_la_SOURCES = \ KrkNLO/KrknloEventReweight.h KrkNLO/KrknloEventReweight.cc HwKrknloEventReweight_la_LDFLAGS = $(AM_LDFLAGS) -module -version-info 2:0:0 diff --git a/Shower/Dipole/SpinCorrelations/DipoleShowerParticle.cc b/Shower/Dipole/SpinCorrelations/DipoleShowerParticle.cc new file mode 100644 --- /dev/null +++ b/Shower/Dipole/SpinCorrelations/DipoleShowerParticle.cc @@ -0,0 +1,341 @@ +// -*- C++ -*- +// +// DipoleShowerParticle.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 2 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 DipoleShowerParticle class. +// + +#include "DipoleShowerParticle.h" + +#include "ThePEG/Interface/ClassDocumentation.h" +#include "ThePEG/Utilities/DescribeClass.h" +#include "ThePEG/EventRecord/SpinInfo.h" +#include "ThePEG/EventRecord/RhoDMatrix.h" + +#include "ThePEG/Helicity/FermionSpinInfo.h" +#include "ThePEG/Helicity/VectorSpinInfo.h" + +#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h" +#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h" + +using namespace Herwig; + +void DipoleShowerParticle::Init() { + + static ClassDocumentation documentation + ("The DipoleShowerParticle class is the implementation of a " + "vertex for a shower for the Herwig spin correlation algorithm"); + +} + + +void DipoleShowerParticle::clear() { + theParticle = PPtr(); + theDecayVertex = DSVertexPtr(); +} + + +void DipoleShowerParticle::prepare( PPtr& part, const Helicity::Direction emmDir, const Helicity::Direction specDir, const Lorentz5Momentum& pVector, const Lorentz5Momentum& nVector ) { + + // Set the member variables + theParticle = part; + theDecayVertex = new_ptr(DipoleShowerVertex()); + + // Set the pVector and nVector of the splitting vertex + theDecayVertex->pVector(pVector); + theDecayVertex->nVector(nVector); + theDecayVertex->dipoleConfig( {emmDir == outgoing,specDir == outgoing} ); + + // Calculate and store the transformation + // between the frame of this splitting and the working frame + theDecayVertex->boostToSplitting(); + + // Create the decay basis states of the emitter in the frame + // of the splitting and compute the state mappings + if ( theParticle->dataPtr()->iSpin() == PDT::Spin0 ) + assert(false); + + else if ( theParticle->dataPtr()->iSpin() == PDT::Spin1Half ) { + vector > decayBasis = createFermionDecayStates(); + setFermionMapping( decayBasis ); + } + + else if ( theParticle->dataPtr()->iSpin() == PDT::Spin1 ) { + createVectorDecayStates(); + setVectorMapping(); + } + + else + assert(false); + + // Set the decay vertex of the particle + assert(theParticle->spinInfo()); + theParticle->spinInfo()->decayVertex(theDecayVertex); + +} + + +vector > DipoleShowerParticle::createFermionDecayStates() { + + // Note - Based on createFermionSpinInfo in ShowerParticle.cc + FermionSpinPtr fspin = dynamic_ptr_cast(theParticle->spinInfo()); + + // Create the decay basis states + LorentzRotation decayRot = theDecayVertex->boostToSplitting(); + Lorentz5Momentum decayPorig = decayRot*Lorentz5Momentum(theParticle->momentum()); + + // Calculate the basis for the particle + // in the frame of its splitting + SpinorWaveFunction wave; + if( theParticle->id()>0 ) + wave=SpinorWaveFunction(decayPorig, theParticle->dataPtr(), incoming); + else + wave=SpinorWaveFunction(decayPorig, theParticle->dataPtr(), outgoing); + + // Initialise basis to return from function + // Store decay states in decay frame in this + vector > decayBasis; + + LorentzRotation decayRotInv = decayRot.inverse(); + // Compute the decay basis states + for(unsigned int ix=0;ix<2;++ix) { + wave.reset(ix); + LorentzSpinor basis = wave.dimensionedWave(); + decayBasis.push_back(basis); + + // Rotate the decay states to the lab frame + // and store them in the spin info + basis.transform(decayRotInv); + fspin->setDecayState(ix,basis); + + } + + return decayBasis; +} + + +void DipoleShowerParticle::createVectorDecayStates() { + + // Note - Based on createVectorSpinInfo in ShowerParticle.cc + VectorSpinPtr vspin = dynamic_ptr_cast(theParticle->spinInfo()); + bool massless(theParticle->id()==ParticleID::g||theParticle->id()==ParticleID::gamma); + + // Create the decay basis states + LorentzRotation decayRot = theDecayVertex->boostToSplitting(); + Lorentz5Momentum decayPorig = decayRot*Lorentz5Momentum(theParticle->momentum()); + + // Calculate the basis for the particle + // in the frame of its splitting + VectorWaveFunction wave(decayPorig, theParticle->dataPtr(), + vspin->timelike() ? outgoing : incoming ); + + + LorentzRotation decayRotInv = decayRot.inverse(); + // Compute the decay basis states + for(unsigned int ix=0;ix<3;++ix) { + LorentzPolarizationVector basis; + if(massless&&ix==1) { + basis = LorentzPolarizationVector(); + } + else { + wave.reset(ix,vector_phase); + basis = wave.wave(); + } + + // Rotate the decay states to the lab frame + // and store them in the spin info + basis.transform(decayRotInv); + vspin->setDecayState(ix,basis); + } + +} + + +void DipoleShowerParticle::setFermionMapping( const vector>& decayBasis ) { + + FermionSpinPtr fspin = dynamic_ptr_cast(theParticle->spinInfo()); + LorentzRotation rotToDecay = theDecayVertex->boostToSplitting(); + + // Access the basis states and transform them into the decay frame + vector > prodBasis; + for(unsigned int ix=0;ix<2;++ix) { + prodBasis.push_back(fspin->getCurrentBasisState(ix)); + prodBasis.back().transform(rotToDecay); + } + + // Calculate the mapping from the production/current + // basis states to the decay basis states + RhoDMatrix mapDec2Prod = RhoDMatrix(PDT::Spin1Half,false); + + // Check the direction of the particle in the decay frame and + // construct the mapping from the basis states accordingly. + Lorentz5Momentum decayPorig = rotToDecay*Lorentz5Momentum(theParticle->momentum()); + SqrtEnergy sqrtGeV = sqrt(1.0*GeV); + if (decayPorig.z() > ZERO ) { + for(unsigned int ix=0;ix<2;++ix) { + if( abs(decayBasis[0].s2()/sqrtGeV) < 1e-3 ) { + assert( abs(decayBasis[1].s2()/sqrtGeV) > 1e-5 ); + mapDec2Prod(ix,0) = prodBasis[ix].s3()/decayBasis[0].s3(); + mapDec2Prod(ix,1) = prodBasis[ix].s2()/decayBasis[1].s2(); + } + else { + assert( abs(decayBasis[1].s2()/sqrtGeV) < 1e-3 ); + mapDec2Prod(ix,0) = prodBasis[ix].s2()/decayBasis[0].s2(); + mapDec2Prod(ix,1) = prodBasis[ix].s3()/decayBasis[1].s3(); + } + } + } + else { + for(unsigned int ix=0;ix<2;++ix) { + if(abs(decayBasis[0].s1()/sqrtGeV) < 1e-3 ) { + assert( abs(decayBasis[1].s1()/sqrtGeV) > 1e-5 ); + mapDec2Prod(ix,0) = prodBasis[ix].s4()/decayBasis[0].s4(); + mapDec2Prod(ix,1) = prodBasis[ix].s1()/decayBasis[1].s1(); + } + else { + assert( abs(decayBasis[1].s1()/sqrtGeV) < 1e-3 ); + mapDec2Prod(ix,0) = prodBasis[ix].s1()/decayBasis[0].s1(); + mapDec2Prod(ix,1) = prodBasis[ix].s4()/decayBasis[1].s4(); + } + } + } + + // The mapping prod->dec is simply the adjoint of the dec->prod mapping + RhoDMatrix mapProd2Dec = RhoDMatrix(mapDec2Prod.iSpin(),false); + for (int ix=0; ixmappingD2P(mapDec2Prod); + theDecayVertex->mappingP2D(mapProd2Dec); + +} + + +void DipoleShowerParticle::setVectorMapping() { + + VectorSpinPtr vspin = dynamic_ptr_cast(theParticle->spinInfo()); + + // Access the basis and decay states + vector prodBasis; + for(unsigned int ix=0;ix<3;++ix) { + prodBasis.push_back(vspin->getCurrentBasisState(ix)); + } + vector decayBasis; + for(unsigned int ix=0;ix<3;++ix) { + decayBasis.push_back(vspin->getDecayBasisState(ix)); + } + + // Compute the mapping from the decay basis states to the + // production/current basis states + RhoDMatrix mapDec2Prod = RhoDMatrix(PDT::Spin1,false); + + for(unsigned int ix=0;ix<3;++ix) { + for(unsigned int iy=0;iy<3;++iy) { + + // For outgoing both the production and decay states are eps* + // Need -eps_a dot eps_i* + if ( vspin->timelike() ) + mapDec2Prod(ix,iy) = -prodBasis[ix].conjugate().dot(decayBasis[iy]); + // For incoming both the production and decay states are eps + else + mapDec2Prod(ix,iy) = -prodBasis[ix].dot(decayBasis[iy].conjugate()); + + if(theParticle->id()<0) { + mapDec2Prod(ix,iy)=conj(mapDec2Prod(ix,iy)); + } + } + } + + // The mapping prod->decay is simply the adjoint of the dec->prod mapping + RhoDMatrix mapProd2Dec = RhoDMatrix(mapDec2Prod.iSpin(),false); + for (int ix=0; ixmappingD2P(mapDec2Prod); + theDecayVertex->mappingP2D(mapProd2Dec); + +} + + +void DipoleShowerParticle::createNewFermionSpinInfo( PPtr& newp, Helicity::Direction dir ) { + + // Create the new spin info + FermionSpinPtr fspin = new_ptr(FermionSpinInfo(newp->momentum(),dir==outgoing)); + newp->spinInfo(fspin); + + // Create the production basis states + LorentzRotation rot = theDecayVertex->boostToSplitting(); + Lorentz5Momentum outPorig = rot*Lorentz5Momentum(newp->momentum()); + + // Calculate the basis for the particle + // in the frame of the splitting that produced it + SpinorWaveFunction wave; + if(newp->id()>0) + wave=SpinorWaveFunction(outPorig,newp->dataPtr(),incoming); + else + wave=SpinorWaveFunction(outPorig,newp->dataPtr(),outgoing); + + // Rotate the basis states back to the lab frame + // and store them in the spin info + LorentzRotation rotInv = rot.inverse(); + for(unsigned int ix=0;ix<2;++ix) { + wave.reset(ix); + LorentzSpinor basis = wave.dimensionedWave(); + basis.transform(rotInv); + fspin->setBasisState(ix,basis); + } +} + + +void DipoleShowerParticle::createNewVectorSpinInfo( PPtr& newp, Helicity::Direction dir ) { + + // Create the new spin info + VectorSpinPtr vspin = new_ptr(VectorSpinInfo(newp->momentum(),dir==outgoing)); + newp->spinInfo(vspin); + + bool massless(newp->id()==ParticleID::g||newp->id()==ParticleID::gamma); + Lorentz5Momentum partMom = newp->momentum(); + + // Extract the required information + LorentzRotation rot = theDecayVertex->boostToSplitting(); + // Get the particle momentum and transform + // it to the frame of the parent splitting + Lorentz5Momentum outPorig = rot*Lorentz5Momentum(partMom); + + // Calculate the basis for the particle in the frame of + // the splitting that produced it + VectorWaveFunction wave(outPorig,newp->dataPtr(), + vspin->timelike() ? outgoing : incoming ); + + LorentzRotation rotInv = rot.inverse(); + for(unsigned int ix=0;ix<3;++ix) { + LorentzPolarizationVector basis; + if(massless&&ix==1) { + basis = LorentzPolarizationVector(); + } + else { + wave.reset(ix,vector_phase); + basis = wave.wave(); + } + + // Rotate the basis states back to the lab frame + // and store them in the spin info + basis.transform(rotInv); + vspin->setBasisState(ix,basis); + } + +} diff --git a/Shower/Dipole/SpinCorrelations/DipoleShowerParticle.fh b/Shower/Dipole/SpinCorrelations/DipoleShowerParticle.fh new file mode 100644 --- /dev/null +++ b/Shower/Dipole/SpinCorrelations/DipoleShowerParticle.fh @@ -0,0 +1,18 @@ +// -*- C++ -*- +// +// This is the forward declaration of the DipoleShowerParticle class. +// +#ifndef HERWIG_DipoleShowerParticle_FH +#define HERWIG_DipoleShowerParticle_FH + +#include "ThePEG/Config/Pointers.h" + +namespace Herwig { + using namespace ThePEG; + + class DipoleShowerParticle; + ThePEG_DECLARE_POINTERS(DipoleShowerParticle,DSParticlePtr); + +} + +#endif diff --git a/Shower/Dipole/SpinCorrelations/DipoleShowerParticle.h b/Shower/Dipole/SpinCorrelations/DipoleShowerParticle.h new file mode 100644 --- /dev/null +++ b/Shower/Dipole/SpinCorrelations/DipoleShowerParticle.h @@ -0,0 +1,182 @@ +// -*- C++ -*- +// +// DipoleShowerParticle.h is a part of Herwig - A multi-purpose Monte Carlo event generator +// Copyright (C) 2002-2007 The Herwig Collaboration +// +// Herwig is licenced under version 2 of the GPL, see COPYING for details. +// Please respect the MCnet academic guidelines, see GUIDELINES for details. +// +#ifndef HERWIG_DipoleShowerParticle_H +#define HERWIG_DipoleShowerParticle_H +// +// This is the declaration of the DipoleShowerParticle class. +// + +#include "ThePEG/EventRecord/Particle.h" +#include "ThePEG/Helicity/WaveFunction/WaveFunctionBase.h" +#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h" +#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h" + +#include "DipoleShowerVertex.h" + +namespace Herwig { + + using namespace ThePEG; + + /** \ingroup DipoleShower + * + * \author Stephen Webster + * + */ + class DipoleShowerParticle : public Base { + + public: + + /** + * Default constructor + */ + DipoleShowerParticle() {} + + /** + * Default destructor + **/ + ~DipoleShowerParticle() {} + + public: + + /** + * Reset the member variables of the object. + */ + void clear(); + + /** + * Set up the decay vertex for the emitter. + */ + void prepare( PPtr& part, + const Helicity::Direction emmDir, + const Helicity::Direction specDir, + const Lorentz5Momentum& pVector, + const Lorentz5Momentum& nVector ); + + /** + * Return the associated decay vertex, + * a DipoleShowerVertex. + **/ + DSVertexPtr decayVertex() { return theDecayVertex; } + + /** + * Create fermion decay basis states. + * It returns the decay basis states in the + * decay frame as required for the mapping. + */ + vector > createFermionDecayStates(); + + /** + * Create vector decay basis states. + */ + void createVectorDecayStates(); + + /** + * Create fermion production basis states + * for the given particle produced in the splitting. + */ + void createNewFermionSpinInfo( PPtr& outgoing, Helicity::Direction dir); + + /** + * Create vector production basis states + * for the given particle produced in the splitting. + */ + void createNewVectorSpinInfo( PPtr& outgoing, Helicity::Direction dir); + + /** + * Create the mappings between the production + * and decay states for the fermion and + * store them in the associated decay vertex. + * (No longer applicable) reason for passing the + * decay states as an argument: + * Previously used a check on zero values for computing + * the mapping, rather than a 1e-5, this would only + * work when using the original decay state as calculated + * in the decay frame (i.e. without transforming to the + * lab frame and back). Now it simply avoids doing an + * unnecessary rotation of the decay basis + */ + void setFermionMapping( const vector>& decayBasis ); + + /** + * Create the mappings between the production + * and decay states the boson and + * store them in the associated decay vertex. + */ + + void setVectorMapping(); + + public: + + /** + * 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(); + + private: + + /** + * The pptr to this particle. + */ + PPtr theParticle; + + /** + * The dipole shower vertex associated + * with this particle. + */ + DSVertexPtr theDecayVertex; + + private: + + /** + * The assignment operator is private and must never be called. + * In fact, it should not even be implemented. + */ + DipoleShowerParticle & operator=(const DipoleShowerParticle &); + + }; +} + +#include "ThePEG/Utilities/ClassTraits.h" + +namespace ThePEG { + + /** @cond TRAITSPECIALIZATIONS */ + + /** This template specialization informs ThePEG about the + * base classes of DipoleShowerParticle. */ + template <> + struct BaseClassTrait { + /** Typedef of the first base class of DipoleShowerParticle. */ + typedef Base NthBase; + }; + + /** This template specialization informs ThePEG about the name of + * the DipoleShowerParticle class and the shared object where it is defined. */ + template <> + struct ClassTraits + : public ClassTraitsBase { + /** Return a platform-independent class name */ + static string className() { return "Herwig::DipoleShowerParticle"; } + /** + * The name of a file containing the dynamic library where the class + * DipoleShowerParticle is implemented. It may also include several, space-separated, + * libraries if the class DipoleShowerParticle depends on other classes (base classes + * excepted). In this case the listed libraries will be dynamically + * linked in the order they are specified. + */ + static string library() { return "HwDipoleShower.so"; } + }; + + /** @endcond */ + +} +#endif /* HERWIG_DipoleShowerParticle_H */ diff --git a/Shower/Dipole/SpinCorrelations/DipoleShowerVertex.cc b/Shower/Dipole/SpinCorrelations/DipoleShowerVertex.cc new file mode 100644 --- /dev/null +++ b/Shower/Dipole/SpinCorrelations/DipoleShowerVertex.cc @@ -0,0 +1,302 @@ +// -*- C++ -*- +// +// DipoleShowerVertex.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 2 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 DipoleShowerVertex class. +// + + +#include "ThePEG/Interface/ClassDocumentation.h" +#include "ThePEG/Utilities/DescribeClass.h" +#include "ThePEG/EventRecord/SpinInfo.h" + +#include "DipoleShowerVertex.h" + +using namespace Herwig; +using namespace Herwig::Helicity; +using namespace ThePEG; + +// Notes: +// The mappings are defined and stored such +// that they are relevant to the particle of +// which this is the decay vertex. +// For a timelike particle this is the incoming to the vertex. +// For a spacelike particle, this is one of the outgoing. + +namespace { + + void doMapping( RhoDMatrix& rho, const RhoDMatrix& mapping, bool isDecayMatrix ) { + RhoDMatrix rhop(rho.iSpin(),false); + + // Note: The mapping of the rho (decay) matrix for FSR + // and the rho (decay) matrix for ISR follow the same + // pattern of mapping conjugates, despite them doing + // different things. + // We simply have to pass the correct state mapping, + // dec2prod or prod2dec, to this function in each case + + // Rho matrix mapping + if ( !isDecayMatrix ) { + for(int ixa=0;ixa +describeDipoleShowerVertex ("Herwig::DipoleShowerVertex",""); + +void DipoleShowerVertex::Init() { + + static ClassDocumentation documentation + ("The DipoleShowerVertex class is the implementation of a " + "vertex for a shower for the Herwig spin correlation algorithm"); + +} + +DipoleShowerVertex::DipoleShowerVertex() : + theBoostCalculated(false) {} + + +RhoDMatrix DipoleShowerVertex::getRhoMatrix(int i, bool) const { + + // Check if we are returning the rho matrix of a spacelike parton + bool spaceLike = false; + if ( !outgoing()[i]->timelike() ) + spaceLike = true; + + // Initialise the output and get the rho matrix of the incoming + RhoDMatrix densityMatrix(outgoing()[i]->iSpin(),false); + RhoDMatrix input=incoming()[0]->rhoMatrix(); + + // If the incoming is timelike, we need to map + // its rho matrix to its decay frame + if ( incoming()[0]->timelike() ) { + RhoDMatrix mapping = theMappingDecay2Prod; + doMapping(input,mapping,false); + } + + // Test that we are dealing with an emitter + assert( theMatrixElement->nOut()==2 ); + assert( outgoing().size() == 2 ); + + // Get the decay matrices for the outgoing particles + vector rhoout; + for(unsigned int ix=0,N=outgoing().size();ixtimelike() ) { + rhoout.push_back(outgoing()[ix]->DMatrix()); + } + + // If the 'outgoing' is spacelike then its + // decay matrix needs mapping to the frame of this vertex + else { + assert( !spaceLike ); + assert( !incoming()[0]->timelike() ); + RhoDMatrix mapping = theMappingDecay2Prod; + RhoDMatrix tempMatrix = outgoing()[ix]->DMatrix(); + doMapping(tempMatrix, mapping, true); + rhoout.push_back(tempMatrix); + } + } + } + + // calculate the spin density matrix + densityMatrix = theMatrixElement->calculateRhoMatrix(i,input,rhoout); + + // If the 'outgoing' particle we are calculating for is spacelike + // we must map the rho matrix to its production frame + if ( spaceLike ) { + RhoDMatrix mapping = theMappingProd2Decay; + doMapping(densityMatrix,mapping, false); + } + + return densityMatrix; + +} + + +RhoDMatrix DipoleShowerVertex::getDMatrix(int) const { + + // Flag indicating if we are returning the + // decay matrix of a spacelike parton + bool spaceLike = false; + + // Initialise the output + RhoDMatrix decayMatrix; + + // Test that we are dealing with an emitter + assert( theMatrixElement->nOut()==2 ); + assert( outgoing().size() == 2); + + // Get the decay matrices for the outgoing particles + vector Dout; + + for(unsigned int ix=0,N=outgoing().size();ixtimelike() ) { + assert(!incoming()[0]->timelike()); + spaceLike = true; + + RhoDMatrix tempMatrix = outgoing()[ix]->DMatrix(); + RhoDMatrix mapping = theMappingDecay2Prod; + doMapping(tempMatrix, mapping, true); + Dout.push_back(tempMatrix); + } + + // If the outgoing is timelike, no need to map. + else + Dout.push_back(outgoing()[ix]->DMatrix()); + } + + // calculate the decay matrix + decayMatrix = theMatrixElement->calculateDMatrix(Dout); + + // For a timelike 'incoming' need to map the decay + // matrix from its decay frame to its production frame. + if ( !spaceLike ) { + RhoDMatrix mapping = theMappingProd2Decay; + doMapping(decayMatrix,mapping, true); + } + + return decayMatrix; + +} + + +LorentzRotation DipoleShowerVertex::boostToSplitting() { + + if ( !theBoostCalculated ) { + + LorentzRotation output; + + bool spacelike = false; + // If dealing with IF or FI, use Breit frame + if ( theDipoleConfig.first != theDipoleConfig.second ) + spacelike = true; + + + // Construct the Lorentz tranformation as in getKt + // see DipoleSplittingKinematics::getKt for more details + Lorentz5Momentum P; + if ( !spacelike ) + P = thePVector + theNVector; + else + P = thePVector - theNVector; + + Energy mag = sqrt(abs(P.m2())); + + Lorentz5Momentum Q = + !spacelike ? + Lorentz5Momentum(ZERO,ZERO,ZERO,mag,mag) : + Lorentz5Momentum(ZERO,ZERO,mag,ZERO,-mag); + + // Required to make sure gamma (the boost parameter) is positive: + if ( spacelike && P.z() < ZERO ) + Q.setZ(-Q.z()); + + Energy2 Q2 = Q.m2(); + bool boost = + abs((P-Q).vect().mag2()/GeV2) > 1e-10 || + abs((P-Q).t()/GeV) > 1e-5; + + if ( boost ) { + + // Separately construct the components of the + // analytic expression in getKt + Lorentz5Momentum PQ = P+Q; + Energy PQt=PQ.t(), Qt=Q.t(); + + InvEnergy2 den1 = 1./(P*Q + Q2); + InvEnergy2 den2 = 2./Q2; + + double tt = 1. - den1*PQt*PQt + den2*Qt*P.t(); + double tx = den1*PQt*PQ.x() - den2*Qt*P.x(); + double ty = den1*PQt*PQ.y() - den2*Qt*P.y(); + double tz = den1*PQt*PQ.z() - den2*Qt*P.z(); + + // Construct the boost + output.boost(tx/tt, ty/tt, tz/tt, tt); + + // In the spacelike case, i.e. Breit frame, the transform + // also involves a rotation + if ( spacelike ) { + Axis axis((output*P).vect().unit()); + if ( axis.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axis.z()))); + if ( Q.z() > ZERO ) + output.rotate(-acos(axis.z()),Axis(-axis.y()/sinth,axis.x()/sinth,0.)); + else + output.rotate(Constants::pi-acos(axis.z()),Axis(-axis.y()/sinth,axis.x()/sinth,0.)); + } + } + } + + + // Now deal with the rotation (timelike) or boost (spacelike) + // to the z-axis + Lorentz5Momentum pTrans = output*thePVector; + + // Timelike : Rotate so the pVector lies along the (+ve) z-axis + if ( !spacelike ) { + Axis axis(pTrans.vect().unit()); + if( axis.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axis.z()))); + output.rotate(-acos(axis.z()),Axis(-axis.y()/sinth,axis.x()/sinth,0.)); + } + else if( axis.z() < 0. ) { + output.rotate(Constants::pi,Axis(1.,0.,0.)); + } + } + + // Spacelike : Boost in x and y so the pVector lies along the (+ve/-ve) z-axis + else { + Boost transX = -ThreeVector(pTrans.x()/pTrans.e(),0.,0.); + output.boost(transX); + pTrans.boost(transX); + Boost transY = -ThreeVector(0.,pTrans.y()/pTrans.e(),0.); + output.boost(transY); + pTrans.boost(transY); + + if ( pTrans.z() < ZERO ) + output.rotate(Constants::pi,Axis(1.,0.,0.)); + } + + theBoostToSplitting = output; + theBoostCalculated = true; + } + return theBoostToSplitting; +} diff --git a/Shower/Dipole/SpinCorrelations/DipoleShowerVertex.fh b/Shower/Dipole/SpinCorrelations/DipoleShowerVertex.fh new file mode 100644 --- /dev/null +++ b/Shower/Dipole/SpinCorrelations/DipoleShowerVertex.fh @@ -0,0 +1,18 @@ +// -*- C++ -*- +// +// This is the forward declaration of the DipoleShowerVertex class. +// +#ifndef HERWIG_DipoleShowerVertex_FH +#define HERWIG_DipoleShowerVertex_FH + +#include "ThePEG/Config/ThePEG.h" +#include "ThePEG/Config/Pointers.h" + +namespace Herwig { + using namespace ThePEG; + + class DipoleShowerVertex; + ThePEG_DECLARE_POINTERS(DipoleShowerVertex, DSVertexPtr); +} + +#endif // HERWIG_DipoleShowerVertex_FH diff --git a/Shower/Dipole/SpinCorrelations/DipoleShowerVertex.h b/Shower/Dipole/SpinCorrelations/DipoleShowerVertex.h new file mode 100644 --- /dev/null +++ b/Shower/Dipole/SpinCorrelations/DipoleShowerVertex.h @@ -0,0 +1,285 @@ +// -*- C++ -*- +// +// DipoleShowerVertex.h is a part of Herwig - A multi-purpose Monte Carlo event generator +// Copyright (C) 2002-2007 The Herwig Collaboration +// +// Herwig is licenced under version 2 of the GPL, see COPYING for details. +// Please respect the MCnet academic guidelines, see GUIDELINES for details. +// +#ifndef HERWIG_DipoleShowerVertex_H +#define HERWIG_DipoleShowerVertex_H +// +// This is the declaration of the DipoleShowerVertex class. +// + + +#include "ThePEG/EventRecord/HelicityVertex.h" +#include "Herwig/Decay/DecayMatrixElement.h" + +#include "DipoleShowerVertex.fh" + +namespace Herwig { + + using namespace ThePEG; + + /** \ingroup DipoleShower + * + * This class represents the vertex for a given splitting + * in the dipole shower. + * + * \author Stephen Webster + * + */ + class DipoleShowerVertex: public HelicityVertex { + + public: + + /** + * Default constructor + */ + DipoleShowerVertex(); + + /** + * Default destructor + **/ + ~DipoleShowerVertex() {} + + public: + + /** + * Return the matrix element for this vertex. + */ + inline const DecayMEPtr ME() const { + return theMatrixElement; + } + + /** + * Set the matrix element + */ + inline void ME(DecayMEPtr in) { + theMatrixElement = in; + } + + + public: + + /** + * Method to calculate the \f$\rho\f$ matrix for one of the decay products + * in the frame of this splitting vertex. + * + * @param iprod The splitting product to compute the \f$\rho\f$ matrix for. + */ + RhoDMatrix getRhoMatrix(int iprod, bool ) const; + + /** + * Method to calculate the \f$D\f$ matrix for the decaying + * particle / the incoming to the vertex, in the frame of + * the vertex. The argument is a dummy argument. + */ + RhoDMatrix getDMatrix(int) const; + + /** + * Get the lorentz rotation from the working frame + * to the frame of the splitting. + */ + LorentzRotation boostToSplitting(); + + /** + * Set the p vector for this splitting + */ + void pVector( const Lorentz5Momentum& emitterMom ) { thePVector = emitterMom; } + + /** + * Set the n vector for this splitting + */ + void nVector( const Lorentz5Momentum& nMom ) { theNVector = nMom; } + + /** + * Set the emitter,Spectator Config (II,IF,FF,FI - F=true, I=false) + */ + void dipoleConfig(const pair& newConfig) { theDipoleConfig = newConfig; } + + /** + * Return the p vector for this splitting + */ + Lorentz5Momentum pVector() const { return thePVector; } + + /** + * Return the n/spectator vector for this splitting + */ + Lorentz5Momentum nVector() const { return theNVector; } + + /** + * Return the emitter,Spectator Config (II,IF,FF,FI - F=true, I=false) + */ + const pair& dipoleConfig() const { return theDipoleConfig; } + + + /** + * Set the decay state to production state + * mapping for this vertex. + */ + void mappingD2P( RhoDMatrix& mapping ) { theMappingDecay2Prod = mapping; } + + /** + * Return the mapping from the decay + * states to the production states. + */ + RhoDMatrix mappingD2P() { return theMappingDecay2Prod; } + + /** + * Set the production state to decay state + * mapping for this vertex. + */ + void mappingP2D( RhoDMatrix& mapping ) { theMappingProd2Decay = mapping; } + + /** + * Return the mapping from the production + * states to the decay states. + */ + RhoDMatrix mappingP2D() { return theMappingProd2Decay; } + + + /** + * Set the new to old spectator mapping + * for this vertex. + */ + void mappingSpecNewToOld( RhoDMatrix& mapping ) { theMappingSpectatorNewToOld = mapping; } + + /** + * Return the new to old spectator mapping + * for this vertex. + */ + RhoDMatrix mappingSpecNewToOld() { return theMappingSpectatorNewToOld; } + + /** + * Set the new to old spectator mapping + * for this vertex. + */ + void mappingSpecOldToNew( RhoDMatrix& mapping ) { theMappingSpectatorOldToNew = mapping; } + + /** + * Return the new to old spectator mapping + * for this vertex. + */ + RhoDMatrix mappingSpecOldToNew() { return theMappingSpectatorOldToNew; } + + + + public: + + /** + * 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(); + + private: + + /** + * Storage of the decay matrix element. + */ + DecayMEPtr theMatrixElement; + + /** + * The p vector of the 'splitting basis' + * associated with this vertex. + **/ + Lorentz5Momentum thePVector; + + /** + * The n vector of the 'splitting basis' + * associated with this vertex. + **/ + Lorentz5Momentum theNVector; + + /** + * Initial/final config {emitter, spectator} + */ + pair theDipoleConfig; + + /** + * An indicator flag to record if the + * boost to shower for this vertex has been done + */ + bool theBoostCalculated; + + /** + * The lorentz transformation from the + * working frame to this splitting. + */ + LorentzRotation theBoostToSplitting; + + /** + * The mapping from the decay basis states + * to the production basis states. + */ + RhoDMatrix theMappingDecay2Prod; + + /** + * The mapping from the production basis states + * to the decay basis states. + */ + RhoDMatrix theMappingProd2Decay; + + + /** + * The mapping from the new spectator basis + * states to the old spectator basis states. + */ + RhoDMatrix theMappingSpectatorNewToOld; + + /** + * The mapping from the old spectator basis + * states to the new spectator basis states. + */ + RhoDMatrix theMappingSpectatorOldToNew; + + private: + + /** + * The assignment operator is private and must never be called. + * In fact, it should not even be implemented. + */ + DipoleShowerVertex & operator=(const DipoleShowerVertex &); + + }; +} + +#include "ThePEG/Utilities/ClassTraits.h" + +namespace ThePEG { + + /** @cond TRAITSPECIALIZATIONS */ + + /** This template specialization informs ThePEG about the + * base classes of DipoleShowerVertex. */ + template <> + struct BaseClassTrait { + /** Typedef of the first base class of DipoleShowerVertex. */ + typedef HelicityVertex NthBase; + }; + + /** This template specialization informs ThePEG about the name of + * the DipoleShowerVertex class and the shared object where it is defined. */ + template <> + struct ClassTraits + : public ClassTraitsBase { + /** Return a platform-independent class name */ + static string className() { return "Herwig::DipoleShowerVertex"; } + /** + * The name of a file containing the dynamic library where the class + * DipoleShowerVertex is implemented. It may also include several, space-separated, + * libraries if the class DipoleShowerVertex depends on other classes (base classes + * excepted). In this case the listed libraries will be dynamically + * linked in the order they are specified. + */ + static string library() { return "HwDipoleShower.so"; } + }; + + /** @endcond */ + +} +#endif /* HERWIG_DipoleShowerVertex_H */ diff --git a/Shower/Dipole/SpinCorrelations/DipoleVertexRecord.cc b/Shower/Dipole/SpinCorrelations/DipoleVertexRecord.cc new file mode 100644 --- /dev/null +++ b/Shower/Dipole/SpinCorrelations/DipoleVertexRecord.cc @@ -0,0 +1,414 @@ +// -*- C++ -*- +// +// DipoleVertexRecord.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 2 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 DipoleVertexRecord class. +// + +#include "DipoleVertexRecord.h" + +#include "ThePEG/Interface/ClassDocumentation.h" +#include "ThePEG/EventRecord/Particle.h" +#include "ThePEG/Repository/UseRandom.h" + +#include "ThePEG/Repository/EventGenerator.h" +#include "ThePEG/Utilities/DescribeClass.h" + +#include "ThePEG/Helicity/FermionSpinInfo.h" +#include "ThePEG/Helicity/VectorSpinInfo.h" + +#include "ThePEG/Helicity/WaveFunction/SpinorWaveFunction.h" +#include "ThePEG/Helicity/WaveFunction/VectorWaveFunction.h" + +#include "ThePEG/EventRecord/HelicityVertex.h" + + +using namespace Herwig; + +void DipoleVertexRecord::clear() { + // Clear all member variables + theCurrentEmitter.clear(); + //theEmitterInfoRecord.clear(); + theDecayParentSpinInfo = SpinPtr(); +} + + +void DipoleVertexRecord::generatePhi(DipoleSplittingInfo& dInfo, Dipole& dip) { + + // Set up the emitter spin info and its decay vertex + prepareSplitting(dInfo, dip); + + // Compute the rho matrix (outgoing emitter) or decay matrix + // (incoming emitter) required to generate phi + PPtr emitter = dip.emitter(dInfo.configuration()); + RhoDMatrix rho = emitterDensityMatrix(emitter); + + // Compute the weights from the kernel + // Pair components A (int) and B (complex): + // weight = B*exp(i*A) + vector< pair > wgts; + wgts = dInfo.splittingKernel()->generatePhi(dInfo,rho); + + // Generate a value of phi + unsigned int nTry = 0; + double phi = 0.0; + double phiMax = 0.0; + double wgt = 0.0; + double wgtMax = 0.0; + static const Complex ii(0.,1.); + + do { + phi = Constants::twopi*UseRandom::rnd(); + + Complex spinWgt = 0.; + for(unsigned int ix=0;ixwgtMax ) { + phiMax = phi; + wgtMax = wgt; + } + nTry++; + } + + // Accept / reject phi + while (wgtME(dInfo.splittingKernel()->matrixElement(dInfo)); +} + + +void DipoleVertexRecord::prepareSplitting(const DipoleSplittingInfo& dInfo, const Dipole& dip ) { + + // Extract the required information from the splitting info and dipole + PPtr emitter = dip.emitter(dInfo.configuration()); + PPtr spectator = dip.spectator(dInfo.configuration()); + + // Get the pVector and nVector that define the decay frame + Lorentz5Momentum pVector = dInfo.splittingKinematics()-> + pVector(emitter->momentum(), spectator->momentum(), dInfo); + Lorentz5Momentum nVector = dInfo.splittingKinematics()-> + nVector(emitter->momentum(), spectator->momentum(), dInfo); + + // Get the emitter and spectator directions + Helicity::Direction emmDir = dInfo.index().initialStateEmitter() ? incoming : outgoing; + Helicity::Direction specDir = dInfo.index().initialStateSpectator() ? incoming : outgoing; + + // Create spinInfo if required (e.g. secondary processes) + if ( !emitter->spinInfo() ) + createSpinInfo(emitter, emmDir); + if ( !spectator->spinInfo() ) + createSpinInfo(spectator, specDir); + + // Setup the emitter for spin correlation calculations + theCurrentEmitter.prepare(emitter, emmDir, specDir, pVector, nVector); +} + + +RhoDMatrix DipoleVertexRecord::emitterDensityMatrix(PPtr emitter) { + + // Update the rho/decay matrices upto the emitter + emitter->spinInfo()->decay(true); + + RhoDMatrix rho = emitter->spinInfo()->timelike() ? + emitter->spinInfo()->rhoMatrix() : emitter->spinInfo()->DMatrix(); + + // Map the rho/decay matrix to the decay frame + RhoDMatrix mapping = theCurrentEmitter.decayVertex()->mappingD2P(); + RhoDMatrix rhop(rho.iSpin(),false); + if ( emitter->spinInfo()->timelike() ) { + for(int ixa=0;ixaspinInfo() && oldSpectator->spinInfo()); + assert(!newEmitter->spinInfo() && !newSpectator->spinInfo() && !emission->spinInfo() ); + + // Create new emitter splitting info + Helicity::Direction emmDir = dInfo.index().initialStateEmitter() ? incoming : outgoing; + if ( abs(newEmitter->id()) <= 6 ) + theCurrentEmitter.createNewFermionSpinInfo(newEmitter, emmDir); + else { + assert( newEmitter->id() == 21 ); + theCurrentEmitter.createNewVectorSpinInfo(newEmitter, emmDir); + } + + // Create new emission splitting info + if ( abs(emission->id()) <= 6 ) + theCurrentEmitter.createNewFermionSpinInfo(emission, outgoing); + else { + assert( emission->id() == 21 ); + theCurrentEmitter.createNewVectorSpinInfo(emission, outgoing); + } + + // Initialise the emitter and emission decay matrices to delta matrices + initDecayMatrix(newEmitter,emmDir); + initDecayMatrix(emission, outgoing); + + // Set the outgoing of the decay vertex + newEmitter->spinInfo()->productionVertex(theCurrentEmitter.decayVertex()); + emission->spinInfo()->productionVertex(theCurrentEmitter.decayVertex()); + + // Develop the emitter + oldEmitter->spinInfo()->needsUpdate(); + oldEmitter->spinInfo()->develop(); + + + // Deal with spectators: + if ( !dInfo.index().incomingDecaySpectator() ) + updateSpinInfo(oldSpectator, newSpectator); + + // If the spectator is a decayed particle, don't want to do any transformations + else + newSpectator->spinInfo(oldSpectator->spinInfo()); + + + // Tidy up + theCurrentEmitter.clear(); +} + + +void DipoleVertexRecord::createSpinInfo(PPtr& part, + const Helicity::Direction& dir) { + + // Identify the type of particle and use the appropriate function + // to create the spinInfo + if ( part->dataPtr()->iSpin() == PDT::Spin0 ) + assert(false); + + else if ( part->dataPtr()->iSpin() == PDT::Spin1Half ) + createFermionSpinInfo(part, dir); + + else if ( part->dataPtr()->iSpin() == PDT::Spin1 ) + createVectorSpinInfo(part, dir); + + else + assert(false); +} + + +void DipoleVertexRecord::createFermionSpinInfo(PPtr& part, + const Helicity::Direction& dir) { + + // Create the spin info + const Lorentz5Momentum& partMom = part->momentum(); + FermionSpinPtr fspin = new_ptr(FermionSpinInfo(partMom, dir==outgoing)); + part->spinInfo(fspin); + + // Calculate the basis for the particle in the lab frame + SpinorWaveFunction wave; + if(part->id()>0) + wave=SpinorWaveFunction(partMom, part->dataPtr(), incoming); + else + wave=SpinorWaveFunction(partMom, part->dataPtr(), outgoing); + + // Store the basis states in the spin info + for(unsigned int ix=0;ix<2;++ix) { + wave.reset(ix); + LorentzSpinor basis = wave.dimensionedWave(); + fspin->setBasisState(ix,basis); + } +} + + +void DipoleVertexRecord::createVectorSpinInfo(PPtr& part, + const Helicity::Direction& dir) { + + // Create the spin info + const Lorentz5Momentum& partMom = part->momentum(); + VectorSpinPtr vspin = new_ptr(VectorSpinInfo(partMom, dir==outgoing)); + part->spinInfo(vspin); + + // Calculate the basis for the particle in the lab frame + VectorWaveFunction wave(partMom, part->dataPtr(), + vspin->timelike() ? outgoing : incoming ); + bool massless(part->id()==ParticleID::g||part->id()==ParticleID::gamma); + for(unsigned int ix=0;ix<3;++ix) { + LorentzPolarizationVector basis; + if(massless&&ix==1) { + basis = LorentzPolarizationVector(); + } + else { + wave.reset(ix,vector_phase); + basis = wave.wave(); + } + + // Store the basis states in the spin info + vspin->setBasisState(ix,basis); + } +} + + +void DipoleVertexRecord::updateSpinInfo( PPtr& oldPart, + PPtr& newPart ) { + + // Copied from DipoleVertexRecord::updateSpinInfo, + // would be better to use a common function + const Lorentz5Momentum& oldMom = oldPart->momentum(); + const Lorentz5Momentum& newMom = newPart->momentum(); + + // Rotation from old momentum to +ve z-axis + LorentzRotation oldToZAxis; + Axis axisOld(oldMom.vect().unit()); + if( axisOld.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axisOld.z()))); + oldToZAxis.rotate( -acos(axisOld.z()),Axis(-axisOld.y()/sinth,axisOld.x()/sinth,0.)); + } + + // Rotation from new momentum to +ve z-axis + LorentzRotation newToZAxis; + Axis axisNew(newMom.vect().unit()); + if( axisNew.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axisNew.z()))); + newToZAxis.rotate( -acos(axisNew.z()),Axis(-axisNew.y()/sinth,axisNew.x()/sinth,0.)); + } + + // Boost from old momentum to new momentum along z-axis + Lorentz5Momentum momOldRotated = oldToZAxis*Lorentz5Momentum(oldMom); + Lorentz5Momentum momNewRotated = newToZAxis*Lorentz5Momentum(newMom); + + Energy2 a = sqr(momOldRotated.z()) + sqr(momNewRotated.t()); + Energy2 b = 2.*momOldRotated.t()*momOldRotated.z(); + Energy2 c = sqr(momOldRotated.t()) - sqr(momNewRotated.t()); + double beta; + + // The rotated momentum should always lie along the +ve z-axis + if ( momOldRotated.z() > ZERO ) + beta = (-b + sqrt(sqr(b)-4.*a*c)) / 2. / a; + else + beta = (-b - sqrt(sqr(b)-4.*a*c)) / 2. / a; + + LorentzRotation boostOldToNew(0., 0., beta); + + // Total transform + LorentzRotation transform = (newToZAxis.inverse())*boostOldToNew*oldToZAxis; + + // Assign the same spin info to the old and new particles + newPart->spinInfo(oldPart->spinInfo()); + newPart->spinInfo()->transform(oldMom, transform); +} + + +void DipoleVertexRecord::prepareParticleDecay( const PPtr& decayIncoming ) { + + // Need to set stopUpdate flag in the latest parent with spinInfo + PPtr parent = decayIncoming; + while ( !parent->spinInfo() ) + parent = parent->parents()[0]; + parent->spinInfo()->stopUpdate(); + theDecayParentSpinInfo = parent->spinInfo(); +} + + +void DipoleVertexRecord::updateParticleDecay() { + theDecayParentSpinInfo->needsUpdate(); + theDecayParentSpinInfo->develop(); + // Clear theDecayParentSpinInfo + theDecayParentSpinInfo = SpinPtr(); +} + + +// Note: The develop function in SpinInfo.cc does not handle this properly +void DipoleVertexRecord::initDecayMatrix( PPtr& particle, Helicity::Direction dir ) { + + // If not a vector boson, no extra considerations + if ( particle->dataPtr()->iSpin() != PDT::Spin1 ) + particle->spinInfo()->develop(); + + // If particle is a vector boson + else { + // Massless is a special case: + if ( particle->id() == ParticleID::g || particle->id() == ParticleID::gamma ) { + if ( dir == outgoing ) { + particle->spinInfo()->DMatrix()(0,0) = 0.5; + particle->spinInfo()->DMatrix()(2,2) = 0.5; + } + else { + particle->spinInfo()->rhoMatrix()(0,0) = 0.5; + particle->spinInfo()->rhoMatrix()(2,2) = 0.5; + + } + } + + // Massive case is the default + else + particle->spinInfo()->develop(); + } + +} + + +// *** Attention *** The following static variable is needed for the type +// description in ThePEG. Please check that the template arguments +// are correct (the class and its base class), and that the constructor +// arguments are correct (the class name and the name of the dynamically +// loadable library where the class implementation can be found). + +DescribeNoPIOClass +describeHerwigDipoleVertexRecord("Herwig::DipoleVertexRecord", "DipoleVertexRecord.so"); + +void DipoleVertexRecord::Init() { + + + // *** Attention *** The following static variable is needed for the type + // description in ThePEG. Please check that the template arguments + // are correct (the class and its base class), and that the constructor + // arguments are correct (the class name and the name of the dynamically + // loadable library where the class implementation can be found). + + static ClassDocumentation documentation + ("There is no documentation for the DipoleVertexRecord class"); + +} diff --git a/Shower/Dipole/SpinCorrelations/DipoleVertexRecord.h b/Shower/Dipole/SpinCorrelations/DipoleVertexRecord.h new file mode 100644 --- /dev/null +++ b/Shower/Dipole/SpinCorrelations/DipoleVertexRecord.h @@ -0,0 +1,207 @@ +// -*- C++ -*- +#ifndef Herwig_DipoleVertexRecord_H +#define Herwig_DipoleVertexRecord_H +// +// This is the declaration of the DipoleVertexRecord class. +// + +#include "ThePEG/Config/ThePEG.h" +#include "Herwig/Shower/Dipole/Base/DipoleSplittingInfo.h" +#include "Herwig/Shower/Dipole/Base/Dipole.h" +#include "ThePEG/EventRecord/RhoDMatrix.h" +#include "DipoleShowerParticle.h" + +namespace Herwig { + + using namespace ThePEG; + + /** + * Here is the documentation of the DipoleVertexRecord class. + */ + class DipoleVertexRecord: public Base { + + public: + + /** @name Standard constructors and destructors. */ + //@{ + /** + * The default constructor. + */ + DipoleVertexRecord() {} + + /** + * The destructor. + */ + virtual ~DipoleVertexRecord() { clear(); } + //@} + + public: + + /** + * Prepare the emitter and spectator + * for the spin correlations computations. + **/ + void prepareSplitting( const DipoleSplittingInfo& dInfo, const Dipole& dip); + + /** + * Correctly initialise the decay matrix + * to a delta matrix for an external particle. + */ + void initDecayMatrix(PPtr& particle, Helicity::Direction dir); + + /** + * Compute the spin density matrix for the given emitter. + * This tracks the path between the given emitter and + * the previous emitter, calculating a rho/decay matrix + * at each vertex as appropriate. + */ + RhoDMatrix emitterDensityMatrix(PPtr emitter); + + /** + * Generate the spin-correlated azimuthal angle for a splitting. + */ + void generatePhi(DipoleSplittingInfo& dInfo, Dipole& dip); + + + /** + * Identify the type of particle and use the appropriate function + * to set up the spin info. + * Required for e.g. MPI + */ + void createSpinInfo(PPtr& part, + const Helicity::Direction& dir); + + /** + * Create and set up fermion spin info. + * Required for e.g. MPI + */ + void createFermionSpinInfo(PPtr& part, + const Helicity::Direction& dir); + + /** + * Create and set up vector spin info. + * Required for e.g. MPI + */ + void createVectorSpinInfo(PPtr& part, + const Helicity::Direction& dir); + + /** + * Update the vertex record following a splitting. + */ + void update(const DipoleSplittingInfo& dInfo); + + /** + * For spectators. Set new particle spin info the that of the + * old particle. Update the spin info to include any momentum changes. + */ + void updateSpinInfo( PPtr& oldPart, + PPtr& newPart ); + + /** + * Set the stopUpdate flag in the spin info of a particle + * incoming to the current decay. + */ + void prepareParticleDecay( const PPtr& parent ); + + /** + * Update the spin info of the incoming to the decay + * following showering of the decay. + */ + void updateParticleDecay(); + + /** + * SW 06/02/2019: Required for NearestNeighbourDipoleAnalysis tests. + * Access the emitter info record. + */ + //map emitterInfoRecord() const { + //return theEmitterInfoRecord; + //} + + /** + * SW 06/02/2019: Required for NearestNeighbourDipoleAnalysis tests. + * Add a splitting to the emitter info record. + */ + // void addToRecord(const DipoleSplittingInfo& dInfo) { + // assert(dInfo.emitter()); + // theEmitterInfoRecord[dInfo.emitter()] = dInfo; + // } + + /** + * Clear the vertex record: Give up ownership + * on any object involved in the evolution. + */ + virtual void clear(); + + /** + * 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(); + + private: + + /** + * The current emitter. + */ + DipoleShowerParticle theCurrentEmitter; + + /** + * SW 06/02/2019: Required for NearestNeighbourDipoleAnalysis tests. + * Record of the splittings as + * required for the testing analysis. + */ + //map theEmitterInfoRecord; + + /** + * The spin info of a particle incoming to the decay + * under consideration. + */ + tcSpinPtr theDecayParentSpinInfo; + + /** + * The assignment operator is private and must never be called. + * In fact, it should not even be implemented. + */ + DipoleVertexRecord & operator=(const DipoleVertexRecord &); + + }; + +} + +#include "ThePEG/Utilities/ClassTraits.h" + +namespace ThePEG { + + /** @cond TRAITSPECIALIZATIONS */ + + /** This template specialization informs ThePEG about the + * base classes of DipoleVertexRecord. */ + template <> + struct BaseClassTrait { + /** Typedef of the first base class of DipoleVertexRecord. */ + typedef Base NthBase; + }; + + /** This template specialization informs ThePEG about the name of + * the DipoleVertexRecord class and the shared object where it is defined. */ + template <> + struct ClassTraits + : public ClassTraitsBase { + /** Return a platform-independent class name */ + static string className() { return "Herwig::DipoleVertexRecord"; } + /** + * The name of a file containing the dynamic library where the class + * DipoleVertexRecord is implemented. It may also include several, space-separated, + * libraries if the class DipoleVertexRecord depends on other classes (base classes + * excepted). In this case the listed libraries will be dynamically + * linked in the order they are specified. + */ + static string library() { return "HwDipoleShower.so"; } + }; + + /** @endcond */ + +} +#endif /* Herwig_DipoleVertexRecord_H */ diff --git a/Shower/Dipole/SpinCorrelations/Makefile.am b/Shower/Dipole/SpinCorrelations/Makefile.am new file mode 100644 --- /dev/null +++ b/Shower/Dipole/SpinCorrelations/Makefile.am @@ -0,0 +1,6 @@ +noinst_LTLIBRARIES = libHwDipoleShowerSpinCorrelations.la + +libHwDipoleShowerSpinCorrelations_la_SOURCES = \ + DipoleShowerVertex.fh DipoleShowerVertex.h DipoleShowerVertex.cc \ + DipoleShowerParticle.h DipoleShowerParticle.cc \ + DipoleVertexRecord.h DipoleVertexRecord.cc diff --git a/Shower/Dipole/Utility/ConstituentReshuffler.cc b/Shower/Dipole/Utility/ConstituentReshuffler.cc --- a/Shower/Dipole/Utility/ConstituentReshuffler.cc +++ b/Shower/Dipole/Utility/ConstituentReshuffler.cc @@ -1,602 +1,659 @@ // -*- C++ -*- // // ConstituentReshuffler.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. // // // This is the implementation of the non-inlined, non-templated member // functions of the ConstituentReshuffler class. // #include #include "ConstituentReshuffler.h" #include "ThePEG/Interface/ClassDocumentation.h" #include #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "DipolePartonSplitter.h" #include "Herwig/Utilities/GSLBisection.h" #include "Herwig/Shower/Dipole/DipoleShowerHandler.h" #include "Herwig/Shower/ShowerHandler.h" using namespace Herwig; ConstituentReshuffler::ConstituentReshuffler() : HandlerBase() {} ConstituentReshuffler::~ConstituentReshuffler() {} IBPtr ConstituentReshuffler::clone() const { return new_ptr(*this); } IBPtr ConstituentReshuffler::fullclone() const { return new_ptr(*this); } double ConstituentReshuffler::ReshuffleEquation::aUnit() { return 1.; } double ConstituentReshuffler::ReshuffleEquation::vUnit() { return 1.; } double ConstituentReshuffler::DecayReshuffleEquation::aUnit() { return 1.; } double ConstituentReshuffler::DecayReshuffleEquation::vUnit() { return 1.; } double ConstituentReshuffler::ReshuffleEquation::operator() (double xi) const { double r = - w/GeV; for (PList::iterator p = p_begin; p != p_end; ++p) { r += sqrt(sqr((**p).dataPtr()->constituentMass()) + xi*xi*(sqr((**p).momentum().t())-sqr((**p).dataPtr()->mass()))) / GeV; } return r; } double ConstituentReshuffler::DecayReshuffleEquation::operator() (double xi) const { double r = - w/GeV; for (PList::iterator pIt = p_begin; pIt != p_end; ++pIt) { r += sqrt(sqr((**pIt).dataPtr()->constituentMass()) + xi*xi*(sqr((**pIt).momentum().t())-sqr((**pIt).dataPtr()->mass()))) / GeV; } for (PList::iterator rIt = r_begin; rIt != r_end; ++rIt) { r += sqrt(sqr((**rIt).momentum().m()) + xi*xi*(sqr((**rIt).momentum().t())-sqr((**rIt).momentum().m()))) / GeV; } return r; } void ConstituentReshuffler::reshuffle(PList& out, PPair& in, PList& intermediates, const bool decay, PList& decayPartons, PList& decayRecoilers) { assert(ShowerHandler::currentHandler()->retConstituentMasses()); if ( !decay ) { if (out.size() == 0) return; if (out.size() == 1) { PPtr recoiler; PPtr parton = out.front(); if (DipolePartonSplitter::colourConnected(parton,in.first) && DipolePartonSplitter::colourConnected(parton,in.second)) { if (UseRandom::rnd() < .5) recoiler = in.first; else recoiler = in.second; } else if (DipolePartonSplitter::colourConnected(parton,in.first)) { recoiler = in.first; } else if (DipolePartonSplitter::colourConnected(parton,in.second)) { recoiler = in.second; } else assert(false); assert(abs(recoiler->momentum().vect().perp2()/GeV2) < 1e-6); double sign = recoiler->momentum().z() < 0.*GeV ? -1. : 1.; Energy2 qperp2 = parton->momentum().perp2(); if (qperp2/GeV2 < Constants::epsilon) { // no emission off a 2 -> singlet process which // needed a single forced splitting: should never happen (?) assert(false); throw Veto(); } Energy2 m2 = sqr(parton->dataPtr()->constituentMass()); Energy abs_q = parton->momentum().vect().mag(); Energy qz = parton->momentum().z(); Energy abs_pz = recoiler->momentum().t(); assert(abs_pz > 0.*GeV); Energy xi_pz = sign*(2.*qperp2*abs_pz + m2*(abs_q + sign*qz))/(2.*qperp2); Energy x_qz = (2.*qperp2*qz + m2*(qz+sign*abs_q))/(2.*qperp2); Lorentz5Momentum recoiler_momentum (0.*GeV,0.*GeV,xi_pz,xi_pz < 0.*GeV ? - xi_pz : xi_pz); recoiler_momentum.rescaleMass(); Lorentz5Momentum parton_momentum (parton->momentum().x(),parton->momentum().y(),x_qz,sqrt(m2+qperp2+x_qz*x_qz)); parton_momentum.rescaleMass(); PPtr n_parton = new_ptr(Particle(parton->dataPtr())); n_parton->set5Momentum(parton_momentum); DipolePartonSplitter::change(parton,n_parton,false); out.pop_front(); intermediates.push_back(parton); out.push_back(n_parton); PPtr n_recoiler = new_ptr(Particle(recoiler->dataPtr())); n_recoiler->set5Momentum(recoiler_momentum); DipolePartonSplitter::change(recoiler,n_recoiler,true); intermediates.push_back(recoiler); if (recoiler == in.first) { in.first = n_recoiler; } if (recoiler == in.second) { in.second = n_recoiler; } return; } } Energy zero (0.*GeV); Lorentz5Momentum Q (zero,zero,zero,zero); for (PList::iterator p = out.begin(); p != out.end(); ++p) { Q += (**p).momentum(); } Boost beta = Q.findBoostToCM(); list mbackup; bool need_boost = (beta.mag2() > Constants::epsilon); if (need_boost) { for (PList::iterator p = out.begin(); p != out.end(); ++p) { Lorentz5Momentum mom = (**p).momentum(); mbackup.push_back(mom); (**p).set5Momentum(mom.boost(beta)); } } double xi; // Only partons if ( decayRecoilers.size()==0 ) { ReshuffleEquation solve (Q.m(),out.begin(),out.end()); GSLBisection solver(1e-10,1e-8,10000); try { xi = solver.value(solve,0.0,1.1); } catch (GSLBisection::GSLerror) { throw DipoleShowerHandler::RedoShower(); } catch (GSLBisection::IntervalError) { throw DipoleShowerHandler::RedoShower(); } } // Partons and decaying recoilers else { DecayReshuffleEquation solve (Q.m(),decayPartons.begin(),decayPartons.end(),decayRecoilers.begin(),decayRecoilers.end()); GSLBisection solver(1e-10,1e-8,10000); try { xi = solver.value(solve,0.0,1.1); } catch (GSLBisection::GSLerror) { throw DipoleShowerHandler::RedoShower(); } catch (GSLBisection::IntervalError) { throw DipoleShowerHandler::RedoShower(); } } PList reshuffled; list::const_iterator backup_it; if (need_boost) backup_it = mbackup.begin(); // Reshuffling of non-decaying partons only if ( decayRecoilers.size()==0 ) { for (PList::iterator p = out.begin(); p != out.end(); ++p) { PPtr rp = new_ptr(Particle((**p).dataPtr())); DipolePartonSplitter::change(*p,rp,false); Lorentz5Momentum rm; rm = Lorentz5Momentum (xi*(**p).momentum().x(), xi*(**p).momentum().y(), xi*(**p).momentum().z(), sqrt(sqr((**p).dataPtr()->constituentMass()) + xi*xi*(sqr((**p).momentum().t())-sqr((**p).dataPtr()->mass())))); rm.rescaleMass(); if (need_boost) { (**p).set5Momentum(*backup_it); ++backup_it; rm.boost(-beta); } rp->set5Momentum(rm); intermediates.push_back(*p); reshuffled.push_back(rp); } } // For the case of a decay process with non-partonic recoilers else { assert ( decay ); for (PList::iterator p = out.begin(); p != out.end(); ++p) { + // Flag to update spinInfo + bool updateSpin = false; + PPtr rp = new_ptr(Particle((**p).dataPtr())); DipolePartonSplitter::change(*p,rp,false); Lorentz5Momentum rm; // If the particle is a parton and not a recoiler if ( find( decayRecoilers.begin(), decayRecoilers.end(), *p ) == decayRecoilers.end() ) { rm = Lorentz5Momentum (xi*(**p).momentum().x(), xi*(**p).momentum().y(), xi*(**p).momentum().z(), sqrt(sqr((**p).dataPtr()->constituentMass()) + xi*xi*(sqr((**p).momentum().t())-sqr((**p).dataPtr()->mass())))); } // Otherwise the parton is a recoiler // and its invariant mass must be preserved else { + if ( (*p)-> spinInfo() ) + updateSpin = true; rm = Lorentz5Momentum (xi*(**p).momentum().x(), xi*(**p).momentum().y(), xi*(**p).momentum().z(), sqrt(sqr((**p).momentum().m()) + xi*xi*(sqr((**p).momentum().t())-sqr((**p).momentum().m())))); } rm.rescaleMass(); if (need_boost) { (**p).set5Momentum(*backup_it); ++backup_it; rm.boost(-beta); } rp->set5Momentum(rm); + // Update SpinInfo if required + if ( updateSpin ) + updateSpinInfo(*p, rp); + intermediates.push_back(*p); reshuffled.push_back(rp); } } out.clear(); out.splice(out.end(),reshuffled); } void ConstituentReshuffler::hardProcDecayReshuffle(PList& decaying, PList& eventOutgoing, PList& eventHard, PPair& eventIncoming, PList& eventIntermediates) { // Note, when this function is called, the particle pointers // in theDecays/decaying are those prior to the showering. // Here we find the newest pointers in the outgoing. // The update of the PPtrs in theDecays is done in DipoleShowerHandler::constituentReshuffle() // as this needs to be done if ConstituentReshuffling is switched off. //Make sure the shower should return constituent masses: assert(ShowerHandler::currentHandler()->retConstituentMasses()); // Find the outgoing decaying particles PList recoilers; for ( PList::iterator decIt = decaying.begin(); decIt != decaying.end(); ++decIt) { // First find the particles in the intermediates PList::iterator pos = find(eventIntermediates.begin(),eventIntermediates.end(), *decIt); // Colourless particle or coloured particle that did not radiate. if(pos==eventIntermediates.end()) { // Check that this is not a particle from a subsequent decay. // e.g. the W from a top decay from an LHE file. if ( find( eventHard.begin(), eventHard.end(), *decIt ) == eventHard.end() && find( eventOutgoing.begin(), eventOutgoing.end(), *decIt ) == eventOutgoing.end() ) continue; else recoilers.push_back( *decIt ); } // Coloured decaying particle that radiated else { PPtr unstable = *pos; while(!unstable->children().empty()) { unstable = unstable->children()[0]; } assert( find( eventOutgoing.begin(),eventOutgoing.end(), unstable ) != eventOutgoing.end() ); recoilers.push_back( unstable ); } } // Make a list of partons PList partons; for ( PList::iterator outPos = eventOutgoing.begin(); outPos != eventOutgoing.end(); ++outPos ) { if ( find (recoilers.begin(), recoilers.end(), *outPos ) == recoilers.end() ) { partons.push_back( *outPos ); } } // If no outgoing partons, do nothing if ( partons.size() == 0 ){ return; } // Otherwise reshuffling needs to be done. // If there is only one parton, attempt to reshuffle with // the incoming to be consistent with the reshuffle for a // hard process with no decays. - else if ( partons.size() == 1 && ( DipolePartonSplitter::colourConnected(partons.front(),eventIncoming.first) || + else if ( partons.size() == 1 && + ( DipolePartonSplitter::colourConnected(partons.front(),eventIncoming.first) || DipolePartonSplitter::colourConnected(partons.front(),eventIncoming.second) ) ) { // Erase the parton from the event outgoing eventOutgoing.erase( find( eventOutgoing.begin(), eventOutgoing.end(), partons.front() ) ); // Perform the reshuffle, this update the intermediates and the incoming reshuffle(partons, eventIncoming, eventIntermediates); // Update the outgoing eventOutgoing.push_back(partons.front()); return; } // If reshuffling amongst the incoming is not possible // or if we have multiple outgoing partons. else { // Create a complete list of the outgoing from the process PList out; // Make an empty list for storing the new intermediates PList intermediates; - // Empty in particles pair + // Empty incoming particles pair PPair in; // A single parton which cannot be reshuffled // with the incoming. if ( partons.size() == 1 ) { // Populate the out for the reshuffling out.insert(out.end(),partons.begin(),partons.end()); out.insert(out.end(),recoilers.begin(),recoilers.end()); assert( out.size() > 1 ); // Perform the reshuffle with the temporary particle lists reshuffle(out, in, intermediates, true, partons, recoilers); } // If there is more than one parton, reshuffle only // amongst the partons else { assert(partons.size() > 1); // Populate the out for the reshuffling out.insert(out.end(),partons.begin(),partons.end()); assert( out.size() > 1 ); // Perform the reshuffle with the temporary particle lists reshuffle(out, in, intermediates, true); } // Update the dipole event record updateEvent(intermediates, eventIntermediates, out, eventOutgoing, eventHard ); return; } } void ConstituentReshuffler::decayReshuffle(PerturbativeProcessPtr& decayProc, PList& eventOutgoing, PList& eventHard, PList& eventIntermediates ) { // Separate particles into those to be assigned constituent masses // i.e. non-decaying coloured partons // and those which must only absorb recoil // i.e. non-coloured and decaying particles PList partons; PList recoilers; //Make sure the shower should return constituent masses: assert(ShowerHandler::currentHandler()->retConstituentMasses()); // Populate the particle lists from the outgoing of the decay process for( unsigned int ix = 0; ixoutgoing().size(); ++ix) { // Identify recoilers if ( !decayProc->outgoing()[ix].first->coloured() || ShowerHandler::currentHandler()->decaysInShower(decayProc->outgoing()[ix].first->id() ) ) recoilers.push_back(decayProc->outgoing()[ix].first); else partons.push_back(decayProc->outgoing()[ix].first); } // If there are no outgoing partons, then no reshuffling // needs to be done if ( partons.size() == 0 ) return; // Reshuffling needs to be done: else { // Create a complete list of the outgoing from the process PList out; // Make an empty list for storing the new intermediates PList intermediates; // Empty incoming particles pair PPair in; // SW - 15/06/2018, 31/01/2019 - Always include 'recoilers' in // reshuffling, regardless of the number of partons to be put on their // constituent mass shell. This is because reshuffling between 2 partons // frequently leads to a redoShower exception. This treatment is // consistent with the AO shower + + // Populate the out for the reshuffling + out.insert(out.end(),partons.begin(),partons.end()); + out.insert(out.end(),recoilers.begin(),recoilers.end()); + assert( out.size() > 1 ); - // Populate the out for the reshuffling - out.insert(out.end(),partons.begin(),partons.end()); - out.insert(out.end(),recoilers.begin(),recoilers.end()); - assert( out.size() > 1 ); - - // Perform the reshuffle with the temporary particle lists - reshuffle(out, in, intermediates, true, partons, recoilers); - + // Perform the reshuffle with the temporary particle lists + reshuffle(out, in, intermediates, true, partons, recoilers); + // Update the dipole event record and the decay process updateEvent(intermediates, eventIntermediates, out, eventOutgoing, eventHard, decayProc ); return; } } void ConstituentReshuffler::updateEvent( PList& intermediates, PList& eventIntermediates, PList& out, PList& eventOutgoing, PList& eventHard, PerturbativeProcessPtr decayProc ) { // Loop over the new intermediates following the reshuffling for (PList::iterator p = intermediates.begin(); p != intermediates.end(); ++p) { // Update the event record intermediates eventIntermediates.push_back(*p); // Identify the reshuffled particle assert( (*p)->children().size()==1 ); PPtr reshuffled = (*p)->children()[0]; assert( find(out.begin(), out.end(), reshuffled) != out.end() ); // Update the event record outgoing PList::iterator posOut = find(eventOutgoing.begin(), eventOutgoing.end(), *p); if ( posOut != eventOutgoing.end() ) { eventOutgoing.erase(posOut); eventOutgoing.push_back(reshuffled); } else { PList::iterator posHard = find(eventHard.begin(), eventHard.end(), *p); assert( posHard != eventHard.end() ); eventHard.erase(posHard); eventHard.push_back(reshuffled); } // Replace the particle in the the decay process outgoing if ( decayProc ) { vector >::iterator decayOutIt = decayProc->outgoing().end(); for ( decayOutIt = decayProc->outgoing().begin(); decayOutIt!= decayProc->outgoing().end(); ++decayOutIt ) { if ( decayOutIt->first == *p ){ break; } } assert( decayOutIt != decayProc->outgoing().end() ); decayOutIt->first = reshuffled; } } } + +void ConstituentReshuffler::updateSpinInfo( PPtr& oldPart, + PPtr& newPart ) { + + const Lorentz5Momentum& oldMom = oldPart->momentum(); + const Lorentz5Momentum& newMom = newPart->momentum(); + + // Rotation from old momentum to +ve z-axis + LorentzRotation oldToZAxis; + Axis axisOld(oldMom.vect().unit()); + if( axisOld.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axisOld.z()))); + oldToZAxis.rotate( -acos(axisOld.z()),Axis(-axisOld.y()/sinth,axisOld.x()/sinth,0.)); + } + + // Rotation from new momentum to +ve z-axis + LorentzRotation newToZAxis; + Axis axisNew(newMom.vect().unit()); + if( axisNew.perp2() > 1e-12 ) { + double sinth(sqrt(1.-sqr(axisNew.z()))); + newToZAxis.rotate( -acos(axisNew.z()),Axis(-axisNew.y()/sinth,axisNew.x()/sinth,0.)); + } + + // Boost from old momentum to new momentum along z-axis + Lorentz5Momentum momOldRotated = oldToZAxis*Lorentz5Momentum(oldMom); + Lorentz5Momentum momNewRotated = newToZAxis*Lorentz5Momentum(newMom); + + Energy2 a = sqr(momOldRotated.z()) + sqr(momNewRotated.t()); + Energy2 b = 2.*momOldRotated.t()*momOldRotated.z(); + Energy2 c = sqr(momOldRotated.t()) - sqr(momNewRotated.t()); + double beta; + + // The rotated momentum should always lie along the +ve z-axis + if ( momOldRotated.z() > ZERO ) + beta = (-b + sqrt(sqr(b)-4.*a*c)) / 2. / a; + else + beta = (-b - sqrt(sqr(b)-4.*a*c)) / 2. / a; + + LorentzRotation boostOldToNew(0., 0., beta); + + // Total transform + LorentzRotation transform = (newToZAxis.inverse())*boostOldToNew*oldToZAxis; + + // Assign the same spin info to the old and new particles + newPart->spinInfo(oldPart->spinInfo()); + newPart->spinInfo()->transform(oldMom, transform); +} // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void ConstituentReshuffler::persistentOutput(PersistentOStream &) const { } void ConstituentReshuffler::persistentInput(PersistentIStream &, int) { } ClassDescription ConstituentReshuffler::initConstituentReshuffler; // Definition of the static class description member. void ConstituentReshuffler::Init() { static ClassDocumentation documentation ("The ConstituentReshuffler class implements reshuffling " "of partons on their nominal mass shell to their constituent " "mass shells."); } diff --git a/Shower/Dipole/Utility/ConstituentReshuffler.h b/Shower/Dipole/Utility/ConstituentReshuffler.h --- a/Shower/Dipole/Utility/ConstituentReshuffler.h +++ b/Shower/Dipole/Utility/ConstituentReshuffler.h @@ -1,290 +1,299 @@ // -*- C++ -*- // // ConstituentReshuffler.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_ConstituentReshuffler_H #define HERWIG_ConstituentReshuffler_H // // This is the declaration of the ConstituentReshuffler class. // #include "ThePEG/Handlers/HandlerBase.h" #include "ThePEG/Utilities/Exception.h" #include "Herwig/Shower/PerturbativeProcess.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer, Stephen Webster * * \brief The ConstituentReshuffler class implements reshuffling * of partons on their nominal mass shell to their constituent * mass shells. * */ class ConstituentReshuffler: public HandlerBase { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ ConstituentReshuffler(); /** * The destructor. */ virtual ~ConstituentReshuffler(); //@} public: /** * Reshuffle the outgoing partons to constituent * masses. Optionally, incoming partons are given * to absorb recoils. Add the non-reshuffled partons * to the intermediates list. Throw ConstituentReshufflerProblem * if a numerical problem prevents the solution of * the reshuffling equation. */ void reshuffle(PList& out, PPair& in, PList& intermediates, const bool decay, PList& decayPartons, PList& decayRecoilers); /** * Reshuffle the outgoing partons to constituent * masses. Optionally, incoming partons are given * to absorb recoils. Add the non-reshuffled partons * to the intermediates list. Throw ConstituentReshufflerProblem * if a numerical problem prevents the solution of * the reshuffling equation. */ void reshuffle(PList& out, PPair& in, PList& intermediates, const bool decay=false) { PList decayPartons; PList decayRecoilers; reshuffle(out, in, intermediates, decay, decayPartons, decayRecoilers); } /** * Reshuffle the outgoing partons following the showering * of the initial hard interaction to constituent masses, * for the case of outgoing decaying particles. * Throw ConstituentReshufflerProblem * if a numerical problem prevents the solution of * the reshuffling equation. */ void hardProcDecayReshuffle(PList& decaying, PList& eventOutgoing, PList& eventHard, PPair& eventIncoming, PList& eventIntermediates) ; /** * Reshuffle the outgoing partons following the showering * of a particle decay to constituent masses. * Throw ConstituentReshufflerProblem * if a numerical problem prevents the solution of * the reshuffling equation. */ void decayReshuffle(PerturbativeProcessPtr& decayProc, PList& eventOutgoing, PList& eventHard, PList& eventIntermediates) ; /** * Update the dipole event record and, if appropriate, * the relevant decay process. **/ void updateEvent( PList& intermediates, PList& eventIntermediates, PList& out, PList& eventOutgoing, PList& eventHard, PerturbativeProcessPtr decayProc = PerturbativeProcessPtr() ) ; + + /** + * Update the spinInfo of a particle following reshuffling + * to take account of the change in momentum. + * Used only for unstable particles that need to be dealt with. + **/ + void updateSpinInfo( PPtr& oldPart, + PPtr& newPart ) ; + protected: /** * The function object defining the equation * to be solved. */ struct ReshuffleEquation { ReshuffleEquation (Energy q, PList::iterator m_begin, PList::iterator m_end) : w(q), p_begin(m_begin), p_end(m_end) {} typedef double ArgType; typedef double ValType; static double aUnit(); static double vUnit(); double operator() (double xi) const; Energy w; PList::iterator p_begin; PList::iterator p_end; }; /** * The function object defining the equation * to be solved in the case of separate recoilers * TODO - refine the whole implementation of separate partons and recoilers */ struct DecayReshuffleEquation { DecayReshuffleEquation (Energy q, PList::iterator m_begin, PList::iterator m_end, PList::iterator n_begin, PList::iterator n_end) : w(q), p_begin(m_begin), p_end(m_end), r_begin(n_begin), r_end(n_end) {} typedef double ArgType; typedef double ValType; static double aUnit(); static double vUnit(); double operator() (double xi) const; Energy w; PList::iterator p_begin; PList::iterator p_end; PList::iterator r_begin; PList::iterator r_end; }; public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initConstituentReshuffler; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ ConstituentReshuffler & operator=(const ConstituentReshuffler &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of ConstituentReshuffler. */ template <> struct BaseClassTrait { /** Typedef of the first base class of ConstituentReshuffler. */ typedef HandlerBase NthBase; }; /** This template specialization informs ThePEG about the name of * the ConstituentReshuffler class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::ConstituentReshuffler"; } /** * The name of a file containing the dynamic library where the class * ConstituentReshuffler is implemented. It may also include several, space-separated, * libraries if the class ConstituentReshuffler depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_ConstituentReshuffler_H */ diff --git a/Shower/Dipole/Utility/IntrinsicPtGenerator.cc b/Shower/Dipole/Utility/IntrinsicPtGenerator.cc --- a/Shower/Dipole/Utility/IntrinsicPtGenerator.cc +++ b/Shower/Dipole/Utility/IntrinsicPtGenerator.cc @@ -1,198 +1,198 @@ // -*- C++ -*- // // IntrinsicPtGenerator.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 IntrinsicPtGenerator class. // #include "IntrinsicPtGenerator.h" #include "ThePEG/Interface/ClassDocumentation.h" #include "ThePEG/Interface/Parameter.h" #include "ThePEG/Persistency/PersistentOStream.h" #include "ThePEG/Persistency/PersistentIStream.h" #include "ThePEG/Config/Constants.h" #include "DipolePartonSplitter.h" #include "Herwig/Shower/ShowerHandler.h" #include "Herwig/PDF/HwRemDecayer.h" using namespace Herwig; IntrinsicPtGenerator::IntrinsicPtGenerator() : HandlerBase(), theValenceIntrinsicPtScale(1.0*GeV), theSeaIntrinsicPtScale(1.0*GeV) {} IntrinsicPtGenerator::~IntrinsicPtGenerator() {} IBPtr IntrinsicPtGenerator::clone() const { return new_ptr(*this); } IBPtr IntrinsicPtGenerator::fullclone() const { return new_ptr(*this); } -SpinOneLorentzRotation IntrinsicPtGenerator::kick(PPair& in, +LorentzRotation IntrinsicPtGenerator::kick(PPair& in, PList& intermediates) { if ( theValenceIntrinsicPtScale == 0.0*GeV && theSeaIntrinsicPtScale == 0.0*GeV ) - return SpinOneLorentzRotation(); + return LorentzRotation(); assert(ShowerHandler::currentHandler()); tHwRemDecPtr remDec = ShowerHandler::currentHandler()->remnantDecayer(); assert(remDec); Lorentz5Momentum Q = in.first->momentum() + in.second->momentum(); // first parton if (in.first->coloured()) { Axis perp; Energy pt = 0.*GeV; double phi = 2.*Constants::pi*UseRandom::rnd(); Axis dir = in.first->momentum().vect().unit(); perp = dir.orthogonal(); perp.rotate(phi,dir); double r = sqrt(-log(1.-UseRandom::rnd())); if ( remDec->content().first.isValenceQuark(in.first) ) pt = sqrt(2.) * theValenceIntrinsicPtScale * r; else { assert(in.first->id() == ParticleID::g || remDec->content().first.isSeaQuark(in.first)); pt = sqrt(2.) * theSeaIntrinsicPtScale * r; } PPtr nin = new_ptr(Particle(in.first->dataPtr())); DipolePartonSplitter::change(in.first,nin,true); nin->set5Momentum(Lorentz5Momentum(0.*GeV,in.first->momentum().vect() + pt * perp)); intermediates.push_back(in.first); in.first = nin; } // second parton if (in.second->coloured()) { Axis perp; Energy pt = 0.*GeV; double phi = 2.*Constants::pi*UseRandom::rnd(); Axis dir = in.second->momentum().vect().unit(); perp = dir.orthogonal(); perp.rotate(phi,dir); double r = sqrt(-log(1.-UseRandom::rnd())); if ( remDec->content().second.isValenceQuark(in.second) ) pt = sqrt(2.) * theValenceIntrinsicPtScale * r; else { assert(in.second->id() == ParticleID::g || remDec->content().second.isSeaQuark(in.second)); pt = sqrt(2.) * theSeaIntrinsicPtScale * r; } PPtr nin = new_ptr(Particle(in.second->dataPtr())); nin->colourInfo(new_ptr(ColourBase())); DipolePartonSplitter::change(in.second,nin,true); nin->set5Momentum(Lorentz5Momentum(0.*GeV,in.second->momentum().vect() + pt * perp)); intermediates.push_back(in.second); in.second = nin; } // restore mometum conservation Lorentz5Momentum nQ = in.first->momentum() + in.second->momentum(); double x = Q.m()/nQ.m(); nQ *= x; // work around for constructor problems // in Lorentz5Vector constructor, mass is not guaranteed // to be zero, event it was set as such before, // since gets recalculated in the LorentzVector Lorentz5Momentum scaled = x * in.first->momentum(); scaled.setMass(ZERO); scaled.rescaleEnergy(); in.first->set5Momentum(scaled); scaled = x * in.second->momentum(); scaled.setMass(ZERO); scaled.rescaleEnergy(); in.second->set5Momentum(scaled); // apparently, this is more stable than boosting directly with // n_Q.boostVector()-Q.boostVector() Boost beta1 = -Q.boostVector(); Boost beta2 = nQ.boostVector(); - SpinOneLorentzRotation transform (beta1); + LorentzRotation transform (beta1); transform.boost(beta2); return transform; } // If needed, insert default implementations of virtual function defined // in the InterfacedBase class here (using ThePEG-interfaced-impl in Emacs). void IntrinsicPtGenerator::persistentOutput(PersistentOStream & os) const { os << ounit(theValenceIntrinsicPtScale,GeV) << ounit(theSeaIntrinsicPtScale,GeV); } void IntrinsicPtGenerator::persistentInput(PersistentIStream & is, int) { is >> iunit(theValenceIntrinsicPtScale,GeV) >> iunit(theSeaIntrinsicPtScale,GeV); } ClassDescription IntrinsicPtGenerator::initIntrinsicPtGenerator; // Definition of the static class description member. void IntrinsicPtGenerator::Init() { static ClassDocumentation documentation ("IntrinsicPtGenerator generates intrinsic pt for massless " "incoming partons in a shower independent way."); static Parameter interfaceValenceIntrinsicPtScale ("ValenceIntrinsicPtScale", "The width of the intrinsic pt Gaussian distribution for valence partons.", &IntrinsicPtGenerator::theValenceIntrinsicPtScale, GeV, 1.0*GeV, 0.0*GeV, 0*GeV, false, false, Interface::lowerlim); static Parameter interfaceSeaIntrinsicPtScale ("SeaIntrinsicPtScale", "The width of the intrinsic pt Gaussian distribution for sea partons.", &IntrinsicPtGenerator::theSeaIntrinsicPtScale, GeV, 1.0*GeV, 0.0*GeV, 0*GeV, false, false, Interface::lowerlim); } diff --git a/Shower/Dipole/Utility/IntrinsicPtGenerator.h b/Shower/Dipole/Utility/IntrinsicPtGenerator.h --- a/Shower/Dipole/Utility/IntrinsicPtGenerator.h +++ b/Shower/Dipole/Utility/IntrinsicPtGenerator.h @@ -1,174 +1,174 @@ // -*- C++ -*- // // IntrinsicPtGenerator.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_IntrinsicPtGenerator_H #define HERWIG_IntrinsicPtGenerator_H // // This is the declaration of the IntrinsicPtGenerator class. // #include "ThePEG/Handlers/HandlerBase.h" -#include "ThePEG/Vectors/SpinOneLorentzRotation.h" +#include "ThePEG/Vectors/LorentzRotation.h" namespace Herwig { using namespace ThePEG; /** * \ingroup DipoleShower * \author Simon Platzer * * \brief IntrinsicPtGenerator generates intrinsic pt for massless * incoming partons in a shower independent way. * * @see \ref IntrinsicPtGeneratorInterfaces "The interfaces" * defined for IntrinsicPtGenerator. */ class IntrinsicPtGenerator: public HandlerBase { public: /** @name Standard constructors and destructors. */ //@{ /** * The default constructor. */ IntrinsicPtGenerator(); /** * The destructor. */ virtual ~IntrinsicPtGenerator(); //@} public: /** * Generate intrinsic pt for the given incoming * partons and return the transformation to be * applied on the final state particles. Add the * old incoming partons to the given list. */ - SpinOneLorentzRotation kick(PPair& in, + LorentzRotation kick(PPair& in, PList& intermediates); public: /** @name Functions used by the persistent I/O system. */ //@{ /** * Function used to write out object persistently. * @param os the persistent output stream written to. */ void persistentOutput(PersistentOStream & os) const; /** * Function used to read in object persistently. * @param is the persistent input stream read from. * @param version the version number of the object when written. */ void persistentInput(PersistentIStream & is, int version); //@} /** * The standard Init function used to initialize the interfaces. * Called exactly once for each class by the class description system * before the main function starts or * when this class is dynamically loaded. */ static void Init(); protected: /** @name Clone Methods. */ //@{ /** * Make a simple clone of this object. * @return a pointer to the new object. */ virtual IBPtr clone() const; /** Make a clone of this object, possibly modifying the cloned object * to make it sane. * @return a pointer to the new object. */ virtual IBPtr fullclone() const; //@} // If needed, insert declarations of virtual function defined in the // InterfacedBase class here (using ThePEG-interfaced-decl in Emacs). private: /** * The mean of the Gaussian distribution for * the intrinsic pt of valence partons. */ Energy theValenceIntrinsicPtScale; /** * The mean of the Gaussian distribution for * the intrinsic pt of sea partons. */ Energy theSeaIntrinsicPtScale; private: /** * The static object used to initialize the description of this class. * Indicates that this is a concrete class with persistent data. */ static ClassDescription initIntrinsicPtGenerator; /** * The assignment operator is private and must never be called. * In fact, it should not even be implemented. */ IntrinsicPtGenerator & operator=(const IntrinsicPtGenerator &) = delete; }; } #include "ThePEG/Utilities/ClassTraits.h" namespace ThePEG { /** @cond TRAITSPECIALIZATIONS */ /** This template specialization informs ThePEG about the * base classes of IntrinsicPtGenerator. */ template <> struct BaseClassTrait { /** Typedef of the first base class of IntrinsicPtGenerator. */ typedef HandlerBase NthBase; }; /** This template specialization informs ThePEG about the name of * the IntrinsicPtGenerator class and the shared object where it is defined. */ template <> struct ClassTraits : public ClassTraitsBase { /** Return a platform-independent class name */ static string className() { return "Herwig::IntrinsicPtGenerator"; } /** * The name of a file containing the dynamic library where the class * IntrinsicPtGenerator is implemented. It may also include several, space-separated, * libraries if the class IntrinsicPtGenerator depends on other classes (base classes * excepted). In this case the listed libraries will be dynamically * linked in the order they are specified. */ static string library() { return "HwDipoleShower.so"; } }; /** @endcond */ } #endif /* HERWIG_IntrinsicPtGenerator_H */ diff --git a/configure.ac b/configure.ac --- a/configure.ac +++ b/configure.ac @@ -1,244 +1,245 @@ dnl Process this file with autoconf to produce a configure script. AC_PREREQ([2.63]) AC_INIT([Herwig],[devel],[herwig@projects.hepforge.org],[Herwig]) AC_CONFIG_SRCDIR([Utilities/HerwigStrategy.cc]) AC_CONFIG_AUX_DIR([Config]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([Config/config.h]) dnl AC_PRESERVE_HELP_ORDER AC_CANONICAL_HOST dnl === disable debug symbols by default ===== if test "x$CXXFLAGS" = "x"; then CXXFLAGS=-O2 fi if test "x$CFLAGS" = "x"; then CFLAGS=-O2 fi AC_LANG([C++]) AM_INIT_AUTOMAKE([1.11 subdir-objects gnu dist-bzip2 no-dist-gzip -Wall -Wno-portability]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) dnl Checks for C++ compiler. Handle C++11 flags. AC_PROG_CXX AX_CXX_COMPILE_STDCXX([11],[noext],[mandatory]) dnl check for POSIX AC_CHECK_HEADER([unistd.h],[], [AC_MSG_ERROR([Herwig needs "unistd.h". Non-POSIX systems are not supported.])]) AC_CHECK_HEADER([sys/stat.h],[], [AC_MSG_ERROR([Herwig needs "sys/stat.h". Non-POSIX systems are not supported.])]) dnl Checks for programs. AC_PROG_INSTALL AC_PROG_MAKE_SET AC_PROG_LN_S dnl modified search order AC_PROG_FC([gfortran g95 g77]) dnl xlf95 f95 fort ifort ifc efc pgf95 lf95 ftn xlf90 f90 pgf90 pghpf epcf90 xlf f77 frt pgf77 cf77 fort77 fl32 af77]) AC_LANG_PUSH([Fortran]) AC_MSG_CHECKING([if the Fortran compiler ($FC) works]) AC_COMPILE_IFELSE( AC_LANG_PROGRAM([],[ print *[,]"Hello"]), [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_MSG_ERROR([A Fortran compiler is required to build Herwig.]) ] ) AC_LANG_POP([Fortran]) AC_FC_WRAPPERS LT_PREREQ([2.2.6]) LT_INIT([disable-static dlopen pic-only]) dnl #################################### dnl #################################### dnl for Doc/fixinterfaces.pl AC_PATH_PROG(PERL, perl) dnl for Models/Feynrules AM_PATH_PYTHON([2.6],, [:]) AM_CONDITIONAL([HAVE_PYTHON], [test "x$PYTHON" != "x:"]) HERWIG_CHECK_GSL HERWIG_CHECK_THEPEG BOOST_REQUIRE([1.41]) BOOST_FIND_HEADER([boost/numeric/ublas/io.hpp]) dnl Boost 1.64 is missing a required header to make these work dnl we just assume they're there if io.hpp has been found OK above dnl BOOST_FIND_HEADER([boost/numeric/ublas/matrix.hpp]) dnl BOOST_FIND_HEADER([boost/numeric/ublas/matrix_proxy.hpp]) dnl BOOST_FIND_HEADER([boost/numeric/ublas/matrix_sparse.hpp]) dnl BOOST_FIND_HEADER([boost/numeric/ublas/symmetric.hpp]) dnl BOOST_FIND_HEADER([boost/numeric/ublas/vector.hpp]) BOOST_FIND_HEADER([boost/operators.hpp]) BOOST_TEST() HERWIG_CHECK_VBFNLO HERWIG_CHECK_NJET HERWIG_CHECK_GOSAM HERWIG_CHECK_GOSAM_CONTRIB HERWIG_CHECK_OPENLOOPS HERWIG_CHECK_MADGRAPH HERWIG_CHECK_EVTGEN HERWIG_CHECK_PYTHIA HERWIG_COMPILERFLAGS HERWIG_LOOPTOOLS FASTJET_CHECK_FASTJET HERWIG_ENABLE_MODELS SHARED_FLAG=-shared AM_CONDITIONAL(NEED_APPLE_FIXES, [test "xx${host/darwin/foundit}xx" != "xx${host}xx"]) if test "xx${host/darwin/foundit}xx" != "xx${host}xx"; then APPLE_DSO_FLAGS=-Wl,-undefined,dynamic_lookup SHARED_FLAG=-bundle fi AC_SUBST([APPLE_DSO_FLAGS]) AC_SUBST([SHARED_FLAG]) AC_CONFIG_FILES([UnderlyingEvent/Makefile Models/Makefile Models/StandardModel/Makefile Models/RSModel/Makefile Models/General/Makefile Models/Susy/Makefile Models/Susy/NMSSM/Makefile Models/Susy/RPV/Makefile Models/UED/Makefile Models/LH/Makefile Models/LHTP/Makefile Models/Transplanckian/Makefile Models/Leptoquarks/Makefile Models/Zprime/Makefile Models/TTbAsymm/Makefile Models/Feynrules/Makefile Models/Feynrules/python/Makefile-FR Models/ADD/Makefile Models/Sextet/Makefile Decay/Makefile Decay/FormFactors/Makefile Decay/Tau/Makefile Decay/Baryon/Makefile Decay/VectorMeson/Makefile Decay/Perturbative/Makefile Decay/ScalarMeson/Makefile Decay/TensorMeson/Makefile Decay/WeakCurrents/Makefile Decay/Partonic/Makefile Decay/General/Makefile Decay/Radiation/Makefile Decay/EvtGen/Makefile Doc/refman.conf Doc/refman.h PDT/Makefile PDF/Makefile MatrixElement/Makefile MatrixElement/General/Makefile MatrixElement/Lepton/Makefile MatrixElement/Hadron/Makefile MatrixElement/DIS/Makefile MatrixElement/Powheg/Makefile MatrixElement/Gamma/Makefile MatrixElement/Reweighters/Makefile MatrixElement/Matchbox/Makefile MatrixElement/Matchbox/Base/Makefile MatrixElement/Matchbox/Utility/Makefile MatrixElement/Matchbox/Phasespace/Makefile MatrixElement/Matchbox/Dipoles/Makefile MatrixElement/Matchbox/InsertionOperators/Makefile MatrixElement/Matchbox/Matching/Makefile MatrixElement/Matchbox/Cuts/Makefile MatrixElement/Matchbox/Scales/Makefile MatrixElement/Matchbox/ColorFull/Makefile MatrixElement/Matchbox/CVolver/Makefile MatrixElement/Matchbox/Builtin/Makefile MatrixElement/Matchbox/Builtin/Amplitudes/Makefile MatrixElement/Matchbox/Tests/Makefile MatrixElement/Matchbox/External/Makefile MatrixElement/Matchbox/External/BLHAGeneric/Makefile MatrixElement/Matchbox/External/VBFNLO/Makefile MatrixElement/Matchbox/External/NJet/Makefile MatrixElement/Matchbox/External/GoSam/Makefile MatrixElement/Matchbox/External/OpenLoops/Makefile MatrixElement/Matchbox/External/MadGraph/Makefile MatrixElement/Matchbox/External/MadGraph/mg2herwig Sampling/Makefile Sampling/CellGrids/Makefile Shower/Makefile Shower/QTilde/Makefile Shower/QTilde/Matching/Makefile Shower/Dipole/Makefile Shower/Dipole/Base/Makefile Shower/Dipole/Kernels/Makefile Shower/Dipole/Kinematics/Makefile Shower/Dipole/Utility/Makefile Shower/Dipole/AlphaS/Makefile + Shower/Dipole/SpinCorrelations/Makefile Utilities/Makefile Utilities/XML/Makefile Utilities/Statistics/Makefile Hadronization/Makefile lib/Makefile include/Makefile src/Makefile src/defaults/Makefile src/snippets/Makefile src/Matchbox/Makefile src/herwig-config Doc/Makefile Doc/HerwigDefaults.in Looptools/Makefile Analysis/Makefile API/Makefile src/Makefile-UserModules src/defaults/Analysis.in src/defaults/MatchboxDefaults.in src/defaults/Decays.in src/defaults/decayers.in src/defaults/setup.gosam.in src/Matchbox/LO-DefaultShower.in src/Matchbox/LO-DipoleShower.in src/Matchbox/MCatLO-DefaultShower.in src/Matchbox/MCatLO-DipoleShower.in src/Matchbox/LO-NoShower.in src/Matchbox/MCatNLO-DefaultShower.in src/Matchbox/MCatNLO-DipoleShower.in src/Matchbox/NLO-NoShower.in src/Matchbox/Powheg-DefaultShower.in src/Matchbox/Powheg-DipoleShower.in src/Merging/Makefile Shower/Dipole/Merging/Makefile Shower/Dipole/Colorea/Makefile src/defaults/MatchboxMergingDefaults.in Contrib/Makefile Contrib/make_makefiles.sh Tests/Makefile Makefile]) AC_CONFIG_LINKS([Doc/BSMlibs.in:Doc/BSMlibs.in]) AC_CONFIG_FILES([Doc/fixinterfaces.pl],[chmod +x Doc/fixinterfaces.pl]) AC_CONFIG_HEADERS([PDF/SaSPhotonPDF.cc]) HERWIG_OVERVIEW AC_CONFIG_COMMANDS([summary],[cat config.herwig]) AC_OUTPUT diff --git a/src/LHC-GammaGamma.in b/src/LHC-GammaGamma.in --- a/src/LHC-GammaGamma.in +++ b/src/LHC-GammaGamma.in @@ -1,73 +1,73 @@ # -*- ThePEG-repository -*- ################################################## # Example generator based on LEP parameters # usage: Herwig read LEP.in ################################################## read snippets/PPCollider.in ################################################## # Technical parameters for this run ################################################## cd /Herwig/Generators set EventGenerator:EventHandler:Sampler:Ntry 10000 ################################################## # Choice of phase-space generation for PDFs ################################################## set /Herwig/Partons/PPExtractor:FlatSHatY 0 ################################################## # Change the proton PDFs to those for photon radiation ################################################## set /Herwig/Particles/p+:PDF /Herwig/Partons/BudnevPDF set /Herwig/Particles/pbar-:PDF /Herwig/Partons/BudnevPDF set /Herwig/Partons/PPExtractor:FirstPDF /Herwig/Partons/BudnevPDF set /Herwig/Partons/PPExtractor:SecondPDF /Herwig/Partons/BudnevPDF set /Herwig/Shower/ShowerHandler:PDFA NULL set /Herwig/Shower/ShowerHandler:PDFB NULL ################################################## # Cuts ################################################## cd /Herwig/Cuts -set Cuts:ScaleMin 0.0 +set Cuts:ScaleMin 0.0*GeV2 set Cuts:X1Min 0 set Cuts:X2Min 0 set Cuts:X1Max 1. set Cuts:X2Max 1. set Cuts:MHatMin 1.*GeV erase Cuts:MultiCuts 0 set LeptonKtCut:MinKT 3*GeV ################################################## # Selected the hard process ################################################## cd /Herwig/MatrixElements # fermion-antifermion insert SubProcess:MatrixElements 0 /Herwig/MatrixElements/MEgg2ff set /Herwig/MatrixElements/MEgg2ff:Process Muon # W+W- #insert SubProcess:MatrixElements 0 /Herwig/MatrixElements/MEgg2WW ################################################## # LHC physics parameters (override defaults) ################################################## cd /Herwig/Generators set EventGenerator:EventHandler:CascadeHandler:MPIHandler NULL ################################################## ## prepare for Rivet analysis or HepMC output ## when running with parton shower ################################################## #read snippets/Rivet.in #insert /Herwig/Analysis/Rivet:Analyses 0 XXX_2015_ABC123 #read snippets/HepMC.in #set /Herwig/Analysis/HepMC:PrintEvent NNN ################################################### # Save run for later usage with 'Herwig run' ################################################## saverun LHC-GammaGamma EventGenerator diff --git a/src/Matchbox/CMEC.in b/src/Matchbox/CMEC.in new file mode 100644 --- /dev/null +++ b/src/Matchbox/CMEC.in @@ -0,0 +1,45 @@ +# -*- ThePEG-repository -*- + +################################################## +## Turn on subleading Nc corrections +################################################## + +set /Herwig/DipoleShower/DipoleShowerHandler:DoSubleadingNc On + +## Set the number of subleading Nc emissions to calculate +set /Herwig/DipoleShower/DipoleShowerHandler:SubleadingNcEmissionsLimit 5 + +################################################## +## Calculate the CMECs +################################################## + +## Set detuning +set /Herwig/DipoleShower/DipoleShowerHandler:Detuning 2 + +## Create an object of the CMEC class +create Herwig::ColourMatrixElementCorrection /Herwig/DipoleShower/DipoleSplittingReweight + +## Set the splitting reweight to be the CMEC (it is a derived class of +## DipoleSplittingReweight) +set /Herwig/DipoleShower/DipoleShowerHandler:SplittingReweight /Herwig/DipoleShower/DipoleSplittingReweight + +################################################## +## Set the density operator evolution scheme +################################################## +## Schemes: +## 0 - Eikonal with cutoff +## 1 - Eikonal without cutoff +## 2 - Constant +## 3 - Semi-leading Nc, only emitter/spectator have non-zero Vijk + +set /Herwig/DipoleShower/DipoleShowerHandler:DensityOperatorEvolution 1 +set /Herwig/DipoleShower/DipoleShowerHandler:DensityOperatorCutoff 1.0*GeV2 + +################################################## +## Turn on partial unweighting and set the +## reference weight +################################################## + +#set /Herwig/DipoleShower/DipoleShowerHandler:DoPartialUnweighting Off +#set /Herwig/DipoleShower/DipoleShowerHandler:DoPartialUnweightingAtEmission Off +#set /Herwig/DipoleShower/DipoleShowerHandler:ReferenceWeight 4.0 diff --git a/src/Matchbox/Makefile.am b/src/Matchbox/Makefile.am --- a/src/Matchbox/Makefile.am +++ b/src/Matchbox/Makefile.am @@ -1,65 +1,66 @@ BUILT_SOURCES = done-all-links Matchboxdir = ${pkgdatadir}/Matchbox INPUTFILES = \ +CMEC.in \ DefaultEEJets.in \ DefaultEPJets.in \ DefaultPPJets.in \ DiagonalCKM.in \ FiveFlavourNoBMassScheme.in \ FiveFlavourScheme.in \ FourFlavourScheme.in \ GoSam-GoSam.in \ HiggsEffective.in \ HJets.in \ IdentifiedBs.in \ InclusiveDurhamJets.in \ IncreaseVerbosity.in \ KrkNLO-DipoleShower.in \ LO-DefaultShower.in \ LO-DipoleShower.in \ LO.in \ LO-NoShower.in \ MadGraph-GoSam.in \ MadGraph-MadGraph.in \ MadGraph-NJet.in \ MadGraph-OpenLoops.in \ MCatLO-DefaultShower.in \ MCatLO-DipoleShower.in \ MCatNLO-DefaultShower.in \ MCatNLO-Dipole-HardAlphaSTune.in \ MCatNLO-DipoleShower.in \ MMHT2014.in \ MuDown.in \ MuQDown.in \ MuQUp.in \ MuUp.in \ NJet-NJet.in \ NLO-NoShower.in \ NonDiagonalCKM.in \ OnShellHProduction.in \ OnShellTopProduction.in \ OnShellWProduction.in \ OnShellZProduction.in \ OpenLoops-OpenLoops.in \ Powheg-DefaultShower.in \ Powheg-DipoleShower.in \ Powheg.in \ PQCDLevel.in \ StandardModelLike.in \ ShowerBenchmarks.in \ VBFDiagramsOnly.in \ VBFNLO.in \ VBFNLOPhasespace.in dist_Matchbox_DATA = $(INPUTFILES) CLEANFILES = done-all-links done-all-links: $(INPUTFILES) @echo "Linking input files" @for i in $(INPUTFILES); do \ if test -f $(srcdir)/$$i -a ! -e $$i; then \ $(LN_S) -f $(srcdir)/$$i; fi; done @touch done-all-links