Rivet analyses

Jet cross-section ratios at 13 TeV

Experiment: ATLAS (LHC)

Inspire ID: 2791854

Status: VALIDATED

Authors: - Jennifer Roloff

References: - Expt page: ATLAS-STDM-2020-04 - arXiv: 2405.20206

Beams: p+ p+

Beam energies: (6500.0, 6500.0)GeV

Run details: - pp -> jets

Measurements of jet cross-section ratios between inclusive bins of jet multiplicity are performed in 140 fb$^{-1}$ of proton–proton collisions with $\sqrt{s}$=13 TeV center-of-mass energy, recorded with the ATLAS detector at CERN’s Large Hadron Collider. Observables that are sensitive the energy-scale and angular distribution of radiation due to the strong interaction in the final state are measured double-differentially, in bins of jet multiplicity, and are unfolded to account for acceptance and detector-related effects. Additionally, the scalar sum of the two leading jets’ transverse momenta is measured triple-differentially, in bins of the third jet’s transverse momentum as well as bins of jet multiplicity. The measured distributions are used to construct ratios of the inclusive jet-multiplicity bins, which have been shown to be sensitive to the strong coupling $\alpha_\text{s}$ while being less sensitive than other observables to systematic uncertainties and parton distribution functions. The measured distributions are compared with state-of-the-art QCD calculations, including next-to-next-to-leading-order predictions. Studies leading to reduced jet energy scale uncertainties significantly improve the precision of this work, and are documented herein.

Source code:ATLAS_2024_I2791854.cc

// -*- C++ -*-
#include "Rivet/Analysis.hh"
#include "Rivet/Projections/FastJets.hh"
#include "Rivet/Projections/FinalState.hh"
#include "Rivet/Tools/HistoGroup.hh"

namespace Rivet {

  /// @brief Jet cross-section ratios at 13 TeV
  class ATLAS_2024_I2791854 : public Analysis {
  public:

    /// Constructor
    RIVET_DEFAULT_ANALYSIS_CTOR(ATLAS_2024_I2791854);


    double maxDY(const Jets& jets) const {
      double biggest_y_gap = 0;
      for (size_t iJet = 0; iJet < jets.size(); ++iJet) {
        for (size_t jJet = iJet + 1; jJet < jets.size(); ++jJet) {
          double y_gap = std::abs(jets[iJet].rap() - jets[jJet].rap());
          if (y_gap > biggest_y_gap) {
            biggest_y_gap = y_gap;
          }
        }
      }
      return biggest_y_gap;
    }

    double maxMjj(const Jets& jets) const {
      double maxMass = 0;
      for (size_t iJet = 0; iJet < jets.size(); ++iJet) {
        for (size_t jJet = iJet + 1; jJet < jets.size(); ++jJet) {
          double mass = (jets[iJet].mom() + jets[jJet].mom()).mass();
          if (mass > maxMass) {
            maxMass = mass;
          }
        }
      }
      return maxMass;
    }


    /// @name Analysis methods
    /// @{

    /// Book histograms and initialise projections before the run
    void init() {

      // Initialise and register projections
      const FinalState fs(Cuts::abseta < 4.9);

      // The final-state particles declared above are clustered using FastJet with
      // the anti-kT algorithm and a jet-radius parameter 0.4
      // muons and neutrinos are excluded from the clustering
      FastJets jet4(fs, JetAlg::ANTIKT, 0.4, JetMuons::NONE, JetInvisibles::NONE);
      declare(jet4, "Jets");

      // booking pT3-binned HT2 results
      for (size_t pt3bin = 0; pt3bin < _pt3_cuts.size(); ++pt3bin) {
        HistoGroupPtr<int, double> tmp;
        _ht2.emplace_back(book(tmp, {2, 3, 4, 5}));
        for (auto& b : _ht2[pt3bin]->bins()) {
          book(b, 33 + b.index() + 4 * pt3bin, 1, 1);
        }
      }

      // booking all other results
      for (size_t njet = 0; njet < 4; ++njet) {
        Histo1DPtr tmp;
        size_t hid = 54 + njet;

        if (njet < 3) {
          _h_ptjet.emplace_back(book(tmp, hid, 1, 1));
        }

        hid += 3;
        _h_mjjmax.emplace_back(book(tmp, hid, 1, 1));

        hid += 4;
        _h_mjj.emplace_back(book(tmp, hid, 1, 1));

        hid += 4;
        _h_dy.emplace_back(book(tmp, hid, 1, 1));

        hid += 4;
        _h_dymax.emplace_back(book(tmp, hid, 1, 1));
      }
    }


    /// Perform the per-event analysis
    void analyze(const Event& event) {


      // Retrieve clustered jets, sorted by pT, with a minimum pT cut
      const Jets jets = apply<FastJets>(event, "Jets").jetsByPt(Cuts::pT > 60 * GeV && Cuts::abseta < 4.5);

      if (jets.size() < 2) vetoEvent; // Dijet events

      const double mjj = (jets[0].mom() + jets[1].mom()).mass();
      const double mjjmax = maxMjj(jets);
      const double dy = std::abs(jets[0].rap() - jets[1].rap());
      const double dymax = maxDY(jets);
      const double ht2 = jets[0].pt() + jets[1].pt();

      if (ht2 < 250) vetoEvent;

      const double pt3 = jets.size() > 2 ? jets[2].pt() : 0.0;
      for (size_t nj = std::min(5ul, jets.size()); nj >= 2; --nj) {
        for (size_t pt3bin = 0; pt3bin < _pt3_cuts.size(); ++pt3bin) {
          if (nj > 2 && pt3 < _pt3_cuts[pt3bin] * ht2) continue;
          _ht2[pt3bin]->fill(nj, ht2);
        }
        _h_mjj[nj - 2]->fill(mjj / GeV);
        _h_mjjmax[nj - 2]->fill(mjjmax / GeV);
        _h_dy[nj - 2]->fill(dy);
        _h_dymax[nj - 2]->fill(dymax);
      }

      size_t jetSize = jets.size();
      for (size_t j = 0; j < 3; ++j) {
        for (int i = std::min(j + 1, jetSize - 1); i >= 0; --i) {
          _h_ptjet[j]->fill(jets[i].pt() / GeV);
        }
      }
    }

    /// Normalise histograms etc., after the run
    void finalize() {
      const double sf = crossSectionPerEvent();
      scale(_ht2, sf);
      scale(_h_dy, sf);
      scale(_h_dymax, sf);
      scale(_h_mjj, sf);
      scale(_h_mjjmax, sf);
      scale(_h_ptjet, sf);

      Estimate1DPtr ratio;
      for (size_t ir = 0; ir < 4; ++ir) {
        size_t hid = 10 * ir + 6;
        // Temporary hack for missing hepdata
        if (ir == 1) hid = 10 + 4;
        if (ir == 2) hid = 18 + 3;
        if (ir == 3) hid = 25 + 4;

        if (ir < 2) {
          book(ratio, hid, 1, 1);
          divide(_h_ptjet[ir + 1], _h_ptjet[ir], ratio);
        }

        book(ratio, ++hid, 1, 1);
        divide(_h_dy[ir == 3 ? 2 : ir + 1], _h_dy[ir == 3 ? 0 : ir], ratio);

        book(ratio, ++hid, 1, 1);
        divide(_h_dymax[ir == 3 ? 2 : ir + 1], _h_dymax[ir == 3 ? 0 : ir], ratio);

        book(ratio, ++hid, 1, 1);
        divide(_h_mjj[ir == 3 ? 2 : ir + 1], _h_mjj[ir == 3 ? 0 : ir], ratio);

        book(ratio, ++hid, 1, 1);
        divide(_h_mjjmax[ir == 3 ? 2 : ir + 1], _h_mjjmax[ir == 3 ? 0 : ir], ratio);
      }

      for (size_t pt3bin = 0; pt3bin < _pt3_cuts.size(); ++pt3bin) {
        for (size_t ir = 0; ir < 4; ++ir) {
          size_t hid = pt3bin + 10 * ir + 1;
          if (ir == 2) hid = pt3bin + 10 + 8 + 1;
          if (ir == 3) hid = pt3bin + 10 + 8 + 7 + 1;

          if ((pt3bin == 1 || pt3bin == 3) && ir > 0) continue;
          if (ir > 0 && pt3bin == 2) hid -= 1;
          if (ir > 0 && pt3bin == 4) hid -= 2;

          book(ratio, hid, 1, 1);
          MSG_DEBUG("ratio HT2: " << hid << " for pt3bin = " << pt3bin << " and ir = " << ir);
          divide(_ht2[pt3bin]->bin(ir == 3 ? 3 : ir + 2), _ht2[pt3bin]->bin(ir == 3 ? 1 : ir + 1), ratio);
        }
      }
    }

    /// @name Histograms
    /// @{

    // for pT-inclusive histograms
    vector<HistoGroupPtr<int, double>> _ht2;
    vector<Histo1DPtr> _h_dymax, _h_dy, _h_mjjmax, _h_mjj, _h_ptjet;

    const vector<double> _pt3_cuts{0.0, 0.05, 0.1, 0.2, 0.3};

    /// @}
  };


  RIVET_DECLARE_PLUGIN(ATLAS_2024_I2791854);

}