rivet is hosted by Hepforge, IPPP Durham

Rivet analyses reference

ATLAS_2022_I2614196

Zy+jets at 13 TeV
Experiment: ATLAS (LHC)
Inspire ID: 2614196
Status: VALIDATED
Authors:
  • Lorenzo Rossini
  • Christian Gutschow
References: Beams: p+ p+
Beam energies: (6500.0, 6500.0) GeV
Run details:
  • pp -> Zy + jets at 13 TeV

Differential cross-section measurements of $Z\gamma$ production in association with hadronic jets are presented, using the full 139 fb${}^{-1}$ dataset of $\sqrt{s}$=13 TeV proton-proton collisions collected by the ATLAS detector during Run 2 of the LHC. Distributions are measured using events in which the $Z$ boson decays leptonically and the photon is usually radiated from an initial-state quark. Measurements are made in both one and two observables, including those sensitive to the hard scattering in the event and others which probe additional soft and collinear radiation. Different Standard Model predictions, from both parton-shower Monte Carlo simulation and fixed-order QCD calculations, are compared with the measurements. In general, good agreement is observed between data and predictions from MATRIX and MiNNLOPS, as well as next-to-leading-order predictions from MadGraph5_aMC@NLO and Sherpa.

Source code: ATLAS_2022_I2614196.cc
  1// -*- C++ -*-
  2#include "Rivet/Analysis.hh"
  3#include "Rivet/Projections/FinalState.hh"
  4#include "Rivet/Projections/FastJets.hh"
  5#include "Rivet/Projections/VetoedFinalState.hh"
  6#include "Rivet/Projections/LeptonFinder.hh"
  7#include "Rivet/Projections/PromptFinalState.hh"
  8#include "Rivet/Projections/InvisibleFinalState.hh"
  9
 10namespace Rivet {
 11
 12
 13  /// @brief Zy+jets at 13 TeV
 14  class ATLAS_2022_I2614196 : public Analysis {
 15  public:
 16
 17    /// Constructor
 18    RIVET_DEFAULT_ANALYSIS_CTOR(ATLAS_2022_I2614196);
 19
 20    /// @name Analysis methods
 21    /// @{
 22
 23    /// Book histograms and initialise projections before the run
 24    void init() {
 25
 26      // Prompt photons
 27      const PromptFinalState photon_fs(Cuts::abspid == PID::PHOTON && Cuts::pT > 30*GeV && Cuts::abseta < 2.37);
 28      declare(photon_fs, "Photons");
 29
 30      // Prompt leptons
 31      const PromptFinalState bareelectron_fs(Cuts::abspid == PID::ELECTRON);
 32      const PromptFinalState baremuon_fs(Cuts::abspid == PID::MUON);
 33
 34      // Dressed leptons
 35      const FinalState allphoton_fs(Cuts::abspid == PID::PHOTON);
 36      const Cut leptoncut = Cuts::pT > 25*GeV && Cuts::abseta < 2.47;
 37      const LeptonFinder dressedelectron_fs(bareelectron_fs, allphoton_fs, 0.1, leptoncut);
 38      const LeptonFinder dressedmuon_fs(baremuon_fs, allphoton_fs, 0.1, leptoncut);
 39
 40      declare(dressedelectron_fs, "Electrons");
 41      declare(dressedmuon_fs, "Muons");
 42
 43      // FS excluding the leading photon
 44      VetoedFinalState vfs;
 45      vfs.addVetoOnThisFinalState(photon_fs);
 46      vfs.addVetoOnThisFinalState(dressedmuon_fs);
 47      vfs.addVetoOnThisFinalState(InvisibleFinalState());
 48      declare(vfs, "isolatedFS");
 49
 50      VetoedFinalState hadrons(FinalState(Cuts::abseta < 4.4));
 51      hadrons.addVetoOnThisFinalState(dressedelectron_fs);
 52      hadrons.addVetoOnThisFinalState(dressedmuon_fs);
 53      declare(hadrons, "hadrons");
 54
 55      //Jets
 56      FastJets jets(hadrons, JetAlg::ANTIKT, 0.4, JetMuons::ALL, JetInvisibles::DECAY);
 57      declare(jets, "jets");
 58
 59      // Histograms
 60      book(_h["pTll"], 1, 1, 1);
 61      book(_h["DiffpTll_pTy"], 2, 1, 1);
 62      book(_h["SumpTll_pTy"], 3, 1, 1);
 63      book(_h["DeltaRll"], 4, 1, 1);
 64      book(_h["NJetsMix"], 5, 1, 1);
 65      book(_h["pTJet1"], 6, 1, 1);
 66      book(_h["pTJet2"], 7, 1, 1);
 67      book(_h["RatiopTJet12"], 8, 1, 1);
 68      book(_h["mjj"],  9, 1, 1);
 69      book(_h["mllyj"], 10, 1, 1);
 70      book(_h["HT"], 11, 1, 1);
 71      book(_h["pTysqrtHT"], 12, 1, 1);
 72      book(_h["DeltaPhiJetY"], 13, 1, 1);
 73      book(_h["pTllyj"], 14, 1, 1);
 74      book(_h["phi_CS_Slice1"], 51, 1, 1);
 75      book(_h["phi_CS_Slice2"], 51, 1, 2);
 76      book(_h["phi_CS_Slice3"], 51, 1, 3);
 77      book(_h["phi_CS_Slice4"], 51, 1, 4);
 78      book(_h["phi_CS_Slice5"], 51, 1, 5);
 79      book(_h["ctheta_CS_Slice1"], 52, 1, 1);
 80      book(_h["ctheta_CS_Slice2"], 52, 1, 2);
 81      book(_h["ctheta_CS_Slice3"], 52, 1, 3);
 82      book(_h["ctheta_CS_Slice4"], 52, 1, 4);
 83      book(_h["ctheta_CS_Slice5"], 52, 1, 5);
 84      book(_h["RatiopTlly_mlly_Slice1"], 17, 1, 1);
 85      book(_h["RatiopTlly_mlly_Slice2"], 18, 1, 1);
 86      book(_h["RatiopTlly_mlly_Slice3"], 19, 1, 1);
 87      book(_h["DiffpTll_pTy_Slice1"], 20, 1, 1);
 88      book(_h["DiffpTll_pTy_Slice2"], 21, 1, 1);
 89      book(_h["DiffpTll_pTy_Slice3"], 22, 1, 1);
 90      book(_h["pTllyj_Slice1"], 23, 1, 1);
 91      book(_h["pTllyj_Slice2"], 24, 1, 1);
 92      book(_h["pTllyj_Slice3"], 25, 1, 1);
 93
 94      _ptllAxis = YODA::Axis<double>({0, 35, 60, 90, 135, 2500});
 95    }
 96
 97
 98   /// Perform the per-event analysis
 99   void analyze(const Event& event) {
100
101     // Get objects
102     Particles electrons = apply<LeptonFinder>(event, "Electrons").particlesByPt();
103     Particles muons = apply<LeptonFinder>(event, "Muons").particlesByPt();
104     Particles photons = apply<PromptFinalState>(event, "Photons").particlesByPt();
105
106     if (photons.empty())  vetoEvent;
107     if (electrons.size()<2 && muons.size()<2)  vetoEvent;
108
109     Particles lep;
110     // Sort the dressed leptons by pt
111     if (electrons.size() >= 2) {
112       lep.push_back(electrons[0]);
113       lep.push_back(electrons[1]);
114     }
115     else {
116       lep.push_back(muons[0]);
117       lep.push_back(muons[1]);
118     }
119
120     if (lep[0].momentum().pT() < 30*GeV)  vetoEvent;
121
122     const double mll = (lep[0].momentum() + lep[1].momentum()).mass();
123     if (mll < 40*GeV)  vetoEvent;
124
125     Particles selectedPh;
126     Particles fs = apply<VetoedFinalState>(event, "isolatedFS").particles();
127     for (const Particle& ph : photons) {
128       // check photon isolation
129       double coneEnergy(0.0);
130       for (const Particle& p : fs) {
131         if ( deltaR(ph, p) < 0.2 )  coneEnergy += p.Et();
132       }
133       if (coneEnergy / ph.momentum().pT() > 0.07 )  continue;
134       if (any(electrons, deltaRLess(ph, 0.4)))  continue;
135       if (any(muons, deltaRLess(ph, 0.4)))  continue;
136       selectedPh.push_back(ph);
137     }
138
139     if(selectedPh.size()<1) vetoEvent;
140
141     const double mlly   = (lep[0].momentum() + lep[1].momentum() + selectedPh[0].momentum()).mass();
142     if (mll + mlly <= 182*GeV)  vetoEvent;
143
144
145     // Get jets
146     // abseta
147     const Cut jetscut = (Cuts::pT > 30*GeV && Cuts::absrap < 2.5) || (Cuts::pT > 50*GeV && Cuts::absrapIn(2.5, 4.5));
148     const Cut jetscut25 = (Cuts::pT > 25*GeV && Cuts::abseta < 2.5) || (Cuts::pT > 50*GeV && Cuts::absetaIn(2.5, 4.4));
149     Jets jetsMix = apply<FastJets>(event, "jets").jetsByPt(jetscut);
150     Jets jetsMix25 = apply<FastJets>(event, "jets").jetsByPt(jetscut25);
151     iselectIfAnyDeltaRLess(jetsMix, photons, 0.4);
152     iselectIfAnyDeltaRLess(jetsMix, electrons, 0.2);
153     iselectIfAnyDeltaRLess(jetsMix25, photons, 0.4);
154     iselectIfAnyDeltaRLess(jetsMix25, electrons, 0.2);
155
156     // Jet multiplicity
157     size_t njets = jetsMix.size();
158     _h["NJetsMix"]->fill(njets);
159
160     double pTJet1 = njets>0 ? jetsMix[0].pT(): -1.;
161     double pTJet2 = njets>1 ? jetsMix[1].pT(): -1.;
162     double mjj    = njets>1 ? (jetsMix[0].mom() + jetsMix[1].mom()).mass() : -1.;
163     double mllyj  = njets>0 ? (lep[0].mom() + lep[1].mom() + selectedPh[0].mom() + jetsMix[0].mom()).mass() : -1.;
164
165     double HT = lep[0].pT() + lep[1].pT() + selectedPh[0].pT();
166     HT = sum(jetsMix25, Kin::pT, HT);
167
168     double pTy    = selectedPh[0].pT();
169     double DeltaPhijy = njets>0 ? fabs(deltaPhi(jetsMix[0], selectedPh[0])) : -1.;
170     double DeltaRll   = fabs(deltaR(lep[0], lep[1]));
171     double pTll   = (lep[0].momentum() + lep[1].momentum()).pT();
172
173     double pTlly = (lep[0].mom() + lep[1].mom() + selectedPh[0].mom()).pT();
174     double Ratio_pTlly_mlly = pTlly/mlly;
175     double Diff_pTll_pTy    = pTll-pTy;
176     double Sum_pTll_pTy     = pTll+pTy;
177     double pTllyj = njets>0 ? (lep[0].mom() + lep[1].mom() + selectedPh[0].mom() + jetsMix[0].mom()).pT() : -1.;
178
179     // Fill plots
180     _h["pTJet1"]->fill(pTJet1/GeV);
181     _h["pTJet2"]->fill(pTJet2/GeV);
182     _h["RatiopTJet12"]->fill(pTJet2/pTJet1);
183     _h["mllyj"]->fill(mllyj/GeV);
184     _h["mjj"]->fill(mjj/GeV);
185     _h["HT"]->fill(HT/GeV);
186     _h["pTysqrtHT"]->fill((pTy/GeV)/sqrt(HT/GeV));
187     _h["DeltaPhiJetY"]->fill(DeltaPhijy);
188     _h["DeltaRll"]->fill(DeltaRll);
189     _h["pTll"]->fill(pTll/GeV);
190
191     if      (mlly > 125*GeV && mlly < 200*GeV)  _h["RatiopTlly_mlly_Slice1"]->fill(Ratio_pTlly_mlly);
192     else if (mlly > 200*GeV && mlly < 300*GeV)  _h["RatiopTlly_mlly_Slice2"]->fill(Ratio_pTlly_mlly);
193     else if (mlly > 300*GeV)                    _h["RatiopTlly_mlly_Slice3"]->fill(Ratio_pTlly_mlly);
194
195     _h["SumpTll_pTy"]->fill(Sum_pTll_pTy/GeV);
196     _h["DiffpTll_pTy"]->fill(Diff_pTll_pTy/GeV);
197     if      (Sum_pTll_pTy < 200*GeV)                            _h["DiffpTll_pTy_Slice1"]->fill(Diff_pTll_pTy/GeV);
198     else if (Sum_pTll_pTy > 200*GeV && Sum_pTll_pTy < 300*GeV)  _h["DiffpTll_pTy_Slice2"]->fill(Diff_pTll_pTy/GeV);
199     else if (Sum_pTll_pTy > 300*GeV)                            _h["DiffpTll_pTy_Slice3"]->fill(Diff_pTll_pTy/GeV);
200
201     _h["pTllyj"]->fill(pTllyj/GeV);
202     if      (pTlly < 50*GeV)                    _h["pTllyj_Slice1"]->fill(pTllyj/GeV);
203     else if (pTlly > 50*GeV && pTlly < 75*GeV)  _h["pTllyj_Slice2"]->fill(pTllyj/GeV);
204     else if (pTlly > 75*GeV)                    _h["pTllyj_Slice3"]->fill(pTllyj/GeV);
205
206     // polarization variables
207     FourMomentum lep1, lep2;
208     if (lep[0].pid() > 0 && lep[1].pid() < 0) {
209       lep1 = lep[0].mom();
210       lep2 = lep[1].mom();
211     }
212     else if (lep[0].pid() < 0 && lep[1].pid() > 0) {
213       lep1 = lep[1].mom();
214       lep2 = lep[0].mom();
215     }
216     else {
217       MSG_DEBUG("Same sign lepton!");
218       vetoEvent;
219     }
220
221     FourMomentum Dilepton = (lep1+lep2);
222     FourMomentum lep1_CS;
223     LorentzTransform boost;
224     boost.setBetaVec(-Dilepton.betaVec());
225     lep1_CS = boost.transform(lep1);
226     const double ctheta_CS = cos(lep1_CS.p3().theta());
227     const double phi_CS    = lep1_CS.p3().azimuthalAngle();
228
229     size_t idx = _ptllAxis.index(pTll);
230     if (mll > 80*GeV && mll<100*GeV && idx && idx <= _ptllAxis.numBins()) {
231       const string s = "_Slice" + to_string(idx);
232       _h["ctheta_CS" + s]->fill(ctheta_CS);
233       _h["phi_CS" + s]->fill(phi_CS);
234     }
235   }
236
237
238   /// Normalise histograms etc., after the run
239   void finalize() {
240     const double sf = crossSection()/femtobarn/sumOfWeights();
241     scale(_h, sf);
242   }
243
244    /// @}
245
246
247  private:
248
249    /// Histograms
250    map<string, Histo1DPtr> _h;
251
252    YODA::Axis<double> _ptllAxis;
253
254  };
255
256
257  RIVET_DECLARE_PLUGIN(ATLAS_2022_I2614196);
258
259}