Rivet analyses referenceATLAS_2017_I1495243ttbar + jets at 13 TeVExperiment: ATLAS (LHC) Inspire ID: 1495243 Status: VALIDATED Authors:
Beam energies: (6500.0, 6500.0) GeV Run details:
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}
|