rivet is hosted by Hepforge, IPPP Durham

Rivet analyses reference

ATLAS_2017_I1495243

ttbar + jets at 13 TeV
Experiment: ATLAS (LHC)
Inspire ID: 1495243
Status: VALIDATED
Authors:
  • Callie Bertsche
  • Judith Katzy
  • Krishna Kulkarni
  • Christian Gutschow
References: Beams: p+ p+
Beam energies: (6500.0, 6500.0) GeV
Run details:
  • p + p -> ttbar (dileptonic, needs high statistics [ 2 million] to populate gap fractions).

Measurements of jet activity in top-quark pair events produced in proton--proton collisions are presented, using 3.2 fb$^{-1}$ of $pp$ collision data at a centre-of-mass energy of 13 TeV collected by the ATLAS experiment at the Large Hadron Collider. Events are chosen by requiring an opposite-charge $e\mu$ pair and two $b$-tagged jets in the final state. The normalised differential cross-sections of top-quark pair production are presented as functions of additional-jet multiplicity and transverse momentum, $p_\text{T}$. The fraction of signal events that do not contain additional jet activity in a given rapidity region, the gap fraction, is measured as a function of the $p_\text{T}$ threshold for additional jets, and is also presented for different invariant mass regions of the $e\mu b\bar{b}$ system. All measurements are corrected for detector effects and presented as particle-level distributions compared to predictions with different theoretical approaches for QCD radiation. While the kinematics of the jets from top-quark decays are described well, the generators show differing levels of agreement with the measurements of observables that depend on the production of additional jets.

Source code: ATLAS_2017_I1495243.cc
  1// -*- C++ -*-
  2#include "Rivet/Analysis.hh"
  3#include "Rivet/Projections/VetoedFinalState.hh"
  4#include "Rivet/Projections/IdentifiedFinalState.hh"
  5#include "Rivet/Projections/PromptFinalState.hh"
  6#include "Rivet/Projections/LeptonFinder.hh"
  7#include "Rivet/Projections/FastJets.hh"
  8
  9namespace Rivet {
 10
 11
 12  /// @brief $t\bar{t}$ + jets at 13 TeV
 13  class ATLAS_2017_I1495243 : public Analysis {
 14  public:
 15
 16    RIVET_DEFAULT_ANALYSIS_CTOR(ATLAS_2017_I1495243);
 17
 18
 19    void init() {
 20
 21      Cut eta_full = Cuts::abseta < 5.0 && Cuts::pT > 1.0*MeV;
 22      Cut eta_lep = Cuts::abseta < 2.5;
 23
 24      // Collect final state particles
 25      FinalState FS(eta_full);
 26
 27      // Get photons to dress leptons
 28      IdentifiedFinalState photons(FS);
 29      photons.acceptIdPair(PID::PHOTON);
 30
 31      // Projection to find the electrons
 32      IdentifiedFinalState el_id(FS);
 33      el_id.acceptIdPair(PID::ELECTRON);
 34      PromptFinalState electrons(el_id);
 35      electrons.acceptTauDecays(false);
 36      LeptonFinder dressedelectrons(electrons, photons, 0.1, Cuts::abseta < 2.5 && Cuts::pT > 25*GeV);
 37      declare(dressedelectrons, "electrons");
 38      LeptonFinder fulldressedelectrons(electrons, photons, 0.1, eta_full);
 39
 40      // Projection to find the muons
 41      IdentifiedFinalState mu_id(FS);
 42      mu_id.acceptIdPair(PID::MUON);
 43      PromptFinalState muons(mu_id);
 44      muons.acceptTauDecays(false);
 45      LeptonFinder dressedmuons(muons, photons, 0.1, Cuts::abseta < 2.5 && Cuts::pT > 25*GeV);
 46      declare(dressedmuons, "muons");
 47      LeptonFinder fulldressedmuons(muons, photons, 0.1, eta_full);
 48
 49      // Projection to find neutrinos to exclude from jets
 50      IdentifiedFinalState nu_id;
 51      nu_id.acceptNeutrinos();
 52      PromptFinalState neutrinos(nu_id);
 53      neutrinos.acceptTauDecays(false);
 54
 55      // Jet clustering
 56      VetoedFinalState vfs;
 57      vfs.addVetoOnThisFinalState(fulldressedelectrons);
 58      vfs.addVetoOnThisFinalState(fulldressedmuons);
 59      vfs.addVetoOnThisFinalState(neutrinos);
 60      FastJets jets(vfs, JetAlg::ANTIKT, 0.4, JetMuons::ALL, JetInvisibles::DECAY);
 61      declare(jets, "jets");
 62
 63      // Book Histograms
 64      book(_h["bjet_pt"] , 5,1,1);
 65      book(_h["2bjet_pt"], 6,1,1);
 66      book(_h["ljet_pt"] , 7,1,1);
 67
 68      for (size_t i = 0; i < 4; ++i) {
 69        book(_d["njet"  + to_str(i)], i+1, 1, 1);
 70        book(_h["Q0"    + to_str(i)], "_Q0"    + to_str(i+ 7), refData((i>1?"d":"d0") + to_str(i+ 8) + "-x01-y01"));
 71        book(_h["MQ0"   + to_str(i)], "_MQ0"   + to_str(i+12), refData("d" + to_str(i+12) + "-x01-y01"));
 72        book(_h["Qsum"  + to_str(i)], "_Qsum"  + to_str(i+16), refData("d" + to_str(i+16) + "-x01-y01"));
 73        book(_h["MQsum" + to_str(i)], "_MQsum" + to_str(i+20), refData("d" + to_str(i+20) + "-x01-y01"));
 74        book(_s["gapFracQ0"    + to_str(i)],  8+i, 1 ,1);
 75        book(_s["gapFracMQ0"   + to_str(i)], 12+i, 1, 1);
 76        book(_s["gapFracQsum"  + to_str(i)], 16+i, 1, 1);
 77        book(_s["gapFracMQsum" + to_str(i)], 20+i, 1, 1);
 78      }
 79    }
 80
 81
 82    void analyze(const Event& event) {
 83      // Get the selected objects, using the projections.
 84      Jets all_jets = apply<FastJets>(event, "jets").jetsByPt(Cuts::pT > 25*GeV && Cuts::abseta < 2.5);
 85
 86      const DressedLeptons electrons = discard(apply<LeptonFinder>(event, "electrons").dressedLeptons(),
 87        [&](const DressedLepton &e) {
 88          return any(all_jets, deltaRLess(e, 0.4));
 89        });
 90
 91      const DressedLeptons muons = discard(apply<LeptonFinder>(event, "muons").dressedLeptons(),
 92        [&](const DressedLepton &m) {
 93          return any(all_jets, deltaRLess(m, 0.4));
 94        });
 95
 96      if (electrons.size() != 1 || muons.size() != 1)  vetoEvent;
 97      if (electrons[0].charge() == muons[0].charge())  vetoEvent;
 98
 99      Jets bjets, extrajets;
100      for (Jet j : all_jets) {
101        size_t b_tagged = j.bTags(Cuts::pT > 5*GeV).size();
102        if (bjets.size() < 2 && b_tagged)  bjets += j;
103        else  extrajets += j;
104      }
105      if (bjets.size() < 2)  vetoEvent;
106
107      double bjetpt = bjets[0].pt();
108      if (bjetpt > 250*GeV)  bjetpt = 275*GeV;
109      _h["bjet_pt"]->fill(bjetpt);
110
111      double b2jetpt = bjets[1].pt();
112      if (b2jetpt > 150*GeV)  b2jetpt = 175*GeV;
113      _h["2bjet_pt"]->fill(b2jetpt);
114
115      if (extrajets.size()) {
116        double ljetpt = extrajets[0].pt();
117        if (ljetpt > 250*GeV)  ljetpt = 275*GeV;
118        _h["ljet_pt"]->fill(ljetpt);
119      }
120
121      double Memubb = (electrons[0].momentum() + muons[0].momentum() + bjets[0].momentum() + bjets[1].momentum()).mass();
122      vector<double> leadpt = { 0., 0., 0., 0. }, ptsum = { 0., 0., 0., 0. };
123      vector<size_t> njetcount = { 0, 0, 0, 0 };
124      for (size_t i = 0; i < extrajets.size(); ++i) {
125        double absrap = extrajets[i].absrap(), pt = extrajets[i].pT();
126        if (pt > 25*GeV)  ++njetcount[0];
127        if (pt > 40*GeV)  ++njetcount[1];
128        if (pt > 60*GeV)  ++njetcount[2];
129        if (pt > 80*GeV)  ++njetcount[3];
130
131        if (absrap < 0.8 && pt > leadpt[0])  leadpt[0] = pt;
132        else if (absrap > 0.8 && absrap < 1.5 && pt > leadpt[1])  leadpt[1] = pt;
133        else if (absrap > 1.5 && absrap < 2.1 && pt > leadpt[2])  leadpt[2] = pt;
134        if (absrap < 2.1 && pt > leadpt[3])  leadpt[3] = pt;
135
136        if (absrap < 0.8)  ptsum[0] += pt;
137        else if (absrap > 0.8 && absrap < 1.5)  ptsum[1] += pt;
138        else if (absrap > 1.5 && absrap < 2.1)  ptsum[2] += pt;
139        if (absrap < 2.1)  ptsum[3] += pt;
140      }
141
142
143      for (size_t i = 0; i < 4; ++i) {
144        size_t cutoff = i? 3 : 4;
145        if (njetcount[i] > cutoff)  njetcount[i] = cutoff;
146        _d["njet" + to_str(i)]->fill(discretise(njetcount[i], i));
147
148        if (leadpt[i] > 305*GeV)  leadpt[i] = 305*GeV;
149        _h["Q0" + to_str(i)]->fill(leadpt[i]);
150
151        if (ptsum[i] > 505*GeV)  ptsum[i] = 505*GeV;
152        _h["Qsum" + to_str(i)]->fill(ptsum[i]);
153      }
154
155
156      for (size_t i = 0; i < 4; ++i) {
157        if (i == 0 && !(Memubb < 300*GeV))  continue;
158        if (i == 1 && !(Memubb > 300*GeV && Memubb < 425*GeV))  continue;
159        if (i == 2 && !(Memubb > 425*GeV && Memubb < 600*GeV))  continue;
160        if (i == 3 && !(Memubb > 600*GeV))  continue;
161        _h["MQ0"   + to_str(i)]->fill(leadpt[3]);
162        _h["MQsum" + to_str(i)]->fill(ptsum[3]);
163      }
164    }
165
166
167    void constructGapFraction(Estimate1DPtr out, Histo1DPtr in) {
168      bool hasWeights = in->effNumEntries() != in->numEntries();
169      double denW  = in->sumW();
170      double denW2 = in->sumW2();
171      size_t nEnd  = out->numBins();
172
173      for (auto& b : out->bins()) {
174          double numW = in->sumW(), numW2 = in->sumW2();
175          for (size_t j = b.index(); j <= nEnd; ++j) {
176            numW  -= in->bin(j).sumW();
177            numW2 -= in->bin(j).sumW2();
178          }
179          double yval = safediv(numW, denW);
180          double yerr = sqrt(safediv(yval * (1 - yval), denW));
181          if (hasWeights) { // use F. James's approximation for weighted events
182            yerr = sqrt( safediv((1 - 2 * yval) * numW2 + yval * yval * denW2, denW * denW) );
183          }
184          b.set(yval, yerr);
185      }
186    }
187
188
189    void finalize() {
190
191      // Build gap fraction plots
192      for (size_t i = 0; i < 4; ++i) {
193        constructGapFraction(_s["gapFracQ0"    + to_str(i)], _h["Q0"    + to_str(i)]);
194        constructGapFraction(_s["gapFracMQ0"   + to_str(i)], _h["MQ0"   + to_str(i)]);
195        constructGapFraction(_s["gapFracQsum"  + to_str(i)], _h["Qsum"  + to_str(i)]);
196        constructGapFraction(_s["gapFracMQsum" + to_str(i)], _h["MQsum" + to_str(i)]);
197      }
198
199      // Normalize to cross-section
200      for (map<string, Histo1DPtr>::iterator hit = _h.begin(); hit != _h.end(); ++hit) {
201        if (hit->first.find("jet") != string::npos)  normalize(hit->second);
202      }
203      normalize(_d);
204    }
205
206    string discretise(const size_t n, const size_t axis) const {
207      if (n == 0) return "0"s;
208      if (n == 1) return "1"s;
209      if (n == 2) return "2"s;
210      if (axis) {
211        return ">= 3"s;
212      }
213      else if (n == 3) return "3"s;
214      else if (n <= 8) return "4.0 - 8.0"s;
215      return "OTHER"s;
216    }
217
218
219  private:
220
221    /// @name Histogram helper functions
222    map<string, Histo1DPtr> _h;
223    map<string, Estimate1DPtr> _s;
224    map<string, BinnedHistoPtr<string>> _d;
225  };
226
227
228  RIVET_DECLARE_PLUGIN(ATLAS_2017_I1495243);
229
230
231}