ATLAS_2012_I1094568.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/IdentifiedFinalState.hh" 00005 #include "Rivet/Projections/LeadingParticlesFinalState.hh" 00006 #include "Rivet/Projections/UnstableFinalState.hh" 00007 #include "Rivet/Projections/HadronicFinalState.hh" 00008 #include "Rivet/Projections/VetoedFinalState.hh" 00009 #include "Rivet/Projections/DressedLeptons.hh" 00010 #include "Rivet/Projections/FastJets.hh" 00011 00012 namespace Rivet { 00013 00014 00015 struct ATLAS_2012_I1094568_Plots { 00016 // Track which veto region this is, to match the autobooked histograms 00017 int region_index; 00018 00019 // Lower rapidity boundary or veto region 00020 double y_low; 00021 // Upper rapidity boundary or veto region 00022 double y_high; 00023 00024 double vetoJetPt_Q0; 00025 double vetoJetPt_Qsum; 00026 00027 // Histograms to store the veto jet pT and sum(veto jet pT) histograms. 00028 Histo1DPtr _h_vetoJetPt_Q0; 00029 Histo1DPtr _h_vetoJetPt_Qsum; 00030 00031 // Scatter2Ds for the gap fractions 00032 Scatter2DPtr _d_gapFraction_Q0; 00033 Scatter2DPtr _d_gapFraction_Qsum; 00034 }; 00035 00036 00037 00038 /// Top pair production with central jet veto 00039 class ATLAS_2012_I1094568 : public Analysis { 00040 public: 00041 00042 /// Constructor 00043 ATLAS_2012_I1094568() 00044 : Analysis("ATLAS_2012_I1094568") 00045 { } 00046 00047 00048 /// Book histograms and initialise projections before the run 00049 void init() { 00050 00051 const FinalState fs(Cuts::abseta < 4.5); 00052 addProjection(fs, "ALL_FS"); 00053 00054 /// Get electrons from truth record 00055 IdentifiedFinalState elec_fs(Cuts::abseta < 2.47 && Cuts::pT > 25*GeV); 00056 elec_fs.acceptIdPair(PID::ELECTRON); 00057 addProjection(elec_fs, "ELEC_FS"); 00058 00059 /// Get muons which pass the initial kinematic cuts: 00060 IdentifiedFinalState muon_fs(Cuts::abseta < 2.5 && Cuts::pT > 20*GeV); 00061 muon_fs.acceptIdPair(PID::MUON); 00062 addProjection(muon_fs, "MUON_FS"); 00063 00064 /// Get all neutrinos. These will not be used to form jets. 00065 /// We'll use the highest 2 pT neutrinos to calculate the MET 00066 IdentifiedFinalState neutrino_fs(Cuts::abseta < 4.5); 00067 neutrino_fs.acceptNeutrinos(); 00068 addProjection(neutrino_fs, "NEUTRINO_FS"); 00069 00070 // Final state used as input for jet-finding. 00071 // We include everything except the muons and neutrinos 00072 VetoedFinalState jet_input(fs); 00073 jet_input.vetoNeutrinos(); 00074 jet_input.addVetoPairId(PID::MUON); 00075 addProjection(jet_input, "JET_INPUT"); 00076 00077 // Get the jets 00078 FastJets jets(jet_input, FastJets::ANTIKT, 0.4); 00079 addProjection(jets, "JETS"); 00080 00081 // Initialise weight counter 00082 m_total_weight = 0.0; 00083 00084 // Init histogramming for the various regions 00085 m_plots[0].region_index = 1; 00086 m_plots[0].y_low = 0.0; 00087 m_plots[0].y_high = 0.8; 00088 initializePlots(m_plots[0]); 00089 // 00090 m_plots[1].region_index = 2; 00091 m_plots[1].y_low = 0.8; 00092 m_plots[1].y_high = 1.5; 00093 initializePlots(m_plots[1]); 00094 // 00095 m_plots[2].region_index = 3; 00096 m_plots[2].y_low = 1.5; 00097 m_plots[2].y_high = 2.1; 00098 initializePlots(m_plots[2]); 00099 // 00100 m_plots[3].region_index = 4; 00101 m_plots[3].y_low = 0.0; 00102 m_plots[3].y_high = 2.1; 00103 initializePlots(m_plots[3]); 00104 } 00105 00106 00107 void initializePlots(ATLAS_2012_I1094568_Plots& plots) { 00108 const string vetoPt_Q0_name = "TMP/vetoJetPt_Q0_" + to_str(plots.region_index); 00109 plots.vetoJetPt_Q0 = 0.0; 00110 plots._h_vetoJetPt_Q0 = bookHisto1D(vetoPt_Q0_name, 200, 0.0, 1000.0); 00111 plots._d_gapFraction_Q0 = bookScatter2D(plots.region_index, 1, 1); 00112 foreach (Point2D p, refData(plots.region_index, 1, 1).points()) { 00113 p.setY(0, 0); 00114 plots._d_gapFraction_Q0->addPoint(p); 00115 } 00116 00117 const string vetoPt_Qsum_name = "TMP/vetoJetPt_Qsum_" + to_str(plots.region_index); 00118 plots._h_vetoJetPt_Qsum = bookHisto1D(vetoPt_Qsum_name, 200, 0.0, 1000.0); 00119 plots._d_gapFraction_Qsum = bookScatter2D(plots.region_index, 2, 1); 00120 plots.vetoJetPt_Qsum = 0.0; 00121 foreach (Point2D p, refData(plots.region_index, 2, 1).points()) { 00122 p.setY(0, 0); 00123 plots._d_gapFraction_Qsum->addPoint(p); 00124 } 00125 } 00126 00127 00128 /// Perform the per-event analysis 00129 void analyze(const Event& event) { 00130 00131 const double weight = event.weight(); 00132 00133 /// Get the various sets of final state particles 00134 const Particles& elecFS = applyProjection<IdentifiedFinalState>(event, "ELEC_FS").particlesByPt(); 00135 const Particles& muonFS = applyProjection<IdentifiedFinalState>(event, "MUON_FS").particlesByPt(); 00136 const Particles& neutrinoFS = applyProjection<IdentifiedFinalState>(event, "NEUTRINO_FS").particlesByPt(); 00137 00138 // Get all jets with pT > 25 GeV 00139 const Jets& jets = applyProjection<FastJets>(event, "JETS").jetsByPt(25.0*GeV); 00140 00141 // Keep any jets that pass the initial rapidity cut 00142 vector<const Jet*> central_jets; 00143 foreach(const Jet& j, jets) { 00144 if (j.absrap() < 2.4) central_jets.push_back(&j); 00145 } 00146 00147 // For each of the jets that pass the rapidity cut, only keep those that are not 00148 // too close to any leptons 00149 vector<const Jet*> good_jets; 00150 foreach(const Jet* j, central_jets) { 00151 bool goodJet = true; 00152 00153 foreach (const Particle& e, elecFS) { 00154 double elec_jet_dR = deltaR(e.momentum(), j->momentum()); 00155 if (elec_jet_dR < 0.4) { goodJet = false; break; } 00156 } 00157 if (!goodJet) continue; 00158 if (!goodJet) continue; 00159 00160 foreach (const Particle& m, muonFS) { 00161 double muon_jet_dR = deltaR(m.momentum(), j->momentum()); 00162 if (muon_jet_dR < 0.4) { goodJet = false; break; } 00163 } 00164 if (!goodJet) continue; 00165 00166 good_jets.push_back(j); 00167 } 00168 00169 // Get b hadrons with pT > 5 GeV 00170 /// @todo This is a hack -- replace with UnstableFinalState 00171 vector<HepMC::GenParticle*> B_hadrons; 00172 vector<HepMC::GenParticle*> allParticles = particles(event.genEvent()); 00173 for (size_t i = 0; i < allParticles.size(); i++) { 00174 GenParticle* p = allParticles[i]; 00175 if (!PID::isHadron(p->pdg_id()) || !PID::hasBottom(p->pdg_id())) continue; 00176 if (p->momentum().perp() < 5*GeV) continue; 00177 B_hadrons.push_back(p); 00178 } 00179 00180 // For each of the good jets, check whether any are b-jets (via dR matching) 00181 vector<const Jet*> b_jets; 00182 foreach(const Jet* j, good_jets) { 00183 bool isbJet = false; 00184 foreach(HepMC::GenParticle* b, B_hadrons) { 00185 if (deltaR(j->momentum(), FourMomentum(b->momentum())) < 0.3) isbJet = true; 00186 } 00187 if (isbJet) b_jets.push_back(j); 00188 } 00189 00190 00191 // Check the good jets again and keep track of the "additional jets" 00192 // i.e. those which are not either of the 2 highest pT b-jets 00193 vector<const Jet*> veto_jets; 00194 int n_bjets_matched = 0; 00195 foreach(const Jet* j, good_jets) { 00196 bool isBJet = false; 00197 foreach(const Jet* b, b_jets) { 00198 if (n_bjets_matched == 2) break; 00199 if (b == j){isBJet = true; ++ n_bjets_matched;} 00200 } 00201 if (!isBJet) veto_jets.push_back(j); 00202 } 00203 00204 00205 // Get the MET by taking the vector sum of all neutrinos 00206 /// @todo Use MissingMomentum instead? 00207 double MET = 0; 00208 FourMomentum p_MET; 00209 foreach (const Particle& p, neutrinoFS) { 00210 p_MET = p_MET + p.momentum(); 00211 } 00212 MET = p_MET.pT(); 00213 00214 // Now we have everything we need to start doing the event selections 00215 bool passed_ee = false; 00216 vector<const Jet*> vetoJets_ee; 00217 00218 // We want exactly 2 electrons... 00219 if (elecFS.size() == 2) { 00220 // ... with opposite sign charges. 00221 if (charge(elecFS[0]) != charge(elecFS[1])) { 00222 // Check the MET 00223 if (MET >= 40*GeV) { 00224 // Do some dilepton mass cuts 00225 const double dilepton_mass = (elecFS[0].momentum() + elecFS[1].momentum()).mass(); 00226 if (dilepton_mass >= 15*GeV) { 00227 if (fabs(dilepton_mass - 91.0*GeV) >= 10.0*GeV) { 00228 // We need at least 2 b-jets 00229 if (b_jets.size() > 1) { 00230 // This event has passed all the cuts; 00231 passed_ee = true; 00232 } 00233 } 00234 } 00235 } 00236 } 00237 } 00238 00239 bool passed_mumu = false; 00240 // Now do the same checks for the mumu channel 00241 vector<const Jet*> vetoJets_mumu; 00242 // So we now want 2 good muons... 00243 if (muonFS.size() == 2) { 00244 // ...with opposite sign charges. 00245 if (charge(muonFS[0]) != charge(muonFS[1])) { 00246 // Check the MET 00247 if (MET >= 40*GeV) { 00248 // and do some di-muon mass cuts 00249 const double dilepton_mass = (muonFS.at(0).momentum() + muonFS.at(1).momentum()).mass(); 00250 if (dilepton_mass >= 15*GeV) { 00251 if (fabs(dilepton_mass - 91.0*GeV) >= 10.0*GeV) { 00252 // Need at least 2 b-jets 00253 if (b_jets.size() > 1) { 00254 // This event has passed all mumu-channel cuts 00255 passed_mumu = true; 00256 } 00257 } 00258 } 00259 } 00260 } 00261 } 00262 00263 bool passed_emu = false; 00264 // Finally, the same again with the emu channel 00265 vector<const Jet*> vetoJets_emu; 00266 // We want exactly 1 electron and 1 muon 00267 if (elecFS.size() == 1 && muonFS.size() == 1) { 00268 // With opposite sign charges 00269 if (charge(elecFS[0]) != charge(muonFS[0])) { 00270 // Calculate HT: scalar sum of the pTs of the leptons and all good jets 00271 double HT = 0; 00272 HT += elecFS[0].pT(); 00273 HT += muonFS[0].pT(); 00274 foreach (const Jet* j, good_jets) 00275 HT += fabs(j->pT()); 00276 // Keep events with HT > 130 GeV 00277 if (HT > 130.0*GeV) { 00278 // And again we want 2 or more b-jets 00279 if (b_jets.size() > 1) { 00280 passed_emu = true; 00281 } 00282 } 00283 } 00284 } 00285 00286 if (passed_ee == true || passed_mumu == true || passed_emu == true) { 00287 // If the event passes the selection, we use it for all gap fractions 00288 m_total_weight += weight; 00289 00290 // Loop over each veto jet 00291 foreach (const Jet* j, veto_jets) { 00292 const double pt = j->pT(); 00293 const double rapidity = fabs(j->rapidity()); 00294 // Loop over each region 00295 for (size_t i = 0; i < 4; ++i) { 00296 // If the jet falls into this region, get its pT and increment sum(pT) 00297 if (inRange(rapidity, m_plots[i].y_low, m_plots[i].y_high)) { 00298 m_plots[i].vetoJetPt_Qsum += pt; 00299 // If we've already got a veto jet, don't replace it 00300 if (m_plots[i].vetoJetPt_Q0 == 0.0) m_plots[i].vetoJetPt_Q0 = pt; 00301 } 00302 } 00303 } 00304 for (size_t i = 0; i < 4; ++i) { 00305 m_plots[i]._h_vetoJetPt_Q0->fill(m_plots[i].vetoJetPt_Q0, weight); 00306 m_plots[i]._h_vetoJetPt_Qsum->fill(m_plots[i].vetoJetPt_Qsum, weight); 00307 m_plots[i].vetoJetPt_Q0 = 0.0; 00308 m_plots[i].vetoJetPt_Qsum = 0.0; 00309 } 00310 } 00311 } 00312 00313 00314 /// Normalise histograms etc., after the run 00315 void finalize() { 00316 for (size_t i = 0; i < 4; ++i) { 00317 finalizeGapFraction(m_total_weight, m_plots[i]._d_gapFraction_Q0, m_plots[i]._h_vetoJetPt_Q0); 00318 finalizeGapFraction(m_total_weight, m_plots[i]._d_gapFraction_Qsum, m_plots[i]._h_vetoJetPt_Qsum); 00319 } 00320 } 00321 00322 00323 /// Convert temporary histos to cumulative efficiency scatters 00324 /// @todo Should be possible to replace this with a couple of YODA one-lines for diff -> integral and "efficiency division" 00325 void finalizeGapFraction(double total_weight, Scatter2DPtr gapFraction, Histo1DPtr vetoPt) { 00326 // Stores the cumulative frequency of the veto jet pT histogram 00327 double vetoPtWeightSum = 0.0; 00328 00329 // Keep track of which gap fraction point we're currently populating (#final_points != #tmp_bins) 00330 size_t fgap_point = 0; 00331 for (size_t i = 0; i < vetoPt->numBins(); ++i) { 00332 // If we've done the last "final" point, stop 00333 if (fgap_point == gapFraction->numPoints()) break; 00334 00335 // Increment the cumulative vetoPt counter for this temp histo bin 00336 /// @todo Get rid of this and use vetoPt->integral(i+1) when points and bins line up? 00337 vetoPtWeightSum += vetoPt->bin(i).sumW(); 00338 00339 // If this temp histo bin's upper edge doesn't correspond to the reference point, don't finalise the scatter. 00340 // Note that points are ON the bin edges and have no width: they represent the integral up to exactly that point. 00341 if ( !fuzzyEquals(vetoPt->bin(i).xMax(), gapFraction->point(fgap_point).x()) ) continue; 00342 00343 // Calculate the gap fraction and its uncertainty 00344 const double frac = (total_weight != 0.0) ? vetoPtWeightSum/total_weight : 0; 00345 const double fracErr = (total_weight != 0.0) ? sqrt(frac*(1-frac)/total_weight) : 0; 00346 gapFraction->point(fgap_point).setY(frac, fracErr); 00347 00348 ++fgap_point; 00349 } 00350 } 00351 00352 00353 private: 00354 00355 // Weight counter 00356 double m_total_weight; 00357 00358 // Structs containing all the plots, for each event selection 00359 ATLAS_2012_I1094568_Plots m_plots[4]; 00360 00361 }; 00362 00363 00364 // The hook for the plugin system 00365 DECLARE_RIVET_PLUGIN(ATLAS_2012_I1094568); 00366 00367 } Generated on Tue Mar 24 2015 17:35:24 for The Rivet MC analysis system by ![]() |