rivet is hosted by Hepforge, IPPP Durham
ATLAS_2016_I1458270.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 #include "Rivet/Analysis.hh"
00003 #include "Rivet/Projections/FinalState.hh"
00004 #include "Rivet/Projections/PromptFinalState.hh"
00005 #include "Rivet/Projections/FastJets.hh"
00006 #include "Rivet/Projections/Sphericity.hh"
00007 #include "Rivet/Projections/SmearedParticles.hh"
00008 #include "Rivet/Projections/SmearedJets.hh"
00009 #include "Rivet/Projections/SmearedMET.hh"
00010 #include "Rivet/Tools/Cutflow.hh"
00011 
00012 namespace Rivet {
00013 
00014 
00015   /// @brief ATLAS 0-lepton SUSY search with 3.2/fb of 13 TeV pp data
00016   class ATLAS_2016_I1458270 : public Analysis {
00017   public:
00018 
00019     /// Constructor
00020     DEFAULT_RIVET_ANALYSIS_CTOR(ATLAS_2016_I1458270);
00021 
00022 
00023     /// @name Analysis methods
00024     //@{
00025 
00026     /// Book histograms and initialise projections before the run
00027     void init() {
00028 
00029       // Initialise and register projections
00030       FinalState calofs(Cuts::abseta < 4.8);
00031       FastJets fj(calofs, FastJets::ANTIKT, 0.4);
00032       declare(fj, "TruthJets");
00033       declare(SmearedJets(fj, JET_SMEAR_ATLAS_RUN2, JET_BTAG_ATLAS_RUN2_MV2C20), "RecoJets");
00034 
00035       MissingMomentum mm(calofs);
00036       declare(mm, "TruthMET");
00037       declare(SmearedMET(mm, MET_SMEAR_ATLAS_RUN2), "RecoMET");
00038 
00039       PromptFinalState es(Cuts::abseta < 2.47 && Cuts::abspid == PID::ELECTRON, true, true);
00040       declare(es, "TruthElectrons");
00041       declare(SmearedParticles(es, ELECTRON_EFF_ATLAS_RUN2, ELECTRON_SMEAR_ATLAS_RUN2), "RecoElectrons");
00042 
00043       PromptFinalState mus(Cuts::abseta < 2.7 && Cuts::abspid == PID::MUON, true);
00044       declare(mus, "TruthMuons");
00045       declare(SmearedParticles(mus, MUON_EFF_ATLAS_RUN2, MUON_SMEAR_ATLAS_RUN2), "RecoMuons");
00046 
00047 
00048       // Book histograms/counters
00049       _h_2jl = bookCounter("2jl");
00050       _h_2jm = bookCounter("2jm");
00051       _h_2jt = bookCounter("2jt");
00052       _h_4jt = bookCounter("4jt");
00053       _h_5j  = bookCounter("5j");
00054       _h_6jm = bookCounter("6jm");
00055       _h_6jt = bookCounter("6jt");
00056 
00057 
00058       // Book cut-flows
00059       const vector<string> cuts2j = {"Pre-sel+MET+pT1", "Njet", "Dphi_min(j,MET)", "pT2", "MET/sqrtHT", "m_eff(incl)"};
00060       _flows.addCutflow("2jl", cuts2j);
00061       _flows.addCutflow("2jm", cuts2j);
00062       _flows.addCutflow("2jt", cuts2j);
00063       const vector<string> cutsXj = {"Pre-sel+MET+pT1", "Njet", "Dphi_min(j,MET)", "pT2", "pT4", "Aplanarity", "MET/m_eff(Nj)", "m_eff(incl)"};
00064       _flows.addCutflow("4jt", cutsXj);
00065       _flows.addCutflow("5j",  cutsXj);
00066       _flows.addCutflow("6jm", cutsXj);
00067       _flows.addCutflow("6jt", cutsXj);
00068 
00069     }
00070 
00071 
00072     /// Perform the per-event analysis
00073     void analyze(const Event& event) {
00074 
00075       _flows.fillinit();
00076 
00077       // Same MET cut for all signal regions
00078       //const Vector3 vmet = -apply<MissingMomentum>(event, "TruthMET").vectorEt();
00079       const Vector3 vmet = -apply<SmearedMET>(event, "RecoMET").vectorEt();
00080       const double met = vmet.mod();
00081       if (met < 200*GeV) vetoEvent;
00082 
00083       // Get baseline electrons, muons, and jets
00084       Particles elecs = apply<ParticleFinder>(event, "RecoElectrons").particles(Cuts::pT > 10*GeV);
00085       Particles muons = apply<ParticleFinder>(event, "RecoMuons").particles(Cuts::pT > 10*GeV);
00086       Jets jets = apply<JetAlg>(event, "RecoJets").jetsByPt(Cuts::pT > 20*GeV && Cuts::abseta < 2.8); ///< @todo Pile-up subtraction
00087 
00088       // Jet/electron/muons overlap removal and selection
00089       // Remove any |eta| < 2.8 jet within dR = 0.2 of a baseline electron
00090       for (const Particle& e : elecs)
00091         ifilter_discard(jets, deltaRLess(e, 0.2, RAPIDITY));
00092       // Remove any electron or muon with dR < 0.4 of a remaining (Nch > 3) jet
00093       for (const Jet& j : jets) {
00094         /// @todo Add track efficiency random filtering
00095         ifilter_discard(elecs, deltaRLess(j, 0.4, RAPIDITY));
00096         if (j.particles(Cuts::abscharge > 0 && Cuts::pT > 500*MeV).size() >= 3)
00097           ifilter_discard(muons, deltaRLess(j, 0.4, RAPIDITY));
00098       }
00099       // Discard the softer of any electrons within dR < 0.05
00100       for (size_t i = 0; i < elecs.size(); ++i) {
00101         const Particle& e1 = elecs[i];
00102         /// @todo Would be nice to pass a "tail view" for the filtering, but awkward without range API / iterator guts
00103         ifilter_discard(elecs, [&](const Particle& e2){ return e2.pT() < e1.pT() && deltaR(e1,e2) < 0.05; });
00104       }
00105 
00106       // Loose electron selection
00107       ifilter_discard(elecs, ParticleEffFilter(ELECTRON_IDEFF_ATLAS_RUN2_LOOSE));
00108 
00109       // Veto the event if there are any remaining baseline leptons
00110       if (!elecs.empty()) vetoEvent;
00111       if (!muons.empty()) vetoEvent;
00112 
00113       // Signal jets have pT > 50 GeV
00114       const Jets jets50 = filter_select(jets, Cuts::pT > 50*GeV);
00115       if (jets50.size() < 2) vetoEvent;
00116       vector<double> jetpts; transform(jets, jetpts, pT);
00117       vector<double> jetpts50; transform(jets50, jetpts50, pT);
00118       const double j1pt = jetpts50[0];
00119       const double j2pt = jetpts50[1];
00120       if (j1pt < 200*GeV) vetoEvent;
00121 
00122       // Construct multi-jet observables
00123       const double ht = sum(jetpts, 0.0);
00124       const double met_sqrt_ht = met / sqrt(ht);
00125       const double meff_incl = sum(jetpts50, met);
00126 
00127       // Get dphis between MET and jets
00128       vector<double> dphimets50; transform(jets50, dphimets50, deltaPhiWRT(vmet));
00129       const double min_dphi_met_2 = min(head(dphimets50, 2));
00130       const double min_dphi_met_3 = min(head(dphimets50, 3));
00131       MSG_DEBUG(dphimets50 << ", " << min_dphi_met_2 << ", " << min_dphi_met_3);
00132 
00133       // Jet aplanarity
00134       Sphericity sph; sph.calc(jets);
00135       const double aplanarity = sph.aplanarity();
00136 
00137 
00138       // Fill SR counters
00139       // 2-jet SRs
00140       if (_flows["2jl"].filltail({true, true, min_dphi_met_2 > 0.8, j2pt > 200*GeV,
00141               met_sqrt_ht > 15*sqrt(GeV), meff_incl > 1200*GeV})) _h_2jl->fill(event.weight());
00142       if (_flows["2jm"].filltail({j1pt > 300*GeV, true, min_dphi_met_2 > 0.4, j2pt > 50*GeV,
00143               met_sqrt_ht > 15*sqrt(GeV), meff_incl > 1600*GeV})) _h_2jm->fill(event.weight());
00144       if (_flows["2jt"].filltail({true, true, min_dphi_met_2 > 0.8, j2pt > 200*GeV,
00145               met_sqrt_ht > 20*sqrt(GeV), meff_incl > 2000*GeV})) _h_2jt->fill(event.weight());
00146 
00147       // Upper multiplicity SRs
00148       const double j4pt = jets50.size() > 3 ? jetpts50[3] : -1;
00149       const double j5pt = jets50.size() > 4 ? jetpts50[4] : -1;
00150       const double j6pt = jets50.size() > 5 ? jetpts50[5] : -1;
00151       const double meff_4 = jets50.size() > 3 ? sum(head(jetpts50, 4), 0.0) : -1;
00152       const double meff_5 = jets50.size() > 4 ? meff_4 + jetpts50[4] : -1;
00153       const double meff_6 = jets50.size() > 5 ? meff_5 + jetpts50[5] : -1;
00154       const double met_meff_4 = met / meff_4;
00155       const double met_meff_5 = met / meff_5;
00156       const double met_meff_6 = met / meff_6;
00157       const double min_dphi_met_more = jets50.size() > 3 ? min(tail(dphimets50, -3)) : -1;
00158 
00159       if (_flows["4jt"].filltail({true, jets50.size() >= 4, min_dphi_met_3 > 0.4 && min_dphi_met_more > 0.2,
00160               jetpts[1] > 200*GeV, j4pt > 100*GeV, aplanarity > 0.04, met_meff_4 > 0.20, meff_incl > 2200*GeV}))
00161         _h_4jt->fill(event.weight());
00162       if (_flows["5j"].filltail({true, jets50.size() >= 5, min_dphi_met_3 > 0.4 && min_dphi_met_more > 0.2,
00163               jetpts[1] > 200*GeV, j4pt > 100*GeV && j5pt > 50*GeV, aplanarity > 0.04, met_meff_5 > 0.25, meff_incl > 1600*GeV}))
00164         _h_5j->fill(event.weight());
00165       if (_flows["6jm"].filltail({true, jets50.size() >= 6, min_dphi_met_3 > 0.4 && min_dphi_met_more > 0.2,
00166               jetpts[1] > 200*GeV, j4pt > 100*GeV && j6pt > 50*GeV, aplanarity > 0.04, met_meff_6 > 0.25, meff_incl > 1600*GeV}))
00167         _h_6jm->fill(event.weight());
00168       if (_flows["6jt"].filltail({true, jets50.size() >= 6, min_dphi_met_3 > 0.4 && min_dphi_met_more > 0.2,
00169               jetpts[1] > 200*GeV, j4pt > 100*GeV && j6pt > 50*GeV, aplanarity > 0.04, met_meff_6 > 0.20, meff_incl > 2000*GeV}))
00170         _h_6jt->fill(event.weight());
00171 
00172     }
00173 
00174 
00175     /// Normalise histograms etc., after the run
00176     void finalize() {
00177 
00178       const double sf = 3.2*crossSection()/femtobarn/sumOfWeights();
00179       scale({_h_2jl, _h_2jl, _h_2jl}, sf);
00180       scale({_h_4jt, _h_5j}, sf);
00181       scale({_h_6jm, _h_6jt}, sf);
00182 
00183       MSG_INFO("CUTFLOWS:\n\n" << _flows);
00184 
00185     }
00186 
00187     //@}
00188 
00189 
00190   private:
00191 
00192     /// @name Histograms
00193     //@{
00194     CounterPtr _h_2jl, _h_2jm, _h_2jt;
00195     CounterPtr _h_4jt, _h_5j;
00196     CounterPtr _h_6jm, _h_6jt;
00197     //@}
00198 
00199     /// Cut-flows
00200     Cutflows _flows;
00201 
00202   };
00203 
00204 
00205 
00206   // The hook for the plugin system
00207   DECLARE_RIVET_PLUGIN(ATLAS_2016_I1458270);
00208 
00209 
00210 }