CMS 3D CMS Logo

JetFlavourIdentifier.cc
Go to the documentation of this file.
1 //
2 // Translation of BTag MCJetFlavour tool to identify real flavour of a jet
3 // work with CaloJet objects
4 // Store Infos by Values in JetFlavour.h
5 // Author: Attilio
6 // Date: 05.10.2007
7 //
8 //
9 // \class JetFlavourIdentifier
10 //
11 // \brief Interface to pull out the proper flavour identifier from a
12 // jet->parton matching collection.
13 //
14 // In detail, the definitions are as follows:
15 //
16 // Definitions:
17 // The default behavior is that the "definition" is NULL_DEF,
18 // so the software will fall back to either "physics" or "algorithmic" definition
19 // as per the "physDefinition" switch.
20 //
21 // If the user specifies "definition", then that definition is taken. However,
22 // if the requested definition is not defined, the software reverts back to
23 // either "physics" or "algorithmic" based on the "physDefinition" switch.
24 // For example, if the user specifies "heaviest" as a flavor ID, and there
25 // are no bottom, charm, or top quarks in the event that match to the jet,
26 // then the software will fall back to the "physics" or "algorithmic" definition.
27 //
28 // Modifications:
29 //
30 // 09.03.2008: Sal Rappoccio.
31 // Added capability for all methods of identification, not just
32 // "physics" or "algorithmic". If the requested method does not exist
33 // (i.e. is unphysical), the "physics" or "algorithmic" definition
34 // is defaulted to.
35 
36 //=======================================================================
37 
38 // user include files
43 
50 
51 //#include "SimGeneral/HepPDTRecord/interface/ParticleDataTable.h"
52 
60 
64 
67 
68 #include <memory>
69 #include <string>
70 #include <iostream>
71 #include <vector>
72 #include <Math/VectorUtil.h>
73 #include <TMath.h>
74 
75 using namespace std;
76 using namespace reco;
77 using namespace edm;
78 using namespace ROOT::Math::VectorUtil;
79 
80 namespace reco {
81  namespace modules {
82 
83  //--------------------------------------------------------------------------
84  //
85  //--------------------------------------------------------------------------
87  public:
88  enum DEFINITION_T { PHYSICS = 0, ALGO, NEAREST_STATUS2, NEAREST_STATUS3, HEAVIEST, N_DEFINITIONS, NULL_DEF };
89 
91  ~JetFlavourIdentifier() override;
92 
93  private:
94  void produce(edm::StreamID, edm::Event&, const edm::EventSetup&) const override;
95 
96  JetFlavour::Leptons findLeptons(const GenParticleRef&) const;
97  std::vector<const reco::Candidate*> findCandidates(const reco::Candidate*,
98  int,
99  math::XYZTLorentzVector const& thePartonLV) const;
100  void fillLeptons(const std::vector<const reco::Candidate*>&,
102  int,
103  int,
104  math::XYZTLorentzVector const& thePartonLV) const;
105  static int heaviestFlavour(int);
106 
111  };
112  } // namespace modules
113 } // namespace reco
115 
116 //=========================================================================
117 
118 JetFlavourIdentifier::JetFlavourIdentifier(const edm::ParameterSet& iConfig) {
119  produces<JetFlavourMatchingCollection>();
120  sourceByReferToken_ = consumes<JetMatchedPartonsCollection>(iConfig.getParameter<InputTag>("srcByReference"));
121  physDefinition = iConfig.getParameter<bool>("physicsDefinition");
122  leptonInfo_ = iConfig.exists("leptonInfo") ? iConfig.getParameter<bool>("leptonInfo") : false;
123  // If we have a definition of which parton to identify, use it,
124  // otherwise we default to the "old" behavior of either "physics" or "algorithmic".
125  // Furthermore, if the specified definition is not sensible for the given jet,
126  // then the "physDefinition" switch is used to identify the flavour of the jet.
127  if (iConfig.exists("definition")) {
128  definition = static_cast<DEFINITION_T>(iConfig.getParameter<int>("definition"));
129  } else {
130  definition = NULL_DEF;
131  }
132 }
133 
134 //=========================================================================
135 
136 JetFlavourIdentifier::~JetFlavourIdentifier() {}
137 
138 // ------------ method called to produce the data ------------
139 
140 void JetFlavourIdentifier::produce(StreamID, Event& iEvent, const EventSetup& iEs) const {
141  // Get the JetMatchedPartons
143  iEvent.getByToken(sourceByReferToken_, theTagByRef);
144 
145  // Create a JetFlavourMatchingCollection
147  if (!theTagByRef->empty()) {
148  RefToBase<Jet> jj = theTagByRef->begin()->first;
150  } else {
151  jfmc = new JetFlavourMatchingCollection();
152  }
153  std::unique_ptr<reco::JetFlavourMatchingCollection> jetFlavMatching(jfmc);
154 
155  // Loop over the matched partons and see which match.
156  for (JetMatchedPartonsCollection::const_iterator j = theTagByRef->begin(); j != theTagByRef->end(); j++) {
157  // Consider this match.
158  const MatchedPartons aMatch = (*j).second;
159 
160  // This will hold the 4-vector, vertex, flavour and the leptonian decays (0: no lepton, xyz: x leptons in layer 2, y in layer 1 and z in layer 0) of the requested object.
161  math::XYZTLorentzVector thePartonLorentzVector(0, 0, 0, 0);
162  math::XYZPoint thePartonVertex(0, 0, 0);
163  int thePartonFlavour = 0;
164  JetFlavour::Leptons theLeptons;
165 
166  // get the partons based on which definition to use.
167  switch (definition) {
168  case PHYSICS: {
169  const GenParticleRef& aPartPhy = aMatch.physicsDefinitionParton();
170  if (aPartPhy.isNonnull()) {
171  thePartonLorentzVector = aPartPhy.get()->p4();
172  thePartonVertex = aPartPhy.get()->vertex();
173  thePartonFlavour = aPartPhy.get()->pdgId();
174  if (leptonInfo_)
175  theLeptons = findLeptons(aPartPhy);
176  }
177  break;
178  }
179  case ALGO: {
180  const GenParticleRef& aPartAlg = aMatch.algoDefinitionParton();
181  if (aPartAlg.isNonnull()) {
182  thePartonLorentzVector = aPartAlg.get()->p4();
183  thePartonVertex = aPartAlg.get()->vertex();
184  thePartonFlavour = aPartAlg.get()->pdgId();
185  if (leptonInfo_)
186  theLeptons = findLeptons(aPartAlg);
187  }
188  break;
189  }
190  case NEAREST_STATUS2: {
191  const GenParticleRef& aPartN2 = aMatch.nearest_status2();
192  if (aPartN2.isNonnull()) {
193  thePartonLorentzVector = aPartN2.get()->p4();
194  thePartonVertex = aPartN2.get()->vertex();
195  thePartonFlavour = aPartN2.get()->pdgId();
196  if (leptonInfo_)
197  theLeptons = findLeptons(aPartN2);
198  }
199  break;
200  }
201  case NEAREST_STATUS3: {
202  const GenParticleRef& aPartN3 = aMatch.nearest_status3();
203  if (aPartN3.isNonnull()) {
204  thePartonLorentzVector = aPartN3.get()->p4();
205  thePartonVertex = aPartN3.get()->vertex();
206  thePartonFlavour = aPartN3.get()->pdgId();
207  if (leptonInfo_)
208  theLeptons = findLeptons(aPartN3);
209  }
210  break;
211  }
212  case HEAVIEST: {
213  const GenParticleRef aPartHeaviest = aMatch.heaviest();
214  if (aPartHeaviest.isNonnull()) {
215  thePartonLorentzVector = aPartHeaviest.get()->p4();
216  thePartonVertex = aPartHeaviest.get()->vertex();
217  thePartonFlavour = aPartHeaviest.get()->pdgId();
218  if (leptonInfo_)
219  theLeptons = findLeptons(aPartHeaviest);
220  }
221  break;
222  }
223  // Default case is backwards-compatible
224  default: {
225  if (physDefinition) {
226  const GenParticleRef& aPartPhy = aMatch.physicsDefinitionParton();
227  if (aPartPhy.isNonnull()) {
228  thePartonLorentzVector = aPartPhy.get()->p4();
229  thePartonVertex = aPartPhy.get()->vertex();
230  thePartonFlavour = aPartPhy.get()->pdgId();
231  if (leptonInfo_)
232  theLeptons = findLeptons(aPartPhy);
233  }
234  } else {
235  const GenParticleRef& aPartAlg = aMatch.algoDefinitionParton();
236  if (aPartAlg.isNonnull()) {
237  thePartonLorentzVector = aPartAlg.get()->p4();
238  thePartonVertex = aPartAlg.get()->vertex();
239  thePartonFlavour = aPartAlg.get()->pdgId();
240  if (leptonInfo_)
241  theLeptons = findLeptons(aPartAlg);
242  }
243  }
244  } break;
245  } // end switch on definition
246 
247  // Now make sure we have a match. If the user specified "heaviest", for instance,
248  // and there is no b- or c-quarks, then fall back to the "physDefinition" switch.
249 
250  if (thePartonFlavour == 0) {
251  if (physDefinition) {
252  const GenParticleRef& aPartPhy = aMatch.physicsDefinitionParton();
253  if (aPartPhy.isNonnull()) {
254  thePartonLorentzVector = aPartPhy.get()->p4();
255  thePartonVertex = aPartPhy.get()->vertex();
256  thePartonFlavour = aPartPhy.get()->pdgId();
257  if (leptonInfo_)
258  theLeptons = findLeptons(aPartPhy);
259  }
260  } else {
261  const GenParticleRef& aPartAlg = aMatch.algoDefinitionParton();
262  if (aPartAlg.isNonnull()) {
263  thePartonLorentzVector = aPartAlg.get()->p4();
264  thePartonVertex = aPartAlg.get()->vertex();
265  thePartonFlavour = aPartAlg.get()->pdgId();
266  if (leptonInfo_)
267  theLeptons = findLeptons(aPartAlg);
268  }
269  }
270  }
271 
272  /*
273  std::cout << "Leptons of " <<thePartonFlavour << " Jet: " << std::endl;
274  std::cout << " electrons: " <<theLeptons.electron << std::endl;
275  std::cout << " muons : " <<theLeptons.muon << std::endl;
276  std::cout << " tau : " <<theLeptons.tau << std::endl;
277 */
278  // Add the jet->flavour match to the map.
279  (*jetFlavMatching)[(*j).first] = JetFlavour(thePartonLorentzVector, thePartonVertex, thePartonFlavour, theLeptons);
280  } // end loop over jets
281 
282  // Put the object into the event.
283  iEvent.put(std::move(jetFlavMatching));
284 }
285 
286 JetFlavour::Leptons JetFlavourIdentifier::findLeptons(const GenParticleRef& parton) const {
287  JetFlavour::Leptons theLeptons;
288 
289  auto const& thePartonLV = parton->p4();
290 
292  const reco::Candidate* mcstring = parton->daughter(0);
293  int partonFlavour = std::abs(parton->pdgId());
294  // std::cout << "parton DeltaR: " << DeltaR(thePartonLV, parton->p4()) << std::endl;
295 
297  std::vector<const reco::Candidate*> candidates = findCandidates(mcstring, partonFlavour, parton->p4());
298  // std::cout << "Candidates are:" << std::endl;
299  // for(unsigned int j = 0; j < candidates.size(); j++) std::cout << " --> " << candidates[j]->pdgId() << std::endl;
300 
302  fillLeptons(candidates, theLeptons, 1, partonFlavour, thePartonLV);
303 
304  return theLeptons;
305 }
306 
307 std::vector<const reco::Candidate*> JetFlavourIdentifier::findCandidates(
308  const reco::Candidate* cand, int partonFlavour, math::XYZTLorentzVector const& thePartonLV) const {
309  std::vector<const reco::Candidate*> cands;
310  if (!cand)
311  return cands;
312 
313  for (unsigned int i = 0; i < cand->numberOfDaughters(); i++) {
314  /*
315  std::cout << "DeltaR - " << partonFlavour << " ";
316  if (DeltaR(thePartonLV, cand->daughter(i)->p4()) > 0.7) std::cout << "(";
317  std::cout << cand->daughter(i)->pdgId() << ": " << DeltaR(thePartonLV, cand->daughter(i)->p4());
318  if (DeltaR(thePartonLV, cand->daughter(i)->p4()) > 0.7) std::cout << ")";
319  std::cout << std::endl;
320 */
321  if (DeltaR(thePartonLV, cand->daughter(i)->p4()) < 0.7) {
322  int pdgId = std::abs(cand->daughter(i)->pdgId());
323  int flavour = heaviestFlavour(pdgId);
324  if (flavour == partonFlavour || (flavour >= 10 && partonFlavour >= 10)) {
325  // std::cout << "<------- " << std::endl;
326  std::vector<const reco::Candidate*> newcands = findCandidates(cand->daughter(i), partonFlavour, thePartonLV);
327  // std::cout << " ------->" << std::endl;
328  std::copy(newcands.begin(), newcands.end(), std::back_inserter(cands));
329  }
330  if (partonFlavour >= 10)
331  cands.push_back(cand->daughter(i));
332  }
333  }
334 
335  if (cands.empty() && std::abs(cand->pdgId()) > 110 &&
336  !(partonFlavour >= 4 && partonFlavour < 10 && heaviestFlavour(cand->pdgId()) < 4))
337  cands.push_back(cand);
338 
339  return cands;
340 }
341 
342 void JetFlavourIdentifier::fillLeptons(const std::vector<const reco::Candidate*>& cands,
344  int rank,
345  int flavour,
346  math::XYZTLorentzVector const& thePartonLV) const {
347  for (unsigned int j = 0; j < cands.size(); j++) {
348  for (unsigned int i = 0; i < cands[j]->numberOfDaughters(); i++) {
349  int pdgId = std::abs(cands[j]->daughter(i)->pdgId());
350 
351  // for(int z = 1; z <= rank; z *= 10) std::cout << " ------ ";
352  // std::cout << pdgId << std::endl;
353 
355  if (pdgId == 12)
356  leptons.electron += rank;
357  else if (pdgId == 14)
358  leptons.muon += rank;
359  else if (pdgId == 16)
360  leptons.tau += rank;
361  else {
362  int heaviest = heaviestFlavour(pdgId);
363  int heaviest_ = heaviest < 10 ? heaviest : 0;
364  if (!heaviest || (flavour < 4 ? (heaviest_ < 4) : (heaviest >= 4))) {
365  std::vector<const reco::Candidate*> newcands = findCandidates(cands[j]->daughter(i), heaviest, thePartonLV);
366  if (pdgId <= 110)
367  newcands.push_back(cands[j]->daughter(i));
368  fillLeptons(newcands, leptons, rank * 10, std::max(heaviest_, flavour), thePartonLV);
369  }
370  }
371  }
372  }
373 }
374 
375 int JetFlavourIdentifier::heaviestFlavour(int pdgId) {
376  int flavour = 0;
377 
378  pdgId = std::abs(pdgId) % 100000;
379  if (pdgId > 110) {
380  while (pdgId % 10 > 0 && pdgId % 10 < 6) {
381  pdgId /= 10;
382  if (pdgId % 10 > flavour)
383  flavour = pdgId % 10;
384  }
385  } else
386  flavour = pdgId;
387 
388  return flavour;
389 }
390 
391 //define this as a plug-in
T getParameter(std::string const &) const
Definition: ParameterSet.h:307
transient_vector_type::const_iterator const_iterator
bool exists(std::string const &parameterName) const
checks if a parameter exists
bool isNonnull() const
Checks for non-null.
Definition: Ref.h:232
const_iterator begin() const
XYZTLorentzVectorD XYZTLorentzVector
Lorentz vector with cylindrical internal representation using pseudorapidity.
Definition: LorentzVector.h:29
int iEvent
Definition: GenABIO.cc:224
const GenParticleRef & nearest_status3() const
const GenParticleRef & nearest_status2() const
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
#define DEFINE_FWK_MODULE(type)
Definition: MakerMacros.h:16
const GenParticleRef & physicsDefinitionParton() const
const GenParticleRef & algoDefinitionParton() const
const_iterator end() const
lepton info struct
Definition: JetFlavour.h:23
XYZPointD XYZPoint
point in space with cartesian internal representation
Definition: Point3D.h:12
const GenParticleRef heaviest() const
RefToBaseProd< T > makeRefToBaseProdFrom(RefToBase< T > const &iRef, Event const &iEvent)
fixed size matrix
HLT enums.
T const * get() const
Returns C++ pointer to the item.
Definition: Ref.h:226
EDGetTokenT< JetMatchedPartonsCollection > sourceByReferToken_
def move(src, dest)
Definition: eostools.py:511