Rivet analyses

Measurement of the mass dependence of the transverse momentum of lepton pairs in Drell–Yan production in proton-proton collisions at 13 TeV

Experiment: CMS (LHC)

Inspire ID: 2079374

Status: VALIDATED

Authors: - cms-pag-conveners-smp@cern.ch - Louis Moureaux - Buğra Bilin - Itana Bubanja - Philippe Gras

References: - Expt page: CMS-SMP-20-003 - arXiv: 2205.04897 - Submitted to Eur. Phys. J. C

Beams: p+ p+

Beam energies: (6500.0, 6500.0)GeV

Run details: - Run MC generators with Z decaying leptonically into both electrons and muons at 13 TeV CoM energy. If only one of the two decay channels is included, set LMODE accordingly.

The double differential cross sections of the Drell-Yan lepton pair (+, dielectron or dimuon) production are measured, as functions of the invariant mass m, transverse momentum pT, and φη*. The φη* observable, derived from angular measurements of the leptons and highly correlated with pT, is used to probe the low-pT region in a complementary way. Dilepton masses up to 1 TeV are investigated. Additionally, a measurement is performed requiring at least one jet in the final state. To benefit from partial cancellation of the systematic uncertainty, the ratios of the differential cross sections for various m ranges to those in the Z mass peak interval are presented. The collected data correspond to an integrated luminosity of 36.3 fb−1 of proton-proton collisions recorded with the CMS detector at the LHC at a centre-of-mass energy of 13 TeV. Measurements are compared with predictions based on perturbative quantum chromodynamics, including soft-gluon resummation.

Source code:CMS_2022_I2079374.cc

// -*- C++ -*-
#include "Rivet/Analysis.hh"
#include "Rivet/Projections/FastJets.hh"
#include "Rivet/Projections/FinalState.hh"
#include "Rivet/Projections/LeptonFinder.hh"
#include "Rivet/Projections/PromptFinalState.hh"
#include "Rivet/Projections/VisibleFinalState.hh"

namespace Rivet {


  /// @brief Z pT over a wide mass range
  class CMS_2022_I2079374 : public Analysis {
  public:

    /// Constructor
    RIVET_DEFAULT_ANALYSIS_CTOR(CMS_2022_I2079374);

    /// Book histograms and initialise projections before the run
    void init() {
      // Get options from the new option system
      // default to combined.
      _mode = 2;
      if (getOption("LMODE") == "EL") {
        _mode = 0;
      }
      else if (getOption("LMODE") == "MU") {
        _mode = 1;
      }
      else if (getOption("LMODE") == "EMU") {
        _mode = 2;
      }
      else {
        MSG_INFO("Assuming a mixed electron/muon sample. Set LMODE=EL/MU if your sample contains a single "
                 "flavor.");
      }

      FinalState fs;
      PromptFinalState pfs(fs);
      PromptFinalState bareMuons(Cuts::abspid == PID::MUON);
      declare(LeptonFinder(bareMuons, pfs, 0.1, Cuts::abseta < 2.4 && Cuts::pT > 20 * GeV), "muons");

      PromptFinalState bareElectrons(Cuts::abspid == PID::ELECTRON);
      declare(LeptonFinder(bareElectrons, pfs, 0.1, Cuts::abseta < 2.4 && Cuts::pT > 20 * GeV), "electrons");

      FastJets jets(fs, JetAlg::ANTIKT, 0.4);
      declare(jets, "jets");

      // Histograms
      book(_pt_0jet_mass50_76, 1, 1, 1);
      book(_pt_0jet_mass76_106, 3, 1, 1);
      book(_pt_0jet_mass106_170, 5, 1, 1);
      book(_pt_0jet_mass170_350, 7, 1, 1);
      book(_pt_0jet_mass350_1000, 9, 1, 1);

      book(_pt_1jet_mass50_76, 11, 1, 1);
      book(_pt_1jet_mass76_106, 13, 1, 1);
      book(_pt_1jet_mass106_170, 15, 1, 1);
      book(_pt_1jet_mass170_350, 17, 1, 1);

      book(_phistar_mass50_76, 19, 1, 1);
      book(_phistar_mass76_106, 21, 1, 1);
      book(_phistar_mass106_170, 23, 1, 1);
      book(_phistar_mass170_350, 25, 1, 1);
      book(_phistar_mass350_1000, 27, 1, 1);
    }

    /// Z boson finder.
    /// Note: we don't use the standard DileptonFinder class in order to stick to
    /// the definition of the publication that is simpler than the DileptonFinder
    /// algorithm
    /// @param leptons pt-ordered of electron or muon collection to use to build
    /// the Z boson
    std::unique_ptr<Particle> zfinder(const DressedLeptons& leptons) {
      if (leptons.size() < 2) {
        return nullptr;
      }
      // Leading lepton pT cut
      if (leptons[0].pT() < 25. * GeV) {
        return nullptr;
      }
      if (leptons[0].charge() * leptons[1].charge() > 0) {
        return nullptr;
      }

      std::unique_ptr<Particle> candidate(new Particle(PID::ZBOSON, leptons[0].mom() + leptons[1].mom()));
      if (candidate->mass() < 50 * GeV || candidate->mass() > 1000 * GeV) {
        return nullptr;
      }
      return candidate;
    }

    /// Perform the per-event analysis
    void analyze(const Event& event) {
      // Fetch dressed leptons
      auto muons = apply<LeptonFinder>(event, "muons").dressedLeptons();
      auto electrons = apply<LeptonFinder>(event, "electrons").dressedLeptons();

      const DressedLeptons* dressedLeptons = nullptr;

      //Look for Z->ee
      std::unique_ptr<Particle> z = zfinder(electrons);
      if (z != nullptr) {
        dressedLeptons = &electrons;
        if (_mode == 1) {
          // Z->ee found, but running with LMODE=MU
          vetoEvent;
        }
      }
      else if (_mode == 0) {
        // LMODE=EL, but no Z->ee found
        vetoEvent;
      }
      else { //look for Z->mumu
        z = zfinder(muons);
        if (z.get() != nullptr) {
          dressedLeptons = &muons;
        }
        else { //no Z boson found
          vetoEvent;
        }
      }

      // Cluster jets
      Jets jets = apply<FastJets>(event, "jets").jetsByPt(Cuts::absrap < 2.4 && Cuts::pT > 30 * GeV);
      idiscardIfAnyDeltaRLess(jets, *dressedLeptons, 0.4);

      //------------------------------------------------

      // Compute phi*
      const Particle& lminus = dressedLeptons->at(0).charge() < 0 ? dressedLeptons->at(0)
                                                                  : dressedLeptons->at(1);
      const Particle& lplus = dressedLeptons->at(0).charge() < 0 ? dressedLeptons->at(1)
                                                                 : dressedLeptons->at(0);
      double phi_acop = M_PI - deltaPhi(lminus, lplus);
      double costhetastar = tanh(0.5 * (lminus.eta() - lplus.eta()));
      double sin2thetastar = (costhetastar > 1) ? 0.0 : (1.0 - sqr(costhetastar));
      double phistar = tan(0.5 * phi_acop) * sqrt(sin2thetastar);

      if (z->mass() > 50. && z->mass() < 76.) {
        _pt_0jet_mass50_76->fill(z->pT());
        _phistar_mass50_76->fill(phistar);
      }
      else if (z->mass() > 76. && z->mass() < 106.) {
        _pt_0jet_mass76_106->fill(z->pT());
        _phistar_mass76_106->fill(phistar);
      }
      else if (z->mass() > 106. && z->mass() < 170.) {
        _pt_0jet_mass106_170->fill(z->pT());
        _phistar_mass106_170->fill(phistar);
      }
      else if (z->mass() > 170. && z->mass() < 350.) {
        _pt_0jet_mass170_350->fill(z->pT());
        _phistar_mass170_350->fill(phistar);
      }
      else if (z->mass() > 350. && z->mass() < 1000.) {
        _pt_0jet_mass350_1000->fill(z->pT());
        _phistar_mass350_1000->fill(phistar);
      }

      //---------------------- at least one jet --------------------------------
      if (jets.size() < 1) {
        return;
      }

      if (z->mass() > 50. && z->mass() < 76.) {
        _pt_1jet_mass50_76->fill(z->pT());
      }
      else if (z->mass() > 76. && z->mass() < 106.) {
        _pt_1jet_mass76_106->fill(z->pT());
      }
      else if (z->mass() > 106. && z->mass() < 170.) {
        _pt_1jet_mass106_170->fill(z->pT());
      }
      else if (z->mass() > 170. && z->mass() < 350.) {
        _pt_1jet_mass170_350->fill(z->pT());
      }
    }

    /// Calculates the ratio between two histograms
    void calculateRatio(int d, const Histo1DPtr& numerator, const Histo1DPtr& denominator) {
      Estimate1DPtr ratio;
      book(ratio, d, 1, 1);

      // The denominator has a finer binning, so rebin it
      auto rebinned_den = denominator->clone();
      rebinned_den.rebinX(numerator->xEdges());

      divide(*numerator, rebinned_den, ratio);
    }

    /// Normalise histograms etc., after the run
    void finalize() {
      double norm = (sumOfWeights() != 0) ? crossSection() / picobarn / sumOfWeights() : 1.0;

      if (_mode == 2) {
        norm /= 2.;
      }

      scale(_pt_0jet_mass50_76, norm);
      scale(_pt_0jet_mass76_106, norm);
      scale(_pt_0jet_mass106_170, norm);
      scale(_pt_0jet_mass170_350, norm);
      scale(_pt_0jet_mass350_1000, norm);

      scale(_pt_1jet_mass50_76, norm);
      scale(_pt_1jet_mass76_106, norm);
      scale(_pt_1jet_mass106_170, norm);
      scale(_pt_1jet_mass170_350, norm);

      scale(_phistar_mass50_76, norm);
      scale(_phistar_mass76_106, norm);
      scale(_phistar_mass106_170, norm);
      scale(_phistar_mass170_350, norm);
      scale(_phistar_mass350_1000, norm);

      // Calculate the ratios
      calculateRatio(29, _pt_0jet_mass50_76, _pt_0jet_mass76_106);
      calculateRatio(31, _pt_0jet_mass106_170, _pt_0jet_mass76_106);
      calculateRatio(33, _pt_0jet_mass170_350, _pt_0jet_mass76_106);
      calculateRatio(35, _pt_0jet_mass350_1000, _pt_0jet_mass76_106);

      calculateRatio(37, _pt_1jet_mass50_76, _pt_1jet_mass76_106);
      calculateRatio(39, _pt_1jet_mass106_170, _pt_1jet_mass76_106);
      calculateRatio(41, _pt_1jet_mass170_350, _pt_1jet_mass76_106);

      calculateRatio(43, _phistar_mass50_76, _phistar_mass76_106);
      calculateRatio(45, _phistar_mass106_170, _phistar_mass76_106);
      calculateRatio(47, _phistar_mass170_350, _phistar_mass76_106);
      calculateRatio(49, _phistar_mass350_1000, _phistar_mass76_106);
    }

  protected:

    size_t _mode;

  private:

    /// @name Histograms
    Histo1DPtr _pt_0jet_mass50_76, _pt_0jet_mass76_106, _pt_0jet_mass106_170, _pt_0jet_mass170_350,
        _pt_0jet_mass350_1000;
    Histo1DPtr _pt_1jet_mass50_76, _pt_1jet_mass76_106, _pt_1jet_mass106_170, _pt_1jet_mass170_350;
    Histo1DPtr _phistar_mass50_76, _phistar_mass76_106, _phistar_mass106_170, _phistar_mass170_350,
        _phistar_mass350_1000;
  };

  RIVET_DECLARE_PLUGIN(CMS_2022_I2079374);
}