## main371

Back to index.

    // main371.cc is a part of the PYTHIA event generator.
    // Copyright (C) 2026 Torbjorn Sjostrand.
    // PYTHIA is licenced under the GNU GPL v2 or later, see COPYING for details.
    // Please respect the MCnet Guidelines, see GUIDELINES for details.

    // Authors:
    //            Torbjorn Sjostrand

    // Keywords:
    //            Top
    //            Toponium

    // Study ttbar production above and below threshold,
    // starting out from the equations in
    // V. Fadin,  V. Khoze and T. Sjostrand, Z. Phys. C48 (1990) 613,
    // with some further assumptions in the below-threshold simulation.

    #include "Pythia8/Pythia.h"
    using namespace Pythia8;

    //============================================================================

    int main() {

      // Restrict to threshold region only or not.
      bool thresholdOnly = true;

      // Full event generation or only cross-section-oriented studies.
      bool fullEvents = false;

      // Number of events.
      int nEvent = 1000000;

      // LHC collision energy.
      double eCM = 13000.;

      // Example of possible setup values.
      // topModelSetup = 0 is Born, = 1 is simple Coulomb, = 2 is Green's,
      // = 3 is reduced top quark width, = 4 reduced width in cross section.
      int    topModelSetup   = 3;

      // Suitable values for given topModelSetup.
      int    topModel        = min( topModelSetup, 2);
      double mt              = 172.5;
      double gammat          = (topModelSetup != 3) ? 1.34 : 0.1;
      // Warning:  a small gammatGreen may lead to event weights above unity.
      double gammatGreen     = (topModelSetup != 4) ? 1.34 : 0.2;
      double thresholdRegion = 10.;
      int    alphasOrder     = 2;
      double alphasValue     = 0.118;
      double ggSingletFrac   = 2./7.;
      double qqSingletFrac   = 0.;
      bool   pseudoscalar    = true;
      double eBegDamp        = 10.;
      double eEndDamp        = 20.;

      // Generator.
      Pythia pythia;

      // Process selection. LHC at 13 TeV.
      pythia.readString("Top:gg2ttbar = on");
      pythia.readString("Top:qqbar2ttbar = on");
      pythia.settings.parm("Beams:eCM", eCM);

      // Restrict to threshold region or not.
      if (thresholdOnly) {
        pythia.readString("PhaseSpace:mHatMin = 300.");
        pythia.readString("PhaseSpace:mHatMax = 400.");
      }
      else pythia.readString("PhaseSpace:mHatMin = 200.");

      // Feed in desired values.
      pythia.settings.mode("TopThreshold:model", topModel);
      pythia.particleData.m0(6, mt);
      pythia.readString("6:doForceWidth = true");
      pythia.particleData.mWidth( 6, gammat);
      pythia.settings.parm("TopThreshold:tWidthGreen", gammatGreen);
      pythia.settings.parm("TopThreshold:thrRegion", thresholdRegion);
      pythia.settings.mode("TopThreshold:alphasOrder", alphasOrder);
      pythia.settings.parm("TopThreshold:alphasValue", alphasValue);
      pythia.settings.parm("TopThreshold:ggSingletFrac", ggSingletFrac);
      pythia.settings.parm("TopThreshold:qqSingletFrac", qqSingletFrac);
      pythia.settings.flag("TopThreshold:pseudoscalar", pseudoscalar);
      pythia.settings.parm("TopThreshold:psEBeginDamp", eBegDamp);
      pythia.settings.parm("TopThreshold:psEEndDamp", eEndDamp);

      // Switch off (most) code parts not relevant here. Reduce printout.
      if (!fullEvents) {
        pythia.readString("PartonLevel:ISR = off");
        pythia.readString("PartonLevel:FSR = off");
        pythia.readString("PartonLevel:MPI = off");
        pythia.readString("HadronLevel:all = off");
      }
      pythia.readString("Next:numberCount = 100000");

      // If Pythia fails to initialize, exit with error.
      if (!pythia.init()) return 1;

      // Histograms.
      Hist mHatOri("original threshold", 100, -20., 80.);
      Hist mHatOriLow("original threshold", 100, -10., 30.);
      Hist mHatThr("corrected threshold", 100, -20., 80.);
      Hist mHatLow("ttbar invariant mass, low", 100, 300., 400.);
      Hist mHatAll("ttbar invariant mass, all", 100, 200., 1200.);
      Hist mTopMax("max(m_t, m_tbar) mass distribution", 100, 100., 200.);
      Hist mTopMin("min(m_t, m_tbar) mass distribution", 100, 100., 200.);
      Hist betaPair("beta factor of pair", 100, 0., 1.);
      Hist mHatOriBT("original threshold, below", 100, -20., 80.);
      Hist mHatOriBTLow("original threshold, below", 100, -10., 30.);
      Hist mHatThrBT("corrected threshold, below", 100, -10., 30.);
      Hist mHatLowBT("ttbar invariant mass, low, below", 100, 300., 400.);
      Hist mHatAllBT("ttbar invariant mass, all, below", 100, 200., 1200.);
      Hist mTopMaxBT("max(m_t, m_tbar) mass distribution, below", 100, 100., 200.);
      Hist mTopMinBT("min(m_t, m_tbar) mass distribution, below", 100, 100., 200.);
      Hist betaPairBT("beta factor of pair, below", 100, 0., 1.);
      Hist mShiftBT("shift in top mass, after - before", 100, -45., 5.);

      // Counters. Real numbers since some events can come with weights > 1.
      double nEventEff = 0., nBelow2mt = 0., nBelow = 0., nBelowgg = 0.,
        nBelowqq = 0., nAbovegg = 0., nAboveqq = 0., eBelow = 0.;

      // Begin event loop.
      for (int iEvent = 0; iEvent < nEvent; ++iEvent) {

        // Generate events.
        if (!pythia.next()) continue;
        double wt   = pythia.info.weight();

        // Original threshold value and other properties.
        double eThr = pythia.info.toponiumE;
        double mt1T = pythia.info.toponiumm3;
        double mt2T = pythia.info.toponiumm4;
        double mHat = pythia.info.mHat();
        double mt1  = pythia.info.m3Hat();
        double mt2  = pythia.info.m4Hat();
        double rat1 = pow2( mt1 / mHat);
        double rat2 = pow2( mt2 / mHat);
        double beta = sqrtpos( pow2(1 - rat1 - rat2) - 4. * rat1 * rat2);

        // Histogram for all events.
        mHatOri.fill( eThr, wt);
        mHatOriLow.fill( eThr, wt);
        mHatThr.fill( mHat - mt1 - mt2, wt);
        mHatLow.fill( mHat, wt);
        mHatAll.fill( mHat, wt);
        mTopMax.fill( max(mt1,mt2), wt);
        mTopMin.fill( min(mt1,mt2), wt);
        betaPair.fill( beta, wt);

        // Histogram for below-threshold events (and some above).
        nEventEff += wt;
        if (mHat < 2. * mt) nBelow2mt += wt;
        if (eThr < 0.) {
          nBelow += wt;
          if (pythia.info.code() == 601) nBelowgg += wt;
          else nBelowqq += wt;
          eBelow += eThr * wt;
          mHatOriBT.fill( eThr, wt);
          mHatOriBTLow.fill( eThr, wt);
          mHatThrBT.fill( mHat - mt1 - mt2, wt);
          mHatLowBT.fill( mHat, wt);
          mHatAllBT.fill( mHat, wt);
          mTopMaxBT.fill( max(mt1,mt2), wt);
          mTopMinBT.fill( min(mt1,mt2), wt);
          betaPairBT.fill( beta, wt);
          mShiftBT.fill( mt1 - mt1T, wt);
          mShiftBT.fill( mt2 - mt2T, wt);
        } else {
          if (pythia.info.code() == 601) nAbovegg += wt;
          else nAboveqq += wt;
        }

      // End of event loop.
      }

      // Normalize histogram to cross section, in pb/GeV.
      double sigmaGen = 1e9 * pythia.info.sigmaGen();
      double sigmapb = sigmaGen  / nEventEff;
      mHatOri      *= sigmapb;
      mHatOriLow   *= 2.5 * sigmapb;
      mHatThr      *= sigmapb;
      mHatLow      *= sigmapb;
      mHatAll      *= 0.1 * sigmapb;
      mTopMax      *= sigmapb;
      mTopMin      *= sigmapb;
      betaPair     *= 100. * sigmapb;
      mHatOriBT    *= sigmapb;
      mHatOriBTLow *= 2.5 * sigmapb;
      mHatThrBT    *= sigmapb;
      mHatLowBT    *= sigmapb;
      mHatAllBT    *= 0.1 * sigmapb;
      mTopMaxBT    *= sigmapb;
      mTopMinBT    *= sigmapb;
      betaPairBT   *= 100. * sigmapb;
      mShiftBT     *= 2. * sigmapb;

      // Statistics and cross sections
      pythia.stat();
      double sigma2mt = sigmapb * nBelow2mt;
      double sigAbv   = sigmapb * (nEventEff - nBelow);
      double sigAbvgg = sigmapb * nAbovegg;
      double sigAbvqq = sigmapb * nAboveqq;
      double sigBel   = sigmapb * nBelow;
      double sigBelgg = sigmapb * nBelowgg;
      double sigBelqq = sigmapb * nBelowqq;
      cout << "\n total sigma in studied region = " << fixed << setprecision(3)
           << sigmaGen << " pb whereof below 2 mt = " << sigma2mt << " pb"
           << endl << " sigma above threshold = "  << sigAbv << " pb" << endl
           << "   whereof gg = " << sigAbvgg << " pb and qq = " << sigAbvqq
           << " pb" << endl << " sigma below threshold = " << sigBel
           << " pb" << endl << "   whereof gg = " << sigBelgg << " pb and qq = "
           << sigBelqq << " pb" << endl;
      eBelow /= max(1., nBelow);
      cout << " average energy for below-threshold part = " << eBelow
           << " GeV" << endl;

      // Histograms with pyplot.
      HistPlot hpl("plot371");
      bool doOptional = true;

      // Spectra relative to event-by-event threshold, including below.
      hpl.frame("fig371", "Energy above/below threshold, original tops",
        "$E$ (GeV)", "$\\mathrm{d}\\sigma/\\mathrm{d}E$ (pb/GeV)");
      hpl.add( mHatOri - mHatOriBT, "h,black", "above-threshold part");
      hpl.add( mHatOriBT, "h,red", "below-threshold part");
      hpl.plot();

      // Ditto, but with new t/tbar masses for below-threshold part.
      hpl.frame("", "Energy above threshold, new top masses where necessary",
        "$E'$ (GeV)", "$\\mathrm{d}\\sigma/\\mathrm{d}E'$ (pb/GeV)");
      hpl.add( mHatThr, "h,black", "all");
      hpl.add( mHatThrBT, "h,red", "below-threshold part");
      hpl.plot();

      // Pair mass spectra close to threshold.
      hpl.frame("", "Invariant mass of ttbar pair, near threshold",
        "$m(t+tbar) (GeV)$", "$\\mathrm{d}\\sigma/\\mathrm{d}m$ (pb/GeV)");
      hpl.add( mHatLow, "h,black", "all");
      hpl.add( mHatLowBT, "h,red", "below-threshold part");
      hpl.plot();

      // Pair mass spectra over full range.
      hpl.frame("", "Invariant mass of ttbar pair, full range",
        "$m(t+tbar) (GeV)$", "$\\mathrm{d}\\sigma/\\mathrm{d}m$ (pb/GeV)");
      hpl.add( mHatAll, "h,black", "all");
      hpl.add( mHatAllBT, "h,red", "below-threshold part");
      hpl.plot();

      // Top/antitop mass spectra.
      hpl.frame("", "Larger of top and antitop masses",
        "max($m(t), m(tbar)$) (GeV)", "$\\mathrm{d}\\sigma/\\mathrm{d}m$ "
        "(pb/GeV)");
      hpl.add( mTopMax, "h,black", "all");
      hpl.add( mTopMaxBT, "h,red", "below-threshold part");
      hpl.plot(100., 200., 0.01, 100., true, false);

      hpl.frame("", "Smaller of top and antitop masses",
        "min($m(t), m(tbar)$) (GeV)", "$\\mathrm{d}\\sigma/\\mathrm{d}m$ "
        "(pb/GeV)");
      hpl.add( mTopMin, "h,black", "all");
      hpl.add( mTopMinBT, "h,red", "below-threshold part");
      hpl.plot(100., 200., 0.01, 100., true, false);

      // Top/antitop separation.
      hpl.frame("", "Beta factor in t-tbar pair",
        "$\\beta$", "$\\mathrm{d}\\sigma/\\mathrm{d}\\beta$ (pb)");
      hpl.add( betaPair, "h,black", "all");
      hpl.add( betaPairBT, "h,red", "below-threshold part");
      hpl.plot();

      // Redefinition change of top masses in below-threshold pairs.
      hpl.frame("", "Shift of below-threshold top masses", "$\\Delta m$ (GeV)",
        "$\\mathrm{d}\\sigma/\\mathrm{d}(\\Delta m)$ (pb/GeV)");
      hpl.add( mShiftBT, "h,red", "below-threshold part");
      hpl.plot();

      // For inclusion in talk.
      if (doOptional) {
        hpl.frame("fig371mass", "", "$\\hat{m}$ (GeV)",
          "$\\mathrm{d}\\sigma/\\mathrm{d}\\hat{m}$ (pb/GeV)", 4.8, 4.8);
        hpl.add( mHatLowBT, "h,red", "below threshold");
        hpl.add( mHatLow, "h,black", "all");
        hpl.plot();
        hpl.frame("fig371BreitWigners", "", "$m$ (GeV)",
          "$\\mathrm{d}\\sigma/\\mathrm{d}m$ (pb/GeV)", 4.8, 4.8);
        hpl.add( mTopMax, "h,black", "max($m_{t1},m_{t2}$) all");
        hpl.add( mTopMin, "h,blue", "min($m_{t1},m_{t2}$) all");
        hpl.add( mTopMaxBT, "h,red", "max($m_{t1},m_{t2}$) below threshold");
        hpl.add( mTopMinBT, "h,magenta", "min($m_{t1},m_{t2}$) below threshold");
        hpl.plot(140., 200., 0.01, 1000., true, false);
      }

      // Done.
      return 0;
    }