MC_TTBAR.cc

Go to the documentation of this file.
00001 #include "Rivet/Analysis.hh"
00002 #include "Rivet/Projections/FinalState.hh"
00003 #include "Rivet/Projections/ChargedLeptons.hh"
00004 #include "Rivet/Projections/MissingMomentum.hh"
00005 #include "Rivet/Projections/FastJets.hh"
00006 #include "Rivet/AnalysisLoader.hh"
00007 #include "Rivet/RivetAIDA.hh"
00008 
00009 namespace Rivet {
00010 
00011 
00012   class MC_TTBAR : public Analysis {
00013   public:
00014 
00015     /// Minimal constructor
00016     MC_TTBAR() : Analysis("MC_TTBAR")
00017     {
00018       _sumwPassedLepJetMET = 0;
00019       _sumwPassedJetID = 0;
00020       _sumwPassedWMass = 0;
00021     }
00022 
00023 
00024     /// @name Analysis methods
00025     //@{
00026 
00027     /// Set up projections and book histograms
00028     void init() {
00029 
00030       // A FinalState is used to select particles within |eta| < 4.2 and with pT
00031       // > 30 GeV, out of which the ChargedLeptons projection picks only the
00032       // electrons and muons, to be accessed later as "LFS".
00033       addProjection(ChargedLeptons(FinalState(-4.2, 4.2, 30*GeV)), "LFS");
00034       // A second FinalState is used to select all particles in |eta| < 4.2,
00035       // with no pT cut. This is used to construct jets and measure missing
00036       // transverse energy.
00037       FinalState fs(-4.2, 4.2, 0*GeV);
00038       addProjection(FastJets(fs, FastJets::ANTIKT, 0.6), "Jets");
00039       addProjection(MissingMomentum(fs), "MissingET");
00040 
00041       // Booking of histograms
00042       _h_njets = bookHistogram1D("jet_mult", 11, -0.5, 10.5);
00043       //
00044       _h_jet_1_pT = bookHistogram1D("jet_1_pT", 50, 0, 500);
00045       _h_jet_2_pT = bookHistogram1D("jet_2_pT", 50, 0, 400);
00046       _h_jet_3_pT = bookHistogram1D("jet_3_pT", 50, 0, 300);
00047       _h_jet_4_pT = bookHistogram1D("jet_4_pT", 50, 0, 200);
00048       _h_jet_HT   = bookHistogram1D("jet_HT", logspace(0, 2000, 50));
00049       //
00050       _h_bjet_1_pT = bookHistogram1D("jetb_1_pT", 50, 0, 400);
00051       _h_bjet_2_pT = bookHistogram1D("jetb_2_pT", 50, 0, 300);
00052       //
00053       _h_ljet_1_pT = bookHistogram1D("jetl_1_pT", 50, 0, 400);
00054       _h_ljet_2_pT = bookHistogram1D("jetl_2_pT", 50, 0, 300);
00055       //
00056       _h_W_mass = bookHistogram1D("W_mass", 75, 30, 180);
00057       _h_t_mass = bookHistogram1D("t_mass", 150, 130, 430);
00058       _h_t_mass_W_cut = bookHistogram1D("t_mass_W_cut", 150, 130, 430);
00059     }
00060 
00061 
00062     void analyze(const Event& event) {
00063       const double weight = event.weight();
00064 
00065       // Use the "LFS" projection to require at least one hard charged
00066       // lepton. This is an experimental signature for the leptonically decaying
00067       // W. This helps to reduce pure QCD backgrounds.
00068       const ChargedLeptons& lfs = applyProjection<ChargedLeptons>(event, "LFS");
00069       MSG_DEBUG("Charged lepton multiplicity = " << lfs.chargedLeptons().size());
00070       foreach (const Particle& lepton, lfs.chargedLeptons()) {
00071         MSG_DEBUG("Lepton pT = " << lepton.momentum().pT());
00072       }
00073       if (lfs.chargedLeptons().empty()) {
00074         MSG_DEBUG("Event failed lepton multiplicity cut");
00075         vetoEvent;
00076       }
00077 
00078       // Use a missing ET cut to bias toward events with a hard neutrino from
00079       // the leptonically decaying W. This helps to reduce pure QCD backgrounds.
00080       const MissingMomentum& met = applyProjection<MissingMomentum>(event, "MissingET");
00081       MSG_DEBUG("Vector ET = " << met.vectorEt().mod() << " GeV");
00082       if (met.vectorEt().mod() < 30*GeV) {
00083         MSG_DEBUG("Event failed missing ET cut");
00084         vetoEvent;
00085       }
00086 
00087       // Use the "Jets" projection to check that there are at least 4 jets of
00088       // any pT. Getting the jets sorted by pT ensures that the first jet is the
00089       // hardest, and so on. We apply no pT cut here only because we want to
00090       // plot all jet pTs to help optimise our jet pT cut.
00091       const FastJets& jetpro = applyProjection<FastJets>(event, "Jets");
00092       const Jets alljets = jetpro.jetsByPt();
00093       if (alljets.size() < 4) {
00094         MSG_DEBUG("Event failed jet multiplicity cut");
00095         vetoEvent;
00096       }
00097 
00098       // Update passed-cuts counter and fill all-jets histograms
00099       _sumwPassedLepJetMET += weight;
00100       _h_jet_1_pT->fill(alljets[0].momentum().pT()/GeV, weight);
00101       _h_jet_2_pT->fill(alljets[1].momentum().pT()/GeV, weight);
00102       _h_jet_3_pT->fill(alljets[2].momentum().pT()/GeV, weight);
00103       _h_jet_4_pT->fill(alljets[3].momentum().pT()/GeV, weight);
00104 
00105       // Insist that the hardest 4 jets pass pT hardness cuts. If we don't find
00106       // at least 4 such jets, we abandon this event.
00107       const Jets jets = jetpro.jetsByPt(30*GeV);
00108       _h_njets->fill(jets.size(), weight);
00109       double ht = 0.0;
00110       foreach (const Jet& j, jets) { ht += j.momentum().pT(); }
00111       _h_jet_HT->fill(ht/GeV, weight);
00112       if (jets.size() < 4 ||
00113           jets[0].momentum().pT() < 60*GeV ||
00114           jets[1].momentum().pT() < 50*GeV ||
00115           jets[3].momentum().pT() < 30*GeV) {
00116         MSG_DEBUG("Event failed jet cuts");
00117         vetoEvent;
00118       }
00119 
00120       // Sort the jets into b-jets and light jets. We expect one hard b-jet from
00121       // each top decay, so our 4 hardest jets should include two b-jets. The
00122       // Jet::containsBottom() method is equivalent to perfect experimental
00123       // b-tagging, in a generator-independent way.
00124       Jets bjets, ljets;
00125       foreach (const Jet& jet, jets) {
00126         // // Don't count jets that overlap with the hard leptons
00127         bool isolated = true;
00128         foreach (const Particle& lepton, lfs.chargedLeptons()) {
00129           if (deltaR(jet.momentum(), lepton.momentum()) < 0.3) {
00130             isolated = false;
00131             break;
00132           }
00133         }
00134         if (!isolated) {
00135           MSG_DEBUG("Jet failed lepton isolation cut");
00136           break;
00137         }
00138         if (jet.containsBottom()) {
00139           bjets.push_back(jet);
00140         } else {
00141           ljets.push_back(jet);
00142         }
00143       }
00144       MSG_DEBUG("Number of b-jets = " << bjets.size());
00145       if (bjets.size() != 2) {
00146         MSG_DEBUG("Event failed post-lepton-isolation b-tagging cut");
00147         vetoEvent;
00148       }
00149       if (ljets.size() < 2) {
00150         MSG_DEBUG("Event failed since not enough light jets remaining after lepton-isolation");
00151         vetoEvent;
00152       }
00153 
00154       // Plot the pTs of the identified jets.
00155       _sumwPassedJetID += weight;
00156       _h_bjet_1_pT->fill(bjets[0].momentum().pT(), weight);
00157       _h_bjet_2_pT->fill(bjets[1].momentum().pT(), weight);
00158       _h_ljet_1_pT->fill(ljets[0].momentum().pT(), weight);
00159       _h_ljet_2_pT->fill(ljets[1].momentum().pT(), weight);
00160 
00161       // Construct the hadronically decaying W momentum 4-vector from pairs of
00162       // non-b-tagged jets. The pair which best matches the W mass is used. We start
00163       // with an always terrible 4-vector estimate which should always be "beaten" by
00164       // a real jet pair.
00165       FourMomentum W(10*sqrtS(), 0, 0, 0);
00166       for (size_t i = 0; i < ljets.size()-1; ++i) {
00167         for (size_t j = i + 1; j < ljets.size(); ++j) {
00168           const FourMomentum Wcand = ljets[i].momentum() + ljets[j].momentum();
00169           MSG_TRACE(i << "," << j << ": candidate W mass = " << Wcand.mass()/GeV
00170                     << " GeV, vs. incumbent candidate with " << W.mass()/GeV << " GeV");
00171           if (fabs(Wcand.mass() - 80.4*GeV) < fabs(W.mass() - 80.4*GeV)) {
00172             W = Wcand;
00173           }
00174         }
00175       }
00176       MSG_DEBUG("Candidate W mass = " << W.mass() << " GeV");
00177 
00178       // There are two b-jets with which this can be combined to make the
00179       // hadronically decaying top, one of which is correct and the other is
00180       // not... but we have no way to identify which is which, so we construct
00181       // both possible top momenta and fill the histograms with both.
00182       const FourMomentum t1 = W + bjets[0].momentum();
00183       const FourMomentum t2 = W + bjets[1].momentum();
00184       _h_W_mass->fill(W.mass(), weight);
00185       _h_t_mass->fill(t1.mass(), weight);
00186       _h_t_mass->fill(t2.mass(), weight);
00187 
00188       // Placing a cut on the well-known W mass helps to reduce backgrounds
00189       if (inRange(W.mass()/GeV, 75, 85)) {
00190         MSG_DEBUG("W found with mass " << W.mass()/GeV << " GeV");
00191         _sumwPassedWMass += weight;
00192         _h_t_mass_W_cut->fill(t1.mass(), weight);
00193         _h_t_mass_W_cut->fill(t2.mass(), weight);
00194       }
00195 
00196     }
00197 
00198 
00199     void finalize() {
00200       scale(_h_njets, 1/_sumwPassedLepJetMET);
00201       scale(_h_jet_1_pT, 1/_sumwPassedLepJetMET);
00202       scale(_h_jet_2_pT, 1/_sumwPassedLepJetMET);
00203       scale(_h_jet_3_pT, 1/_sumwPassedLepJetMET);
00204       scale(_h_jet_4_pT, 1/_sumwPassedLepJetMET);
00205       scale(_h_jet_HT, 1/_sumwPassedLepJetMET);
00206       scale(_h_bjet_1_pT, 1/_sumwPassedJetID);
00207       scale(_h_bjet_2_pT, 1/_sumwPassedJetID);
00208       scale(_h_ljet_1_pT, 1/_sumwPassedJetID);
00209       scale(_h_ljet_2_pT, 1/_sumwPassedJetID);
00210       scale(_h_W_mass, 1/_sumwPassedJetID);
00211       scale(_h_t_mass, 1/_sumwPassedJetID);
00212       scale(_h_t_mass_W_cut, 1/_sumwPassedWMass);
00213     }
00214 
00215     //@}
00216 
00217 
00218   private:
00219 
00220     // Passed-cuts counters
00221     double _sumwPassedLepJetMET, _sumwPassedJetID, _sumwPassedWMass;
00222 
00223     // @name Histogram data members
00224     //@{
00225 
00226     AIDA::IHistogram1D *_h_njets;
00227     AIDA::IHistogram1D *_h_jet_1_pT, *_h_jet_2_pT, *_h_jet_3_pT, *_h_jet_4_pT;
00228     AIDA::IHistogram1D *_h_jet_HT;
00229     AIDA::IHistogram1D *_h_bjet_1_pT, *_h_bjet_2_pT;
00230     AIDA::IHistogram1D *_h_ljet_1_pT, *_h_ljet_2_pT;
00231     AIDA::IHistogram1D *_h_W_mass;
00232     AIDA::IHistogram1D *_h_t_mass, *_h_t_mass_W_cut;
00233 
00234     //@}
00235 
00236   };
00237 
00238 
00239 
00240   // The hook for the plugin system
00241   DECLARE_RIVET_PLUGIN(MC_TTBAR);
00242 
00243 }