rivet is hosted by Hepforge, IPPP Durham

Rivet analyses reference

MC_HFDECAYS

Monte Carlo validation observables for heavy-flavour decays
Experiment: ()
Status: VALIDATED
Authors:
  • Francesco Curcio
  • Christian Gutschow
No references listed
Beams: * *
Beam energies: ANY
Run details:
  • Anything with heavy-flavour jets

Jets with $p_\perp>25$ GeV are constructed with an anti-$k_\perp$ jet finder with $R=0.4$ and projected onto many different observables sensitive to the decays of $b$- and $c$-hadrons in the heavy-flavour-tagged jet.

Source code: MC_HFDECAYS.cc
  1// -*- C++ -*-
  2#include "Rivet/Analysis.hh"
  3#include "Rivet/Projections/FinalState.hh"
  4#include "Rivet/Projections/FastJets.hh"
  5#include "Rivet/Projections/HeavyHadrons.hh"
  6
  7namespace Rivet {
  8
  9
 10    class MC_HFDECAYS : public Analysis {
 11    public:
 12
 13      /// Constructor
 14      RIVET_DEFAULT_ANALYSIS_CTOR(MC_HFDECAYS);
 15
 16      const string whoDis(const int pid) const {
 17        switch (pid) {
 18          case PID::B0:           return "B0";
 19          case PID::BPLUS:        return "BPLUS";
 20          case PID::B0S:          return "B0S";
 21          case PID::LAMBDAB:      return "LAMBDAB";
 22          case PID::D0:           return "D0";
 23          case PID::DPLUS:        return "DPLUS";
 24          case PID::DSPLUS:       return "DSPLUS";
 25          case PID::LAMBDACPLUS:  return "LAMBDACPLUS";
 26          default:                return "";
 27        }
 28      }
 29
 30      /// Book histograms and initialise projections before the run
 31      void init() {
 32        
 33        declare(HeavyHadrons(),"HA");
 34
 35        FastJets jetpro(FinalState(), JetAlg::ANTIKT, 0.4, JetMuons::DECAY, JetInvisibles::DECAY);
 36        declare(jetpro, "Jets");
 37
 38        // bar charts
 39        book(_h["bar_b_jet_width"], "width_B_jet", 7, 0., 0.3);
 40        book(_h["bar_c_jet_width"], "width_C_jet", 7, 0., 0.3);
 41        // profiles
 42        book(_p["b_jet_rho"], "avg_rho_B_jet", 10, 0., 0.4);
 43        book(_p["c_jet_rho"], "avg_rho_C_jet", 10, 0., 0.4);
 44        book(_p["b_jet_psi"], "avg_psi_B_jet", 10, 0., 0.4);
 45        book(_p["c_jet_psi"], "avg_psi_C_jet", 10, 0., 0.4);
 46        // histograms
 47        book(_h["b_frac"], "prod_frac_B", 11, 0.5,11.5);
 48        book(_h["c_frac"], "prod_frac_C",  7, 0.5, 7.5);
 49        book(_h["b_jet_frag"], "frag_B_jet", 50, 0., 1.1);
 50        book(_h["c_jet_frag"], "frag_C_jet", 50, 0., 1.1);
 51        book(_h["b_jet_pT"], "pT_B_jet", 25, 25., 1025.);
 52        book(_h["c_jet_pT"], "pT_C_jet", 25, 25., 1025.);
 53        book(_h["b_jet_pThad"], "pThad_B_jet", 10, 0., 200.);
 54        book(_h["c_jet_pThad"], "pThad_C_jet", 10, 0., 200.);
 55        book(_h["b_jet_high_pT"], "high_pT_frag_B_jet", 46, 0., 1.);
 56        book(_h["c_jet_high_pT"], "high_pT_frag_C_jet", 46, 0., 1.);
 57        book(_h["b_jet_high_pT_1H"], "high_pT_frag_B_jet_1H", 46, 0., 1.);
 58        book(_h["c_jet_high_pT_1H"], "high_pT_frag_C_jet_1H", 46, 0., 1.);
 59
 60
 61        book(_h["ch_B0"         ], "B0_charged_mult",          30, 0.5, 30.5);
 62        book(_h["ch_BPLUS"      ], "BPLUS_charged_mult",       30, 0.5, 30.5);
 63        book(_h["ch_B0S"        ], "B0S_charged_mult",         30, 0.5, 30.5);
 64        book(_h["ch_LAMBDAB"    ], "LAMBDAB_charged_mult",     30, 0.5, 30.5);
 65        book(_h["ch_D0"         ], "D0_charged_mult",          16, 0.5, 16.5);
 66        book(_h["ch_DPLUS"      ], "DPLUS_charged_multh",      16, 0.5, 16.5);
 67        book(_h["ch_DSPLUS"     ], "DSPLUS_charged_mult",      16, 0.5, 16.5);
 68        book(_h["ch_LAMBDACPLUS"], "LAMBDACPLUS_charged_mult", 16, 0.5, 16.5);
 69
 70        book(_h["st_B0"         ], "B0_stable_mult",          40, 0.5, 40.5);
 71        book(_h["st_BPLUS"      ], "BPLUS_stable_mult",       40, 0.5, 40.5);
 72        book(_h["st_B0S"        ], "B0S_stable_mult",         40, 0.5, 40.5);
 73        book(_h["st_LAMBDAB"    ], "LAMBDAB_stable_mult",     40, 0.5, 40.5);
 74        book(_h["st_D0"         ], "D0_stable_mult",          20, 0.5, 20.5);
 75        book(_h["st_DPLUS"      ], "DPLUS_stable_mult",       20, 0.5, 20.5);
 76        book(_h["st_DSPLUS"     ], "DSPLUS_stable_mult",      20, 0.5, 20.5);
 77        book(_h["st_LAMBDACPLUS"], "LAMBDACPLUS_stable_mult", 20, 0.5, 20.5);
 78
 79        book(_h["pt_B0"         ], "B0_pT",          10, 25., 425.);
 80        book(_h["pt_BPLUS"      ], "BPLUS_pT",       10, 25., 425.);
 81        book(_h["pt_B0S"        ], "B0S_pT",         10, 25., 425.);
 82        book(_h["pt_LAMBDAB"    ], "LAMBDAB_pT",     10, 25., 425.);
 83        book(_h["pt_D0"         ], "D0_pT",          10, 25., 425.);
 84        book(_h["pt_DPLUS"      ], "DPLUS_pT",       10, 25., 425.);
 85        book(_h["pt_DSPLUS"     ], "DSPLUS_pT",      10, 25., 425.);
 86        book(_h["pt_LAMBDACPLUS"], "LAMBDACPLUS_pT", 10, 25., 425.);
 87
 88        book(_h["b_jet_ch_mult"], "charged_mult_B_jets", 40, 0.5, 40.5);
 89        book(_h["c_jet_ch_mult"], "charged_mult_C_jets", 40, 0.5, 40.5);
 90
 91        book(_h["b_jet_l_pTrel"], "lepton_pTrel_B_jets", 8, 0., 15.);
 92        book(_h["c_jet_l_pTrel"], "lepton_pTrel_C_jets", 8, 0., 10.);
 93
 94        book(_h["b_jet_l_pT"], "lepton_pT_B_jets", 10, 0., 100.);
 95        book(_h["c_jet_l_pT"], "lepton_pT_C_jets", 10, 0., 100.);
 96
 97        // double-differentials
 98        vector<double> groupEdges = ptaxis.edges(); // includes +/- inf
 99        groupEdges.erase(groupEdges.begin()); groupEdges.pop_back(); // removes +/- inf
100        book(_g["avg_B_jet_ch_mult"], groupEdges);
101        book(_g["avg_C_jet_ch_mult"], groupEdges);
102        book(_g["avg_B_jet_l_pTrel"], groupEdges);
103        book(_g["avg_C_jet_l_pTrel"], groupEdges);
104        for (size_t i = 0; i < ptaxis.numBins(); ++i) {
105          const std::pair<double,double> ptbin = std::make_pair(ptaxis.min(i+1),ptaxis.max(i+1));
106          const string suff = to_string(int(ptbin.first)) + "_" + to_string(int(ptbin.second));
107          book(_p["avg_B_jet_rho_"+to_string(i)], "avg_B_jet_rho_"+suff, 10, 0., 0.4);
108          book(_p["avg_C_jet_rho_"+to_string(i)], "avg_C_jet_rho_"+suff, 10, 0., 0.4);
109
110          book(_g["avg_B_jet_ch_mult"]->bin(i+1), "avg_B_jet_ch_mult_"+suff, 40, 0.5, 40.5);
111          book(_g["avg_C_jet_ch_mult"]->bin(i+1), "avg_C_jet_ch_mult_"+suff, 40, 0.5, 40.5);
112          if (i == 0) {
113            book(_g["avg_B_jet_l_pTrel"]->bin(i+1), "avg_B_jet_l_pTrel_"+suff, 2, 0., 4.);
114            book(_g["avg_C_jet_l_pTrel"]->bin(i+1), "avg_C_jet_l_pTrel_"+suff, 2, 0., 3.);
115          }
116          else if (i <= 2) {
117            book(_g["avg_B_jet_l_pTrel"]->bin(i+1), "avg_B_jet_l_pTrel_"+suff, 4, 0., 8.);
118            book(_g["avg_C_jet_l_pTrel"]->bin(i+1), "avg_C_jet_l_pTrel_"+suff, 5, 0., 6.);
119          }
120          else {
121            book(_g["avg_B_jet_l_pTrel"]->bin(i+1), "avg_B_jet_l_pTrel_"+suff, 8, 0., 15.);
122            book(_g["avg_C_jet_l_pTrel"]->bin(i+1), "avg_C_jet_l_pTrel_"+suff, 8, 0., 10.);
123          }
124        }
125      }
126
127      double p_annulus(const Jet &jet, const double a, const double b) const {
128        // calculate the total momentum inside an annulus with a <= R < b
129        return sum(select(jet.particles(), [&](const Particle &p) {
130          const double dr = deltaR(p, jet);
131          return (dr < b && dr >= a);
132        }), Kin::pT, 0.)/GeV;
133      }
134
135      void count_mult(const Particle& p) {
136        unsigned int nst = p.stableDescendants().size() + 0.5;
137        unsigned int nch = p.stableDescendants(Cuts::charge != 0).size() + 0.5;
138        _h["st_"+whoDis(p.abspid())]->fill(nst);
139        _h["ch_"+whoDis(p.abspid())]->fill(nch);
140      }
141
142      double pTrel (const Jet& jet, const Particle& p) const {
143        return (p.p3().cross(jet.p3())).mod()/(jet.p3().mod());
144      }
145
146
147      /// Perform the per-event analysis
148      void analyze(const Event& event) {
149
150        const HeavyHadrons &ha = apply<HeavyHadrons>(event,"HA");
151
152        if (ha.bHadrons().empty() && ha.cHadrons().empty()) vetoEvent;
153
154        for (const Particle &p : ha.bHadrons()) {
155          const string name = "pt_" + whoDis(p.abspid());
156          if (p.abspid() == PID::B0) { //Take into consideration both particles and anti-particles with abs()
157            count_mult(p);
158            _h["b_frac"]->fill(1);
159            _h[name]->fill(p.pT()/GeV);
160          }
161          else if (p.abspid() == PID::BPLUS) {
162            count_mult(p);
163            _h["b_frac"]->fill(2);
164            _h[name]->fill(p.pT()/GeV);
165          }
166          else if (p.abspid() == PID::B0S) {
167            count_mult(p);
168            _h["b_frac"]->fill(3);
169            _h[name]->fill(p.pT()/GeV);
170          }
171          else if (p.abspid() == PID::BCPLUS)   _h["b_frac"]->fill(4);
172          else if (p.abspid() == PID::LAMBDAB) {
173            count_mult(p);
174            _h["b_frac"]->fill(5);
175            _h[name]->fill(p.pT()/GeV);
176          }
177          else if (p.abspid() == PID::XIBMINUS)     _h["b_frac"]->fill(6);
178          else if (p.abspid() == PID::XI0B)         _h["b_frac"]->fill(7);
179          else if (p.abspid() == PID::OMEGABMINUS)  _h["b_frac"]->fill(8);
180          else if (p.abspid() == PID::SIGMABMINUS)  _h["b_frac"]->fill(9);
181          else if (p.abspid() == PID::SIGMAB)       _h["b_frac"]->fill(10);
182          else if (p.abspid() == PID::SIGMABPLUS)   _h["b_frac"]->fill(11);
183        }
184
185        for (const Particle &p : ha.cHadrons()) {
186          if( !p.fromBottom()){ //take into account only c-hadrons that don't come from a b-hadron decay
187                                //Avoid double-counting b-hadron fractions
188            const string name = "pt_" + whoDis(p.abspid());
189            if (p.abspid() == PID::DPLUS) {
190              count_mult(p);
191              _h["c_frac"]->fill(1);
192              _h[name]->fill(p.pT()/GeV);
193            }
194            else if (p.abspid() == PID::D0) {
195              count_mult(p);
196              _h["c_frac"]->fill(2);
197              _h[name]->fill(p.pT()/GeV);
198            }
199            else if (p.abspid() == PID::DSPLUS) {
200              count_mult(p);
201              _h["c_frac"]->fill(3);
202              _h[name]->fill(p.pT()/GeV);
203            }
204            else if (p.abspid() == PID::LAMBDACPLUS) {
205              count_mult(p);
206              _h["c_frac"]->fill(4);
207              _h[name]->fill(p.pT()/GeV);
208            }
209            else if (p.abspid() == PID::XICPLUS)  _h["c_frac"]->fill(5);
210            else if (p.abspid() == PID::XI0C)     _h["c_frac"]->fill(6);
211            else if (p.abspid() == PID::OMEGA0C)  _h["c_frac"]->fill(7);
212          }
213        }
214        Jets jets = apply<FastJets>(event, "Jets").jetsByPt(Cuts::pT > 25.*GeV && Cuts::absrap < 2.5);
215        if (jets.empty()) vetoEvent;
216
217        auto r_bins = _p["b_jet_rho"]->xEdges();
218        const double dr = r_bins[1]-r_bins[0]; //dr is equal for all bins
219        for (const Jet& thisJet : jets) {
220          double p_0_R = p_annulus(thisJet, 0., 0.4);
221          if (fuzzyEquals(p_0_R, 0., 1e-5))  continue;
222          Particles bjets = thisJet.bTags(Cuts::pT > 5*GeV);
223          iselect(bjets, deltaRLess(thisJet, 0.3));
224          for (const Particle &thisB : bjets) {
225            _h["b_jet_pThad"]->fill(thisB.pT()/GeV);
226            double z = thisJet.p3().dot(thisB.p3())/thisJet.p2();
227            _h["b_jet_frag"]->fill(z);
228            if (inRange(thisJet.pT(), 500*GeV, 1000*GeV)) {
229              _h["b_jet_high_pT"]->fill(z);
230              if (bjets.size() == 1)  _h["b_jet_high_pT_1H"]->fill(z);
231            }
232            for (size_t i = 0; i < r_bins.size()-1; ++i) {
233              double r = 0.5*(r_bins[i]+r_bins[i+1]);
234              double rho = p_annulus(thisJet, r_bins[i], r_bins[i+1])/p_0_R/dr;
235              double psi = p_annulus(thisJet, 0, r_bins[i+1])/p_0_R;
236              _p["b_jet_rho"]->fill(r, rho);
237              _p["b_jet_psi"]->fill(r, psi);
238              size_t ptb = ptaxis.index(thisJet.pT()/GeV) - 1; // index in {1, nBins}
239              if (ptb < ptaxis.numBins()) _p["avg_B_jet_rho_"+to_string(ptb)]->fill(r, rho);
240            }
241          }
242
243          Particles cjets = thisJet.cTags(Cuts::pT > 5*GeV);
244          iselect(cjets, deltaRLess(thisJet, 0.3));
245          if (bjets.empty()) {
246            for (const Particle& thisC : cjets) {
247              _h["c_jet_pThad"]->fill(thisC.pT()/GeV);
248              double z = thisJet.p3().dot(thisC.p3())/thisJet.p2();
249              _h["c_jet_frag"]->fill(z);
250              if (inRange(thisJet.pT(), 500*GeV, 1000*GeV)) {
251                _h["c_jet_high_pT"]->fill(z);
252                if (cjets.size() == 1) {
253                  _h["c_jet_high_pT_1H"]->fill(z);
254                }
255              }
256              for (size_t i = 0; i < r_bins.size()-1; ++i) {
257                double r = 0.5*(r_bins[i]+r_bins[i+1]);
258                double rho = p_annulus(thisJet, r_bins[i], r_bins[i+1])/p_0_R/dr;
259                double psi = p_annulus(thisJet, 0, r_bins[i+1])/p_0_R;
260                _p["c_jet_rho"]->fill(r, rho);
261                _p["c_jet_psi"]->fill(r, psi);
262                size_t ptb = ptaxis.index(thisJet.pT()/GeV) - 1; // index in {0, nBins - 1}
263                if (ptb < ptaxis.numBins()) _p["avg_C_jet_rho_"+to_string(ptb)]->fill(r, rho);
264              }
265            }
266          }
267
268          if (bjets.size()) {
269            double W_num = 0., W_den = 0.;
270            long N_charged = 0;
271            for (const Particle& pp : thisJet.particles()) {
272              if(pp.isStable()) {
273                W_num += pp.pT()*deltaR(thisJet,pp);
274                W_den += pp.pT();
275                if (pp.isCharged())  ++N_charged;
276              }
277              if(pp.isLepton()) {
278                _h["b_jet_l_pT"]->fill(pp.pT()/GeV);
279                _h["b_jet_l_pTrel"]->fill(pTrel(thisJet,pp)/GeV);
280                _g["avg_B_jet_l_pTrel"]->fill(thisJet.pT()/GeV, pTrel(thisJet,pp)/GeV);
281              }
282            }
283            if (W_den)  _h["bar_b_jet_width"]->fill(W_num/W_den);
284            _h["b_jet_pT"]->fill(thisJet.pT()/GeV);
285            if (N_charged) {
286              _h["b_jet_ch_mult"]->fill(N_charged);
287              _g["avg_B_jet_ch_mult"]->fill(thisJet.pT()/GeV, (double)N_charged);
288            }
289          }
290          else if(cjets.size()) {
291            double W_num = 0., W_den = 0.;
292            long N_charged = 0;
293            for (const Particle& pp : thisJet.particles()) {
294              if(pp.isStable()) {
295                W_num += pp.pT()*deltaR(thisJet,pp);
296                W_den += pp.pT();
297                if(pp.isCharged()) ++N_charged;
298              }
299              if(pp.isLepton()) {
300                _h["c_jet_l_pT"]->fill(pp.pT()/GeV);
301                _h["c_jet_l_pTrel"]->fill(pTrel(thisJet,pp)/GeV);
302                _g["avg_C_jet_l_pTrel"]->fill(thisJet.pT()/GeV, pTrel(thisJet,pp)/GeV);
303              }
304            }
305            if (W_den)  _h["bar_c_jet_width"]->fill(W_num/W_den);
306            _h["c_jet_pT"]->fill(thisJet.pT()/GeV);
307            if (N_charged) {
308              _h["c_jet_ch_mult"]->fill(N_charged);
309              _g["avg_C_jet_ch_mult"]->fill(thisJet.pT()/GeV, (double)N_charged);
310            }
311          }
312        }
313      }
314
315      /// Normalise histograms etc., after the run
316      void finalize() {
317        for (const auto &hit : _h) {
318          double sf = 1.0;
319          if (hit.first.find("bar_") != string::npos) {
320            sf = (hit.second->xMax()-hit.second->xMin())/hit.second->numBins();
321          }
322          normalize(hit.second, sf);
323        }
324        normalize(_g);
325      }
326
327    private:
328      map<string, Histo1DPtr> _h;
329      map<string, Profile1DPtr> _p;
330      map<string, Histo1DGroupPtr> _g;
331      YODA::Axis<double> ptaxis{25, 30, 50, 70, 100, 150, 300, 500, 1000};
332
333  };
334
335  RIVET_DECLARE_PLUGIN(MC_HFDECAYS);
336}