Rivet analyses

Dilepton mass spectrum in 13 TeV pp collisions with 139/fb Run 2 dataset

Experiment: ATLAS (LHC)

Inspire ID: 1725190

Status: VALIDATED SINGLEWEIGHT NOREENTRY

Authors: - Andy Buckley

References: - Expt page: ATLAS-EXOT-2018-08 - Phys.Lett. B796 (2019) 68-87 - DOI:10.1016/j.physletb.2019.07.016 - arXiv: 1903.06248

Beams: p+ p+

Beam energies: (6500.0, 6500.0)GeV

Run details: - Dilepton events, either full process, BSM signal-only, or signal + SM interference.

A search for high-mass dielectron and dimuon resonances in the mass range of 250 GeV to 6 TeV. Functional forms have been fitted to the components of Crystal Ball + Gaussian dilepton invariant-mass distribution smearing functions for electrons and muons separately, which are encoded in this analysis to allow particle-level MC to be compared to the reco-level experimental data.

Source code:ATLAS_2019_I1725190.cc

// -*- C++ -*-
#include "Rivet/Analysis.hh"
#include "Rivet/Projections/FinalState.hh"
#include "Rivet/Projections/ChargedFinalState.hh"
#include "Rivet/Projections/PromptFinalState.hh"
#include "Rivet/Projections/FastJets.hh"
#include "Rivet/Projections/Smearing.hh"
#include "Rivet/Tools/Random.hh"

namespace Rivet {


  /// @brief Dilepton high-mass resonance search
  ///
  /// @todo Use the proper smearing system rather than hand-rolled sampling
  class ATLAS_2019_I1725190 : public Analysis {
  public:

    /// Constructor
    RIVET_DEFAULT_ANALYSIS_CTOR(ATLAS_2019_I1725190);


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

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

      PromptFinalState electrons(Cuts::abspid == PID::ELECTRON && Cuts::Et > 30*GeV &&
                                 (Cuts::abseta < 2.47 && !Cuts::absetaIn(1.37, 1.52)));
      SmearedParticles recoelectrons(electrons, PARTICLE_EFF_ONE); //ELECTRON_EFF_ATLAS_RUN2_MEDIUM);
      declare(recoelectrons, "Elecs");

      PromptFinalState muons(Cuts::abspid == PID::MUON && Cuts::pT > 30*GeV && Cuts::abseta < 2.5);
      SmearedParticles recomuons(muons, PARTICLE_EFF_ONE);
      // [](const Particle& m) -> double {
      //   if (m.pT() < 1*TeV) return 0.69;
      //   if (m.pT() > 2.5*TeV) return 0.57;
      //   return 0.69 - 0.12*(m.pT() - 1*TeV)/(2.5*TeV - 1*TeV);
      // });
      declare(recomuons, "Muons");

      // Book histograms
      book(_h_mee, 1, 1, 1);
      book(_h_mmm, 2, 1, 1);
    }


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

      // Get leptons
      const Particles elecs = apply<ParticleFinder>(event, "Elecs").particlesByPt();
      const Particles muons = apply<ParticleFinder>(event, "Muons").particlesByPt();

      // MSG_INFO("Num e, mu = " << elecs.size() << ", " << muons.size());
      // for (const Particle& e : elecs) MSG_INFO(e.mom());
      // for (const Particle& m : muons) MSG_INFO(m.mom());

      // Isolation
      /// @todo Can't be done from provided information?
      // Particles isoelecs, isomuons;
      // for (const Particle& e : elecs) {
      // }

      // Choose the highest-pT lepton pair, preferring electrons
      if (elecs.size() < 2 && muons.size() < 2) vetoEvent;
      const Particle l1 = (elecs.size() >= 2) ? elecs[0] : muons[0];
      const Particle l2 = (elecs.size() >= 2) ? elecs[1] : muons[1];

      // Require opposite sign for muons only
      const bool mumu = (l1.abspid() == PID::MUON);
      if (mumu && l1.pid()*l2.pid() > 0) vetoEvent;

      // Get the true dilepton pair
      const FourMomentum pll = l1.mom() + l2.mom();
      const double mll = pll.mass();

      // Make sure we're in a region where the smearing and efficiencies are well-behaved
      if (mll < 200*GeV) vetoEvent;

      // Apply dilepton efficiency curves (as function of mX ~ mll)
      const double eff = mumu ?
        (0.54 - (mll - 200*GeV)/(6000*GeV - 200*GeV) * (0.54 - 0.38))  :
        (0.74 - 0.04*exp(-(mll-200*GeV)/100*GeV) - 0.08*exp(-(mll-200*GeV)/1000*GeV));
      if (rand01() > eff) vetoEvent;

      // Smear the dilepton mass with a CB + Gauss function
      double muCB, sigCB, alpCB, nCB, muG, sigG, kappa;
      if (!mumu) {
        const double lnm = log(mll);
        static const vector<double> pmuCB = {0.13287, -0.410663, -0.0126743, 2.9547e-6};
        muCB = pmuCB[0] + pmuCB[1]/lnm + pmuCB[2]*lnm + pmuCB[3]*pow(lnm, 4);
        static const vector<double> psigCB = {0.0136624, 0.230678, 1.73254};
        sigCB = sqrt(pow(psigCB[0],2) + pow(psigCB[1],2)/mll + pow(psigCB[2]/mll, 2));
        alpCB = 1.59112;
        static const vector<double> pnCB = {1.13055, 0.76705, 0.00298312};
        nCB = pnCB[0] + pnCB[1]*exp(-pnCB[2]*mll);
        static const vector<double> pmuG = {-0.00402708, 0.814172, -3.94281e-7, 7.97076e-6, -87.6397, -1.64806e-11};
        muG = pmuG[0] + pmuG[1]/mll + pmuG[2]*mll + pmuG[3]*pow(lnm,3) + pmuG[4]/sqr(mll) + pmuG[5]*sqr(mll);
        static const vector<double> psigG = {0.00553858, 0.140909, 0.644418};
        sigG = sqrt(sqr(psigG[0]) + sqr(psigG[1])/mll + sqr(psigG[2]/mll));
        static const vector<double> pkappa = {0.347003, 0.135768, 0.00372497, -2.2822e-5, 5.06351e-13};
        kappa = pkappa[0] + pkappa[1]*exp(-pkappa[2]*mll) + pkappa[3]*mll + pkappa[4]*pow(mll,3);
      } else {
        static const vector<double> pmuCB = {-0.0891397, 10.6169, -951.712, 74775.3, 5.60192e-5, -1.58827e-9, -3.81706e-13};
        muCB = pmuCB[0] + pmuCB[1]/mll + pmuCB[2]/sqr(mll) + pmuCB[3]/pow(mll,3) + pmuCB[4]*mll + pmuCB[5]*sqr(mll) + pmuCB[6]*pow(mll,3);
        static const vector<double> psigCB = {0.0836349, -8.98476, 491.19, 5.18068e-5, -3.45042e-10};
        sigCB = psigCB[0] + psigCB[1]/mll + psigCB[2]/sqr(mll) + psigCB[3]*mll + psigCB[4]*sqr(mll);
        static const vector<double> palpCB = {0.512577, 252.922, -79337.4, 7.31863e6, 0.000237883};
        alpCB = palpCB[0] + palpCB[1]/mll + palpCB[2]/sqr(mll) + palpCB[3]/pow(mll,3) + palpCB[4]*mll;
        static const vector<double> pnCB = {1.13055, 0.76705, 0.00298312};
        nCB = 6.08818;
        static const vector<double> pmuG = {-0.00410659, 2.82352e-6, -6.264e-9, 1.25547e-12, -9.94431e-17};
        muG = pmuG[0] + pmuG[1]*mll + pmuG[2]*sqr(mll) + pmuG[3]*pow(mll,3) + pmuG[4]*pow(mll,4);
        static const vector<double> psigG = {0.0214264, -0.795058, 15.4726, 3.38205e-5, -1.64984e-9};
        sigG = psigG[0] + psigG[1]/mll + psigG[2]/sqr(mll) + psigG[3]*mll + psigG[4]*sqr(mll);
        static const vector<double> pkappa = {0.235477, -31.2227, 3447.34, 4.54408e-5, -3.25374e-9};
        kappa = pkappa[0] + pkappa[1]/mll + pkappa[2]/sqr(mll) + pkappa[3]*mll + pkappa[4]*sqr(mll);
      }

      // Do the smearing
      double mll_scale = -1;
      do {
        mll_scale = (rand01() > kappa) ? randnorm(muG, sigG) : randcrystalball(alpCB, nCB, muCB, sigCB);
      } while (fabs(mll_scale) > 0.5);
      const double mll_reco = (1 + mll_scale) * mll;

      // Require high-Mll to avoid the Z peak
      if (mll_reco < 225*GeV) vetoEvent;

      // Fill Mll histograms
      (mumu ? _h_mmm : _h_mee)->fill(mll_reco/GeV);
    }


    /// Normalise histograms etc., after the run.
    //  The plot is expressed as number of events in a 10 GeV bin.
    //  Multiply by luminosity*cross section (in same units!) to get number
    //  of events a 1 GeV bin, then by 10 to get number of events in a 10 GeV.
    void finalize() {
      scale(_h_mee, 10.*crossSection()/picobarn*luminosity()/sumOfWeights());
      scale(_h_mmm, 10.*crossSection()/picobarn*luminosity()/sumOfWeights());
    }

    /// @}


    /// @name Histograms
    /// @{
    Histo1DPtr _h_mee, _h_mmm;
    /// @}


  };


  RIVET_DECLARE_PLUGIN(ATLAS_2019_I1725190);

}