CMS 3D CMS Logo

PFCandConnector.cc
Go to the documentation of this file.
6 
7 using namespace reco;
8 using namespace std;
9 
10 const double PFCandConnector::pion_mass2 = 0.0194;
11 
14 
16  bool bCalibPrimary,
17  double dptRel_PrimaryTrack,
18  double dptRel_MergedTrack,
19  double ptErrorSecondary,
20  const std::vector<double>& nuclCalibFactors) {
21  bCorrect_ = bCorrect;
22  bCalibPrimary_ = bCalibPrimary;
23  dptRel_PrimaryTrack_ = dptRel_PrimaryTrack;
24  dptRel_MergedTrack_ = dptRel_MergedTrack;
25  ptErrorSecondary_ = ptErrorSecondary;
26 
27  if (nuclCalibFactors.size() == 5) {
28  fConst_[0] = nuclCalibFactors[0];
29  fConst_[1] = nuclCalibFactors[1];
30 
31  fNorm_[0] = nuclCalibFactors[2];
32  fNorm_[1] = nuclCalibFactors[3];
33 
34  fExp_[0] = nuclCalibFactors[4];
35  } else {
36  edm::LogWarning("PFCandConnector")
37  << "Wrong calibration factors for nuclear interactions. The calibration procedure would not be applyed."
38  << std::endl;
39  bCalibPrimary_ = false;
40  }
41 
42  std::string sCorrect = bCorrect_ ? "On" : "Off";
43  edm::LogInfo("PFCandConnector") << " ====================== The PFCandConnector is switched " << sCorrect.c_str()
44  << " ==================== " << std::endl;
45  std::string sCalibPrimary = bCalibPrimary_ ? "used for calibration" : "not used for calibration";
46  if (bCorrect_)
47  edm::LogInfo("PFCandConnector") << "Primary Tracks are " << sCalibPrimary.c_str() << std::endl;
48  if (bCorrect_ && bCalibPrimary_)
49  edm::LogInfo("PFCandConnector") << "Under the condition that the precision on the Primary track is better than "
50  << dptRel_PrimaryTrack_ << " % " << std::endl;
51  if (bCorrect_ && bCalibPrimary_)
52  edm::LogInfo("PFCandConnector") << " and on merged tracks better than " << dptRel_MergedTrack_ << " % "
53  << std::endl;
54  if (bCorrect_ && bCalibPrimary_)
55  edm::LogInfo("PFCandConnector") << " and secondary tracks in some cases more precise than "
56  << ptErrorSecondary_ << " GeV" << std::endl;
57  if (bCorrect_ && bCalibPrimary_)
58  edm::LogInfo("PFCandConnector") << "factor = (" << fConst_[0] << " + " << fConst_[1] << "*cFrac) - (" << fNorm_[0]
59  << " - " << fNorm_[1] << "cFrac)*exp( " << -1 * fExp_[0] << "*pT )" << std::endl;
60  edm::LogInfo("PFCandConnector") << " =========================================================== " << std::endl;
61 }
62 
67  std::vector<bool> bMask;
68  bMask.resize(pfCand.size(), false);
69 
70  // loop on primary
71  if (bCorrect_) {
72  LogTrace("PFCandConnector|connect") << "pfCand.size()=" << pfCand.size() << "bCalibPrimary_=" << bCalibPrimary_;
73 
74  for (unsigned int ce1 = 0; ce1 < pfCand.size(); ++ce1) {
75  if (isPrimaryNucl(pfCand.at(ce1))) {
76  LogTrace("PFCandConnector|connect")
77  << "" << endl
78  << "Nuclear Interaction w Primary Candidate " << ce1 << " " << pfCand.at(ce1) << endl
79  << " based on the Track " << pfCand.at(ce1).trackRef().key()
80  << " w pT = " << pfCand.at(ce1).trackRef()->pt() << " #pm "
81  << pfCand.at(ce1).trackRef()->ptError() / pfCand.at(ce1).trackRef()->pt() * 100 << " %"
82  << " ECAL = " << pfCand.at(ce1).ecalEnergy() << " HCAL = " << pfCand.at(ce1).hcalEnergy() << endl;
83 
84 #ifdef EDM_ML_DEBUG
85  (pfCand.at(ce1)).displacedVertexRef(fT_TO_DISP_)->Dump();
86 #endif
87 
88  analyseNuclearWPrim(pfCand, bMask, ce1);
89 
90 #ifdef EDM_ML_DEBUG
91  LogTrace("PFCandConnector|connect")
92  << "After Connection the candidate " << ce1 << " is " << pfCand.at(ce1) << endl
93  << endl;
94 
95  PFCandidate::ElementsInBlocks elementsInBlocks = pfCand.at(ce1).elementsInBlocks();
96  for (unsigned blockElem = 0; blockElem < elementsInBlocks.size(); blockElem++) {
97  if (blockElem == 0)
98  LogTrace("PFCandConnector|connect") << *(elementsInBlocks[blockElem].first) << endl;
99  LogTrace("PFCandConnector|connect") << " position " << elementsInBlocks[blockElem].second;
100  }
101 #endif
102  }
103  }
104 
105  for (unsigned int ce1 = 0; ce1 < pfCand.size(); ++ce1) {
106  if (!bMask[ce1] && isSecondaryNucl(pfCand.at(ce1))) {
107  LogTrace("PFCandConnector|connect")
108  << "" << endl
109  << "Nuclear Interaction w no Primary Candidate " << ce1 << " " << pfCand.at(ce1) << endl
110  << " based on the Track " << pfCand.at(ce1).trackRef().key()
111  << " w pT = " << pfCand.at(ce1).trackRef()->pt() << " #pm " << pfCand.at(ce1).trackRef()->ptError() << " %"
112  << " ECAL = " << pfCand.at(ce1).ecalEnergy() << " HCAL = " << pfCand.at(ce1).hcalEnergy()
113  << " dE(Trk-CALO) = "
114  << pfCand.at(ce1).trackRef()->p() - pfCand.at(ce1).ecalEnergy() - pfCand.at(ce1).hcalEnergy()
115  << " Nmissing hits = "
116  << pfCand.at(ce1).trackRef()->hitPattern().numberOfLostHits(reco::HitPattern::MISSING_OUTER_HITS) << endl;
117 
118 #ifdef EDM_ML_DEBUG
119  (pfCand.at(ce1)).displacedVertexRef(fT_FROM_DISP_)->Dump();
120 #endif
121 
122  analyseNuclearWSec(pfCand, bMask, ce1);
123 
124 #ifdef EDM_ML_DEBUG
125  LogTrace("PFCandConnector|connect") << "After Connection the candidate " << ce1 << " is " << pfCand.at(ce1)
126  << " and elements connected to it are: " << endl;
127 
128  PFCandidate::ElementsInBlocks elementsInBlocks = pfCand.at(ce1).elementsInBlocks();
129  for (unsigned blockElem = 0; blockElem < elementsInBlocks.size(); blockElem++) {
130  if (blockElem == 0)
131  LogTrace("PFCandConnector|connect") << *(elementsInBlocks[blockElem].first) << endl;
132  LogTrace("PFCandConnector|connect") << " position " << elementsInBlocks[blockElem].second;
133  }
134 #endif
135  }
136  }
137  }
138 
139  for (unsigned int ce1 = 0; ce1 < pfCand.size(); ++ce1)
140  if (!bMask[ce1])
141  pfC.push_back(pfCand.at(ce1));
142 
143  LogTrace("PFCandConnector|connect") << "end of function";
144 
145  return pfC;
146 }
147 
149  std::vector<bool>& bMask,
150  unsigned int ce1) const {
151  PFDisplacedVertexRef ref1, ref2, ref1_bis;
152 
153  PFCandidate primaryCand = pfCand.at(ce1);
154 
155  // ------- look for the little friends -------- //
156 
157  const math::XYZTLorentzVectorD& momentumPrim = primaryCand.p4();
158 
159  math::XYZTLorentzVectorD momentumSec(0., 0., 0., 0.);
160 
161  if (momentumPrim.E() > 0)
162  momentumSec = momentumPrim / momentumPrim.E() * (primaryCand.ecalEnergy() + primaryCand.hcalEnergy());
163 
164  map<double, math::XYZTLorentzVectorD> candidatesWithTrackExcess;
165  map<double, math::XYZTLorentzVectorD> candidatesWithoutCalo;
166 
167  ref1 = primaryCand.displacedVertexRef(fT_TO_DISP_);
168 
169  for (unsigned int ce2 = 0; ce2 < pfCand.size(); ++ce2) {
170  if (ce2 != ce1 && isSecondaryNucl(pfCand.at(ce2))) {
171  ref2 = (pfCand.at(ce2)).displacedVertexRef(fT_FROM_DISP_);
172 
173  if (ref1 == ref2) {
174  LogTrace("PFCandConnector|analyseNuclearWPrim")
175  << "\t here is a Secondary Candidate " << ce2 << " " << pfCand.at(ce2) << endl
176  << "\t based on the Track " << pfCand.at(ce2).trackRef().key()
177  << " w p = " << pfCand.at(ce2).trackRef()->p() << " w pT = " << pfCand.at(ce2).trackRef()->pt() << " #pm "
178  << pfCand.at(ce2).trackRef()->ptError() << " %"
179  << " ECAL = " << pfCand.at(ce2).ecalEnergy() << " HCAL = " << pfCand.at(ce2).hcalEnergy()
180  << " dE(Trk-CALO) = "
181  << pfCand.at(ce2).trackRef()->p() - pfCand.at(ce2).ecalEnergy() - pfCand.at(ce2).hcalEnergy()
182  << " Nmissing hits = "
183  << pfCand.at(ce2).trackRef()->hitPattern().numberOfLostHits(reco::HitPattern::MISSING_OUTER_HITS) << endl;
184 
185  if (isPrimaryNucl(pfCand.at(ce2))) {
186  LogTrace("PFCandConnector|analyseNuclearWPrim") << "\t\t but it is also a Primary Candidate " << ce2 << endl;
187 
188  ref1_bis = (pfCand.at(ce2)).displacedVertexRef(fT_TO_DISP_);
189  if (ref1_bis.isNonnull())
190  analyseNuclearWPrim(pfCand, bMask, ce2);
191  }
192 
193  // Take now the parameters of the secondary track that are relevant and use them to construct the NI candidate
194 
195  PFCandidate::ElementsInBlocks elementsInBlocks = pfCand.at(ce2).elementsInBlocks();
196  PFCandidate::ElementsInBlocks elementsAlreadyInBlocks = pfCand.at(ce1).elementsInBlocks();
197  for (unsigned blockElem = 0; blockElem < elementsInBlocks.size(); blockElem++) {
198  bool isAlreadyHere = false;
199  for (unsigned alreadyBlock = 0; alreadyBlock < elementsAlreadyInBlocks.size(); alreadyBlock++) {
200  if (elementsAlreadyInBlocks[alreadyBlock].second == elementsInBlocks[blockElem].second)
201  isAlreadyHere = true;
202  }
203  if (!isAlreadyHere)
204  pfCand.at(ce1).addElementInBlock(elementsInBlocks[blockElem].first, elementsInBlocks[blockElem].second);
205  }
206 
207  double candE = pfCand.at(ce2).p4().E();
208  double caloEn = pfCand.at(ce2).ecalEnergy() + pfCand.at(ce2).hcalEnergy();
209  double deltaEn = candE - caloEn;
210  int nMissOuterHits =
211  pfCand.at(ce2).trackRef()->hitPattern().numberOfLostHits(reco::HitPattern::MISSING_OUTER_HITS);
212 
213  // Check if the difference Track Calo is not too large and if we can trust the track, ie it doesn't miss too much hits.
214  if (deltaEn > 1 && nMissOuterHits > 1 && candE > 0) {
215  math::XYZTLorentzVectorD momentumToAdd = pfCand.at(ce2).p4() * caloEn / candE;
216  momentumSec += momentumToAdd;
217  LogTrace("PFCandConnector|analyseNuclearWPrim")
218  << "The difference track-calo s really large and the track miss at least 2 hits. A secondary NI may "
219  "have happened. Let's trust the calo energy"
220  << endl
221  << "add " << momentumToAdd << endl;
222 
223  } else {
224  // Check if the difference Track Calo is not too large and if we can trust the track, ie it doesn't miss too much hits.
225  if (caloEn > 0.01 && deltaEn > 1 && nMissOuterHits > 0 && candE > 0) {
226  math::XYZTLorentzVectorD momentumExcess = pfCand.at(ce2).p4() * deltaEn / candE;
227  candidatesWithTrackExcess[pfCand.at(ce2).trackRef()->pt() / pfCand.at(ce2).trackRef()->ptError()] =
228  momentumExcess;
229  } else if (caloEn < 0.01)
230  candidatesWithoutCalo[pfCand.at(ce2).trackRef()->pt() / pfCand.at(ce2).trackRef()->ptError()] =
231  pfCand.at(ce2).p4();
232  momentumSec += (pfCand.at(ce2)).p4();
233  }
234 
235  bMask[ce2] = true;
236  }
237  }
238  }
239 
240  // We have more primary energy than secondary: reject all secondary tracks which have no calo energy attached.
241 
242  if (momentumPrim.E() < momentumSec.E()) {
243  LogTrace("PFCandConnector|analyseNuclearWPrim")
244  << "Size of 0 calo Energy secondary candidates" << candidatesWithoutCalo.size() << endl;
245  for (map<double, math::XYZTLorentzVectorD>::iterator iter = candidatesWithoutCalo.begin();
246  iter != candidatesWithoutCalo.end() && momentumPrim.E() < momentumSec.E();
247  iter++)
248  if (momentumSec.E() > iter->second.E() + 0.1) {
249  momentumSec -= iter->second;
250 
251  LogTrace("PFCandConnector|analyseNuclearWPrim")
252  << "\t Remove a SecondaryCandidate with 0 calo energy " << iter->second << endl;
253  LogTrace("PFCandConnector|analyseNuclearWPrim")
254  << "momentumPrim.E() = " << momentumPrim.E() << " and momentumSec.E() = " << momentumSec.E() << endl;
255  }
256  }
257 
258  if (momentumPrim.E() < momentumSec.E()) {
259  LogTrace("PFCandConnector|analyseNuclearWPrim")
260  << "0 Calo Energy rejected but still not sufficient. Size of not enough calo Energy secondary candidates"
261  << candidatesWithTrackExcess.size() << endl;
262  for (map<double, math::XYZTLorentzVectorD>::iterator iter = candidatesWithTrackExcess.begin();
263  iter != candidatesWithTrackExcess.end() && momentumPrim.E() < momentumSec.E();
264  iter++)
265  if (momentumSec.E() > iter->second.E() + 0.1)
266  momentumSec -= iter->second;
267  }
268 
269  double dpt = pfCand.at(ce1).trackRef()->ptError() / pfCand.at(ce1).trackRef()->pt() * 100;
270 
271  if (momentumSec.E() < 0.1) {
272  bMask[ce1] = true;
273  return;
274  }
275 
276  // Rescale the secondary candidates to account for the loss of energy, but only if we can trust the primary track:
277  // if it has more energy than secondaries and is precise enough and secondary exist and was not eaten or rejected during the PFAlgo step.
278 
279  if (((ref1->isTherePrimaryTracks() && dpt < dptRel_PrimaryTrack_) ||
280  (ref1->isThereMergedTracks() && dpt < dptRel_MergedTrack_)) &&
281  momentumPrim.E() > momentumSec.E() && momentumSec.E() > 0.1) {
282  if (bCalibPrimary_) {
283  double factor = momentumPrim.E() > 0 ? rescaleFactor(momentumPrim.Pt(), momentumSec.E() / momentumPrim.E()) : 1.;
284  LogTrace("PFCandConnector|analyseNuclearWPrim") << "factor = " << factor << endl;
285  if (factor * momentumPrim.Pt() < momentumSec.Pt())
286  momentumSec = momentumPrim;
287  else
288  momentumSec += (1 - factor) * momentumPrim;
289  }
290 
291  double px = 0, py = 0, pz = 0.;
292  if (momentumPrim.P() > 0) {
293  px = momentumPrim.Px() * momentumSec.P() / momentumPrim.P();
294  py = momentumPrim.Py() * momentumSec.P() / momentumPrim.P();
295  pz = momentumPrim.Pz() * momentumSec.P() / momentumPrim.P();
296  }
297  double E = sqrt(px * px + py * py + pz * pz + pion_mass2);
298  math::XYZTLorentzVectorD momentum(px, py, pz, E);
299  pfCand.at(ce1).setP4(momentum);
300 
301  return;
302 
303  } else {
304  math::XYZVector primDir = ref1->primaryDirection();
305 
306  if (primDir.Mag2() < 0.1) {
307  // It might be 0 but this situation should never happend. Throw a warning if it happens.
308  edm::LogWarning("PFCandConnector") << "A Nuclear Interaction do not have primary direction" << std::endl;
309  pfCand.at(ce1).setP4(momentumSec);
310  return;
311  } else {
312  // rescale the primary direction to the optimal momentum. But take care of the factthat it shall not be completly 0 to avoid a warning if Jet Area.
313  double momentumS = momentumSec.P();
314  if (momentumS < 1e-4)
315  momentumS = 1e-4;
316  double px = momentumS * primDir.x();
317  double py = momentumS * primDir.y();
318  double pz = momentumS * primDir.z();
319  double E = sqrt(px * px + py * py + pz * pz + pion_mass2);
320 
321  math::XYZTLorentzVectorD momentum(px, py, pz, E);
322  pfCand.at(ce1).setP4(momentum);
323  return;
324  }
325  }
326 }
327 
329  std::vector<bool>& bMask,
330  unsigned int ce1) const {
331  PFDisplacedVertexRef ref1, ref2;
332 
333  // Check if the track excess was not too large and track may miss some outer hits. This may point to a secondary NI.
334 
335  double caloEn = pfCand.at(ce1).ecalEnergy() + pfCand.at(ce1).hcalEnergy();
336  double deltaEn = pfCand.at(ce1).p4().E() - caloEn;
337  int nMissOuterHits = pfCand.at(ce1).trackRef()->hitPattern().numberOfLostHits(reco::HitPattern::MISSING_OUTER_HITS);
338 
339  ref1 = pfCand.at(ce1).displacedVertexRef(fT_FROM_DISP_);
340 
341  // ------- check if an electron or a muon vas spotted as incoming track -------- //
342  // ------- this mean probably that the NI was fake thus we do not correct it -------- /
343 
344  if (ref1->isTherePrimaryTracks() || ref1->isThereMergedTracks()) {
345  std::vector<reco::Track> refittedTracks = ref1->refittedTracks();
346  for (unsigned it = 0; it < refittedTracks.size(); it++) {
347  reco::TrackBaseRef primaryBaseRef = ref1->originalTrack(refittedTracks[it]);
348  if (ref1->isIncomingTrack(primaryBaseRef))
349  LogTrace("PFCandConnector|analyseNuclearWSec")
350  << "There is a Primary track ref with pt = " << primaryBaseRef->pt() << endl;
351 
352  for (unsigned int ce = 0; ce < pfCand.size(); ++ce) {
353  // cout << "PFCand Id = " << (pfCand.at(ce)).particleId() << endl;
354  if ((pfCand.at(ce)).particleId() == reco::PFCandidate::e ||
355  (pfCand.at(ce)).particleId() == reco::PFCandidate::mu) {
356  LogTrace("PFCandConnector|analyseNuclearWSec")
357  << " It is an electron and it has a ref to a track " << (pfCand.at(ce)).trackRef().isNonnull() << endl;
358 
359  if ((pfCand.at(ce)).trackRef().isNonnull()) {
360  reco::TrackRef tRef = (pfCand.at(ce)).trackRef();
361  reco::TrackBaseRef bRef(tRef);
362  LogTrace("PFCandConnector|analyseNuclearWSec")
363  << "With Track Ref pt = " << (pfCand.at(ce)).trackRef()->pt() << endl;
364 
365  if (bRef == primaryBaseRef) {
366  if ((pfCand.at(ce)).particleId() == reco::PFCandidate::e)
367  LogTrace("PFCandConnector|analyseNuclearWSec")
368  << "It is a NI from electron. NI Discarded. Just release the candidate." << endl;
369  if ((pfCand.at(ce)).particleId() == reco::PFCandidate::mu)
370  LogTrace("PFCandConnector|analyseNuclearWSec")
371  << "It is a NI from muon. NI Discarded. Just release the candidate" << endl;
372 
373  // release the track but take care of not overcounting bad tracks. In fact those tracks was protected against destruction in
374  // PFAlgo. Now we treat them as if they was treated in PFAlgo
375 
376  if (caloEn < 0.1 && pfCand.at(ce1).trackRef()->ptError() > ptErrorSecondary_) {
377  edm::LogInfo("PFCandConnector|analyseNuclearWSec")
378  << "discarded track since no calo energy and ill measured" << endl;
379  bMask[ce1] = true;
380  }
381  if (caloEn > 0.1 && deltaEn > ptErrorSecondary_ &&
382  pfCand.at(ce1).trackRef()->ptError() > ptErrorSecondary_) {
383  edm::LogInfo("PFCandConnector|analyseNuclearWSec")
384  << "rescaled momentum of the track since no calo energy and ill measured" << endl;
385 
386  double factor = caloEn / pfCand.at(ce1).p4().E();
387  pfCand.at(ce1).rescaleMomentum(factor);
388  }
389 
390  return;
391  }
392  }
393  }
394  }
395  }
396  }
397 
398  PFCandidate secondaryCand = pfCand.at(ce1);
399 
400  math::XYZTLorentzVectorD momentumSec = secondaryCand.p4();
401 
402  if (deltaEn > ptErrorSecondary_ && nMissOuterHits > 1) {
403  math::XYZTLorentzVectorD momentumToAdd(0., 0., 0., 0.);
404  float candE = pfCand.at(ce1).p4().E();
405  if (candE > 0)
406  momentumToAdd = pfCand.at(ce1).p4() * caloEn / candE;
407  momentumSec = momentumToAdd;
408  LogTrace("PFCandConnector|analyseNuclearWSec")
409  << "The difference track-calo s really large and the track miss at least 2 hits. A secondary NI may have "
410  "happened. Let's trust the calo energy"
411  << endl
412  << "add " << momentumToAdd << endl;
413  }
414 
415  // ------- look for the little friends -------- //
416  for (unsigned int ce2 = ce1 + 1; ce2 < pfCand.size(); ++ce2) {
417  if (isSecondaryNucl(pfCand.at(ce2))) {
418  ref2 = (pfCand.at(ce2)).displacedVertexRef(fT_FROM_DISP_);
419 
420  if (ref1 == ref2) {
421  LogTrace("PFCandConnector|analyseNuclearWSec")
422  << "\t here is a Secondary Candidate " << ce2 << " " << pfCand.at(ce2) << endl
423  << "\t based on the Track " << pfCand.at(ce2).trackRef().key()
424  << " w pT = " << pfCand.at(ce2).trackRef()->pt() << " #pm " << pfCand.at(ce2).trackRef()->ptError() << " %"
425  << " ECAL = " << pfCand.at(ce2).ecalEnergy() << " HCAL = " << pfCand.at(ce2).hcalEnergy()
426  << " dE(Trk-CALO) = "
427  << pfCand.at(ce2).trackRef()->p() - pfCand.at(ce2).ecalEnergy() - pfCand.at(ce2).hcalEnergy()
428  << " Nmissing hits = "
429  << pfCand.at(ce2).trackRef()->hitPattern().numberOfLostHits(reco::HitPattern::MISSING_OUTER_HITS) << endl;
430 
431  // Take now the parameters of the secondary track that are relevant and use them to construct the NI candidate
432  PFCandidate::ElementsInBlocks elementsInBlocks = pfCand.at(ce2).elementsInBlocks();
433  PFCandidate::ElementsInBlocks elementsAlreadyInBlocks = pfCand.at(ce1).elementsInBlocks();
434  for (unsigned blockElem = 0; blockElem < elementsInBlocks.size(); blockElem++) {
435  bool isAlreadyHere = false;
436  for (unsigned alreadyBlock = 0; alreadyBlock < elementsAlreadyInBlocks.size(); alreadyBlock++) {
437  if (elementsAlreadyInBlocks[alreadyBlock].second == elementsInBlocks[blockElem].second)
438  isAlreadyHere = true;
439  }
440  if (!isAlreadyHere)
441  pfCand.at(ce1).addElementInBlock(elementsInBlocks[blockElem].first, elementsInBlocks[blockElem].second);
442  }
443 
444  double candE = pfCand.at(ce2).p4().E();
445  double caloEn = pfCand.at(ce2).ecalEnergy() + pfCand.at(ce2).hcalEnergy();
446  double deltaEn = candE - caloEn;
447  int nMissOuterHits =
448  pfCand.at(ce2).trackRef()->hitPattern().numberOfLostHits(reco::HitPattern::MISSING_OUTER_HITS);
449  if (deltaEn > ptErrorSecondary_ && nMissOuterHits > 1) {
450  math::XYZTLorentzVectorD momentumToAdd = pfCand.at(ce2).p4() * caloEn / candE;
451  momentumSec += momentumToAdd;
452  LogTrace("PFCandConnector|analyseNuclearWSec")
453  << "The difference track-calo s really large and the track miss at least 2 hits. A secondary NI may "
454  "have happened. Let's trust the calo energy"
455  << endl
456  << "add " << momentumToAdd << endl;
457  } else {
458  momentumSec += (pfCand.at(ce2)).p4();
459  }
460 
461  bMask[ce2] = true;
462  }
463  }
464  }
465 
466  math::XYZVector primDir = ref1->primaryDirection();
467 
468  if (primDir.Mag2() < 0.1) {
469  // It might be 0 but this situation should never happend. Throw a warning if it happens.
470  pfCand.at(ce1).setP4(momentumSec);
471  edm::LogWarning("PFCandConnector") << "A Nuclear Interaction do not have primary direction" << std::endl;
472  return;
473  } else {
474  // rescale the primary direction to the optimal momentum. But take care of the factthat it shall not be completly 0 to avoid a warning if Jet Area.
475  double momentumS = momentumSec.P();
476  if (momentumS < 1e-4)
477  momentumS = 1e-4;
478  double px = momentumS * primDir.x();
479  double py = momentumS * primDir.y();
480  double pz = momentumS * primDir.z();
481  double E = sqrt(px * px + py * py + pz * pz + pion_mass2);
482 
483  math::XYZTLorentzVectorD momentum(px, py, pz, E);
484 
485  pfCand.at(ce1).setP4(momentum);
486  return;
487  }
488 }
489 
492  // nuclear
493  if (pf.flag(fT_FROM_DISP_)) {
494  ref1 = pf.displacedVertexRef(fT_FROM_DISP_);
495  // ref1->Dump();
496  if (!ref1.isNonnull())
497  return false;
498  else if (ref1->isNucl() || ref1->isNucl_Loose() || ref1->isNucl_Kink())
499  return true;
500  }
501 
502  return false;
503 }
504 
507 
508  // nuclear
509  if (pf.flag(fT_TO_DISP_)) {
510  ref1 = pf.displacedVertexRef(fT_TO_DISP_);
511  //ref1->Dump();
512 
513  if (!ref1.isNonnull())
514  return false;
515  else if (ref1->isNucl() || ref1->isNucl_Loose() || ref1->isNucl_Kink())
516  return true;
517  }
518 
519  return false;
520 }
521 
522 double PFCandConnector::rescaleFactor(const double pt, const double cFrac) const {
523  /*
524  LOG NORMAL FIT
525  FCN=35.8181 FROM MIGRAD STATUS=CONVERGED 257 CALLS 258 TOTAL
526  EDM=8.85763e-09 STRATEGY= 1 ERROR MATRIX ACCURATE
527  EXT PARAMETER STEP FIRST
528  NO. NAME VALUE ERROR SIZE DERIVATIVE
529  1 p0 7.99434e-01 2.77264e-02 6.59108e-06 9.80247e-03
530  2 p1 1.51303e-01 2.89981e-02 1.16775e-05 6.99035e-03
531  3 p2 -5.03829e-01 2.87929e-02 1.90070e-05 1.37015e-03
532  4 p3 4.54043e-01 5.00908e-02 3.17625e-05 3.86622e-03
533  5 p4 -4.61736e-02 8.07940e-03 3.25775e-06 -1.37247e-02
534  */
535 
536  /*
537  FCN=34.4051 FROM MIGRAD STATUS=CONVERGED 221 CALLS 222 TOTAL
538  EDM=1.02201e-09 STRATEGY= 1 ERROR MATRIX UNCERTAINTY 2.3 per cent
539 
540  fConst
541  1 p0 7.99518e-01 2.23519e-02 1.41523e-06 4.05975e-04
542  2 p1 1.44619e-01 2.39398e-02 -7.68117e-07 -2.55775e-03
543 
544  fNorm
545  3 p2 -5.16571e-01 3.12362e-02 5.74932e-07 3.42292e-03
546  4 p3 4.69055e-01 5.09665e-02 1.94353e-07 1.69031e-03
547 
548  fExp
549  5 p4 -5.18044e-02 8.13458e-03 4.29815e-07 -1.07624e-02
550  */
551 
552  double fConst, fNorm, fExp;
553 
554  fConst = fConst_[0] + fConst_[1] * cFrac;
555  fNorm = fNorm_[0] - fNorm_[1] * cFrac;
556  fExp = fExp_[0];
557 
558  double factor = fConst - fNorm * exp(-fExp * pt);
559 
560  return factor;
561 }
562 
564  iDesc.add<bool>("bCorrect", true);
565  iDesc.add<bool>("bCalibPrimary", true);
566  iDesc.add<double>("dptRel_PrimaryTrack", 10.0);
567  iDesc.add<double>("dptRel_MergedTrack", 5.0);
568  iDesc.add<double>("ptErrorSecondary", 1.0);
569  iDesc.add<std::vector<double>>("nuclCalibFactors", {0.8, 0.15, 0.5, 0.5, 0.05});
570 }
reco::PFDisplacedVertexRef displacedVertexRef(Flags type) const
Definition: PFCandidate.cc:508
bool isPrimaryNucl(const reco::PFCandidate &pf) const
ROOT::Math::LorentzVector< ROOT::Math::PxPyPzE4D< double > > XYZTLorentzVectorD
Lorentz vector with cylindrical internal representation using pseudorapidity.
Definition: LorentzVector.h:14
static const reco::PFCandidate::Flags fT_FROM_DISP_
bool isNonnull() const
Checks for non-null.
Definition: Ref.h:238
static const reco::PFCandidate::Flags fT_TO_DISP_
const LorentzVector & p4() const final
four-momentum Lorentz vector
#define LogTrace(id)
std::vector< ElementInBlock > ElementsInBlocks
Definition: PFCandidate.h:404
U second(std::pair< T, U > const &p)
void analyseNuclearWPrim(reco::PFCandidateCollection &, std::vector< bool > &, unsigned int) const
Analyse nuclear interactions where a primary or merged track is present.
void analyseNuclearWSec(reco::PFCandidateCollection &, std::vector< bool > &, unsigned int) const
Analyse nuclear interactions where a secondary track is present.
double pt() const
track transverse momentum
Definition: TrackBase.h:637
double hcalEnergy() const
return corrected Hcal energy
Definition: PFCandidate.h:233
T sqrt(T t)
Definition: SSEVec.h:19
static const double pion_mass2
Useful constants.
ParameterDescriptionBase * add(U const &iLabel, T const &value)
double ecalEnergy() const
return corrected Ecal energy
Definition: PFCandidate.h:221
static void fillPSetDescription(edm::ParameterSetDescription &iDesc)
Log< level::Info, false > LogInfo
std::vector< reco::PFCandidate > PFCandidateCollection
collection of PFCandidates
XYZVectorD XYZVector
spatial vector with cartesian internal representation
Definition: Vector3D.h:31
reco::PFCandidateCollection connect(reco::PFCandidateCollection &pfCand) const
Particle reconstructed by the particle flow algorithm.
Definition: PFCandidate.h:41
fixed size matrix
void setParameters(const edm::ParameterSet &iCfgCandConnector)
bool isSecondaryNucl(const reco::PFCandidate &pf) const
Log< level::Warning, false > LogWarning
double rescaleFactor(const double pt, const double cFrac) const
Return a calibration factor for a reconstructed nuclear interaction.