CMS 3D CMS Logo

PFAlgo.cc
Go to the documentation of this file.
6 
7 #include "TDecompChol.h"
8 
9 #include <numeric>
10 #include <fstream>
11 
12 using namespace std;
13 using namespace reco;
14 
15 PFAlgo::PFAlgo(double nSigmaECAL,
16  double nSigmaHCAL,
17  double nSigmaHFEM,
18  double nSigmaHFHAD,
19  std::vector<double> resolHF_square,
21  PFEnergyCalibrationHF& thepfEnergyCalibrationHF,
22  const edm::ParameterSet& pset)
23  : pfCandidates_(new PFCandidateCollection),
24  nSigmaECAL_(nSigmaECAL),
25  nSigmaHCAL_(nSigmaHCAL),
26  nSigmaHFEM_(nSigmaHFEM),
27  nSigmaHFHAD_(nSigmaHFHAD),
28  resolHF_square_(resolHF_square),
29  calibration_(calibration),
30  thepfEnergyCalibrationHF_(thepfEnergyCalibrationHF),
31  connector_() {
32  const edm::ParameterSet pfMuonAlgoParams = pset.getParameter<edm::ParameterSet>("PFMuonAlgoParameters");
33  bool postMuonCleaning = pset.getParameter<bool>("postMuonCleaning");
34  pfmu_ = std::make_unique<PFMuonAlgo>(pfMuonAlgoParams, postMuonCleaning);
35 
36  // HF resolution parameters
37  assert(resolHF_square_.size() == 3); // make sure that stochastic, constant, noise (i.e. three) terms are specified.
38 
39  // Muon parameters
40  muonHCAL_ = pset.getParameter<std::vector<double>>("muon_HCAL");
41  muonECAL_ = pset.getParameter<std::vector<double>>("muon_ECAL");
42  muonHO_ = pset.getParameter<std::vector<double>>("muon_HO");
43  assert(muonHCAL_.size() == 2 && muonECAL_.size() == 2 && muonHO_.size() == 2);
44  nSigmaTRACK_ = pset.getParameter<double>("nsigma_TRACK");
45  ptError_ = pset.getParameter<double>("pt_Error");
46  factors45_ = pset.getParameter<std::vector<double>>("factors_45");
47  assert(factors45_.size() == 2);
48 
49  // Bad Hcal Track Parameters
50  goodTrackDeadHcal_ptErrRel_ = pset.getParameter<double>("goodTrackDeadHcal_ptErrRel");
51  goodTrackDeadHcal_chi2n_ = pset.getParameter<double>("goodTrackDeadHcal_chi2n");
52  goodTrackDeadHcal_layers_ = pset.getParameter<uint32_t>("goodTrackDeadHcal_layers");
53  goodTrackDeadHcal_validFr_ = pset.getParameter<double>("goodTrackDeadHcal_validFr");
54  goodTrackDeadHcal_dxy_ = pset.getParameter<double>("goodTrackDeadHcal_dxy");
55 
56  goodPixelTrackDeadHcal_minEta_ = pset.getParameter<double>("goodPixelTrackDeadHcal_minEta");
57  goodPixelTrackDeadHcal_maxPt_ = pset.getParameter<double>("goodPixelTrackDeadHcal_maxPt");
58  goodPixelTrackDeadHcal_ptErrRel_ = pset.getParameter<double>("goodPixelTrackDeadHcal_ptErrRel");
59  goodPixelTrackDeadHcal_chi2n_ = pset.getParameter<double>("goodPixelTrackDeadHcal_chi2n");
60  goodPixelTrackDeadHcal_maxLost3Hit_ = pset.getParameter<int32_t>("goodPixelTrackDeadHcal_maxLost3Hit");
61  goodPixelTrackDeadHcal_maxLost4Hit_ = pset.getParameter<int32_t>("goodPixelTrackDeadHcal_maxLost4Hit");
62  goodPixelTrackDeadHcal_dxy_ = pset.getParameter<double>("goodPixelTrackDeadHcal_dxy");
63  goodPixelTrackDeadHcal_dz_ = pset.getParameter<double>("goodPixelTrackDeadHcal_dz");
64 }
65 
67 
68 void PFAlgo::setEGammaParameters(bool use_EGammaFilters, bool useProtectionsForJetMET) {
69  useEGammaFilters_ = use_EGammaFilters;
70 
71  if (!useEGammaFilters_)
72  return;
73 
75 }
77  const edm::ValueMap<reco::GsfElectronRef>& valueMapGedElectrons,
78  const edm::ValueMap<reco::PhotonRef>& valueMapGedPhotons) {
79  if (useEGammaFilters_) {
81  valueMapGedElectrons_ = &valueMapGedElectrons;
82  valueMapGedPhotons_ = &valueMapGedPhotons;
83  }
84 }
85 
87 
90  minHFCleaningPt_ = pfHFCleaningParams.getParameter<double>("minHFCleaningPt");
91  minSignificance_ = pfHFCleaningParams.getParameter<double>("minSignificance");
92  maxSignificance_ = pfHFCleaningParams.getParameter<double>("maxSignificance");
93  minSignificanceReduction_ = pfHFCleaningParams.getParameter<double>("minSignificanceReduction");
94  maxDeltaPhiPt_ = pfHFCleaningParams.getParameter<double>("maxDeltaPhiPt");
95  minDeltaMet_ = pfHFCleaningParams.getParameter<double>("minDeltaMet");
96 }
97 
101  bool usePFConversions,
102  bool usePFDecays,
103  double dptRel_DispVtx) {
110 }
111 
114 
115  //Set the vertices for muon cleaning
116  pfmu_->setInputsForCleaning(primaryVertices);
117 
118  //Now find the primary vertex!
119  bool primaryVertexFound = false;
120  nVtx_ = primaryVertices.size();
121  for (auto const& vertex : primaryVertices) {
122  if (vertex.isValid() && (!vertex.isFake())) {
124  primaryVertexFound = true;
125  break;
126  }
127  }
128  //Use vertices if the user wants to but only if it exists a good vertex
129  useVertices_ = useVertex && primaryVertexFound;
130 }
131 
132 void PFAlgo::reconstructParticles(const reco::PFBlockHandle& blockHandle, PFEGammaFilters const* pfegamma) {
133  auto const& blocks = *blockHandle;
134 
135  // reset output collection
136  pfCandidates_->clear();
137 
138  LogTrace("PFAlgo|reconstructParticles")
139  << "start of function PFAlgo::reconstructParticles, blocks.size()=" << blocks.size();
140 
141  // sort elements in three lists:
142  std::list<reco::PFBlockRef> hcalBlockRefs;
143  std::list<reco::PFBlockRef> ecalBlockRefs;
144  std::list<reco::PFBlockRef> hoBlockRefs;
145  std::list<reco::PFBlockRef> otherBlockRefs;
146 
147  for (unsigned i = 0; i < blocks.size(); ++i) {
148  reco::PFBlockRef blockref = reco::PFBlockRef(blockHandle, i);
149 
150  const reco::PFBlock& block = *blockref;
152 
153  bool singleEcalOrHcal = false;
154  if (elements.size() == 1) {
156  ecalBlockRefs.push_back(blockref);
157  singleEcalOrHcal = true;
158  }
160  hcalBlockRefs.push_back(blockref);
161  singleEcalOrHcal = true;
162  }
163  if (elements[0].type() == reco::PFBlockElement::HO) {
164  // Single HO elements are likely to be noise. Not considered for now.
165  hoBlockRefs.push_back(blockref);
166  singleEcalOrHcal = true;
167  }
168  }
169 
170  if (!singleEcalOrHcal) {
171  otherBlockRefs.push_back(blockref);
172  }
173  } //loop blocks
174 
175  LogTrace("PFAlgo|reconstructParticles")
176  << "# Ecal blocks: " << ecalBlockRefs.size() << ", # Hcal blocks: " << hcalBlockRefs.size()
177  << ", # HO blocks: " << hoBlockRefs.size() << ", # Other blocks: " << otherBlockRefs.size();
178 
179  // loop on blocks that are not single ecal,
180  // and not single hcal.
181 
182  unsigned nblcks = 0;
183  for (auto const& other : otherBlockRefs) {
184  LogTrace("PFAlgo|reconstructParticles") << "processBlock, Block number " << nblcks++;
185  processBlock(other, hcalBlockRefs, ecalBlockRefs, pfegamma);
186  }
187 
188  std::list<reco::PFBlockRef> empty;
189 
190  unsigned hblcks = 0;
191  // process remaining single hcal blocks
192  for (auto const& hcal : hcalBlockRefs) {
193  LogTrace("PFAlgo|reconstructParticles") << "processBlock, HCAL block number " << hblcks++;
194  processBlock(hcal, empty, empty, pfegamma);
195  }
196 
197  unsigned eblcks = 0;
198  // process remaining single ecal blocks
199  for (auto const& ecal : ecalBlockRefs) {
200  LogTrace("PFAlgo|reconstructParticles") << "processBlock, ECAL block number " << eblcks++;
201  processBlock(ecal, empty, empty, pfegamma);
202  }
203 
204  // Post HF Cleaning
205  pfCleanedCandidates_.clear();
206  // Check if the post HF Cleaning was requested - if not, do nothing
207  if (postHFCleaning_) {
208  postCleaning();
209  }
210 
211  //Muon post cleaning
212  pfmu_->postClean(pfCandidates_.get());
213 
214  //Add Missing muons
215  if (muonHandle_.isValid())
216  pfmu_->addMissingMuons(muonHandle_, pfCandidates_.get());
217 
218  LogTrace("PFAlgo|reconstructParticles")
219  << "end of function PFAlgo::reconstructParticles, pfCandidates_->size()=" << pfCandidates_->size();
220 }
221 
223  std::vector<bool>& active,
224  PFEGammaFilters const* pfegamma) {
225  // const edm::ValueMap<reco::GsfElectronRef> & myGedElectronValMap(*valueMapGedElectrons_);
226 
227  unsigned int negmcandidates = pfEgammaCandidates_->size();
228  LogTrace("PFAlgo|egammaFilters") << "start of function PFAlgo::egammaFilters(), negmcandidates=" << negmcandidates;
229 
230  for (unsigned int ieg = 0; ieg < negmcandidates; ++ieg) {
231  // const reco::PFCandidate & egmcand((*pfEgammaCandidates_)[ieg]);
233 
234  const PFCandidate::ElementsInBlocks& theElements = (*pfEgmRef).elementsInBlocks();
235  PFCandidate::ElementsInBlocks::const_iterator iegfirst = theElements.begin();
236  bool sameBlock = false;
237  bool isGoodElectron = false;
238  bool isGoodPhoton = false;
239  bool isPrimaryElectron = false;
240  if (iegfirst->first == blockref)
241  sameBlock = true;
242  if (sameBlock) {
243  LogTrace("PFAlgo|egammaFilters") << " I am in looping on EGamma Candidates: pt " << (*pfEgmRef).pt()
244  << " eta,phi " << (*pfEgmRef).eta() << ", " << (*pfEgmRef).phi() << " charge "
245  << (*pfEgmRef).charge();
246 
247  if ((*pfEgmRef).gsfTrackRef().isNonnull()) {
248  reco::GsfElectronRef gedEleRef = (*valueMapGedElectrons_)[pfEgmRef];
249  if (gedEleRef.isNonnull()) {
250  isGoodElectron = pfegamma->passElectronSelection(*gedEleRef, *pfEgmRef, nVtx_);
251  isPrimaryElectron = pfegamma->isElectron(*gedEleRef);
252  if (isGoodElectron)
253  LogTrace("PFAlgo|egammaFilters")
254  << "** Good Electron, pt " << gedEleRef->pt() << " eta, phi " << gedEleRef->eta() << ", "
255  << gedEleRef->phi() << " charge " << gedEleRef->charge() << " isPrimary " << isPrimaryElectron
256  << " isoDr03 "
257  << (gedEleRef->dr03TkSumPt() + gedEleRef->dr03EcalRecHitSumEt() + gedEleRef->dr03HcalTowerSumEt())
258  << " mva_isolated " << gedEleRef->mva_Isolated() << " mva_e_pi " << gedEleRef->mva_e_pi();
259  }
260  }
261  if ((*pfEgmRef).superClusterRef().isNonnull()) {
262  reco::PhotonRef gedPhoRef = (*valueMapGedPhotons_)[pfEgmRef];
263  if (gedPhoRef.isNonnull()) {
264  isGoodPhoton = pfegamma->passPhotonSelection(*gedPhoRef);
265  if (isGoodPhoton)
266  LogTrace("PFAlgo|egammaFilters") << "** Good Photon, pt " << gedPhoRef->pt() << " eta, phi "
267  << gedPhoRef->eta() << ", " << gedPhoRef->phi() << endl;
268  }
269  }
270  } // end sameBlock
271 
272  if (isGoodElectron && isGoodPhoton) {
273  if (isPrimaryElectron)
274  isGoodPhoton = false;
275  else
276  isGoodElectron = false;
277  }
278 
279  // isElectron
280  if (isGoodElectron) {
281  reco::GsfElectronRef gedEleRef = (*valueMapGedElectrons_)[pfEgmRef];
282  reco::PFCandidate myPFElectron = *pfEgmRef;
283  // run protections
284  bool lockTracks = false;
285  bool isSafe = true;
287  lockTracks = true;
288  isSafe = pfegamma->isElectronSafeForJetMET(*gedEleRef, myPFElectron, primaryVertex_, lockTracks);
289  }
290 
291  if (isSafe) {
293  myPFElectron.setParticleType(particleType);
294  myPFElectron.setCharge(gedEleRef->charge());
295  myPFElectron.setP4(gedEleRef->p4());
296  myPFElectron.set_mva_e_pi(gedEleRef->mva_e_pi());
297  myPFElectron.set_mva_Isolated(gedEleRef->mva_Isolated());
298 
299  LogTrace("PFAlgo|egammaFilters") << " PFAlgo: found an electron with NEW EGamma code ";
300  LogTrace("PFAlgo|egammaFilters") << " myPFElectron: pt " << myPFElectron.pt() << " eta,phi "
301  << myPFElectron.eta() << ", " << myPFElectron.phi() << " mva "
302  << myPFElectron.mva_e_pi() << " charge " << myPFElectron.charge();
303 
304  LogTrace("PFAlgo|egammaFilters") << " THE BLOCK " << *blockref;
305  for (auto const& eb : theElements) {
306  active[eb.second] = false;
307  LogTrace("PFAlgo|egammaFilters") << " Elements used " << eb.second;
308  }
309 
310  // The electron is considered safe for JetMET and the additional tracks pointing to it are locked
311  if (lockTracks) {
312  const PFCandidate::ElementsInBlocks& extraTracks = myPFElectron.egammaExtraRef()->extraNonConvTracks();
313  for (auto const& trk : extraTracks) {
314  LogTrace("PFAlgo|egammaFilters") << " Extra locked track " << trk.second;
315  active[trk.second] = false;
316  }
317  }
318 
319  LogTrace("PFAlgo|egammaFilters") << "Creating PF electron: pt=" << myPFElectron.pt()
320  << " eta=" << myPFElectron.eta() << " phi=" << myPFElectron.phi();
321  pfCandidates_->push_back(myPFElectron);
322 
323  } else {
324  LogTrace("PFAlgo|egammaFilters") << "PFAlgo: Electron DISCARDED, NOT SAFE FOR JETMET ";
325  }
326  } //isGoodElectron
327 
328  if (isGoodPhoton) {
329  reco::PhotonRef gedPhoRef = (*valueMapGedPhotons_)[pfEgmRef];
330  reco::PFCandidate myPFPhoton = *pfEgmRef;
331  bool isSafe = true;
333  isSafe = pfegamma->isPhotonSafeForJetMET(*gedPhoRef, myPFPhoton);
334  }
335 
336  if (isSafe) {
338  myPFPhoton.setParticleType(particleType);
339  myPFPhoton.setCharge(0);
340  myPFPhoton.set_mva_nothing_gamma(1.);
342  myPFPhoton.setVertex(v);
343  myPFPhoton.setP4(gedPhoRef->p4());
344  LogTrace("PFAlgo|egammaFilters") << " PFAlgo: found a photon with NEW EGamma code ";
345  LogTrace("PFAlgo|egammaFilters") << " myPFPhoton: pt " << myPFPhoton.pt() << " eta,phi " << myPFPhoton.eta()
346  << ", " << myPFPhoton.phi() << " charge " << myPFPhoton.charge();
347 
348  // Lock all the elements
349  LogTrace("PFAlgo|egammaFilters") << " THE BLOCK " << *blockref;
350  for (auto const& eb : theElements) {
351  active[eb.second] = false;
352  LogTrace("PFAlgo|egammaFilters") << " Elements used " << eb.second;
353  }
354  LogTrace("PFAlgo|egammaFilters") << "Creating PF photon: pt=" << myPFPhoton.pt() << " eta=" << myPFPhoton.eta()
355  << " phi=" << myPFPhoton.phi();
356  pfCandidates_->push_back(myPFPhoton);
357 
358  } // end isSafe
359  } // end isGoodPhoton
360  } // end loop on EGM candidates
361  LogTrace("PFAlgo|egammaFilters") << "end of function PFAlgo::egammaFilters";
362 }
363 
365  LogTrace("PFAlgo|conversionAlgo") << "start of function PFAlgo::conversionAlgo(), elements.size()="
366  << elements.size();
367  for (unsigned iEle = 0; iEle < elements.size(); iEle++) {
368  PFBlockElement::Type type = elements[iEle].type();
369  if (type == PFBlockElement::TRACK) {
370  LogTrace("PFAlgo|conversionAlgo") << "elements[" << iEle << "].type() == TRACK, active[" << iEle
371  << "]=" << active[iEle];
372  if (elements[iEle].trackRef()->algo() == reco::TrackBase::conversionStep) {
373  active[iEle] = false;
374  }
375  if (elements[iEle].trackRef()->quality(reco::TrackBase::highPurity)) {
376  LogTrace("PFAlgo|conversionAlgo") << "Track is high purity";
377  continue;
378  }
379  const auto* trackRef = dynamic_cast<const reco::PFBlockElementTrack*>((&elements[iEle]));
380  if (!(trackRef->trackType(reco::PFBlockElement::T_FROM_GAMMACONV))) {
381  LogTrace("PFAlgo|conversionAlgo") << "!trackType(T_FROM_GAMMACONV)";
382  continue;
383  }
384  if (!elements[iEle].convRefs().empty()) {
385  active[iEle] = false;
386  }
387  LogTrace("PFAlgo|conversionAlgo") << "active[iEle=" << iEle << "]=" << active[iEle];
388  }
389  }
390  LogTrace("PFAlgo|conversionAlgo") << "end of function PFAlgo::conversionAlgo";
391 }
392 
394  reco::PFBlock::LinkData& linkData,
396  const reco::PFBlockRef& blockref,
397  std::vector<bool>& active,
398  bool goodTrackDeadHcal,
399  bool hasDeadHcal,
400  unsigned int iTrack,
401  std::multimap<double, unsigned>& ecalElems,
402  reco::TrackRef& trackRef) {
403  LogTrace("PFAlgo|recoTracksNotHCAL") << "start of function PFAlgo::recoTracksNotHCAL(), now dealing with tracks "
404  "linked to no HCAL clusters. Was HCal active? "
405  << (!hasDeadHcal);
406  // vector<unsigned> elementIndices;
407  // elementIndices.push_back(iTrack);
408 
409  // The track momentum
410  double trackMomentum = elements[iTrack].trackRef()->p();
411  LogTrace("PFAlgo|recoTracksNotHCAL") << elements[iTrack];
412 
413  // Is it a "tight" muon ? In that case assume no
414  //Track momentum corresponds to the calorimeter
415  //energy for now
416  bool thisIsAMuon = PFMuonAlgo::isMuon(elements[iTrack]);
417  if (thisIsAMuon)
418  trackMomentum = 0.;
419 
420  // If it is not a muon check if Is it a fake track ?
421  //Michalis: I wonder if we should convert this to dpt/pt?
422  if (!thisIsAMuon && elements[iTrack].trackRef()->ptError() > ptError_) {
423  double deficit = trackMomentum;
424  double resol = neutralHadronEnergyResolution(trackMomentum, elements[iTrack].trackRef()->eta());
425  resol *= trackMomentum;
426 
427  if (!ecalElems.empty()) {
428  unsigned thisEcal = ecalElems.begin()->second;
429  reco::PFClusterRef clusterRef = elements[thisEcal].clusterRef();
430  bool useCluster = true;
431  if (hasDeadHcal) {
432  std::multimap<double, unsigned> sortedTracks;
433  block.associatedElements(
434  thisEcal, linkData, sortedTracks, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
435  useCluster = (sortedTracks.begin()->second == iTrack);
436  }
437  if (useCluster) {
438  deficit -= clusterRef->energy();
439  resol = neutralHadronEnergyResolution(trackMomentum, clusterRef->positionREP().Eta());
440  resol *= trackMomentum;
441  }
442  }
443 
444  bool isPrimary = isFromSecInt(elements[iTrack], "primary");
445 
446  if (deficit > nSigmaTRACK_ * resol && !isPrimary && !goodTrackDeadHcal) {
447  active[iTrack] = false;
448  LogTrace("PFAlgo|recoTracksNotHCAL")
449  << elements[iTrack] << std::endl
450  << " deficit " << deficit << " > " << nSigmaTRACK_ << " x " << resol << " track pt " << trackRef->pt()
451  << " +- " << trackRef->ptError() << " layers valid " << trackRef->hitPattern().trackerLayersWithMeasurement()
452  << ", lost " << trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::TRACK_HITS)
453  << ", lost outer " << trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::MISSING_OUTER_HITS)
454  << ", lost inner " << trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::MISSING_INNER_HITS)
455  << "(valid fraction " << trackRef->validFraction() << ")"
456  << " chi2/ndf " << trackRef->normalizedChi2() << " |dxy| "
457  << std::abs(trackRef->dxy(primaryVertex_.position())) << " +- " << trackRef->dxyError() << " |dz| "
458  << std::abs(trackRef->dz(primaryVertex_.position())) << " +- " << trackRef->dzError()
459  << "is probably a fake (1) --> lock the track";
460  return true;
461  }
462  } // !thisIsaMuon
463 
464  // Create a track candidate
465  // unsigned tmpi = reconstructTrack( elements[iTrack] );
466  //active[iTrack] = false;
467  std::vector<unsigned> tmpi;
468  std::vector<unsigned> kTrack;
469 
470  // Some cleaning : secondary tracks without calo's and large momentum must be fake
471  double dpt = trackRef->ptError();
472 
473  if (rejectTracks_Step45_ && ecalElems.empty() && trackMomentum > 30. && dpt > 0.5 &&
474  (PFTrackAlgoTools::step45(trackRef->algo()) && !goodTrackDeadHcal)) {
475  double dptRel = dpt / trackRef->pt() * 100;
476  bool isPrimaryOrSecondary = isFromSecInt(elements[iTrack], "all");
477 
478  if (isPrimaryOrSecondary && dptRel < dptRel_DispVtx_) {
479  return true;
480  };
481  unsigned nHits = elements[iTrack].trackRef()->hitPattern().trackerLayersWithMeasurement();
482  unsigned int NLostHit = trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::TRACK_HITS);
483 
484  LogTrace("PFAlgo|recoTracksNotHCAL") << "A track (algo = " << trackRef->algo() << ") with momentum "
485  << trackMomentum << " / " << elements[iTrack].trackRef()->pt() << " +/- "
486  << dpt << " / " << elements[iTrack].trackRef()->eta()
487  << " without any link to ECAL/HCAL and with " << nHits << " (" << NLostHit
488  << ") hits (lost hits) has been cleaned";
489 
490  active[iTrack] = false;
491  return true;
492  } //rejectTracks_Step45_ && ...
493 
494  tmpi.push_back(reconstructTrack(elements[iTrack]));
495 
496  kTrack.push_back(iTrack);
497  active[iTrack] = false;
498 
499  // No ECAL cluster either ... continue...
500  if (ecalElems.empty()) {
501  (*pfCandidates_)[tmpi[0]].setEcalEnergy(0., 0.);
502  (*pfCandidates_)[tmpi[0]].setHcalEnergy(0., 0.);
503  (*pfCandidates_)[tmpi[0]].setHoEnergy(0., 0.);
504  (*pfCandidates_)[tmpi[0]].setPs1Energy(0);
505  (*pfCandidates_)[tmpi[0]].setPs2Energy(0);
506  (*pfCandidates_)[tmpi[0]].addElementInBlock(blockref, kTrack[0]);
507  return true;
508  }
509 
510  // Look for closest ECAL cluster
511  const unsigned int thisEcal = ecalElems.begin()->second;
512  reco::PFClusterRef clusterRef = elements[thisEcal].clusterRef();
513  LogTrace("PFAlgo|recoTracksNotHCAL") << " is associated to " << elements[thisEcal];
514 
515  // Set ECAL energy for muons
516  if (thisIsAMuon) {
517  (*pfCandidates_)[tmpi[0]].setEcalEnergy(clusterRef->energy(), std::min(clusterRef->energy(), muonECAL_[0]));
518  (*pfCandidates_)[tmpi[0]].setHcalEnergy(0., 0.);
519  (*pfCandidates_)[tmpi[0]].setHoEnergy(0., 0.);
520  (*pfCandidates_)[tmpi[0]].setPs1Energy(0);
521  (*pfCandidates_)[tmpi[0]].setPs2Energy(0);
522  (*pfCandidates_)[tmpi[0]].addElementInBlock(blockref, kTrack[0]);
523  }
524 
525  double slopeEcal = 1.;
526  bool connectedToEcal = false;
527  unsigned iEcal = 99999;
528  double calibEcal = 0.;
529  double calibHcal = 0.;
530  double totalEcal = thisIsAMuon ? -muonECAL_[0] : 0.;
531 
532  // Consider charged particles closest to the same ECAL cluster
533  std::multimap<double, unsigned> sortedTracks;
534  block.associatedElements(thisEcal, linkData, sortedTracks, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
535  LogTrace("PFAlgo|recoTracksNotHCAL") << "the closest ECAL cluster, id " << thisEcal << ", has " << sortedTracks.size()
536  << " associated tracks, now processing them. ";
537 
538  if (hasDeadHcal && !sortedTracks.empty()) {
539  // GP: only allow the ecal cluster closest to this track to be considered
540  LogTrace("PFAlgo|recoTracksNotHCAL") << " the closest track to ECAL " << thisEcal << " is "
541  << sortedTracks.begin()->second << " (distance " << sortedTracks.begin()->first
542  << ")";
543  if (sortedTracks.begin()->second != iTrack) {
544  LogTrace("PFAlgo|recoTracksNotHCAL")
545  << " the closest track to ECAL " << thisEcal << " is " << sortedTracks.begin()->second
546  << " which is not the one being processed. Will skip ECAL linking for this track";
547  (*pfCandidates_)[tmpi[0]].setEcalEnergy(0., 0.);
548  (*pfCandidates_)[tmpi[0]].setHcalEnergy(0., 0.);
549  (*pfCandidates_)[tmpi[0]].setHoEnergy(0., 0.);
550  (*pfCandidates_)[tmpi[0]].setPs1Energy(0);
551  (*pfCandidates_)[tmpi[0]].setPs2Energy(0);
552  (*pfCandidates_)[tmpi[0]].addElementInBlock(blockref, kTrack[0]);
553  return true;
554  } else {
555  LogTrace("PFAlgo|recoTracksNotHCAL")
556  << " the closest track to ECAL " << thisEcal << " is " << sortedTracks.begin()->second
557  << " which is the one being processed. Will skip ECAL linking for all other track";
558  sortedTracks.clear();
559  }
560  }
561 
562  for (auto const& trk : sortedTracks) {
563  unsigned jTrack = trk.second;
564 
565  // Don't consider already used tracks
566  if (!active[jTrack])
567  continue;
568 
569  // The loop is on the other tracks !
570  if (jTrack == iTrack)
571  continue;
572 
573  // Check if the ECAL cluster closest to this track is
574  // indeed the current ECAL cluster. Only tracks not
575  // linked to an HCAL cluster are considered here
576  // (GP: this is because if there's a jTrack linked
577  // to the same Ecal cluster and that also has an Hcal link
578  // we would have also linked iTrack to that Hcal above)
579  std::multimap<double, unsigned> sortedECAL;
580  block.associatedElements(jTrack, linkData, sortedECAL, reco::PFBlockElement::ECAL, reco::PFBlock::LINKTEST_ALL);
581  if (sortedECAL.begin()->second != thisEcal)
582  continue;
583 
584  // Check if this track is a muon
585  bool thatIsAMuon = PFMuonAlgo::isMuon(elements[jTrack]);
586  LogTrace("PFAlgo|recoTracksNotHCAL") << " found track " << jTrack << (thatIsAMuon ? " (muon) " : " (non-muon)")
587  << ", with distance = " << sortedECAL.begin()->first;
588 
589  // Check if this track is not a fake
590  bool rejectFake = false;
591  reco::TrackRef trackRef = elements[jTrack].trackRef();
592  if (!thatIsAMuon && trackRef->ptError() > ptError_) {
593  // GP: FIXME this selection should be adapted depending on hasDeadHcal
594  // but we don't know if jTrack is linked to a dead Hcal or not
595  double deficit = trackMomentum + trackRef->p() - clusterRef->energy();
596  double resol =
597  nSigmaTRACK_ * neutralHadronEnergyResolution(trackMomentum + trackRef->p(), clusterRef->positionREP().Eta());
598  resol *= (trackMomentum + trackRef->p());
599  if (deficit > nSigmaTRACK_ * resol && !goodTrackDeadHcal) {
600  rejectFake = true;
601  kTrack.push_back(jTrack);
602  active[jTrack] = false;
603  LogTrace("PFAlgo|recoTracksNotHCAL")
604  << elements[jTrack] << std::endl
605  << "is probably a fake (2) --> lock the track"
606  << "(trackMomentum = " << trackMomentum << ", clusterEnergy = " << clusterRef->energy()
607  << ", deficit = " << deficit << " > " << nSigmaTRACK_ << " x " << resol
608  << " assuming neutralHadronEnergyResolution "
609  << neutralHadronEnergyResolution(trackMomentum + trackRef->p(), clusterRef->positionREP().Eta()) << ") ";
610  }
611  }
612  if (rejectFake)
613  continue;
614 
615  // Otherwise, add this track momentum to the total track momentum
616  /* */
617  // reco::TrackRef trackRef = elements[jTrack].trackRef();
618  if (!thatIsAMuon) {
619  LogTrace("PFAlgo|recoTracksNotHCAL") << "Track momentum increased from " << trackMomentum << " GeV ";
620  trackMomentum += trackRef->p();
621  LogTrace("PFAlgo|recoTracksNotHCAL") << "to " << trackMomentum << " GeV.";
622  LogTrace("PFAlgo|recoTracksNotHCAL") << "with " << elements[jTrack];
623  } else {
624  totalEcal -= muonECAL_[0];
625  totalEcal = std::max(totalEcal, 0.);
626  }
627 
628  // And create a charged particle candidate !
629 
630  tmpi.push_back(reconstructTrack(elements[jTrack]));
631 
632  kTrack.push_back(jTrack);
633  active[jTrack] = false;
634 
635  if (thatIsAMuon) {
636  (*pfCandidates_)[tmpi.back()].setEcalEnergy(clusterRef->energy(), std::min(clusterRef->energy(), muonECAL_[0]));
637  (*pfCandidates_)[tmpi.back()].setHcalEnergy(0., 0.);
638  (*pfCandidates_)[tmpi.back()].setHoEnergy(0., 0.);
639  (*pfCandidates_)[tmpi.back()].setPs1Energy(0);
640  (*pfCandidates_)[tmpi.back()].setPs2Energy(0);
641  (*pfCandidates_)[tmpi.back()].addElementInBlock(blockref, kTrack.back());
642  }
643  }
644 
645  LogTrace("PFAlgo|recoTracksNotHCAL") << "Loop over all associated ECAL clusters";
646  // Loop over all ECAL linked clusters ordered by increasing distance.
647  for (auto const& ecal : ecalElems) {
648  const unsigned index = ecal.second;
649  const PFBlockElement::Type type = elements[index].type();
651  LogTrace("PFAlgo|recoTracksNotHCAL") << elements[index];
652 
653  // Just skip clusters already taken
654  if (!active[index]) {
655  LogTrace("PFAlgo|recoTracksNotHCAL") << "is not active - ignore ";
656  continue;
657  }
658 
659  // Just skip this cluster if it's closer to another track
660  block.associatedElements(index, linkData, sortedTracks, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
661 
662  const bool skip = std::none_of(
663  kTrack.begin(), kTrack.end(), [&](unsigned iTrack) { return sortedTracks.begin()->second == iTrack; });
664 
665  if (skip) {
666  LogTrace("PFAlgo|recoTracksNotHCAL") << "is closer to another track - ignore ";
667  continue;
668  }
669 
670  // The corresponding PFCluster and energy
671  const reco::PFClusterRef clusterRef = elements[index].clusterRef();
672  assert(!clusterRef.isNull());
673 
674  LogTrace("PFAlgo|recoTracksNotHCAL") << "Ecal cluster with raw energy = " << clusterRef->energy()
675  << " linked with distance = " << ecal.first;
676 
677  // Check the presence of preshower clusters in the vicinity
678  // Preshower cluster closer to another ECAL cluster are ignored.
679  //JOSH: This should use the association map already produced by the cluster corrector for consistency,
680  //but should be ok for now
681  vector<double> ps1Ene{0.};
682  associatePSClusters(index, reco::PFBlockElement::PS1, block, elements, linkData, active, ps1Ene);
683  vector<double> ps2Ene{0.};
684  associatePSClusters(index, reco::PFBlockElement::PS2, block, elements, linkData, active, ps2Ene);
685 
686  // KH: use raw ECAL energy for PF hadron calibration. use calibrated ECAL energy when adding PF photons
687  const double ecalEnergy = clusterRef->energy();
688  const double ecalEnergyCalibrated = clusterRef->correctedEnergy(); // calibrated based on the egamma hypothesis
689  LogTrace("PFAlgo|recoTracksNotHCAL") << "Corrected ECAL(+PS) energy = " << ecalEnergy;
690 
691  // Since the electrons were found beforehand, this track must be a hadron. Calibrate
692  // the energy under the hadron hypothesis.
693  totalEcal += ecalEnergy;
694  const double previousCalibEcal = calibEcal;
695  const double previousSlopeEcal = slopeEcal;
696  calibEcal = std::max(totalEcal, 0.);
697  calibHcal = 0.;
699  trackMomentum, calibEcal, calibHcal, clusterRef->positionREP().Eta(), clusterRef->positionREP().Phi());
700  if (totalEcal > 0.)
701  slopeEcal = calibEcal / totalEcal;
702 
703  LogTrace("PFAlgo|recoTracksNotHCAL") << "The total calibrated energy so far amounts to = " << calibEcal
704  << " (slope = " << slopeEcal << ")";
705 
706  // Stop the loop when adding more ECAL clusters ruins the compatibility
707  if (connectedToEcal && calibEcal - trackMomentum >= 0.) {
708  // if ( connectedToEcal && calibEcal - trackMomentum >=
709  // nSigmaECAL_*neutralHadronEnergyResolution(trackMomentum,clusterRef->positionREP().Eta()) ) {
710  calibEcal = previousCalibEcal;
711  slopeEcal = previousSlopeEcal;
712  totalEcal = calibEcal / slopeEcal;
713 
714  // Turn this last cluster in a photon
715  // (The PS clusters are already locked in "associatePSClusters")
716  active[index] = false;
717 
718  // Find the associated tracks
719  std::multimap<double, unsigned> assTracks;
720  block.associatedElements(index, linkData, assTracks, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
721 
722  auto& ecalCand = (*pfCandidates_)[reconstructCluster(
723  *clusterRef, ecalEnergyCalibrated)]; // KH: use the PF ECAL cluster calibrated energy
724  ecalCand.setEcalEnergy(clusterRef->energy(), ecalEnergyCalibrated);
725  ecalCand.setHcalEnergy(0., 0.);
726  ecalCand.setHoEnergy(0., 0.);
727  ecalCand.setPs1Energy(ps1Ene[0]);
728  ecalCand.setPs2Energy(ps2Ene[0]);
729  ecalCand.addElementInBlock(blockref, index);
730  // Check that there is at least one track
731  if (!assTracks.empty()) {
732  ecalCand.addElementInBlock(blockref, assTracks.begin()->second);
733 
734  // Assign the position of the track at the ECAL entrance
735  const ::math::XYZPointF& chargedPosition =
736  dynamic_cast<const reco::PFBlockElementTrack*>(&elements[assTracks.begin()->second])
737  ->positionAtECALEntrance();
738  ecalCand.setPositionAtECALEntrance(chargedPosition);
739  }
740  break;
741  }
742 
743  // Lock used clusters.
744  connectedToEcal = true;
745  iEcal = index;
746  active[index] = false;
747  for (unsigned ic : tmpi)
748  (*pfCandidates_)[ic].addElementInBlock(blockref, iEcal);
749 
750  } // Loop ecal elements
751 
752  bool bNeutralProduced = false;
753 
754  // Create a photon if the ecal energy is too large
755  if (connectedToEcal) {
756  reco::PFClusterRef pivotalRef = elements[iEcal].clusterRef();
757 
758  double neutralEnergy = calibEcal - trackMomentum;
759 
760  /*
761  // Check if there are other tracks linked to that ECAL
762  for(IE ie = sortedTracks.begin(); ie != sortedTracks.end() && neutralEnergy > 0; ++ie ) {
763  unsigned jTrack = ie->second;
764 
765  // The loop is on the other tracks !
766  if ( jTrack == iTrack ) continue;
767 
768  // Check if the ECAL closest to this track is the current ECAL
769  // Otherwise ignore this track in the neutral energy determination
770  std::multimap<double, unsigned> sortedECAL;
771  block.associatedElements( jTrack, linkData,
772  sortedECAL,
773  reco::PFBlockElement::ECAL,
774  reco::PFBlock::LINKTEST_ALL );
775  if ( sortedECAL.begin()->second != iEcal ) continue;
776 
777  // Check if this track is also linked to an HCAL
778  // (in which case it goes to the next loop and is ignored here)
779  std::multimap<double, unsigned> sortedHCAL;
780  block.associatedElements( jTrack, linkData,
781  sortedHCAL,
782  reco::PFBlockElement::HCAL,
783  reco::PFBlock::LINKTEST_ALL );
784  if ( sortedHCAL.size() ) continue;
785 
786  // Otherwise, subtract this track momentum from the ECAL energy
787  reco::TrackRef trackRef = elements[jTrack].trackRef();
788  neutralEnergy -= trackRef->p();
789 
790  } // End other tracks
791  */
792 
793  // Add a photon if the energy excess is large enough
794  double resol = neutralHadronEnergyResolution(trackMomentum, pivotalRef->positionREP().Eta());
795  resol *= trackMomentum;
796  if (neutralEnergy > std::max(0.5, nSigmaECAL_ * resol)) {
797  neutralEnergy /= slopeEcal;
798  unsigned tmpj = reconstructCluster(*pivotalRef, neutralEnergy);
799  (*pfCandidates_)[tmpj].setEcalEnergy(pivotalRef->energy(), neutralEnergy);
800  (*pfCandidates_)[tmpj].setHcalEnergy(0., 0.);
801  (*pfCandidates_)[tmpj].setHoEnergy(0., 0.);
802  (*pfCandidates_)[tmpj].setPs1Energy(0.);
803  (*pfCandidates_)[tmpj].setPs2Energy(0.);
804  (*pfCandidates_)[tmpj].addElementInBlock(blockref, iEcal);
805  bNeutralProduced = true;
806  for (unsigned ic = 0; ic < kTrack.size(); ++ic)
807  (*pfCandidates_)[tmpj].addElementInBlock(blockref, kTrack[ic]);
808  } // End neutral energy
809 
810  // Set elements in blocks and ECAL energies to all tracks
811  for (unsigned ic = 0; ic < tmpi.size(); ++ic) {
812  // Skip muons
813  if ((*pfCandidates_)[tmpi[ic]].particleId() == reco::PFCandidate::mu)
814  continue;
815 
816  double fraction = trackMomentum > 0 ? (*pfCandidates_)[tmpi[ic]].trackRef()->p() / trackMomentum : 0;
817  double ecalCal = bNeutralProduced ? (calibEcal - neutralEnergy * slopeEcal) * fraction : calibEcal * fraction;
818  double ecalRaw = totalEcal * fraction;
819 
820  LogTrace("PFAlgo|recoTracksNotHCAL")
821  << "The fraction after photon supression is " << fraction << " calibrated ecal = " << ecalCal;
822 
823  (*pfCandidates_)[tmpi[ic]].setEcalEnergy(ecalRaw, ecalCal);
824  (*pfCandidates_)[tmpi[ic]].setHcalEnergy(0., 0.);
825  (*pfCandidates_)[tmpi[ic]].setHoEnergy(0., 0.);
826  (*pfCandidates_)[tmpi[ic]].setPs1Energy(0);
827  (*pfCandidates_)[tmpi[ic]].setPs2Energy(0);
828  (*pfCandidates_)[tmpi[ic]].addElementInBlock(blockref, kTrack[ic]);
829  }
830 
831  } // End connected ECAL
832 
833  // Fill the element_in_block for tracks that are eventually linked to no ECAL clusters at all.
834  for (unsigned ic = 0; ic < tmpi.size(); ++ic) {
835  const PFCandidate& pfc = (*pfCandidates_)[tmpi[ic]];
836  const PFCandidate::ElementsInBlocks& eleInBlocks = pfc.elementsInBlocks();
837  if (eleInBlocks.empty()) {
838  LogTrace("PFAlgo|recoTracksNotHCAL") << "Single track / Fill element in block! ";
839  (*pfCandidates_)[tmpi[ic]].addElementInBlock(blockref, kTrack[ic]);
840  }
841  }
842  LogTrace("PFAlgo|recoTracksNotHCAL") << "end of function PFAlgo::recoTracksNotHCAL";
843  return false;
844 }
845 
846 //Check if the track is a primary track of a secondary interaction
847 //If that is the case reconstruct a charged hadron only using that
848 //track
851  bool isActive,
852  int iElement) {
853  bool ret = isActive;
854  if (isActive && isFromSecInt(elements[iElement], "primary")) {
855  bool isPrimaryTrack =
856  elements[iElement].displacedVertexRef(PFBlockElement::T_TO_DISP)->displacedVertexRef()->isTherePrimaryTracks();
857  if (isPrimaryTrack) {
858  LogTrace("PFAlgo|elementLoop") << "Primary Track reconstructed alone";
859 
860  unsigned tmpi = reconstructTrack(elements[iElement]);
861  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iElement);
862  ret = false;
863  }
864  }
865 
866  return ret;
867 }
868 
869 bool PFAlgo::checkHasDeadHcal(const std::multimap<double, unsigned>& hcalElems, const std::vector<bool>& deadArea) {
870  // there's 3 possible options possible here, in principle:
871  // 1) flag everything that may be associated to a dead hcal marker
872  // 2) flag everything whose closest hcal link is a dead hcal marker
873  // 3) flag only things that are linked only to dead hcal marker
874  // in our first test we go for (2)
875  //--- option (1) --
876  //bool hasDeadHcal = false;
877  //for (auto it = hcalElems.begin(), ed = hcalElems.end(); it != ed; /*NOTE NO ++it HERE */ ) {
878  // if (deadArea[it->second]) { hasDeadHcal = true; it = hcalElems.erase(it); } // std::multimap::erase returns iterator to next
879  // else ++it;
880  //}
881  //--- option (2) --
882  bool hasDeadHcal = false;
883  if (!hcalElems.empty() && deadArea[hcalElems.begin()->second]) {
884  hasDeadHcal = true;
885  }
886  //--- option (3) --
887  //bool hasDeadHcal = true;
888  //for (auto it = hcalElems.begin(), ed = hcalElems.end(); it != ed; /*NOTE NO ++it HERE */ ) {
889  // if (deadArea[it->second]) { it = hcalElems.erase(it); } // std::multimap::erase returns iterator to next
890  // else { hasDeadHcal = false; }
891  //}
892  return hasDeadHcal;
893 }
894 
895 // for tracks with bad Hcal, check the track quality
896 bool PFAlgo::checkGoodTrackDeadHcal(const reco::TrackRef& trackRef, bool hasDeadHcal) {
897  bool goodTrackDeadHcal = false;
898  if (hasDeadHcal) {
899  goodTrackDeadHcal = (trackRef->ptError() < goodTrackDeadHcal_ptErrRel_ * trackRef->pt() &&
900  trackRef->normalizedChi2() < goodTrackDeadHcal_chi2n_ &&
901  trackRef->hitPattern().trackerLayersWithMeasurement() >= goodTrackDeadHcal_layers_ &&
902  trackRef->validFraction() > goodTrackDeadHcal_validFr_ &&
904  // now we add an extra block for tracks at high |eta|
905  if (!goodTrackDeadHcal && std::abs(trackRef->eta()) > goodPixelTrackDeadHcal_minEta_ && // high eta
906  trackRef->hitPattern().pixelLayersWithMeasurement() >= 3 && // pixel track
907  trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::TRACK_HITS) == 0 &&
908  trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::MISSING_INNER_HITS) == 0 &&
909  trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::MISSING_OUTER_HITS) <=
910  (trackRef->hitPattern().pixelLayersWithMeasurement() > 3 ? goodPixelTrackDeadHcal_maxLost4Hit_
912  trackRef->normalizedChi2() < goodPixelTrackDeadHcal_chi2n_ && // tighter cut
915  trackRef->ptError() < goodPixelTrackDeadHcal_ptErrRel_ * trackRef->pt() && // sanity
916  trackRef->pt() < goodPixelTrackDeadHcal_maxPt_) { // sanity
917  goodTrackDeadHcal = true;
918  // FIXME: may decide to do something to the track pT
919  }
920  //if (!goodTrackDeadHcal && trackRef->hitPattern().trackerLayersWithMeasurement() == 4 && trackRef->validFraction() == 1
921  LogTrace("PFAlgo|elementLoop")
922  << " track pt " << trackRef->pt() << " +- " << trackRef->ptError() << " layers valid "
923  << trackRef->hitPattern().trackerLayersWithMeasurement() << ", lost "
924  << trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::TRACK_HITS) << ", lost outer "
925  << trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::MISSING_OUTER_HITS) << ", lost inner "
926  << trackRef->hitPattern().trackerLayersWithoutMeasurement(HitPattern::MISSING_INNER_HITS) << "(valid fraction "
927  << trackRef->validFraction() << ")"
928  << " chi2/ndf " << trackRef->normalizedChi2() << " |dxy| " << std::abs(trackRef->dxy(primaryVertex_.position()))
929  << " +- " << trackRef->dxyError() << " |dz| " << std::abs(trackRef->dz(primaryVertex_.position())) << " +- "
930  << trackRef->dzError() << (goodTrackDeadHcal ? " passes " : " fails ") << "quality cuts";
931  }
932  return goodTrackDeadHcal;
933 }
934 
936  std::multimap<double, unsigned>& ecalElems,
937  std::multimap<double, unsigned>& hcalElems,
938  const std::vector<bool>& active,
939  reco::PFBlock::LinkData& linkData,
940  unsigned int iTrack) {
941  unsigned ntt = 1;
942  unsigned index = ecalElems.begin()->second;
943  std::multimap<double, unsigned> sortedTracks;
944  block.associatedElements(index, linkData, sortedTracks, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
945  LogTrace("PFAlgo|elementLoop") << "The closest ECAL cluster is linked to " << sortedTracks.size()
946  << " tracks, with distance = " << ecalElems.begin()->first;
947 
948  LogTrace("PFAlgo|elementLoop") << "Looping over sortedTracks";
949  // Loop over all tracks
950  for (auto const& trk : sortedTracks) {
951  unsigned jTrack = trk.second;
952  LogTrace("PFAlgo|elementLoop") << "jTrack=" << jTrack;
953  // Track must be active
954  if (!active[jTrack])
955  continue;
956  LogTrace("PFAlgo|elementLoop") << "active[jTrack]=" << active[jTrack];
957 
958  // The loop is on the other tracks !
959  if (jTrack == iTrack)
960  continue;
961  LogTrace("PFAlgo|elementLoop") << "skipping jTrack=" << jTrack << " for same iTrack";
962 
963  // Check if the ECAL closest to this track is the current ECAL
964  // Otherwise ignore this track in the neutral energy determination
965  std::multimap<double, unsigned> sortedECAL;
966  block.associatedElements(jTrack, linkData, sortedECAL, reco::PFBlockElement::ECAL, reco::PFBlock::LINKTEST_ALL);
967  if (sortedECAL.begin()->second != index)
968  continue;
969  LogTrace("PFAlgo|elementLoop") << " track " << jTrack << " with closest ECAL identical ";
970 
971  // Check if this track is also linked to an HCAL
972  std::multimap<double, unsigned> sortedHCAL;
973  block.associatedElements(jTrack, linkData, sortedHCAL, reco::PFBlockElement::HCAL, reco::PFBlock::LINKTEST_ALL);
974  if (sortedHCAL.empty())
975  continue;
976  LogTrace("PFAlgo|elementLoop") << " and with an HCAL cluster " << sortedHCAL.begin()->second;
977  ntt++;
978 
979  // In that case establish a link with the first track
980  block.setLink(iTrack, sortedHCAL.begin()->second, sortedECAL.begin()->first, linkData, PFBlock::LINKTEST_RECHIT);
981 
982  } // End other tracks
983 
984  // Redefine HCAL elements
985  block.associatedElements(iTrack, linkData, hcalElems, reco::PFBlockElement::HCAL, reco::PFBlock::LINKTEST_ALL);
986 
987  if (!hcalElems.empty())
988  LogTrace("PFAlgo|elementLoop") << "Track linked back to HCAL due to ECAL sharing with other tracks";
989 }
990 
992  reco::PFBlock::LinkData& linkData,
994  std::vector<bool>& active,
995  const reco::PFBlockRef& blockref,
996  ElementIndices& inds,
997  std::vector<bool>& deadArea) {
998  LogTrace("PFAlgo|elementLoop") << "start of function PFAlgo::elementLoop, elements.size()" << elements.size();
999 
1000  for (unsigned iEle = 0; iEle < elements.size(); iEle++) {
1001  PFBlockElement::Type type = elements[iEle].type();
1002 
1003  LogTrace("PFAlgo|elementLoop") << "elements[iEle=" << iEle << "]=" << elements[iEle];
1004  //only process TRACK elements, but fill the ElementIndices vector with indices for all elements.
1005  //Mark the active & deadArea for bad HCAL
1006  auto ret_decideType = decideType(elements, type, active, inds, deadArea, iEle);
1007  if (ret_decideType == 1) {
1008  LogTrace("PFAlgo|elementLoop") << "ret_decideType==1, continuing";
1009  continue;
1010  }
1011  LogTrace("PFAlgo|elementLoop") << "ret_decideType=" << ret_decideType << " type=" << type;
1012 
1013  active[iEle] = checkAndReconstructSecondaryInteraction(blockref, elements, active[iEle], iEle);
1014 
1015  if (!active[iEle]) {
1016  LogTrace("PFAlgo|elementLoop") << "Already used by electrons, muons, conversions";
1017  continue;
1018  }
1019 
1020  reco::TrackRef trackRef = elements[iEle].trackRef();
1021  assert(!trackRef.isNull());
1022 
1023  LogTrace("PFAlgo|elementLoop") << "PFAlgo:processBlock"
1024  << " trackIs.size()=" << inds.trackIs.size()
1025  << " ecalIs.size()=" << inds.ecalIs.size() << " hcalIs.size()=" << inds.hcalIs.size()
1026  << " hoIs.size()=" << inds.hoIs.size() << " hfEmIs.size()=" << inds.hfEmIs.size()
1027  << " hfHadIs.size()=" << inds.hfHadIs.size();
1028 
1029  // look for associated elements of all types
1030  //COLINFEB16
1031  // all types of links are considered.
1032  // the elements are sorted by increasing distance
1033  std::multimap<double, unsigned> ecalElems;
1034  block.associatedElements(iEle, linkData, ecalElems, reco::PFBlockElement::ECAL, reco::PFBlock::LINKTEST_ALL);
1035 
1036  std::multimap<double, unsigned> hcalElems;
1037  block.associatedElements(iEle, linkData, hcalElems, reco::PFBlockElement::HCAL, reco::PFBlock::LINKTEST_ALL);
1038 
1039  std::multimap<double, unsigned> hfEmElems;
1040  std::multimap<double, unsigned> hfHadElems;
1041  block.associatedElements(iEle, linkData, hfEmElems, reco::PFBlockElement::HFEM, reco::PFBlock::LINKTEST_ALL);
1042  block.associatedElements(iEle, linkData, hfHadElems, reco::PFBlockElement::HFHAD, reco::PFBlock::LINKTEST_ALL);
1043 
1044  LogTrace("PFAlgo|elementLoop") << "\tTrack " << iEle << " is linked to " << ecalElems.size() << " ecal and "
1045  << hcalElems.size() << " hcal and " << hfEmElems.size() << " hfEm and "
1046  << hfHadElems.size() << " hfHad elements";
1047 
1048 #ifdef EDM_ML_DEBUG
1049  for (const auto& pair : ecalElems) {
1050  LogTrace("PFAlgo|elementLoop") << "ecal: dist " << pair.first << "\t elem " << pair.second;
1051  }
1052  for (const auto& pair : hcalElems) {
1053  LogTrace("PFAlgo|elementLoop") << "hcal: dist " << pair.first << "\t elem " << pair.second
1054  << (deadArea[pair.second] ? " DEAD AREA MARKER" : "");
1055  }
1056 #endif
1057 
1058  const bool hasDeadHcal = checkHasDeadHcal(hcalElems, deadArea);
1059  if (hasDeadHcal) {
1060  hcalElems.clear();
1061  }
1062  const bool goodTrackDeadHcal = checkGoodTrackDeadHcal(trackRef, hasDeadHcal);
1063 
1064  // When a track has no HCAL cluster linked, but another track is linked to the same
1065  // ECAL cluster and an HCAL cluster, link the track to the HCAL cluster for
1066  // later analysis
1067  if (hcalElems.empty() && !ecalElems.empty() && !hasDeadHcal) {
1068  relinkTrackToHcal(block, ecalElems, hcalElems, active, linkData, iEle);
1069  }
1070 
1071  //MICHELE
1072  //TEMPORARY SOLUTION FOR ELECTRON REJECTION IN PFTAU
1073  //COLINFEB16
1074  // in case particle flow electrons are not reconstructed,
1075  // the mva_e_pi of the charged hadron will be set to 1
1076  // if a GSF element is associated to the current TRACK element
1077  // This information will be used in the electron rejection for tau ID.
1078  std::multimap<double, unsigned> gsfElems;
1079  block.associatedElements(iEle, linkData, gsfElems, reco::PFBlockElement::GSF);
1080 
1081  if (hcalElems.empty()) {
1082  LogTrace("PFAlgo|elementLoop") << "no hcal element connected to track " << iEle;
1083  }
1084 
1085  // will now loop on associated elements ...
1086  bool hcalFound = false;
1087  bool hfhadFound = false;
1088 
1089  LogTrace("PFAlgo|elementLoop") << "now looping on elements associated to the track: ecalElems";
1090 
1091  // ... first on associated ECAL elements
1092  // Check if there is still a free ECAL for this track
1093  for (auto const& ecal : ecalElems) {
1094  unsigned index = ecal.second;
1095  // Sanity checks and optional printout
1097 #ifdef EDM_ML_DEBUG
1098  double dist = ecal.first;
1099  LogTrace("PFAlgo|elementLoop") << "\telement " << elements[index] << " linked with distance = " << dist;
1100  if (!active[index])
1101  LogTrace("PFAlgo|elementLoop") << "This ECAL is already used - skip it";
1102 #endif
1104 
1105  // This ECAL is not free (taken by an electron?) - just skip it
1106  if (!active[index])
1107  continue;
1108 
1109  // Flag ECAL clusters for which the corresponding track is not linked to an HCAL cluster
1110 
1111  //reco::PFClusterRef clusterRef = elements[index].clusterRef();
1112  //assert( !clusterRef.isNull() );
1113  if (!hcalElems.empty())
1114  LogTrace("PFAlgo|elementLoop") << "\t\tat least one hcal element connected to the track."
1115  << " Sparing Ecal cluster for the hcal loop";
1116 
1117  } //loop print ecal elements
1118 
1119  // tracks which are not linked to an HCAL (or HFHAD)
1120  // are reconstructed now.
1121 
1122  if (hcalElems.empty() && hfHadElems.empty()) {
1123  auto ret_continue = recoTracksNotHCAL(
1124  block, linkData, elements, blockref, active, goodTrackDeadHcal, hasDeadHcal, iEle, ecalElems, trackRef);
1125  if (ret_continue) {
1126  continue;
1127  }
1128  } // end if( hcalElems.empty() && hfHadElems.empty() )
1129 
1130  // In case several HCAL elements are linked to this track,
1131  // unlinking all of them except the closest.
1132  for (auto const& hcal : hcalElems) {
1133  unsigned index = hcal.second;
1134 
1136 
1137 #ifdef EDM_ML_DEBUG
1138  double dist = block.dist(iEle, index, linkData, reco::PFBlock::LINKTEST_ALL);
1139  LogTrace("PFAlgo|elementLoop") << "\telement " << elements[index] << " linked with distance " << dist;
1140 #endif
1142 
1143  // all hcal clusters except the closest
1144  // will be unlinked from the track
1145  if (!hcalFound) { // closest hcal
1146  LogTrace("PFAlgo|elementLoop") << "\t\tclosest hcal cluster, doing nothing";
1147 
1148  hcalFound = true;
1149 
1150  // active[index] = false;
1151  // hcalUsed.push_back( index );
1152  } else { // other associated hcal
1153  // unlink from the track
1154  LogTrace("PFAlgo|elementLoop") << "\t\tsecondary hcal cluster. unlinking";
1155  block.setLink(iEle, index, -1., linkData, PFBlock::LINKTEST_RECHIT);
1156  }
1157  } //loop hcal elements
1158 
1159  // ---Same for HFHAD---
1160  // In case several HFHAD elements are linked to this track,
1161  // unlinking all of them except the closest.
1162  for (auto const& hfhad : hfHadElems) {
1163  unsigned index = hfhad.second;
1164 
1166 
1167 #ifdef EDM_ML_DEBUG
1168  double dist = block.dist(iEle, index, linkData, reco::PFBlock::LINKTEST_ALL);
1169  LogTrace("PFAlgo|elementLoop") << "\telement " << elements[index] << " linked with distance " << dist;
1170 #endif
1171  assert(type == PFBlockElement::HFHAD);
1172 
1173  // all hfhad clusters except the closest
1174  // will be unlinked from the track
1175  if (!hfhadFound) { // closest hfhad
1176  LogTrace("PFAlgo|elementLoop") << "\t\tclosest hfhad cluster, doing nothing";
1177 
1178  hfhadFound = true;
1179 
1180  } else { // other associated hfhad
1181  // unlink from the track
1182  LogTrace("PFAlgo|elementLoop") << "\t\tsecondary hfhad cluster. unlinking";
1183  block.setLink(iEle, index, -1., linkData, PFBlock::LINKTEST_RECHIT);
1184  }
1185  } //loop hfhad elements
1186 
1187  LogTrace("PFAlgo|elementLoop") << "end of loop over iEle";
1188  } // end of outer loop on elements iEle of any type
1189  LogTrace("PFAlgo|elementLoop") << "end of function PFAlgo::elementLoop";
1190 }
1191 
1192 //Arranges the PFBlock elements according to type into the ElementIndices output vector.
1193 //Also checks for dead HCAL area and updates the active and deadArea vectors.
1194 //Returns 0 for elements of TRACK type, 1 otherwise
1197  std::vector<bool>& active,
1198  ElementIndices& inds,
1199  std::vector<bool>& deadArea,
1200  unsigned int iEle) {
1201  switch (type) {
1202  case PFBlockElement::TRACK:
1203  if (active[iEle]) {
1204  inds.trackIs.push_back(iEle);
1205  LogTrace("PFAlgo|decideType") << "TRACK, stored index, continue";
1206  }
1207  break;
1208  case PFBlockElement::ECAL:
1209  if (active[iEle]) {
1210  inds.ecalIs.push_back(iEle);
1211  LogTrace("PFAlgo|decideType") << "ECAL, stored index, continue";
1212  }
1213  return 1; //continue
1214  case PFBlockElement::HCAL:
1215  if (active[iEle]) {
1216  if (elements[iEle].clusterRef()->flags() & reco::CaloCluster::badHcalMarker) {
1217  LogTrace("PFAlgo|decideType") << "HCAL DEAD AREA: remember and skip.";
1218  active[iEle] = false;
1219  deadArea[iEle] = true;
1220  return 1; //continue
1221  }
1222  inds.hcalIs.push_back(iEle);
1223  LogTrace("PFAlgo|decideType") << "HCAL, stored index, continue";
1224  }
1225  return 1; //continue
1226  case PFBlockElement::HO:
1227  if (useHO_) {
1228  if (active[iEle]) {
1229  inds.hoIs.push_back(iEle);
1230  LogTrace("PFAlgo|decideType") << "HO, stored index, continue";
1231  }
1232  }
1233  return 1; //continue
1234  case PFBlockElement::HFEM:
1235  if (active[iEle]) {
1236  inds.hfEmIs.push_back(iEle);
1237  LogTrace("PFAlgo|decideType") << "HFEM, stored index, continue";
1238  }
1239  return 1; //continue
1240  case PFBlockElement::HFHAD:
1241  if (active[iEle]) {
1242  inds.hfHadIs.push_back(iEle);
1243  LogTrace("PFAlgo|decideType") << "HFHAD, stored index, continue";
1244  }
1245  return 1; //continue
1246  default:
1247  return 1; //continue
1248  }
1249  LogTrace("PFAlgo|decideType") << "Did not match type to anything, return 0";
1250  return 0;
1251 }
1252 
1254  reco::PFBlock::LinkData& linkData,
1256  std::vector<bool>& active,
1257  const reco::PFBlockRef& blockref,
1258  ElementIndices& inds) {
1259  LogTrace("PFAlgo|createCandidatesHF") << "starting function PFAlgo::createCandidatesHF";
1260 
1261  bool trackInBlock = !inds.trackIs.empty();
1262  // inds.trackIs can be empty, even if there are tracks in this block,
1263  // but what we want to check is if this block has any track including inactive ones
1264  if (!trackInBlock)
1265  for (unsigned iEle = 0; iEle < elements.size(); iEle++) {
1266  PFBlockElement::Type type = elements[iEle].type();
1267  if (type == PFBlockElement::TRACK) {
1268  trackInBlock = true;
1269  break;
1270  }
1271  }
1272  // there is at least one HF element in this block.
1273  // in case of no track, all elements must be HF
1274  if (!trackInBlock)
1275  assert(inds.hfEmIs.size() + inds.hfHadIs.size() == elements.size());
1276 
1277  //
1278  // Dealing with a block with at least one track
1279  // Occasionally, there are only inactive tracks and multiple HF clusters. Consider such blocks too.
1280  //
1281  if (trackInBlock) { // count any tracks (not only active tracks)
1282  // sorted tracks associated with a HfHad cluster
1283  std::multimap<double, unsigned> sortedTracks;
1284  std::multimap<double, unsigned> sortedTracksActive; // only active ones
1285  // HfEms associated with tracks linked to a HfHad cluster
1286  std::multimap<unsigned, std::pair<double, unsigned>> associatedHfEms;
1287  // Temporary map for HfEm satellite clusters
1288  std::multimap<double, std::pair<unsigned, double>> hfemSatellites;
1289 
1290  //
1291  // Loop over active HfHad clusters
1292  //
1293  for (unsigned iHfHad : inds.hfHadIs) {
1294  PFBlockElement::Type type = elements[iHfHad].type();
1295  assert(type == PFBlockElement::HFHAD);
1296 
1297  PFClusterRef hclusterRef = elements[iHfHad].clusterRef();
1298  assert(!hclusterRef.isNull());
1299 
1300  sortedTracks.clear();
1301  sortedTracksActive.clear();
1302  associatedHfEms.clear();
1303  hfemSatellites.clear();
1304 
1305  // Look for associated tracks
1306  block.associatedElements(
1307  iHfHad, linkData, sortedTracks, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
1308 
1309  LogTrace("PFAlgo|createCandidatesHF") << "elements[" << iHfHad << "]=" << elements[iHfHad];
1310 
1311  if (sortedTracks.empty()) {
1312  LogTrace("PFAlgo|createCandidatesHCF") << "\tno associated tracks, keep for later";
1313  continue;
1314  }
1315 
1316  // Lock the HFHAD cluster
1317  active[iHfHad] = false;
1318 
1319  LogTrace("PFAlgo|createCandidatesHF") << sortedTracks.size() << " associated tracks:";
1320 
1321  double totalChargedMomentum = 0.;
1322  double sumpError2 = 0.;
1323 
1324  //
1325  // Loop over all tracks associated to this HFHAD cluster
1326  //
1327  for (auto const& trk : sortedTracks) {
1328  unsigned iTrack = trk.second;
1329 
1330  // Check the track has not already been used
1331  if (!active[iTrack])
1332  continue;
1333  // Sanity check 1
1334  PFBlockElement::Type type = elements[iTrack].type();
1336  // Sanity check 2
1337  reco::TrackRef trackRef = elements[iTrack].trackRef();
1338  assert(!trackRef.isNull());
1339 
1340  // Introduce tracking errors
1341  double trackMomentum = trackRef->p();
1342  totalChargedMomentum += trackMomentum;
1343 
1344  // Also keep the total track momentum error for comparison with the calo energy
1345  double dp = trackRef->qoverpError() * trackMomentum * trackMomentum;
1346  sumpError2 += dp * dp;
1347 
1348  // Store active tracks for 2nd loop to create charged hadrons
1349  sortedTracksActive.emplace(trk);
1350 
1351  // look for HFEM elements associated to iTrack (associated to iHfHad)
1352  std::multimap<double, unsigned> sortedHfEms;
1353  block.associatedElements(
1354  iTrack, linkData, sortedHfEms, reco::PFBlockElement::HFEM, reco::PFBlock::LINKTEST_ALL);
1355 
1356  LogTrace("PFAlgo|createCandidatesHF") << "number of HfEm elements linked to this track: " << sortedHfEms.size();
1357 
1358  bool connectedToHfEm = false; // Will become true if there is at least one HFEM cluster connected
1359 
1360  //
1361  // Loop over all HFEM clusters connected to iTrack
1362  //
1363  for (auto const& hfem : sortedHfEms) {
1364  unsigned iHfEm = hfem.second;
1365  double dist = hfem.first;
1366 
1367  // Ignore HFEM cluters already used
1368  if (!active[iHfEm]) {
1369  LogTrace("PFAlgo|createCandidatesHF") << "cluster locked";
1370  continue;
1371  }
1372 
1373  // Sanity checks
1374  PFBlockElement::Type type = elements[iHfEm].type();
1375  assert(type == PFBlockElement::HFEM);
1376  PFClusterRef eclusterRef = elements[iHfEm].clusterRef();
1377  assert(!eclusterRef.isNull());
1378 
1379  // Check if this HFEM is not closer to another track - ignore it in that case
1380  std::multimap<double, unsigned> sortedTracksHfEm;
1381  block.associatedElements(
1382  iHfEm, linkData, sortedTracksHfEm, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
1383  unsigned jTrack = sortedTracksHfEm.begin()->second;
1384  if (jTrack != iTrack)
1385  continue;
1386 
1387  double distHfEm = block.dist(jTrack, iHfEm, linkData, reco::PFBlock::LINKTEST_ALL);
1388  double hfemEnergy = eclusterRef->energy();
1389 
1390  if (!connectedToHfEm) { // This is the closest HFEM cluster - will add its energy later
1391 
1392  LogTrace("PFAlgo|createCandidatesHF") << "closest: " << elements[iHfEm];
1393  connectedToHfEm = true;
1394  std::pair<unsigned, double> satellite(iHfEm, hfemEnergy);
1395  hfemSatellites.emplace(-1., satellite);
1396 
1397  } else { // Keep satellite clusters for later
1398 
1399  // KH: same as above.
1400  std::pair<unsigned, double> satellite(iHfEm, hfemEnergy);
1401  hfemSatellites.emplace(dist, satellite);
1402  }
1403 
1404  std::pair<double, unsigned> associatedHfEm(distHfEm, iHfEm);
1405  associatedHfEms.emplace(iTrack, associatedHfEm);
1406 
1407  } // End loop hfem associated to iTrack
1408  } // sortedTracks
1409 
1410  // HfHad energy
1411  double uncalibratedenergyHfHad = hclusterRef->energy();
1412  double energyHfHad = uncalibratedenergyHfHad;
1414  energyHfHad = thepfEnergyCalibrationHF_.energyHad( // HAD only calibration
1415  uncalibratedenergyHfHad,
1416  hclusterRef->positionREP().Eta(),
1417  hclusterRef->positionREP().Phi());
1418  }
1419  double calibFactorHfHad = (uncalibratedenergyHfHad > 0.) ? energyHfHad / uncalibratedenergyHfHad : 1.;
1420 
1421  // HfEm energy
1422  double energyHfEmTmp = 0.;
1423  double uncalibratedenergyHfEmTmp = 0.;
1424  double energyHfEm = 0.;
1425  double uncalibratedenergyHfEm = 0.;
1426 
1427  // estimated HF resolution and track p error
1428  double caloResolution = hfEnergyResolution(totalChargedMomentum);
1429  caloResolution *= totalChargedMomentum;
1430  double totalError = sqrt(caloResolution * caloResolution + sumpError2);
1431  double nsigmaHFEM = nSigmaHFEM(totalChargedMomentum);
1432  double nsigmaHFHAD = nSigmaHFHAD(totalChargedMomentum);
1433 
1434  // Handle case that no active track gets associated to HfHad cluster
1435  if (sortedTracksActive.empty()) {
1436  // look for HFEM elements associated to iHfHad
1437  std::multimap<double, unsigned> sortedHfEms;
1438  std::multimap<double, unsigned> sortedHfEmsActive;
1439  block.associatedElements(
1440  iHfHad, linkData, sortedHfEms, reco::PFBlockElement::HFEM, reco::PFBlock::LINKTEST_ALL);
1441  //
1442  // If iHfHad is connected to HFEM cluster, Loop over all of them
1443  //
1444  if (!sortedHfEms.empty()) {
1445  for (auto const& hfem : sortedHfEms) {
1446  unsigned iHfEm = hfem.second;
1447  // Ignore HFEM cluters already used
1448  if (!active[iHfEm])
1449  continue;
1450  sortedHfEmsActive.emplace(hfem);
1451  PFClusterRef eclusterRef = elements[iHfEm].clusterRef();
1452  assert(!eclusterRef.isNull());
1453  double hfemEnergy = eclusterRef->energy();
1454  uncalibratedenergyHfEm += hfemEnergy;
1455  energyHfEm = uncalibratedenergyHfEm;
1458  uncalibratedenergyHfEm, 0.0, eclusterRef->positionREP().Eta(), eclusterRef->positionREP().Phi());
1459  energyHfHad = thepfEnergyCalibrationHF_.energyEmHad(
1460  0.0, uncalibratedenergyHfHad, hclusterRef->positionREP().Eta(), hclusterRef->positionREP().Phi());
1461  } // calib true
1462  } // loop over sortedHfEm
1463  } // if !sortedHfEms.empty()
1464  //
1465  // Create HF candidates
1466  unsigned tmpi = reconstructCluster(*hclusterRef, energyHfEm + energyHfHad);
1467  (*pfCandidates_)[tmpi].setHcalEnergy(uncalibratedenergyHfHad, energyHfHad);
1468  (*pfCandidates_)[tmpi].setEcalEnergy(uncalibratedenergyHfEm, energyHfEm);
1469  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHfHad);
1470  for (auto const& hfem : sortedHfEmsActive) {
1471  unsigned iHfEm = hfem.second;
1472  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHfEm);
1473  active[iHfEm] = false;
1474  }
1475 
1476  } // if sortedTracksActive.empty() ends
1477  //
1478  // Active tracks are associated.
1479  // Create HFHAD candidates from excess energy w.r.t. tracks
1480  else if ((energyHfHad - totalChargedMomentum) > nsigmaHFHAD * totalError) { // HfHad is excessive
1481  assert(energyHfEm == 0.);
1482  // HfHad candidate from excess
1483  double energyHfHadExcess = max(energyHfHad - totalChargedMomentum, 0.);
1484  double uncalibratedenergyHfHadExcess = energyHfHadExcess / calibFactorHfHad;
1485  unsigned tmpi = reconstructCluster(*hclusterRef, energyHfHadExcess);
1486  (*pfCandidates_)[tmpi].setHcalEnergy(uncalibratedenergyHfHadExcess, energyHfHadExcess);
1487  (*pfCandidates_)[tmpi].setEcalEnergy(0., 0.);
1488  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHfHad);
1489  energyHfHad = max(energyHfHad - energyHfHadExcess, 0.);
1490  uncalibratedenergyHfHad = max(uncalibratedenergyHfHad - uncalibratedenergyHfHadExcess, 0.);
1491  }
1492  //
1493  // If there is a room for HFEM satellites to get associated,
1494  // loop over all HFEM satellites, starting for the closest to the various tracks
1495  // and adding other satellites until saturation of the total track momentum
1496  //
1497  else {
1498  for (auto const& hfemSatellite : hfemSatellites) {
1499  //
1500  uncalibratedenergyHfEmTmp += std::get<1>(hfemSatellite.second); // KH: raw HFEM energy
1501  energyHfEmTmp = uncalibratedenergyHfEmTmp;
1502  double energyHfHadTmp = uncalibratedenergyHfHad; // now to test hfhad calibration with EM+HAD cases
1503  unsigned iHfEm = std::get<0>(hfemSatellite.second);
1504  PFClusterRef eclusterRef = elements[iHfEm].clusterRef();
1505  assert(!eclusterRef.isNull());
1507  energyHfEmTmp = thepfEnergyCalibrationHF_.energyEmHad(
1508  uncalibratedenergyHfEmTmp, 0.0, eclusterRef->positionREP().Eta(), eclusterRef->positionREP().Phi());
1509  energyHfHadTmp = thepfEnergyCalibrationHF_.energyEmHad(
1510  0.0, uncalibratedenergyHfHad, hclusterRef->positionREP().Eta(), hclusterRef->positionREP().Phi());
1511  }
1512 
1513  double caloEnergyTmp = energyHfEmTmp + energyHfHadTmp;
1514  double calibFactorHfEm = (uncalibratedenergyHfEmTmp > 0.) ? energyHfEmTmp / uncalibratedenergyHfEmTmp : 1.;
1515 
1516  // Continue looping until all closest clusters are exhausted and as long as
1517  // the calorimetric energy does not saturate the total momentum.
1518  if (hfemSatellite.first < 0. || caloEnergyTmp < totalChargedMomentum) {
1519  LogTrace("PFAlgo|createCandidatesHF")
1520  << "\t\t\tactive, adding " << std::get<1>(hfemSatellite.second) << " to HFEM energy, and locking";
1521  active[std::get<0>(hfemSatellite.second)] = false;
1522  // HfEm is excessive (possible for the first hfemSatellite)
1523  if (hfemSatellite.first < 0. && (caloEnergyTmp - totalChargedMomentum) > nsigmaHFEM * totalError) {
1524  // HfEm candidate from excess
1525  double energyHfEmExcess = max(caloEnergyTmp - totalChargedMomentum, 0.);
1526  double uncalibratedenergyHfEmExcess = energyHfEmExcess / calibFactorHfEm;
1527  unsigned tmpi = reconstructCluster(*eclusterRef, energyHfEmExcess);
1528  (*pfCandidates_)[tmpi].setEcalEnergy(uncalibratedenergyHfEmExcess, energyHfEmExcess);
1529  (*pfCandidates_)[tmpi].setHcalEnergy(0, 0.);
1530  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHfEm);
1531  energyHfEmTmp = max(energyHfEmTmp - energyHfEmExcess, 0.);
1532  uncalibratedenergyHfEmTmp = max(uncalibratedenergyHfEmTmp - uncalibratedenergyHfEmExcess, 0.);
1533  }
1534  energyHfEm = energyHfEmTmp;
1535  uncalibratedenergyHfEm = uncalibratedenergyHfEmTmp;
1536  energyHfHad = energyHfHadTmp;
1537  continue;
1538  }
1539  break;
1540  } // loop over hfemsattelites ends
1541  } // if HFHAD is excessive or not
1542 
1543  //
1544  // Loop over all tracks associated to this HFHAD cluster *again* in order to produce charged hadrons
1545  //
1546  for (auto const& trk : sortedTracksActive) {
1547  unsigned iTrack = trk.second;
1548 
1549  // Sanity check
1550  reco::TrackRef trackRef = elements[iTrack].trackRef();
1551  assert(!trackRef.isNull());
1552 
1553  //
1554  // Reconstructing charged hadrons
1555  //
1556  unsigned tmpi = reconstructTrack(elements[iTrack]);
1557  active[iTrack] = false;
1558  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHfHad);
1559  auto myHfEms = associatedHfEms.equal_range(iTrack);
1560  for (auto ii = myHfEms.first; ii != myHfEms.second; ++ii) {
1561  unsigned iHfEm = ii->second.second;
1562  if (active[iHfEm])
1563  continue;
1564  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHfEm);
1565  }
1566  double frac = 0.;
1567  if (totalChargedMomentum)
1568  frac = trackRef->p() / totalChargedMomentum;
1569  (*pfCandidates_)[tmpi].setEcalEnergy(uncalibratedenergyHfEm * frac, energyHfEm * frac);
1570  (*pfCandidates_)[tmpi].setHcalEnergy(uncalibratedenergyHfHad * frac, energyHfHad * frac);
1571 
1572  } // sortedTracks loop ends
1573 
1574  } // iHfHad element loop ends
1575 
1576  //
1577  // Loop over remaining active HfEm clusters
1578  //
1579  for (unsigned iHfEm = 0; iHfEm < elements.size(); iHfEm++) {
1580  PFBlockElement::Type type = elements[iHfEm].type();
1581  if (type == PFBlockElement::HFEM && active[iHfEm]) {
1582  reco::PFClusterRef eclusterRef = elements[iHfEm].clusterRef();
1583  double energyHF = 0.;
1584  double uncalibratedenergyHF = 0.;
1585  unsigned tmpi = 0;
1586  // do EM-only calibration here
1587  energyHF = eclusterRef->energy();
1588  uncalibratedenergyHF = energyHF;
1591  uncalibratedenergyHF, eclusterRef->positionREP().Eta(), eclusterRef->positionREP().Phi());
1592  }
1593  tmpi = reconstructCluster(*eclusterRef, energyHF);
1594  (*pfCandidates_)[tmpi].setEcalEnergy(uncalibratedenergyHF, energyHF);
1595  (*pfCandidates_)[tmpi].setHcalEnergy(0., 0.);
1596  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHfEm);
1597  active[iHfEm] = false;
1598  LogTrace("PFAlgo|createCandidatesHF") << "HF EM alone from blocks with tracks! " << energyHF;
1599  }
1600  } // remaining active HfEm cluster loop ends
1601 
1602  } // if-statement for blocks including tracks ends here
1603  //
1604  // -----------------------------------------------
1605  // From here, traditional PF HF candidate creation
1606  // -----------------------------------------------
1607  //
1608  else if (elements.size() == 1) {
1609  //Auguste: HAD-only calibration here
1610  reco::PFClusterRef clusterRef = elements[0].clusterRef();
1611  double energyHF = 0.;
1612  double uncalibratedenergyHF = 0.;
1613  unsigned tmpi = 0;
1614  switch (clusterRef->layer()) {
1615  case PFLayer::HF_EM:
1616  // do EM-only calibration here
1617  energyHF = clusterRef->energy();
1618  uncalibratedenergyHF = energyHF;
1621  uncalibratedenergyHF, clusterRef->positionREP().Eta(), clusterRef->positionREP().Phi());
1622  }
1623  tmpi = reconstructCluster(*clusterRef, energyHF);
1624  (*pfCandidates_)[tmpi].setEcalEnergy(uncalibratedenergyHF, energyHF);
1625  (*pfCandidates_)[tmpi].setHcalEnergy(0., 0.);
1626  (*pfCandidates_)[tmpi].setHoEnergy(0., 0.);
1627  (*pfCandidates_)[tmpi].setPs1Energy(0.);
1628  (*pfCandidates_)[tmpi].setPs2Energy(0.);
1629  (*pfCandidates_)[tmpi].addElementInBlock(blockref, inds.hfEmIs[0]);
1630  LogTrace("PFAlgo|createCandidatesHF") << "HF EM alone ! " << energyHF;
1631  break;
1632  case PFLayer::HF_HAD:
1633  // do HAD-only calibration here
1634  energyHF = clusterRef->energy();
1635  uncalibratedenergyHF = energyHF;
1638  uncalibratedenergyHF, clusterRef->positionREP().Eta(), clusterRef->positionREP().Phi());
1639  }
1640  tmpi = reconstructCluster(*clusterRef, energyHF);
1641  (*pfCandidates_)[tmpi].setHcalEnergy(uncalibratedenergyHF, energyHF);
1642  (*pfCandidates_)[tmpi].setEcalEnergy(0., 0.);
1643  (*pfCandidates_)[tmpi].setHoEnergy(0., 0.);
1644  (*pfCandidates_)[tmpi].setPs1Energy(0.);
1645  (*pfCandidates_)[tmpi].setPs2Energy(0.);
1646  (*pfCandidates_)[tmpi].addElementInBlock(blockref, inds.hfHadIs[0]);
1647  LogTrace("PFAlgo|createCandidatesHF") << "HF Had alone ! " << energyHF;
1648  break;
1649  default:
1650  assert(0);
1651  }
1652  } else if (elements.size() == 2) {
1653  //Auguste: EM + HAD calibration here
1654  reco::PFClusterRef c0 = elements[0].clusterRef();
1655  reco::PFClusterRef c1 = elements[1].clusterRef();
1656  // 2 HF elements. Must be in each layer.
1657  reco::PFClusterRef cem = (c0->layer() == PFLayer::HF_EM ? c0 : c1);
1658  reco::PFClusterRef chad = (c1->layer() == PFLayer::HF_HAD ? c1 : c0);
1659 
1660  if (cem->layer() != PFLayer::HF_EM || chad->layer() != PFLayer::HF_HAD) {
1661  edm::LogError("PFAlgo::createCandidatesHF") << "Error: 2 elements, but not 1 HFEM and 1 HFHAD";
1662  edm::LogError("PFAlgo::createCandidatesHF") << block;
1663  assert(0);
1664  // assert( c1->layer()== PFLayer::HF_EM &&
1665  // c0->layer()== PFLayer::HF_HAD );
1666  }
1667  // do EM+HAD calibration here
1668  double energyHfEm = cem->energy();
1669  double energyHfHad = chad->energy();
1670  double uncalibratedenergyHfEm = energyHfEm;
1671  double uncalibratedenergyHfHad = energyHfHad;
1674  uncalibratedenergyHfEm, 0.0, c0->positionREP().Eta(), c0->positionREP().Phi());
1675  energyHfHad = thepfEnergyCalibrationHF_.energyEmHad(
1676  0.0, uncalibratedenergyHfHad, c1->positionREP().Eta(), c1->positionREP().Phi());
1677  }
1678  auto& cand = (*pfCandidates_)[reconstructCluster(*chad, energyHfEm + energyHfHad)];
1679  cand.setEcalEnergy(uncalibratedenergyHfEm, energyHfEm);
1680  cand.setHcalEnergy(uncalibratedenergyHfHad, energyHfHad);
1681  cand.setHoEnergy(0., 0.);
1682  cand.setPs1Energy(0.);
1683  cand.setPs2Energy(0.);
1684  cand.addElementInBlock(blockref, inds.hfEmIs[0]);
1685  cand.addElementInBlock(blockref, inds.hfHadIs[0]);
1686  LogTrace("PFAlgo|createCandidatesHF") << "HF EM+HAD found ! " << energyHfEm << " " << energyHfHad;
1687  } else {
1688  // Unusual blocks including HF elements, but do not fit any of the above categories
1689  edm::LogWarning("PFAlgo::createCandidatesHF")
1690  << "Warning: HF, but n elem different from 1 or 2 or >=3 or !trackIs.empty()";
1691  edm::LogWarning("PFAlgo::createCandidatesHF") << block;
1692  }
1693  LogTrace("PFAlgo|createCandidateHF") << "end of function PFAlgo::createCandidateHF";
1694 }
1695 
1697  reco::PFBlock::LinkData& linkData,
1699  std::vector<bool>& active,
1700  const reco::PFBlockRef& blockref,
1701  ElementIndices& inds,
1702  std::vector<bool>& deadArea) {
1703  LogTrace("PFAlgo|createCandidatesHCAL")
1704  << "start of function PFAlgo::createCandidatesHCAL, inds.hcalIs.size()=" << inds.hcalIs.size();
1705 
1706  // --------------- loop hcal ------------------
1707 
1708  for (unsigned iHcal : inds.hcalIs) {
1709  PFBlockElement::Type type = elements[iHcal].type();
1710 
1712 
1713  LogTrace("PFAlgo|createCandidatesHCAL") << "elements[" << iHcal << "]=" << elements[iHcal];
1714 
1715  // associated tracks
1716  std::multimap<double, unsigned> sortedTracks;
1717  block.associatedElements(iHcal, linkData, sortedTracks, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
1718 
1719  std::multimap<unsigned, std::pair<double, unsigned>> associatedEcals;
1720 
1721  std::map<unsigned, std::pair<double, double>> associatedPSs;
1722 
1723  std::multimap<double, std::pair<unsigned, bool>> associatedTracks;
1724 
1725  // A temporary maps for ECAL satellite clusters
1726  std::multimap<double, std::tuple<unsigned, ::math::XYZVector, double>>
1727  ecalSatellites; // last element (double) : correction under the egamma hypothesis
1728  std::tuple<unsigned, ::math::XYZVector, double> fakeSatellite(iHcal, ::math::XYZVector(0., 0., 0.), 1.);
1729  ecalSatellites.emplace(-1., fakeSatellite);
1730 
1731  std::multimap<unsigned, std::pair<double, unsigned>> associatedHOs;
1732 
1733  PFClusterRef hclusterref = elements[iHcal].clusterRef();
1734  assert(!hclusterref.isNull());
1735 
1736  //if there is no track attached to that HCAL, then do not
1737  //reconstruct an HCAL alone, check if it can be recovered
1738  //first
1739 
1740  // if no associated tracks, create a neutral hadron
1741  //if(sortedTracks.empty() ) {
1742 
1743  if (sortedTracks.empty()) {
1744  LogTrace("PFAlgo|createCandidatesHCAL") << "\tno associated tracks, keep for later";
1745  continue;
1746  }
1747 
1748  // Lock the HCAL cluster
1749  active[iHcal] = false;
1750 
1751  // in the following, tracks are associated to this hcal cluster.
1752  // will look for an excess of energy in the calorimeters w/r to
1753  // the charged energy, and turn this excess into a neutral hadron or
1754  // a photon.
1755 
1756  LogTrace("PFAlgo|createCandidatesHCAL") << sortedTracks.size() << " associated tracks:";
1757 
1758  double totalChargedMomentum = 0;
1759  double sumpError2 = 0.;
1760  double totalHO = 0.;
1761  double totalEcal = 0.;
1762  double totalEcalEGMCalib = 0.;
1763  double totalHcal = hclusterref->energy();
1764  vector<double> hcalP;
1765  vector<double> hcalDP;
1766  vector<unsigned> tkIs;
1767  double maxDPovP = -9999.;
1768 
1769  //Keep track of how much energy is assigned to calorimeter-vs-track energy/momentum excess
1770  vector<unsigned> chargedHadronsIndices;
1771  vector<unsigned> chargedHadronsInBlock;
1772  double mergedNeutralHadronEnergy = 0;
1773  double mergedPhotonEnergy = 0;
1774  double muonHCALEnergy = 0.;
1775  double muonECALEnergy = 0.;
1776  double muonHCALError = 0.;
1777  double muonECALError = 0.;
1778  unsigned nMuons = 0;
1779 
1780  ::math::XYZVector photonAtECAL(0., 0., 0.);
1781  std::vector<std::tuple<unsigned, ::math::XYZVector, double>>
1782  ecalClusters; // last element (double) : correction under the egamma hypothesis
1783  double sumEcalClusters = 0;
1784  ::math::XYZVector hadronDirection(
1785  hclusterref->position().X(), hclusterref->position().Y(), hclusterref->position().Z());
1786  hadronDirection = hadronDirection.Unit();
1787  ::math::XYZVector hadronAtECAL = totalHcal * hadronDirection;
1788 
1789  // Loop over all tracks associated to this HCAL cluster
1790  for (auto const& trk : sortedTracks) {
1791  unsigned iTrack = trk.second;
1792 
1793  // Check the track has not already been used (e.g., in electrons, conversions...)
1794  if (!active[iTrack])
1795  continue;
1796  // Sanity check 1
1797  PFBlockElement::Type type = elements[iTrack].type();
1799  // Sanity check 2
1800  reco::TrackRef trackRef = elements[iTrack].trackRef();
1801  assert(!trackRef.isNull());
1802 
1803  // The direction at ECAL entrance
1804  const ::math::XYZPointF& chargedPosition =
1805  dynamic_cast<const reco::PFBlockElementTrack*>(&elements[iTrack])->positionAtECALEntrance();
1806  ::math::XYZVector chargedDirection(chargedPosition.X(), chargedPosition.Y(), chargedPosition.Z());
1807  chargedDirection = chargedDirection.Unit();
1808 
1809  // look for ECAL elements associated to iTrack (associated to iHcal)
1810  std::multimap<double, unsigned> sortedEcals;
1811  block.associatedElements(iTrack, linkData, sortedEcals, reco::PFBlockElement::ECAL, reco::PFBlock::LINKTEST_ALL);
1812 
1813  LogTrace("PFAlgo|createCandidatesHCAL") << "number of Ecal elements linked to this track: " << sortedEcals.size();
1814 
1815  // look for HO elements associated to iTrack (associated to iHcal)
1816  std::multimap<double, unsigned> sortedHOs;
1817  if (useHO_) {
1818  block.associatedElements(iTrack, linkData, sortedHOs, reco::PFBlockElement::HO, reco::PFBlock::LINKTEST_ALL);
1819  }
1820  LogTrace("PFAlgo|createCandidatesHCAL")
1821  << "PFAlgo : number of HO elements linked to this track: " << sortedHOs.size();
1822 
1823  // Create a PF Candidate right away if the track is a tight muon
1824  reco::MuonRef muonRef = elements[iTrack].muonRef();
1825 
1826  bool thisIsAMuon = PFMuonAlgo::isMuon(elements[iTrack]);
1827  bool thisIsAnIsolatedMuon = PFMuonAlgo::isIsolatedMuon(elements[iTrack]);
1828  bool thisIsALooseMuon = false;
1829 
1830  if (!thisIsAMuon) {
1831  thisIsALooseMuon = PFMuonAlgo::isLooseMuon(elements[iTrack]);
1832  }
1833 
1834  if (thisIsAMuon) {
1835  LogTrace("PFAlgo|createCandidatesHCAL") << "This track is identified as a muon - remove it from the stack";
1836  LogTrace("PFAlgo|createCandidatesHCAL") << elements[iTrack];
1837 
1838  // Create a muon.
1839 
1840  unsigned tmpi = reconstructTrack(elements[iTrack]);
1841 
1842  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iTrack);
1843  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHcal);
1844  double muonHcal = std::min(muonHCAL_[0] + muonHCAL_[1], totalHcal);
1845 
1846  // if muon is isolated and muon momentum exceeds the calo energy, absorb the calo energy
1847  // rationale : there has been a EM showering by the muon in the calorimeter - or the coil -
1848  // and we don't want to double count the energy
1849  bool letMuonEatCaloEnergy = false;
1850 
1851  if (thisIsAnIsolatedMuon) {
1852  // The factor 1.3 is the e/pi factor in HCAL...
1853  double totalCaloEnergy = totalHcal / 1.30;
1854  unsigned iEcal = 0;
1855  if (!sortedEcals.empty()) {
1856  iEcal = sortedEcals.begin()->second;
1857  PFClusterRef eclusterref = elements[iEcal].clusterRef();
1858  totalCaloEnergy += eclusterref->energy();
1859  }
1860 
1861  if (useHO_) {
1862  // The factor 1.3 is assumed to be the e/pi factor in HO, too.
1863  unsigned iHO = 0;
1864  if (!sortedHOs.empty()) {
1865  iHO = sortedHOs.begin()->second;
1866  PFClusterRef eclusterref = elements[iHO].clusterRef();
1867  totalCaloEnergy += eclusterref->energy() / 1.30;
1868  }
1869  }
1870 
1871  if ((pfCandidates_->back()).p() > totalCaloEnergy)
1872  letMuonEatCaloEnergy = true;
1873  }
1874 
1875  if (letMuonEatCaloEnergy)
1876  muonHcal = totalHcal;
1877  double muonEcal = 0.;
1878  unsigned iEcal = 0;
1879  if (!sortedEcals.empty()) {
1880  iEcal = sortedEcals.begin()->second;
1881  PFClusterRef eclusterref = elements[iEcal].clusterRef();
1882  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iEcal);
1883  muonEcal = std::min(muonECAL_[0] + muonECAL_[1], eclusterref->energy());
1884  if (letMuonEatCaloEnergy)
1885  muonEcal = eclusterref->energy();
1886  // If the muon expected energy accounts for the whole ecal cluster energy, lock the ecal cluster
1887  if (eclusterref->energy() - muonEcal < 0.2)
1888  active[iEcal] = false;
1889  (*pfCandidates_)[tmpi].setEcalEnergy(eclusterref->energy(), muonEcal);
1890  }
1891  unsigned iHO = 0;
1892  double muonHO = 0.;
1893  if (useHO_) {
1894  if (!sortedHOs.empty()) {
1895  iHO = sortedHOs.begin()->second;
1896  PFClusterRef hoclusterref = elements[iHO].clusterRef();
1897  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHO);
1898  muonHO = std::min(muonHO_[0] + muonHO_[1], hoclusterref->energy());
1899  if (letMuonEatCaloEnergy)
1900  muonHO = hoclusterref->energy();
1901  // If the muon expected energy accounts for the whole HO cluster energy, lock the HO cluster
1902  if (hoclusterref->energy() - muonHO < 0.2)
1903  active[iHO] = false;
1904  (*pfCandidates_)[tmpi].setHcalEnergy(totalHcal, muonHcal);
1905  (*pfCandidates_)[tmpi].setHoEnergy(hoclusterref->energy(), muonHO);
1906  }
1907  } else {
1908  (*pfCandidates_)[tmpi].setHcalEnergy(totalHcal, muonHcal);
1909  }
1910  setHcalDepthInfo((*pfCandidates_)[tmpi], *hclusterref);
1911 
1912  if (letMuonEatCaloEnergy) {
1913  muonHCALEnergy += totalHcal;
1914  if (useHO_)
1915  muonHCALEnergy += muonHO;
1916  muonHCALError += 0.;
1917  muonECALEnergy += muonEcal;
1918  muonECALError += 0.;
1919  photonAtECAL -= muonEcal * chargedDirection;
1920  hadronAtECAL -= totalHcal * chargedDirection;
1921  if (!sortedEcals.empty())
1922  active[iEcal] = false;
1923  active[iHcal] = false;
1924  if (useHO_ && !sortedHOs.empty())
1925  active[iHO] = false;
1926  } else {
1927  // Estimate of the energy deposit & resolution in the calorimeters
1928  muonHCALEnergy += muonHCAL_[0];
1929  muonHCALError += muonHCAL_[1] * muonHCAL_[1];
1930  if (muonHO > 0.) {
1931  muonHCALEnergy += muonHO_[0];
1932  muonHCALError += muonHO_[1] * muonHO_[1];
1933  }
1934  muonECALEnergy += muonECAL_[0];
1935  muonECALError += muonECAL_[1] * muonECAL_[1];
1936  // ... as well as the equivalent "momentum" at ECAL entrance
1937  photonAtECAL -= muonECAL_[0] * chargedDirection;
1938  hadronAtECAL -= muonHCAL_[0] * chargedDirection;
1939  }
1940 
1941  // Remove it from the stack
1942  active[iTrack] = false;
1943  // Go to next track
1944  continue;
1945  }
1946 
1947  //
1948 
1949  LogTrace("PFAlgo|createCandidatesHCAL") << "elements[iTrack=" << iTrack << "]=" << elements[iTrack];
1950 
1951  // introduce tracking errors
1952  double trackMomentum = trackRef->p();
1953  totalChargedMomentum += trackMomentum;
1954 
1955  // If the track is not a tight muon, but still resembles a muon
1956  // keep it for later for blocks with too large a charged energy
1957  if (thisIsALooseMuon && !thisIsAMuon)
1958  nMuons += 1;
1959 
1960  // ... and keep anyway the pt error for possible fake rejection
1961  // ... blow up errors of 5th and 4th iteration, to reject those
1962  // ... tracks first (in case it's needed)
1963  double dpt = trackRef->ptError();
1964  double blowError = PFTrackAlgoTools::errorScale(trackRef->algo(), factors45_);
1965  // except if it is from an interaction
1966  bool isPrimaryOrSecondary = isFromSecInt(elements[iTrack], "all");
1967 
1968  if (isPrimaryOrSecondary)
1969  blowError = 1.;
1970 
1971  std::pair<unsigned, bool> tkmuon(iTrack, thisIsALooseMuon);
1972  associatedTracks.emplace(-dpt * blowError, tkmuon);
1973 
1974  // Also keep the total track momentum error for comparison with the calo energy
1975  double dp = trackRef->qoverpError() * trackMomentum * trackMomentum;
1976  sumpError2 += dp * dp;
1977 
1978  bool connectedToEcal = false; // Will become true if there is at least one ECAL cluster connected
1979  if (!sortedEcals.empty()) { // start case: at least one ecal element associated to iTrack
1980 
1981  // Loop over all connected ECAL clusters
1982  for (auto const& ecal : sortedEcals) {
1983  unsigned iEcal = ecal.second;
1984  double dist = ecal.first;
1985 
1986  // Ignore ECAL cluters already used
1987  if (!active[iEcal]) {
1988  LogTrace("PFAlgo|createCandidatesHCAL") << "cluster locked";
1989  continue;
1990  }
1991 
1992  // Sanity checks
1993  PFBlockElement::Type type = elements[iEcal].type();
1995  PFClusterRef eclusterref = elements[iEcal].clusterRef();
1996  assert(!eclusterref.isNull());
1997 
1998  // Check if this ECAL is not closer to another track - ignore it in that case
1999  std::multimap<double, unsigned> sortedTracksEcal;
2000  block.associatedElements(
2001  iEcal, linkData, sortedTracksEcal, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
2002  unsigned jTrack = sortedTracksEcal.begin()->second;
2003  if (jTrack != iTrack)
2004  continue;
2005 
2006  double distEcal = block.dist(jTrack, iEcal, linkData, reco::PFBlock::LINKTEST_ALL);
2007 
2008  float ecalEnergyCalibrated = eclusterref->correctedEnergy(); // calibrated based on the egamma hypothesis
2009  float ecalEnergy = eclusterref->energy();
2010  ::math::XYZVector photonDirection(
2011  eclusterref->position().X(), eclusterref->position().Y(), eclusterref->position().Z());
2012  photonDirection = photonDirection.Unit();
2013 
2014  if (!connectedToEcal) { // This is the closest ECAL cluster - will add its energy later
2015 
2016  LogTrace("PFAlgo|createCandidatesHCAL") << "closest: " << elements[iEcal];
2017 
2018  connectedToEcal = true;
2019  // PJ 1st-April-09 : To be done somewhere !!! (Had to comment it, but it is needed)
2020  // currentChargedHadron.addElementInBlock( blockref, iEcal );
2021 
2022  // KH: we don't know if this satellite is due to egamma or hadron shower. use raw energy for PF hadron calibration._ store also calibration constant.
2023  double ecalCalibFactor = (ecalEnergy > 1E-9) ? ecalEnergyCalibrated / ecalEnergy : 1.;
2024  std::tuple<unsigned, ::math::XYZVector, double> satellite(
2025  iEcal, ecalEnergy * photonDirection, ecalCalibFactor);
2026  ecalSatellites.emplace(-1., satellite);
2027 
2028  } else { // Keep satellite clusters for later
2029 
2030  // KH: same as above.
2031  double ecalCalibFactor = (ecalEnergy > 1E-9) ? ecalEnergyCalibrated / ecalEnergy : 1.;
2032  std::tuple<unsigned, ::math::XYZVector, double> satellite(
2033  iEcal, ecalEnergy * photonDirection, ecalCalibFactor);
2034  ecalSatellites.emplace(dist, satellite);
2035  }
2036 
2037  std::pair<double, unsigned> associatedEcal(distEcal, iEcal);
2038  associatedEcals.emplace(iTrack, associatedEcal);
2039 
2040  } // End loop ecal associated to iTrack
2041  } // end case: at least one ecal element associated to iTrack
2042 
2043  if (useHO_ && !sortedHOs.empty()) { // start case: at least one ho element associated to iTrack
2044 
2045  // Loop over all connected HO clusters
2046  for (auto const& ho : sortedHOs) {
2047  unsigned iHO = ho.second;
2048  double distHO = ho.first;
2049 
2050  // Ignore HO cluters already used
2051  if (!active[iHO]) {
2052  LogTrace("PFAlgo|createCandidatesHCAL") << "cluster locked";
2053  continue;
2054  }
2055 
2056  // Sanity checks
2057  PFBlockElement::Type type = elements[iHO].type();
2059  PFClusterRef hoclusterref = elements[iHO].clusterRef();
2060  assert(!hoclusterref.isNull());
2061 
2062  // Check if this HO is not closer to another track - ignore it in that case
2063  std::multimap<double, unsigned> sortedTracksHO;
2064  block.associatedElements(
2065  iHO, linkData, sortedTracksHO, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
2066  unsigned jTrack = sortedTracksHO.begin()->second;
2067  if (jTrack != iTrack)
2068  continue;
2069 
2070  // double chi2HO = block.chi2(jTrack,iHO,linkData,
2071  // reco::PFBlock::LINKTEST_ALL);
2072  //double distHO = block.dist(jTrack,iHO,linkData,
2073  // reco::PFBlock::LINKTEST_ALL);
2074 
2075  // Increment the total energy by the energy of this HO cluster
2076  totalHO += hoclusterref->energy();
2077  active[iHO] = false;
2078  // Keep track for later reference in the PFCandidate.
2079  std::pair<double, unsigned> associatedHO(distHO, iHO);
2080  associatedHOs.emplace(iTrack, associatedHO);
2081 
2082  } // End loop ho associated to iTrack
2083  } // end case: at least one ho element associated to iTrack
2084 
2085  } // end loop on tracks associated to hcal element iHcal
2086 
2087  // Include totalHO in totalHCAL for the time being (it will be calibrated as HCAL energy)
2088  totalHcal += totalHO;
2089 
2090  // test compatibility between calo and tracker. //////////////
2091 
2092  double caloEnergy = 0.;
2093  double slopeEcal = 1.0;
2094  double calibEcal = 0.;
2095  double calibHcal = 0.;
2096  hadronDirection = hadronAtECAL.Unit();
2097 
2098  // Determine the expected calo resolution from the total charged momentum
2099  double caloResolution = neutralHadronEnergyResolution(totalChargedMomentum, hclusterref->positionREP().Eta());
2100  caloResolution *= totalChargedMomentum;
2101  // Account for muons
2102  caloResolution = std::sqrt(caloResolution * caloResolution + muonHCALError + muonECALError);
2103  totalEcal -= std::min(totalEcal, muonECALEnergy);
2104  totalEcalEGMCalib -= std::min(totalEcalEGMCalib, muonECALEnergy);
2105  totalHcal -= std::min(totalHcal, muonHCALEnergy);
2106  if (totalEcal < 1E-9)
2107  photonAtECAL = ::math::XYZVector(0., 0., 0.);
2108  if (totalHcal < 1E-9)
2109  hadronAtECAL = ::math::XYZVector(0., 0., 0.);
2110 
2111  // Loop over all ECAL satellites, starting for the closest to the various tracks
2112  // and adding other satellites until saturation of the total track momentum
2113  // Note : for code simplicity, the first element of the loop is the HCAL cluster
2114  // with 0 energy in the ECAL
2115  for (auto const& ecalSatellite : ecalSatellites) {
2116  // Add the energy of this ECAL cluster
2117  double previousCalibEcal = calibEcal;
2118  double previousCalibHcal = calibHcal;
2119  double previousCaloEnergy = caloEnergy;
2120  double previousSlopeEcal = slopeEcal;
2121  ::math::XYZVector previousHadronAtECAL = hadronAtECAL;
2122  //
2123  totalEcal +=
2124  sqrt(std::get<1>(ecalSatellite.second).Mag2()); // KH: raw ECAL energy for input to PF hadron calibration
2125  totalEcalEGMCalib += sqrt(std::get<1>(ecalSatellite.second).Mag2()) *
2126  std::get<2>(ecalSatellite.second); // KH: calibrated ECAL energy under the egamma hypothesis
2127  photonAtECAL += std::get<1>(ecalSatellite.second) *
2128  std::get<2>(ecalSatellite.second); // KH: calibrated ECAL energy under the egamma hypothesis
2129  calibEcal = std::max(0., totalEcal); // KH: preparing for hadron calibration
2130  calibHcal = std::max(0., totalHcal);
2131  hadronAtECAL = calibHcal * hadronDirection;
2132  // Calibrate ECAL and HCAL energy under the hadron hypothesis.
2133  calibration_.energyEmHad(totalChargedMomentum,
2134  calibEcal,
2135  calibHcal,
2136  hclusterref->positionREP().Eta(),
2137  hclusterref->positionREP().Phi());
2138  caloEnergy = calibEcal + calibHcal;
2139  if (totalEcal > 0.)
2140  slopeEcal = calibEcal / totalEcal;
2141 
2142  hadronAtECAL = calibHcal * hadronDirection;
2143 
2144  // Continue looping until all closest clusters are exhausted and as long as
2145  // the calorimetric energy does not saturate the total momentum.
2146  if (ecalSatellite.first < 0. || caloEnergy - totalChargedMomentum <= 0.) {
2147  LogTrace("PFAlgo|createCandidatesHCAL")
2148  << "\t\t\tactive, adding " << std::get<1>(ecalSatellite.second) << " to ECAL energy, and locking";
2149  active[std::get<0>(ecalSatellite.second)] = false;
2150  double clusterEnergy =
2151  sqrt(std::get<1>(ecalSatellite.second).Mag2()) *
2152  std::get<2>(ecalSatellite.second); // KH: ECAL energy calibrated under the egamma hypothesis
2153  if (clusterEnergy > 50) { // KH: used to split energetic ecal clusters (E>50 GeV)
2154  ecalClusters.push_back(ecalSatellite.second);
2155  sumEcalClusters += clusterEnergy;
2156  }
2157  continue;
2158  }
2159 
2160  // Otherwise, do not consider the last cluster examined and exit.
2161  // active[is->second.first] = true;
2162  totalEcal -= sqrt(std::get<1>(ecalSatellite.second).Mag2());
2163  totalEcalEGMCalib -= sqrt(std::get<1>(ecalSatellite.second).Mag2()) * std::get<2>(ecalSatellite.second);
2164  photonAtECAL -= std::get<1>(ecalSatellite.second) * std::get<2>(ecalSatellite.second);
2165  calibEcal = previousCalibEcal;
2166  calibHcal = previousCalibHcal;
2167  hadronAtECAL = previousHadronAtECAL;
2168  slopeEcal = previousSlopeEcal;
2169  caloEnergy = previousCaloEnergy;
2170 
2171  break;
2172  }
2173 
2174  // Sanity check !
2175  assert(caloEnergy >= 0);
2176 
2177  // And now check for hadronic energy excess...
2178 
2179  //colin: resolution should be measured on the ecal+hcal case.
2180  // however, the result will be close.
2181  // double caloResolution = neutralHadronEnergyResolution( caloEnergy );
2182  // caloResolution *= caloEnergy;
2183  // PJ The resolution is on the expected charged calo energy !
2184  //double caloResolution = neutralHadronEnergyResolution( totalChargedMomentum, hclusterref->positionREP().Eta());
2185  //caloResolution *= totalChargedMomentum;
2186  // that of the charged particles linked to the cluster!
2187 
2189  if (totalChargedMomentum - caloEnergy > nSigmaTRACK_ * caloResolution) {
2190  // First consider loose muons
2191  if (nMuons > 0) {
2192  for (auto const& trk : associatedTracks) {
2193  // Only muons
2194  if (!trk.second.second)
2195  continue;
2196 
2197  const unsigned int iTrack = trk.second.first;
2198  // Only active tracks
2199  if (!active[iTrack])
2200  continue;
2201 
2202  const double trackMomentum = elements[trk.second.first].trackRef()->p();
2203 
2204  // look for ECAL elements associated to iTrack (associated to iHcal)
2205  std::multimap<double, unsigned> sortedEcals;
2206  block.associatedElements(
2207  iTrack, linkData, sortedEcals, reco::PFBlockElement::ECAL, reco::PFBlock::LINKTEST_ALL);
2208  std::multimap<double, unsigned> sortedHOs;
2209  block.associatedElements(iTrack, linkData, sortedHOs, reco::PFBlockElement::HO, reco::PFBlock::LINKTEST_ALL);
2210 
2211  //Here allow for loose muons!
2212  auto& muon = (*pfCandidates_)[reconstructTrack(elements[iTrack], true)];
2213 
2214  muon.addElementInBlock(blockref, iTrack);
2215  muon.addElementInBlock(blockref, iHcal);
2216  const double muonHcal = std::min(muonHCAL_[0] + muonHCAL_[1], totalHcal - totalHO);
2217  double muonHO = 0.;
2218  muon.setHcalEnergy(totalHcal, muonHcal);
2219  if (!sortedEcals.empty()) {
2220  const unsigned int iEcal = sortedEcals.begin()->second;
2221  PFClusterRef eclusterref = elements[iEcal].clusterRef();
2222  muon.addElementInBlock(blockref, iEcal);
2223  const double muonEcal = std::min(muonECAL_[0] + muonECAL_[1], eclusterref->energy());
2224  muon.setEcalEnergy(eclusterref->energy(), muonEcal);
2225  }
2226  if (useHO_ && !sortedHOs.empty()) {
2227  const unsigned int iHO = sortedHOs.begin()->second;
2228  PFClusterRef hoclusterref = elements[iHO].clusterRef();
2229  muon.addElementInBlock(blockref, iHO);
2230  muonHO = std::min(muonHO_[0] + muonHO_[1], hoclusterref->energy());
2231  muon.setHcalEnergy(max(totalHcal - totalHO, 0.0), muonHcal);
2232  muon.setHoEnergy(hoclusterref->energy(), muonHO);
2233  }
2234  setHcalDepthInfo(muon, *hclusterref);
2235  // Remove it from the block
2236  const ::math::XYZPointF& chargedPosition =
2237  dynamic_cast<const reco::PFBlockElementTrack*>(&elements[trk.second.first])->positionAtECALEntrance();
2238  ::math::XYZVector chargedDirection(chargedPosition.X(), chargedPosition.Y(), chargedPosition.Z());
2239  chargedDirection = chargedDirection.Unit();
2240  totalChargedMomentum -= trackMomentum;
2241  // Update the calo energies
2242  if (totalEcal > 0.)
2243  calibEcal -= std::min(calibEcal, muonECAL_[0] * calibEcal / totalEcal);
2244  if (totalHcal > 0.)
2245  calibHcal -= std::min(calibHcal, muonHCAL_[0] * calibHcal / totalHcal);
2246  totalEcal -= std::min(totalEcal, muonECAL_[0]);
2247  totalHcal -= std::min(totalHcal, muonHCAL_[0]);
2248  if (totalEcal > muonECAL_[0])
2249  photonAtECAL -= muonECAL_[0] * chargedDirection;
2250  if (totalHcal > muonHCAL_[0])
2251  hadronAtECAL -= muonHCAL_[0] * calibHcal / totalHcal * chargedDirection;
2252  caloEnergy = calibEcal + calibHcal;
2253  muonHCALEnergy += muonHCAL_[0];
2254  muonHCALError += muonHCAL_[1] * muonHCAL_[1];
2255  if (muonHO > 0.) {
2256  muonHCALEnergy += muonHO_[0];
2257  muonHCALError += muonHO_[1] * muonHO_[1];
2258  if (totalHcal > 0.) {
2259  calibHcal -= std::min(calibHcal, muonHO_[0] * calibHcal / totalHcal);
2260  totalHcal -= std::min(totalHcal, muonHO_[0]);
2261  }
2262  }
2263  muonECALEnergy += muonECAL_[0];
2264  muonECALError += muonECAL_[1] * muonECAL_[1];
2265  active[iTrack] = false;
2266  // Stop the loop whenever enough muons are removed
2267  //Commented out: Keep looking for muons since they often come in pairs -Matt
2268  //if ( totalChargedMomentum < caloEnergy ) break;
2269  }
2270  // New calo resolution.
2271  caloResolution = neutralHadronEnergyResolution(totalChargedMomentum, hclusterref->positionREP().Eta());
2272  caloResolution *= totalChargedMomentum;
2273  caloResolution = std::sqrt(caloResolution * caloResolution + muonHCALError + muonECALError);
2274  }
2275  }
2276 
2277 #ifdef EDM_ML_DEBUG
2278  LogTrace("PFAlgo|createCandidatesHCAL") << "\tBefore Cleaning ";
2279  LogTrace("PFAlgo|createCandidatesHCAL") << "\tCompare Calo Energy to total charged momentum ";
2280  LogTrace("PFAlgo|createCandidatesHCAL") << "\t\tsum p = " << totalChargedMomentum << " +- " << sqrt(sumpError2);
2281  LogTrace("PFAlgo|createCandidatesHCAL") << "\t\tsum ecal = " << totalEcal;
2282  LogTrace("PFAlgo|createCandidatesHCAL") << "\t\tsum hcal = " << totalHcal;
2283  LogTrace("PFAlgo|createCandidatesHCAL") << "\t\t => Calo Energy = " << caloEnergy << " +- " << caloResolution;
2284  LogTrace("PFAlgo|createCandidatesHCAL")
2285  << "\t\t => Calo Energy- total charged momentum = " << caloEnergy - totalChargedMomentum << " +- "
2286  << sqrt(sumpError2 + caloResolution * caloResolution);
2287 #endif
2288 
2289  // Second consider bad tracks (if still needed after muon removal)
2290  unsigned corrTrack = 10000000;
2291  double corrFact = 1.;
2292 
2293  if (rejectTracks_Bad_ && totalChargedMomentum - caloEnergy > nSigmaTRACK_ * caloResolution) {
2294  for (auto const& trk : associatedTracks) {
2295  const unsigned iTrack = trk.second.first;
2296  // Only active tracks
2297  if (!active[iTrack])
2298  continue;
2299  const reco::TrackRef& trackref = elements[trk.second.first].trackRef();
2300 
2301  const double dptRel = fabs(trk.first) / trackref->pt() * 100;
2302  const bool isSecondary = isFromSecInt(elements[iTrack], "secondary");
2303  const bool isPrimary = isFromSecInt(elements[iTrack], "primary");
2304 
2305  if (isSecondary && dptRel < dptRel_DispVtx_)
2306  continue;
2307  // Consider only bad tracks
2308  if (fabs(trk.first) < ptError_)
2309  break;
2310  // What would become the block charged momentum if this track were removed
2311  const double wouldBeTotalChargedMomentum = totalChargedMomentum - trackref->p();
2312  // Reject worst tracks, as long as the total charged momentum
2313  // is larger than the calo energy
2314 
2315  if (wouldBeTotalChargedMomentum > caloEnergy) {
2316  if (isSecondary) {
2317  LogTrace("PFAlgo|createCandidatesHCAL")
2318  << "In bad track rejection step dptRel = " << dptRel << " dptRel_DispVtx_ = " << dptRel_DispVtx_;
2319  LogTrace("PFAlgo|createCandidatesHCAL")
2320  << "The calo energy would be still smaller even without this track but it is attached to a NI";
2321  }
2322 
2323  if (isPrimary || (isSecondary && dptRel < dptRel_DispVtx_))
2324  continue;
2325  active[iTrack] = false;
2326  totalChargedMomentum = wouldBeTotalChargedMomentum;
2327  LogTrace("PFAlgo|createCandidatesHCAL")
2328  << "\tElement " << elements[iTrack] << " rejected (dpt = " << -trk.first
2329  << " GeV/c, algo = " << trackref->algo() << ")";
2330  // Just rescale the nth worst track momentum to equalize the calo energy
2331  } else {
2332  if (isPrimary)
2333  break;
2334  corrTrack = iTrack;
2335  corrFact = (caloEnergy - wouldBeTotalChargedMomentum) / elements[trk.second.first].trackRef()->p();
2336  if (trackref->p() * corrFact < 0.05) {
2337  corrFact = 0.;
2338  active[iTrack] = false;
2339  }
2340  totalChargedMomentum -= trackref->p() * (1. - corrFact);
2341  LogTrace("PFAlgo|createCandidatesHCAL")
2342  << "\tElement " << elements[iTrack] << " (dpt = " << -trk.first << " GeV/c, algo = " << trackref->algo()
2343  << ") rescaled by " << corrFact << " Now the total charged momentum is " << totalChargedMomentum;
2344  break;
2345  }
2346  }
2347  }
2348 
2349  // New determination of the calo and track resolution avec track deletion/rescaling.
2350  caloResolution = neutralHadronEnergyResolution(totalChargedMomentum, hclusterref->positionREP().Eta());
2351  caloResolution *= totalChargedMomentum;
2352  caloResolution = std::sqrt(caloResolution * caloResolution + muonHCALError + muonECALError);
2353 
2354  // Check if the charged momentum is still very inconsistent with the calo measurement.
2355  // In this case, just drop all tracks from 4th and 5th iteration linked to this block
2356 
2357  if (rejectTracks_Step45_ && sortedTracks.size() > 1 &&
2358  totalChargedMomentum - caloEnergy > nSigmaTRACK_ * caloResolution) {
2359  for (auto const& trk : associatedTracks) {
2360  unsigned iTrack = trk.second.first;
2361  reco::TrackRef trackref = elements[iTrack].trackRef();
2362  if (!active[iTrack])
2363  continue;
2364 
2365  double dptRel = fabs(trk.first) / trackref->pt() * 100;
2366  bool isPrimaryOrSecondary = isFromSecInt(elements[iTrack], "all");
2367 
2368  if (isPrimaryOrSecondary && dptRel < dptRel_DispVtx_)
2369  continue;
2370 
2371  if (PFTrackAlgoTools::step5(trackref->algo())) {
2372  active[iTrack] = false;
2373  totalChargedMomentum -= trackref->p();
2374 
2375  LogTrace("PFAlgo|createCandidatesHCAL")
2376  << "\tElement " << elements[iTrack] << " rejected (dpt = " << -trk.first
2377  << " GeV/c, algo = " << trackref->algo() << ")";
2378  }
2379  }
2380  }
2381 
2382  // New determination of the calo and track resolution avec track deletion/rescaling.
2383  caloResolution = neutralHadronEnergyResolution(totalChargedMomentum, hclusterref->positionREP().Eta());
2384  caloResolution *= totalChargedMomentum;
2385  caloResolution = std::sqrt(caloResolution * caloResolution + muonHCALError + muonECALError);
2386 
2387  // Make PF candidates with the remaining tracks in the block
2388  sumpError2 = 0.;
2389  for (auto const& trk : associatedTracks) {
2390  unsigned iTrack = trk.second.first;
2391  if (!active[iTrack])
2392  continue;
2393  reco::TrackRef trackRef = elements[iTrack].trackRef();
2394  double trackMomentum = trackRef->p();
2395  double dp = trackRef->qoverpError() * trackMomentum * trackMomentum;
2396  unsigned tmpi = reconstructTrack(elements[iTrack]);
2397 
2398  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iTrack);
2399  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHcal);
2400  setHcalDepthInfo((*pfCandidates_)[tmpi], *hclusterref);
2401  auto myEcals = associatedEcals.equal_range(iTrack);
2402  for (auto ii = myEcals.first; ii != myEcals.second; ++ii) {
2403  unsigned iEcal = ii->second.second;
2404  if (active[iEcal])
2405  continue;
2406  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iEcal);
2407  }
2408 
2409  if (useHO_) {
2410  auto myHOs = associatedHOs.equal_range(iTrack);
2411  for (auto ii = myHOs.first; ii != myHOs.second; ++ii) {
2412  unsigned iHO = ii->second.second;
2413  if (active[iHO])
2414  continue;
2415  (*pfCandidates_)[tmpi].addElementInBlock(blockref, iHO);
2416  }
2417  }
2418 
2419  if (iTrack == corrTrack) {
2420  if (corrFact < 0.)
2421  corrFact = 0.; // protect against negative scaling
2422  (*pfCandidates_)[tmpi].rescaleMomentum(corrFact);
2423  trackMomentum *= corrFact;
2424  }
2425  chargedHadronsIndices.push_back(tmpi);
2426  chargedHadronsInBlock.push_back(iTrack);
2427  active[iTrack] = false;
2428  hcalP.push_back(trackMomentum);
2429  hcalDP.push_back(dp);
2430  if (dp / trackMomentum > maxDPovP)
2431  maxDPovP = dp / trackMomentum;
2432  sumpError2 += dp * dp;
2433  }
2434 
2435  // The total uncertainty of the difference Calo-Track
2436  double totalError = sqrt(sumpError2 + caloResolution * caloResolution);
2437 
2438 #ifdef EDM_ML_DEBUG
2439  LogTrace("PFAlgo|createCandidatesHCAL")
2440  << "\tCompare Calo Energy to total charged momentum " << endl
2441  << "\t\tsum p = " << totalChargedMomentum << " +- " << sqrt(sumpError2) << endl
2442  << "\t\tsum ecal = " << totalEcal << endl
2443  << "\t\tsum hcal = " << totalHcal << endl
2444  << "\t\t => Calo Energy = " << caloEnergy << " +- " << caloResolution << endl
2445  << "\t\t => Calo Energy- total charged momentum = " << caloEnergy - totalChargedMomentum << " +- "
2446  << totalError;
2447 #endif
2448 
2449  /* */
2450 
2452  double nsigma = nSigmaHCAL(totalChargedMomentum, hclusterref->positionREP().Eta());
2453  //double nsigma = nSigmaHCAL(caloEnergy,hclusterref->positionREP().Eta());
2454  if (abs(totalChargedMomentum - caloEnergy) < nsigma * totalError) {
2455  // deposited caloEnergy compatible with total charged momentum
2456  // if tracking errors are large take weighted average
2457 
2458 #ifdef EDM_ML_DEBUG
2459  LogTrace("PFAlgo|createCandidatesHCAL")
2460  << "\t\tcase 1: COMPATIBLE "
2461  << "|Calo Energy- total charged momentum| = " << abs(caloEnergy - totalChargedMomentum) << " < " << nsigma
2462  << " x " << totalError;
2463  if (maxDPovP < 0.1)
2464  LogTrace("PFAlgo|createCandidatesHCAL") << "\t\t\tmax DP/P = " << maxDPovP << " less than 0.1: do nothing ";
2465  else
2466  LogTrace("PFAlgo|createCandidatesHCAL")
2467  << "\t\t\tmax DP/P = " << maxDPovP << " > 0.1: take weighted averages ";
2468 #endif
2469 
2470  // if max DP/P < 10% do nothing
2471  if (maxDPovP > 0.1) {
2472  // for each track associated to hcal
2473  // int nrows = tkIs.size();
2474  int nrows = chargedHadronsIndices.size();
2475  TMatrixTSym<double> a(nrows);
2476  TVectorD b(nrows);
2477  TVectorD check(nrows);
2478  double sigma2E = caloResolution * caloResolution;
2479  for (int i = 0; i < nrows; i++) {
2480  double sigma2i = hcalDP[i] * hcalDP[i];
2481  LogTrace("PFAlgo|createCandidatesHCAL")
2482  << "\t\t\ttrack associated to hcal " << i << " P = " << hcalP[i] << " +- " << hcalDP[i];
2483  a(i, i) = 1. / sigma2i + 1. / sigma2E;
2484  b(i) = hcalP[i] / sigma2i + caloEnergy / sigma2E;
2485  for (int j = 0; j < nrows; j++) {
2486  if (i == j)
2487  continue;
2488  a(i, j) = 1. / sigma2E;
2489  } // end loop on j
2490  } // end loop on i
2491 
2492  // solve ax = b
2493  TDecompChol decomp(a);
2494  bool ok = false;
2495  TVectorD x = decomp.Solve(b, ok);
2496  // for each track create a PFCandidate track
2497  // with a momentum rescaled to weighted average
2498  if (ok) {
2499  for (int i = 0; i < nrows; i++) {
2500  // unsigned iTrack = trackInfos[i].index;
2501  unsigned ich = chargedHadronsIndices[i];
2502  double rescaleFactor = x(i) / hcalP[i];
2503  if (rescaleFactor < 0.)
2504  rescaleFactor = 0.; // protect against negative scaling
2505  (*pfCandidates_)[ich].rescaleMomentum(rescaleFactor);
2506 
2507  LogTrace("PFAlgo|createCandidatesHCAL")
2508  << "\t\t\told p " << hcalP[i] << " new p " << x(i) << " rescale " << rescaleFactor;
2509  }
2510  } else {
2511  edm::LogError("PFAlgo::createCandidatesHCAL") << "TDecompChol.Solve returned ok=false";
2512  assert(0);
2513  }
2514  }
2515  }
2516 
2518  else if (caloEnergy > totalChargedMomentum) {
2519  //case 2: caloEnergy > totalChargedMomentum + nsigma*totalError
2520  //there is an excess of energy in the calos
2521  //create a neutral hadron or a photon
2522 
2523  double eNeutralHadron = caloEnergy - totalChargedMomentum;
2524  double ePhoton = (caloEnergy - totalChargedMomentum) /
2525  slopeEcal; // KH: this slopeEcal is computed based on ECAL energy under the hadron hypothesis,
2526  // thought we are creating photons.
2527  // This is a fuzzy case, but it should be better than corrected twice under both egamma and hadron hypotheses.
2528 
2529 #ifdef EDM_ML_DEBUG
2530  if (!sortedTracks.empty()) {
2531  LogTrace("PFAlgo|createCandidatesHCAL") << "\t\tcase 2: NEUTRAL DETECTION " << caloEnergy << " > " << nsigma
2532  << "x" << totalError << " + " << totalChargedMomentum;
2533  LogTrace("PFAlgo|createCandidatesHCAL") << "\t\tneutral activity detected: " << endl
2534  << "\t\t\t photon = " << ePhoton << endl
2535  << "\t\t\tor neutral hadron = " << eNeutralHadron;
2536 
2537  LogTrace("PFAlgo|createCandidatesHCAL") << "\t\tphoton or hadron ?";
2538  }
2539 
2540  if (sortedTracks.empty())
2541  LogTrace("PFAlgo|createCandidatesHCAL") << "\t\tno track -> hadron ";
2542  else
2543  LogTrace("PFAlgo|createCandidatesHCAL")
2544  << "\t\t" << sortedTracks.size() << " tracks -> check if the excess is photonic or hadronic";
2545 #endif
2546 
2547  double ratioMax = 0.;
2548  reco::PFClusterRef maxEcalRef;
2549  unsigned maxiEcal = 9999;
2550 
2551  // for each track associated to hcal: iterator IE ie :
2552 
2553  LogTrace("PFAlgo|createCandidatesHCAL") << "loop over sortedTracks.size()=" << sortedTracks.size();
2554  for (auto const& trk : sortedTracks) {
2555  unsigned iTrack = trk.second;
2556 
2557  PFBlockElement::Type type = elements[iTrack].type();
2559 
2560  reco::TrackRef trackRef = elements[iTrack].trackRef();
2561  assert(!trackRef.isNull());
2562 
2563  auto iae = associatedEcals.find(iTrack);
2564 
2565  if (iae == associatedEcals.end())
2566  continue;
2567 
2568  // double distECAL = iae->second.first;
2569  unsigned iEcal = iae->second.second;
2570 
2571  PFBlockElement::Type typeEcal = elements[iEcal].type();
2572  assert(typeEcal == reco::PFBlockElement::ECAL);
2573 
2574  reco::PFClusterRef clusterRef = elements[iEcal].clusterRef();
2575  assert(!clusterRef.isNull());
2576 
2577  double pTrack = trackRef->p();
2578  double eECAL = clusterRef->energy();
2579  double eECALOverpTrack = eECAL / pTrack;
2580 
2581  if (eECALOverpTrack > ratioMax) {
2582  ratioMax = eECALOverpTrack;
2583  maxEcalRef = clusterRef;
2584  maxiEcal = iEcal;
2585  }
2586 
2587  } // end loop on tracks associated to hcal: iterator IE ie
2588 
2589  std::vector<reco::PFClusterRef> pivotalClusterRef;
2590  std::vector<unsigned> iPivotal;
2591  std::vector<double> particleEnergy, ecalEnergy, hcalEnergy, rawecalEnergy, rawhcalEnergy;
2592  std::vector<::math::XYZVector> particleDirection;
2593 
2594  // If the excess is smaller than the ecal energy, assign the whole
2595  // excess to photons
2596  if (ePhoton < totalEcal || eNeutralHadron - calibEcal < 1E-10) {
2597  if (!maxEcalRef.isNull()) {
2598  // So the merged photon energy is,
2599  mergedPhotonEnergy = ePhoton;
2600  }
2601  } else {
2602  // Otherwise assign the whole ECAL energy to the photons
2603  if (!maxEcalRef.isNull()) {
2604  // So the merged photon energy is,
2605  mergedPhotonEnergy = totalEcalEGMCalib; // KH: use calibrated ECAL energy under the egamma hypothesis
2606  }
2607  // ... and assign the remaining excess to neutral hadrons using the direction of ecal clusters
2608  mergedNeutralHadronEnergy = eNeutralHadron - calibEcal;
2609  }
2610 
2611  if (mergedPhotonEnergy > 0) {
2612  // Split merged photon into photons for each energetic ecal cluster (necessary for jet substructure reconstruction)
2613  // make only one merged photon if less than 2 ecal clusters
2614  // KH: this part still needs review, after using non-corrected ECAL energy for PF hadron calibrations
2615  if (ecalClusters.size() <= 1) {
2616  ecalClusters.clear();
2617  ecalClusters.emplace_back(
2618  maxiEcal,
2619  photonAtECAL,
2620  1.); // KH: calibration factor of 1, which should be ok as long as sumEcalClusters is consistent with photonAtECAL in this case
2621  sumEcalClusters = sqrt(photonAtECAL.Mag2());
2622  }
2623  for (auto const& pae : ecalClusters) {
2624  const double clusterEnergyCalibrated =
2625  sqrt(std::get<1>(pae).Mag2()) *
2626  std::get<2>(
2627  pae); // KH: calibrated under the egamma hypothesis. Note: sumEcalClusters is normally calibrated under egamma hypothesis
2628  particleEnergy.push_back(mergedPhotonEnergy * clusterEnergyCalibrated / sumEcalClusters);
2629  particleDirection.push_back(std::get<1>(pae));
2630  ecalEnergy.push_back(mergedPhotonEnergy * clusterEnergyCalibrated / sumEcalClusters);
2631  hcalEnergy.push_back(0.);
2632  rawecalEnergy.push_back(totalEcal);
2633  rawhcalEnergy.push_back(0.);
2634  pivotalClusterRef.push_back(elements[std::get<0>(pae)].clusterRef());
2635  iPivotal.push_back(std::get<0>(pae));
2636  }
2637  } // mergedPhotonEnergy > 0
2638 
2639  if (mergedNeutralHadronEnergy > 1.0) {
2640  // Split merged neutral hadrons according to directions of energetic ecal clusters (necessary for jet substructure reconstruction)
2641  // make only one merged neutral hadron if less than 2 ecal clusters
2642  if (ecalClusters.size() <= 1) {
2643  ecalClusters.clear();
2644  ecalClusters.emplace_back(
2645  iHcal,
2646  hadronAtECAL,
2647  1.); // KH: calibration factor of 1, which should be ok as long as sumEcalClusters is consistent with photonAtECAL
2648  sumEcalClusters = sqrt(hadronAtECAL.Mag2());
2649  }
2650  for (auto const& pae : ecalClusters) {
2651  const double clusterEnergyCalibrated =
2652  sqrt(std::get<1>(pae).Mag2()) *
2653  std::get<2>(
2654  pae); // KH: calibrated under the egamma hypothesis. Note: sumEcalClusters is normally calibrated under egamma hypothesis
2655  particleEnergy.push_back(mergedNeutralHadronEnergy * clusterEnergyCalibrated / sumEcalClusters);
2656  particleDirection.push_back(std::get<1>(pae));
2657  ecalEnergy.push_back(0.);
2658  hcalEnergy.push_back(mergedNeutralHadronEnergy * clusterEnergyCalibrated / sumEcalClusters);
2659  rawecalEnergy.push_back(0.);
2660  rawhcalEnergy.push_back(totalHcal);
2661  pivotalClusterRef.push_back(hclusterref);
2662  iPivotal.push_back(iHcal);
2663  }
2664  } //mergedNeutralHadronEnergy > 1.0
2665 
2666  // reconstructing a merged neutral
2667  // the type of PFCandidate is known from the
2668  // reference to the pivotal cluster.
2669 
2670  for (unsigned iPivot = 0; iPivot < iPivotal.size(); ++iPivot) {
2671  if (particleEnergy[iPivot] < 0.)
2672  edm::LogWarning("PFAlgo|createCandidatesHCAL")
2673  << "ALARM = Negative energy for iPivot=" << iPivot << ", " << particleEnergy[iPivot];
2674 
2675  const bool useDirection = true;
2676  auto& neutral = (*pfCandidates_)[reconstructCluster(*pivotalClusterRef[iPivot],
2677  particleEnergy[iPivot],
2678  useDirection,
2679  particleDirection[iPivot].X(),
2680  particleDirection[iPivot].Y(),
2681  particleDirection[iPivot].Z())];
2682 
2683  neutral.setEcalEnergy(rawecalEnergy[iPivot], ecalEnergy[iPivot]);
2684  if (!useHO_) {
2685  neutral.setHcalEnergy(rawhcalEnergy[iPivot], hcalEnergy[iPivot]);
2686  neutral.setHoEnergy(0., 0.);
2687  } else { // useHO_
2688  if (rawhcalEnergy[iPivot] == 0.) { // photons should be here
2689  neutral.setHcalEnergy(0., 0.);
2690  neutral.setHoEnergy(0., 0.);
2691  } else {
2692  neutral.setHcalEnergy(max(rawhcalEnergy[iPivot] - totalHO, 0.0),
2693  hcalEnergy[iPivot] * max(1. - totalHO / rawhcalEnergy[iPivot], 0.));
2694  neutral.setHoEnergy(totalHO, totalHO * hcalEnergy[iPivot] / rawhcalEnergy[iPivot]);
2695  }
2696  }
2697  neutral.setPs1Energy(0.);
2698  neutral.setPs2Energy(0.);
2699  neutral.set_mva_nothing_gamma(-1.);
2700  // neutral.addElement(&elements[iPivotal]);
2701  // neutral.addElementInBlock(blockref, iPivotal[iPivot]);
2702  neutral.addElementInBlock(blockref, iHcal);
2703  for (unsigned iTrack : chargedHadronsInBlock) {
2704  neutral.addElementInBlock(blockref, iTrack);
2705  // Assign the position of the track at the ECAL entrance
2706  const ::math::XYZPointF& chargedPosition =
2707  dynamic_cast<const reco::PFBlockElementTrack*>(&elements[iTrack])->positionAtECALEntrance();
2708  neutral.setPositionAtECALEntrance(chargedPosition);
2709 
2710  auto myEcals = associatedEcals.equal_range(iTrack);
2711  for (auto ii = myEcals.first; ii != myEcals.second; ++ii) {
2712  unsigned iEcal = ii->second.second;
2713  if (active[iEcal])
2714  continue;
2715  neutral.addElementInBlock(blockref, iEcal);
2716  }
2717  }
2718  }
2719 
2720  } // excess of energy
2721 
2722  // will now share the hcal energy between the various charged hadron
2723  // candidates, taking into account the potential neutral hadrons
2724 
2725  //JB: The question is: we've resolved the merged photons cleanly, but how should
2726  //the remaining hadrons be assigned the remaining ecal energy?
2727  //*Temporary solution*: follow HCAL example with fractions...
2728 
2729  // remove the energy of the potential neutral hadron
2730  double totalHcalEnergyCalibrated = std::max(calibHcal - mergedNeutralHadronEnergy, 0.);
2731  // similarly for the merged photons
2732  double totalEcalEnergyCalibrated = std::max(calibEcal - mergedPhotonEnergy, 0.);
2733  // share between the charged hadrons
2734 
2735  //COLIN can compute this before
2736  // not exactly equal to sum p, this is sum E
2737  double chargedHadronsTotalEnergy = 0;
2738  for (unsigned index : chargedHadronsIndices) {
2739  reco::PFCandidate& chargedHadron = (*pfCandidates_)[index];
2740  chargedHadronsTotalEnergy += chargedHadron.energy();
2741  }
2742 
2743  for (unsigned index : chargedHadronsIndices) {
2744  reco::PFCandidate& chargedHadron = (*pfCandidates_)[index];
2745  float fraction = chargedHadron.energy() / chargedHadronsTotalEnergy;
2746 
2747  if (!useHO_) {
2748  chargedHadron.setHcalEnergy(fraction * totalHcal, fraction * totalHcalEnergyCalibrated);
2749  chargedHadron.setHoEnergy(0., 0.);
2750  } else {
2751  chargedHadron.setHcalEnergy(fraction * max(totalHcal - totalHO, 0.0),
2752  fraction * totalHcalEnergyCalibrated * (1. - totalHO / totalHcal));
2753  chargedHadron.setHoEnergy(fraction * totalHO, fraction * totalHO * totalHcalEnergyCalibrated / totalHcal);
2754  }
2755  //JB: fixing up (previously omitted) setting of ECAL energy gouzevit
2756  chargedHadron.setEcalEnergy(fraction * totalEcal, fraction * totalEcalEnergyCalibrated);
2757  }
2758 
2759  // Finally treat unused ecal satellites as photons.
2760  for (auto const& ecalSatellite : ecalSatellites) {
2761  // Ignore satellites already taken
2762  unsigned iEcal = std::get<0>(ecalSatellite.second);
2763  if (!active[iEcal])
2764  continue;
2765 
2766  // Sanity checks again (well not useful, this time!)
2767  PFBlockElement::Type type = elements[iEcal].type();
2769  PFClusterRef eclusterref = elements[iEcal].clusterRef();
2770  assert(!eclusterref.isNull());
2771 
2772  // Lock the cluster
2773  active[iEcal] = false;
2774 
2775  // Find the associated tracks
2776  std::multimap<double, unsigned> assTracks;
2777  block.associatedElements(iEcal, linkData, assTracks, reco::PFBlockElement::TRACK, reco::PFBlock::LINKTEST_ALL);
2778 
2779  // Create a photon
2780  double ecalClusterEnergyCalibrated =
2781  sqrt(std::get<1>(ecalSatellite.second).Mag2()) *
2782  std::get<2>(
2783  ecalSatellite.second); // KH: calibrated under the egamma hypothesis (rawEcalClusterEnergy * calibration)
2784  auto& cand = (*pfCandidates_)[reconstructCluster(*eclusterref, ecalClusterEnergyCalibrated)];
2785  cand.setEcalEnergy(eclusterref->energy(), ecalClusterEnergyCalibrated);
2786  cand.setHcalEnergy(0., 0.);
2787  cand.setHoEnergy(0., 0.);
2788  cand.setPs1Energy(associatedPSs[iEcal].first);
2789  cand.setPs2Energy(associatedPSs[iEcal].second);
2790  cand.addElementInBlock(blockref, iEcal);
2791  cand.addElementInBlock(blockref, sortedTracks.begin()->second);
2792 
2793  if (fabs(eclusterref->energy() - sqrt(std::get<1>(ecalSatellite.second).Mag2())) > 1e-3 ||
2794  fabs(eclusterref->correctedEnergy() - ecalClusterEnergyCalibrated) > 1e-3)
2795  edm::LogWarning("PFAlgo:processBlock")
2796  << "ecalCluster vs ecalSatellites look inconsistent (eCluster E, calibE, ecalSatellite E, calib E): "
2797  << eclusterref->energy() << " " << eclusterref->correctedEnergy() << " "
2798  << sqrt(std::get<1>(ecalSatellite.second).Mag2()) << " " << ecalClusterEnergyCalibrated;
2799 
2800  } // ecalSatellites
2801 
2802  } // hcalIs
2803  // end loop on hcal element iHcal= hcalIs[i]
2804  LogTrace("PFAlgo|createCandidatesHCAL") << "end of function PFAlgo::createCandidatesHCAL";
2805 }
2806 
2808  reco::PFBlock::LinkData& linkData,
2810  std::vector<bool>& active,
2811  const reco::PFBlockRef& blockref,
2812  ElementIndices& inds,
2813  std::vector<bool>& deadArea) {
2814  // Processing the remaining HCAL clusters
2815  LogTrace("PFAlgo|createCandidatesHCALUnlinked")
2816  << "start of function PFAlgo::createCandidatesHCALUnlinked, hcalIs.size()=" << inds.hcalIs.size();
2817 
2818  // --------------- loop remaining hcal ------------------
2819 
2820  for (unsigned iHcal : inds.hcalIs) {
2821  // Keep ECAL and HO elements for reference in the PFCandidate
2822  std::vector<unsigned> ecalRefs;
2823  std::vector<unsigned> hoRefs;
2824 
2825  LogTrace("PFAlgo|createCandidatesHCALUnlinked") << elements[iHcal] << " ";
2826 
2827  if (!active[iHcal]) {
2828  LogTrace("PFAlgo|createCandidatesHCALUnlinked") << "not active " << iHcal;
2829  continue;
2830  }
2831 
2832  // Find the ECAL elements linked to it
2833  std::multimap<double, unsigned> ecalElems;
2834  block.associatedElements(iHcal, linkData, ecalElems, reco::PFBlockElement::ECAL, reco::PFBlock::LINKTEST_ALL);
2835 
2836  // Loop on these ECAL elements
2837  float totalEcal = 0.;
2838  float ecalMax = 0.;
2839  reco::PFClusterRef eClusterRef;
2840  for (auto const& ecal : ecalElems) {
2841  unsigned iEcal = ecal.second;
2842  double dist = ecal.first;
2843  PFBlockElement::Type type = elements[iEcal].type();
2845 
2846  // Check if already used
2847  if (!active[iEcal])
2848  continue;
2849 
2850  // Check the distance (one HCALPlusECAL tower, roughly)
2851  // if ( dist > 0.15 ) continue;
2852 
2853  //COLINFEB16
2854  // what could be done is to
2855  // - link by rechit.
2856  // - take in the neutral hadron all the ECAL clusters
2857  // which are within the same CaloTower, according to the distance,
2858  // except the ones which are closer to another HCAL cluster.
2859  // - all the other ECAL linked to this HCAL are photons.
2860  //
2861  // about the closest HCAL cluster.
2862  // it could maybe be easier to loop on the ECAL clusters first
2863  // to cut the links to all HCAL clusters except the closest, as is
2864  // done in the first track loop. But maybe not!
2865  // or add an helper function to the PFAlgo class to ask
2866  // if a given element is the closest of a given type to another one?
2867 
2868  // Check if not closer from another free HCAL
2869  std::multimap<double, unsigned> hcalElems;
2870  block.associatedElements(iEcal, linkData, hcalElems, reco::PFBlockElement::HCAL, reco::PFBlock::LINKTEST_ALL);
2871 
2872  const bool isClosest = std::none_of(hcalElems.begin(), hcalElems.end(), [&](auto const& hcal) {
2873  return active[hcal.second] && hcal.first < dist;
2874  });
2875 
2876  if (!isClosest)
2877  continue;
2878 
2879 #ifdef EDM_ML_DEBUG
2880  LogTrace("PFAlgo|createCandidatesHCALUnlinked")
2881  << "\telement " << elements[iEcal] << " linked with dist " << dist;
2882  LogTrace("PFAlgo|createCandidatesHCALUnlinked") << "Added to HCAL cluster to form a neutral hadron";
2883 #endif
2884 
2885  reco::PFClusterRef eclusterRef = elements[iEcal].clusterRef();
2886  assert(!eclusterRef.isNull());
2887 
2888  // KH: use raw ECAL energy for PF hadron calibration_.
2889  double ecalEnergy = eclusterRef->energy(); // ecalEnergy = eclusterRef->correctedEnergy();
2890 
2891  totalEcal += ecalEnergy;
2892  if (ecalEnergy > ecalMax) {
2893  ecalMax = ecalEnergy;
2894  eClusterRef = eclusterRef;
2895  }
2896 
2897  ecalRefs.push_back(iEcal);
2898  active[iEcal] = false;
2899 
2900  } // End loop ECAL
2901 
2902  // Now find the HO clusters linked to the HCAL cluster
2903  double totalHO = 0.;
2904  double hoMax = 0.;
2905  //unsigned jHO = 0;
2906  if (useHO_) {
2907  std::multimap<double, unsigned> hoElems;
2908  block.associatedElements(iHcal, linkData, hoElems, reco::PFBlockElement::HO, reco::PFBlock::LINKTEST_ALL);
2909 
2910  // Loop on these HO elements
2911  // double totalHO = 0.;
2912  // double hoMax = 0.;
2913  // unsigned jHO = 0;
2914  reco::PFClusterRef hoClusterRef;
2915  for (auto const& ho : hoElems) {
2916  unsigned iHO = ho.second;
2917  double dist = ho.first;
2918  PFBlockElement::Type type = elements[iHO].type();
2920 
2921  // Check if already used
2922  if (!active[iHO])
2923  continue;
2924 
2925  // Check the distance (one HCALPlusHO tower, roughly)
2926  // if ( dist > 0.15 ) continue;
2927 
2928  // Check if not closer from another free HCAL
2929  std::multimap<double, unsigned> hcalElems;
2930  block.associatedElements(iHO, linkData, hcalElems, reco::PFBlockElement::HCAL, reco::PFBlock::LINKTEST_ALL);
2931 
2932  const bool isClosest = std::none_of(hcalElems.begin(), hcalElems.end(), [&](auto const& hcal) {
2933  return active[hcal.second] && hcal.first < dist;
2934  });
2935 
2936  if (!isClosest)
2937  continue;
2938 
2939 #ifdef EDM_ML_DEBUG
2940  if (useHO_) {
2941  LogTrace("PFAlgo|createCandidatesHCALUnlinked")
2942  << "\telement " << elements[iHO] << " linked with dist " << dist;
2943  LogTrace("PFAlgo|createCandidatesHCALUnlinked") << "Added to HCAL cluster to form a neutral hadron";
2944  }
2945 #endif
2946 
2947  reco::PFClusterRef hoclusterRef = elements[iHO].clusterRef();
2948  assert(!hoclusterRef.isNull());
2949 
2950  double hoEnergy =
2951  hoclusterRef->energy(); // calibration_.energyEm(*hoclusterRef,ps1Ene,ps2Ene,crackCorrection);
2952 
2953  totalHO += hoEnergy;
2954  if (hoEnergy > hoMax) {
2955  hoMax = hoEnergy;
2956  hoClusterRef = hoclusterRef;
2957  //jHO = iHO;
2958  }
2959 
2960  hoRefs.push_back(iHO);
2961  active[iHO] = false;
2962 
2963  } // End loop HO
2964  }
2965 
2966  PFClusterRef hclusterRef = elements[iHcal].clusterRef();
2967  assert(!hclusterRef.isNull());
2968 
2969  // HCAL energy
2970  double totalHcal = hclusterRef->energy();
2971  // Include the HO energy
2972  if (useHO_)
2973  totalHcal += totalHO;
2974 
2975  // Calibration
2976  double calibEcal = totalEcal > 0. ? totalEcal : 0.;
2977  double calibHcal = std::max(0., totalHcal);
2978  if (hclusterRef->layer() == PFLayer::HF_HAD || hclusterRef->layer() == PFLayer::HF_EM) {
2979  calibEcal = totalEcal;
2980  } else {
2982  -1., calibEcal, calibHcal, hclusterRef->positionREP().Eta(), hclusterRef->positionREP().Phi());
2983  }
2984 
2985  auto& cand = (*pfCandidates_)[reconstructCluster(*hclusterRef, calibEcal + calibHcal)];
2986 
2987  cand.setEcalEnergy(totalEcal, calibEcal);
2988  if (!useHO_) {
2989  cand.setHcalEnergy(totalHcal, calibHcal);
2990  cand.setHoEnergy(0., 0.);
2991  } else {
2992  cand.setHcalEnergy(max(totalHcal - totalHO, 0.0), calibHcal * (1. - totalHO / totalHcal));
2993  cand.setHoEnergy(totalHO, totalHO * calibHcal / totalHcal);
2994  }
2995  cand.setPs1Energy(0.);
2996  cand.setPs2Energy(0.);
2997  cand.addElementInBlock(blockref, iHcal);
2998  for (auto const& ec : ecalRefs)
2999  cand.addElementInBlock(blockref, ec);
3000  for (auto const& ho : hoRefs)
3001  cand.addElementInBlock(blockref, ho);
3002 
3003  } //loop hcal elements
3004 }
3005 
3007  reco::PFBlock::LinkData& linkData,
3009  std::vector<bool>& active,
3010  const reco::PFBlockRef& blockref,
3011  ElementIndices& inds,
3012  std::vector<bool>& deadArea) {
3013  LogTrace("PFAlgo|createCandidatesECAL")
3014  << "start of function PFAlgo::createCandidatesECAL(), ecalIs.size()=" << inds.ecalIs.size();
3015 
3016  // --------------- loop ecal ------------------
3017 
3018  // for each ecal element iEcal = ecalIs[i] in turn:
3019 
3020  for (unsigned i = 0; i < inds.ecalIs.size(); i++) {
3021  unsigned iEcal = inds.ecalIs[i];
3022 
3023  LogTrace("PFAlgo|createCandidatesECAL") << "elements[" << iEcal << "]=" << elements[iEcal];
3024 
3025  if (!active[iEcal]) {
3026  LogTrace("PFAlgo|createCandidatesECAL") << "iEcal=" << iEcal << " not active";
3027  continue;
3028  }
3029 
3030  PFBlockElement::Type type = elements[iEcal].type();
3032 
3033  PFClusterRef clusterref = elements[iEcal].clusterRef();
3034  assert(!clusterref.isNull());
3035 
3036  active[iEcal] = false;
3037 
3038  float ecalEnergy = clusterref->correctedEnergy();
3039  // float ecalEnergy = calibration_.energyEm( clusterref->energy() );
3040  double particleEnergy = ecalEnergy;
3041 
3042  auto& cand = (*pfCandidates_)[reconstructCluster(*clusterref, particleEnergy)];
3043 
3044  cand.setEcalEnergy(clusterref->energy(), ecalEnergy);
3045  cand.setHcalEnergy(0., 0.);
3046  cand.setHoEnergy(0., 0.);
3047  cand.setPs1Energy(0.);
3048  cand.setPs2Energy(0.);
3049  cand.addElementInBlock(blockref, iEcal);
3050 
3051  } // end loop on ecal elements iEcal = ecalIs[i]
3052  LogTrace("PFAlgo|createCandidatesECAL") << "end of function PFALgo::createCandidatesECAL";
3053 }
3054 
3056  std::list<reco::PFBlockRef>& hcalBlockRefs,
3057  std::list<reco::PFBlockRef>& ecalBlockRefs,
3058  PFEGammaFilters const* pfegamma) {
3059  assert(!blockref.isNull());
3060  const reco::PFBlock& block = *blockref;
3061 
3062  LogTrace("PFAlgo|processBlock") << "start of function PFAlgo::processBlock, block=" << block;
3063 
3065  LogTrace("PFAlgo|processBlock") << "elements.size()=" << elements.size();
3066  // make a copy of the link data, which will be edited.
3067  PFBlock::LinkData linkData = block.linkData();
3068 
3069  // keep track of the elements which are still active.
3070  vector<bool> active(elements.size(), true);
3071 
3072  // //PFElectrons:
3073  // usePFElectrons_ external configurable parameter to set the usage of pf electron
3074  std::vector<reco::PFCandidate> tempElectronCandidates;
3075  tempElectronCandidates.clear();
3076 
3077  // New EGamma Reconstruction 10/10/2013
3078  if (useEGammaFilters_) {
3079  egammaFilters(blockref, active, pfegamma);
3080  } // end if use EGammaFilters
3081 
3082  //Lock extra conversion tracks not used by Photon Algo
3083  if (usePFConversions_) {
3084  conversionAlgo(elements, active);
3085  }
3086 
3087  // In the following elementLoop() function, the primary goal is to deal with tracks that are:
3088  // - not associated to an HCAL cluster
3089  // - not identified as an electron.
3090  // Those tracks should be predominantly relatively low energy charged
3091  // hadrons which are not detected in the ECAL.
3092 
3093  // The secondary goal is to prepare for the next loops
3094  // - The ecal and hcal elements are sorted in separate vectors in `ElementIndices inds`
3095  // which will be used as a base for the corresponding loops.
3096  // - For tracks which are connected to more than one HCAL cluster,
3097  // the links between the track and the cluster are cut for all clusters
3098  // but the closest one.
3099  // - HF only blocks ( HFEM, HFHAD, HFEM+HFAD) are identified
3100 
3101  // obsolete comments?
3102  // loop1:
3103  // - sort ecal and hcal elements in separate vectors
3104  // - for tracks:
3105  // - lock closest ecal cluster
3106  // - cut link to farthest hcal cluster, if more than 1.
3107 
3108  vector<bool> deadArea(elements.size(), false);
3109 
3110  // vectors to store element indices to ho, hcal and ecal elements, will be filled by elementLoop()
3111  ElementIndices inds;
3112 
3113  elementLoop(block, linkData, elements, active, blockref, inds, deadArea);
3114 
3115  // Reconstruct pfCandidate from HF (either EM-only, Had-only or both)
3116  // For phase2, process also pfblocks containing HF clusters and linked tracks
3117  if (!(inds.hfEmIs.empty() && inds.hfHadIs.empty())) {
3118  createCandidatesHF(block, linkData, elements, active, blockref, inds);
3119  if (inds.hcalIs.empty() && inds.ecalIs.empty())
3120  return;
3121  LogDebug("PFAlgo::processBlock")
3122  << "Block contains HF clusters, and also contains ECAL or HCAL clusters. Continue.\n"
3123  << block;
3124  }
3125 
3126  createCandidatesHCAL(block, linkData, elements, active, blockref, inds, deadArea);
3127  // COLINFEB16: now dealing with the HCAL elements that are not linked to any track
3128  createCandidatesHCALUnlinked(block, linkData, elements, active, blockref, inds, deadArea);
3129  createCandidatesECAL(block, linkData, elements, active, blockref, inds, deadArea);
3130 
3131  LogTrace("PFAlgo|processBlock") << "end of function PFAlgo::processBlock";
3132 } // end processBlock
3133 
3135 unsigned PFAlgo::reconstructTrack(const reco::PFBlockElement& elt, bool allowLoose) {
3136  const auto* eltTrack = dynamic_cast<const reco::PFBlockElementTrack*>(&elt);
3137 
3138  const reco::TrackRef& trackRef = eltTrack->trackRef();
3139  const reco::Track& track = *trackRef;
3140  const reco::MuonRef& muonRef = eltTrack->muonRef();
3141  int charge = track.charge() > 0 ? 1 : -1;
3142 
3143  // Assume this particle is a charged Hadron
3144  double px = track.px();
3145  double py = track.py();
3146  double pz = track.pz();
3147  double energy = sqrt(track.p() * track.p() + 0.13957 * 0.13957);
3148 
3149  LogTrace("PFAlgo|reconstructTrack") << "Reconstructing PF candidate from track of pT = " << track.pt()
3150  << " eta = " << track.eta() << " phi = " << track.phi() << " px = " << px
3151  << " py = " << py << " pz = " << pz << " energy = " << energy;
3152 
3153  // Create a PF Candidate
3154  ::math::XYZTLorentzVector momentum(px, py, pz, energy);
3156 
3157  // Add it to the stack
3158  LogTrace("PFAlgo|reconstructTrack") << "Creating PFCandidate charge=" << charge << ", type=" << particleType
3159  << ", pt=" << momentum.pt() << ", eta=" << momentum.eta()
3160  << ", phi=" << momentum.phi();
3161  pfCandidates_->push_back(PFCandidate(charge, momentum, particleType));
3162  //Set vertex and stuff like this
3163  pfCandidates_->back().setVertex(trackRef->vertex());
3164  pfCandidates_->back().setTrackRef(trackRef);
3165  pfCandidates_->back().setPositionAtECALEntrance(eltTrack->positionAtECALEntrance());
3166  if (muonRef.isNonnull())
3167  pfCandidates_->back().setMuonRef(muonRef);
3168 
3169  //Set time
3170  if (elt.isTimeValid())
3171  pfCandidates_->back().setTime(elt.time(), elt.timeError());
3172 
3173  //OK Now try to reconstruct the particle as a muon
3174  bool isMuon = pfmu_->reconstructMuon(pfCandidates_->back(), muonRef, allowLoose);
3175  bool isFromDisp = isFromSecInt(elt, "secondary");
3176 
3177  if ((!isMuon) && isFromDisp) {
3178  double dpt = trackRef->ptError();
3179  double dptRel = dpt / trackRef->pt() * 100;
3180  //If the track is ill measured it is better to not refit it, since the track information probably would not be used.
3181  //In the PFAlgo we use the trackref information. If the track error is too big the refitted information might be very different
3182  // from the not refitted one.
3183  if (dptRel < dptRel_DispVtx_) {
3184  LogTrace("PFAlgo|reconstructTrack")
3185  << "Not refitted px = " << px << " py = " << py << " pz = " << pz << " energy = " << energy;
3186  //reco::TrackRef trackRef = eltTrack->trackRef();
3188  eltTrack->displacedVertexRef(reco::PFBlockElement::T_FROM_DISP)->displacedVertexRef();
3189  reco::Track trackRefit = vRef->refittedTrack(trackRef);
3190  //change the momentum with the refitted track
3191  ::math::XYZTLorentzVector momentum(
3192  trackRefit.px(), trackRefit.py(), trackRefit.pz(), sqrt(trackRefit.p() * trackRefit.p() + 0.13957 * 0.13957));
3193  LogTrace("PFAlgo|reconstructTrack")
3194  << "Refitted px = " << px << " py = " << py << " pz = " << pz << " energy = " << energy;
3195  }
3196  pfCandidates_->back().setFlag(reco::PFCandidate::T_FROM_DISP, true);
3197  pfCandidates_->back().setDisplacedVertexRef(
3198  eltTrack->displacedVertexRef(reco::PFBlockElement::T_FROM_DISP)->displacedVertexRef(),
3200  }
3201 
3202  // do not label as primary a track which would be recognised as a muon. A muon cannot produce NI. It is with high probability a fake
3203  if (isFromSecInt(elt, "primary") && !isMuon) {
3204  pfCandidates_->back().setFlag(reco::PFCandidate::T_TO_DISP, true);
3205  pfCandidates_->back().setDisplacedVertexRef(
3206  eltTrack->displacedVertexRef(reco::PFBlockElement::T_TO_DISP)->displacedVertexRef(),
3208  }
3209 
3210  // returns index to the newly created PFCandidate
3211  return pfCandidates_->size() - 1;
3212 }
3213 
3215  double particleEnergy,
3216  bool useDirection,
3217  double particleX,
3218  double particleY,
3219  double particleZ) {
3220  LogTrace("PFAlgo|reconstructCluster") << "start of function PFAlgo::reconstructCluster, cluster=" << cluster
3221  << "particleEnergy=" << particleEnergy << "useDirection=" << useDirection
3222  << "particleX=" << particleX << "particleY=" << particleY
3223  << "particleZ=" << particleZ;
3224 
3226 
3227  // need to convert the ::math::XYZPoint data member of the PFCluster class=
3228  // to a displacement vector:
3229 
3230  // Transform particleX,Y,Z to a position at ECAL/HCAL entrance
3231  double factor = 1.;
3232  if (useDirection) {
3233  switch (cluster.layer()) {
3234  case PFLayer::ECAL_BARREL:
3235  case PFLayer::HCAL_BARREL1:
3236  factor = std::sqrt(cluster.position().Perp2() / (particleX * particleX + particleY * particleY));
3237  break;
3238  case PFLayer::ECAL_ENDCAP:
3239  case PFLayer::HCAL_ENDCAP:
3240  case PFLayer::HF_HAD:
3241  case PFLayer::HF_EM:
3242  factor = cluster.position().Z() / particleZ;
3243  break;
3244  default:
3245  assert(0);
3246  }
3247  }
3248  //MIKE First of all let's check if we have vertex.
3249  ::math::XYZPoint vertexPos;
3250  if (useVertices_)
3252  else
3253  vertexPos = ::math::XYZPoint(0.0, 0.0, 0.0);
3254 
3255  ::math::XYZVector clusterPos(cluster.position().X() - vertexPos.X(),
3256  cluster.position().Y() - vertexPos.Y(),
3257  cluster.position().Z() - vertexPos.Z());
3258  ::math::XYZVector particleDirection(
3259  particleX * factor - vertexPos.X(), particleY * factor - vertexPos.Y(), particleZ * factor - vertexPos.Z());
3260 
3261  //::math::XYZVector clusterPos( cluster.position().X(), cluster.position().Y(),cluster.position().Z() );
3262  //::math::XYZVector particleDirection ( particleX, particleY, particleZ );
3263 
3264  clusterPos = useDirection ? particleDirection.Unit() : clusterPos.Unit();
3265  clusterPos *= particleEnergy;
3266 
3267  // clusterPos is now a vector along the cluster direction,
3268  // with a magnitude equal to the cluster energy.
3269 
3270  double mass = 0;
3271  ROOT::Math::LorentzVector<ROOT::Math::PxPyPzM4D<double>> momentum(
3272  clusterPos.X(), clusterPos.Y(), clusterPos.Z(), mass);
3273  // mathcore is a piece of #$%
3275  // implicit constructor not allowed
3276  tmp = momentum;
3277 
3278  // Charge
3279  int charge = 0;
3280 
3281  // Type
3282  switch (cluster.layer()) {
3283  case PFLayer::ECAL_BARREL:
3284  case PFLayer::ECAL_ENDCAP:
3286  break;
3287  case PFLayer::HCAL_BARREL1:
3288  case PFLayer::HCAL_ENDCAP:
3289  particleType = PFCandidate::h0;
3290  break;
3291  case PFLayer::HF_HAD:
3292  particleType = PFCandidate::h_HF;
3293  break;
3294  case PFLayer::HF_EM:
3295  particleType = PFCandidate::egamma_HF;
3296  break;
3297  default:
3298  assert(0);
3299  }
3300 
3301  // The pf candidate
3302  LogTrace("PFAlgo|reconstructCluster") << "Creating PFCandidate charge=" << charge << ", type=" << particleType
3303  << ", pt=" << tmp.pt() << ", eta=" << tmp.eta() << ", phi=" << tmp.phi();
3305 
3306  // The position at ECAL entrance (well: watch out, it is not true
3307  // for HCAL clusters... to be fixed)
3308  pfCandidates_->back().setPositionAtECALEntrance(
3309  ::math::XYZPointF(cluster.position().X(), cluster.position().Y(), cluster.position().Z()));
3310 
3311  //Set the cnadidate Vertex
3312  pfCandidates_->back().setVertex(vertexPos);
3313 
3314  // depth info
3315  setHcalDepthInfo(pfCandidates_->back(), cluster);
3316 
3317  //*TODO* cluster time is not reliable at the moment, so only use track timing
3318 
3319  LogTrace("PFAlgo|reconstructCluster") << "** candidate: " << pfCandidates_->back();
3320 
3321  // returns index to the newly created PFCandidate
3322  return pfCandidates_->size() - 1;
3323 }
3324 
3326  std::array<double, 7> energyPerDepth;
3327  std::fill(energyPerDepth.begin(), energyPerDepth.end(), 0.0);
3328  for (auto& hitRefAndFrac : cluster.recHitFractions()) {
3329  const auto& hit = *hitRefAndFrac.recHitRef();
3330  if (DetId(hit.detId()).det() == DetId::Hcal) {
3331  if (hit.depth() == 0) {
3332  edm::LogWarning("setHcalDepthInfo") << "Depth zero found";
3333  continue;
3334  }
3335  if (hit.depth() < 1 || hit.depth() > 7) {
3336  throw cms::Exception("CorruptData") << "Bogus depth " << hit.depth() << " at detid " << hit.detId() << "\n";
3337  }
3338  energyPerDepth[hit.depth() - 1] += hitRefAndFrac.fraction() * hit.energy();
3339  }
3340  }
3341  double sum = std::accumulate(energyPerDepth.begin(), energyPerDepth.end(), 0.);
3342  std::array<float, 7> depthFractions;
3343  if (sum > 0) {
3344  for (unsigned int i = 0; i < depthFractions.size(); ++i) {
3345  depthFractions[i] = energyPerDepth[i] / sum;
3346  }
3347  } else {
3348  std::fill(depthFractions.begin(), depthFractions.end(), 0.f);
3349  }
3350  cand.setHcalDepthEnergyFractions(depthFractions);
3351 }
3352 
3353 //GMA need the followign two for HO also
3354 
3355 double PFAlgo::neutralHadronEnergyResolution(double clusterEnergyHCAL, double eta) const {
3356  // Add a protection
3357  clusterEnergyHCAL = std::max(clusterEnergyHCAL, 1.);
3358 
3359  double resol = fabs(eta) < 1.48 ? sqrt(1.02 * 1.02 / clusterEnergyHCAL + 0.065 * 0.065)
3360  : sqrt(1.20 * 1.20 / clusterEnergyHCAL + 0.028 * 0.028);
3361 
3362  return resol;
3363 }
3364 
3365 double PFAlgo::nSigmaHCAL(double clusterEnergyHCAL, double eta) const {
3366  double nS = fabs(eta) < 1.48 ? nSigmaHCAL_ * (1. + exp(-clusterEnergyHCAL / nSigmaEConstHCAL))
3367  : nSigmaHCAL_ * (1. + exp(-clusterEnergyHCAL / nSigmaEConstHCAL));
3368 
3369  return nS;
3370 }
3371 
3372 double PFAlgo::hfEnergyResolution(double clusterEnergyHF) const {
3373  // Add a protection
3374  clusterEnergyHF = std::max(clusterEnergyHF, 1.);
3375 
3376  double resol =
3377  sqrt(resolHF_square_[0] / clusterEnergyHF + resolHF_square_[1] + resolHF_square_[2] / pow(clusterEnergyHF, 2));
3378  // 0: stochastic term, 1: constant term, 2: noise term
3379  // Note: resolHF_square_[0,1,2] should be already squared
3380 
3381  return resol;
3382 }
3383 
3384 double PFAlgo::nSigmaHFEM(double clusterEnergyHF) const {
3385  double nS = nSigmaHFEM_ * (1. + exp(-clusterEnergyHF / nSigmaEConstHFEM));
3386  return nS;
3387 }
3388 
3389 double PFAlgo::nSigmaHFHAD(double clusterEnergyHF) const {
3390  double nS = nSigmaHFHAD_ * (1. + exp(-clusterEnergyHF / nSigmaEConstHFHAD));
3391  return nS;
3392 }
3393 
3394 ostream& operator<<(ostream& out, const PFAlgo& algo) {
3395  if (!out)
3396  return out;
3397 
3398  out << "====== Particle Flow Algorithm ======= ";
3399  out << endl;
3400  out << "nSigmaECAL_ " << algo.nSigmaECAL_ << endl;
3401  out << "nSigmaHCAL_ " << algo.nSigmaHCAL_ << endl;
3402  out << "nSigmaHFEM_ " << algo.nSigmaHFEM_ << endl;
3403  out << "nSigmaHFHAD_ " << algo.nSigmaHFHAD_ << endl;
3404  out << endl;
3405  out << algo.calibration_ << endl;
3406  out << endl;
3407  out << "reconstructed particles: " << endl;
3408 
3409  if (!algo.pfCandidates_.get()) {
3410  out << "candidates already transfered" << endl;
3411  return out;
3412  }
3413  for (auto const& c : *algo.pfCandidates_)
3414  out << c << endl;
3415 
3416  return out;
3417 }
3418 
3419 void PFAlgo::associatePSClusters(unsigned iEcal,
3420  reco::PFBlockElement::Type psElementType,
3421  const reco::PFBlock& block,
3423  const reco::PFBlock::LinkData& linkData,
3424  std::vector<bool>& active,
3425  std::vector<double>& psEne) {
3426  // Find all PS clusters with type psElement associated to ECAL cluster iEcal,
3427  // within all PFBlockElement "elements" of a given PFBlock "block"
3428  // psElement can be reco::PFBlockElement::PS1 or reco::PFBlockElement::PS2
3429  // Returns a vector of PS cluster energies, and updates the "active" vector.
3430 
3431  // Find all PS clusters linked to the iEcal cluster
3432  std::multimap<double, unsigned> sortedPS;
3433  block.associatedElements(iEcal, linkData, sortedPS, psElementType, reco::PFBlock::LINKTEST_ALL);
3434 
3435  // Loop over these PS clusters
3436  double totalPS = 0.;
3437  for (auto const& ps : sortedPS) {
3438  // CLuster index and distance to iEcal
3439  unsigned iPS = ps.second;
3440  // double distPS = ps.first;
3441 
3442  // Ignore clusters already in use
3443  if (!active[iPS])
3444  continue;
3445 
3446  // Check that this cluster is not closer to another ECAL cluster
3447  std::multimap<double, unsigned> sortedECAL;
3448  block.associatedElements(iPS, linkData, sortedECAL, reco::PFBlockElement::ECAL, reco::PFBlock::LINKTEST_ALL);
3449  unsigned jEcal = sortedECAL.begin()->second;
3450  if (jEcal != iEcal)
3451  continue;
3452 
3453  // Update PS energy
3454  PFBlockElement::Type pstype = elements[iPS].type();
3455  assert(pstype == psElementType);
3456  PFClusterRef psclusterref = elements[iPS].clusterRef();
3457  assert(!psclusterref.isNull());
3458  totalPS += psclusterref->energy();
3459  psEne[0] += psclusterref->energy();
3460  active[iPS] = false;
3461  }
3462 }
3463 
3464 bool PFAlgo::isFromSecInt(const reco::PFBlockElement& eTrack, string order) const {
3467  // reco::PFBlockElement::TrackType T_FROM_GAMMACONV = reco::PFBlockElement::T_FROM_GAMMACONV;
3469 
3470  bool bPrimary = (order.find("primary") != string::npos);
3471  bool bSecondary = (order.find("secondary") != string::npos);
3472  bool bAll = (order.find("all") != string::npos);
3473 
3474  bool isToDisp = usePFNuclearInteractions_ && eTrack.trackType(T_TO_DISP);
3475  bool isFromDisp = usePFNuclearInteractions_ && eTrack.trackType(T_FROM_DISP);
3476 
3477  if (bPrimary && isToDisp)
3478  return true;
3479  if (bSecondary && isFromDisp)
3480  return true;
3481  if (bAll && (isToDisp || isFromDisp))
3482  return true;
3483 
3484  // bool isFromConv = usePFConversions_ && eTrack.trackType(T_FROM_GAMMACONV);
3485 
3486  // if ((bAll || bSecondary)&& isFromConv) return true;
3487 
3488  bool isFromDecay = (bAll || bSecondary) && usePFDecays_ && eTrack.trackType(T_FROM_V0);
3489 
3490  return isFromDecay;
3491 }
3492 
3494  //Compute met and met significance (met/sqrt(SumEt))
3495  double metX = 0.;
3496  double metY = 0.;
3497  double sumet = 0;
3498  std::vector<unsigned int> pfCandidatesToBeRemoved;
3499  for (auto const& pfc : *pfCandidates_) {
3500  metX += pfc.px();
3501  metY += pfc.py();
3502  sumet += pfc.pt();
3503  }
3504  double met2 = metX * metX + metY * metY;
3505  // Select events with large MET significance.
3506  double significance = std::sqrt(met2 / sumet);
3507  double significanceCor = significance;
3509  double metXCor = metX;
3510  double metYCor = metY;
3511  double sumetCor = sumet;
3512  double met2Cor = met2;
3513  double deltaPhi = 3.14159;
3514  double deltaPhiPt = 100.;
3515  bool next = true;
3516  unsigned iCor = 1E9;
3517 
3518  // Find the HF candidate with the largest effect on the MET
3519  while (next) {
3520  double metReduc = -1.;
3521  // Loop on the candidates
3522  for (unsigned i = 0; i < pfCandidates_->size(); ++i) {
3523  const PFCandidate& pfc = (*pfCandidates_)[i];
3524 
3525  // Check that the pfCandidate is in the HF
3527  continue;
3528 
3529  // Check if has meaningful pt
3530  if (pfc.pt() < minHFCleaningPt_)
3531  continue;
3532 
3533  // Check that it is not already scheculed to be cleaned
3534  const bool skip = std::any_of(
3535  pfCandidatesToBeRemoved.begin(), pfCandidatesToBeRemoved.end(), [&](unsigned int j) { return i == j; });
3536  if (skip)
3537  continue;
3538 
3539  // Check that the pt and the MET are aligned
3540  deltaPhi = std::acos((metX * pfc.px() + metY * pfc.py()) / (pfc.pt() * std::sqrt(met2)));
3541  deltaPhiPt = deltaPhi * pfc.pt();
3542  if (deltaPhiPt > maxDeltaPhiPt_)
3543  continue;
3544 
3545  // Now remove the candidate from the MET
3546  double metXInt = metX - pfc.px();
3547  double metYInt = metY - pfc.py();
3548  double sumetInt = sumet - pfc.pt();
3549  double met2Int = metXInt * metXInt + metYInt * metYInt;
3550  if (met2Int < met2Cor) {
3551  metXCor = metXInt;
3552  metYCor = metYInt;
3553  metReduc = (met2 - met2Int) / met2Int;
3554  met2Cor = met2Int;
3555  sumetCor = sumetInt;
3556  significanceCor = std::sqrt(met2Cor / sumetCor);
3557  iCor = i;
3558  }
3559  }
3560  //
3561  // If the MET must be significanly reduced, schedule the candidate to be cleaned
3562  if (metReduc > minDeltaMet_) {
3563  pfCandidatesToBeRemoved.push_back(iCor);
3564  metX = metXCor;
3565  metY = metYCor;
3566  sumet = sumetCor;
3567  met2 = met2Cor;
3568  } else {
3569  // Otherwise just stop the loop
3570  next = false;
3571  }
3572  }
3573  //
3574  // The significance must be significantly reduced to indeed clean the candidates
3575  if (significance - significanceCor > minSignificanceReduction_ && significanceCor < maxSignificance_) {
3576  edm::LogInfo("PFAlgo|postCleaning") << "Significance reduction = " << significance << " -> " << significanceCor
3577  << " = " << significanceCor - significance;
3578  for (unsigned int toRemove : pfCandidatesToBeRemoved) {
3579  edm::LogInfo("PFAlgo|postCleaning") << "Removed : " << (*pfCandidates_)[toRemove];
3580  pfCleanedCandidates_.push_back((*pfCandidates_)[toRemove]);
3581  (*pfCandidates_)[toRemove].rescaleMomentum(1E-6);
3582  //reco::PFCandidate::ParticleType unknown = reco::PFCandidate::X;
3583  //(*pfCandidates_)[toRemove].setParticleType(unknown);
3584  }
3585  }
3586  } //significance
3587 } //postCleaning
3588 
3590  // No hits to recover, leave.
3591  if (cleanedHits.empty())
3592  return;
3593 
3594  //Compute met and met significance (met/sqrt(SumEt))
3595  double metX = 0.;
3596  double metY = 0.;
3597  double sumet = 0;
3598  std::vector<unsigned int> hitsToBeAdded;
3599  for (auto const& pfc : *pfCandidates_) {
3600  metX += pfc.px();
3601  metY += pfc.py();
3602  sumet += pfc.pt();
3603  }
3604  double met2 = metX * metX + metY * metY;
3605  double met2_Original = met2;
3606  // Select events with large MET significance.
3607  // double significance = std::sqrt(met2/sumet);
3608  // double significanceCor = significance;
3609  double metXCor = metX;
3610  double metYCor = metY;
3611  double sumetCor = sumet;
3612  double met2Cor = met2;
3613  bool next = true;
3614  unsigned iCor = 1E9;
3615 
3616  // Find the cleaned hit with the largest effect on the MET
3617  while (next) {
3618  double metReduc = -1.;
3619  // Loop on the candidates
3620  for (unsigned i = 0; i < cleanedHits.size(); ++i) {
3621  const PFRecHit& hit = cleanedHits[i];
3622  double length = std::sqrt(hit.position().mag2());
3623  double px = hit.energy() * hit.position().x() / length;
3624  double py = hit.energy() * hit.position().y() / length;
3625  double pt = std::sqrt(px * px + py * py);
3626 
3627  // Check that it is not already scheculed to be cleaned
3628  bool skip = false;
3629  for (unsigned int hitIdx : hitsToBeAdded) {
3630  if (i == hitIdx)
3631  skip = true;
3632  if (skip)
3633  break;
3634  }
3635  if (skip)
3636  continue;
3637 
3638  // Now add the candidate to the MET
3639  double metXInt = metX + px;
3640  double metYInt = metY + py;
3641  double sumetInt = sumet + pt;
3642  double met2Int = metXInt * metXInt + metYInt * metYInt;
3643 
3644  // And check if it could contribute to a MET reduction
3645  if (met2Int < met2Cor) {
3646  metXCor = metXInt;
3647  metYCor = metYInt;
3648  metReduc = (met2 - met2Int) / met2Int;
3649  met2Cor = met2Int;
3650  sumetCor = sumetInt;
3651  // significanceCor = std::sqrt(met2Cor/sumetCor);
3652  iCor = i;
3653  }
3654  }
3655  //
3656  // If the MET must be significanly reduced, schedule the candidate to be added
3657  //
3658  if (metReduc > minDeltaMet_) {
3659  hitsToBeAdded.push_back(iCor);
3660  metX = metXCor;
3661  metY = metYCor;
3662  sumet = sumetCor;
3663  met2 = met2Cor;
3664  } else {
3665  // Otherwise just stop the loop
3666  next = false;
3667  }
3668  }
3669  //
3670  // At least 10 GeV MET reduction
3671  if (std::sqrt(met2_Original) - std::sqrt(met2) > 5.) {
3672  LogTrace("PFAlgo|checkCleaning") << hitsToBeAdded.size() << " hits were re-added ";
3673  LogTrace("PFAlgo|checkCleaning") << "MET reduction = " << std::sqrt(met2_Original) << " -> " << std::sqrt(met2Cor)
3674  << " = " << std::sqrt(met2Cor) - std::sqrt(met2_Original);
3675  LogTrace("PFAlgo|checkCleaning") << "Added after cleaning check : ";
3676  for (unsigned int hitIdx : hitsToBeAdded) {
3677  const PFRecHit& hit = cleanedHits[hitIdx];
3678  PFCluster cluster(hit.layer(), hit.energy(), hit.position().x(), hit.position().y(), hit.position().z());
3679  reconstructCluster(cluster, hit.energy());
3680  LogTrace("PFAlgo|checkCleaning") << pfCandidates_->back() << ". time = " << hit.time();
3681  }
3682  }
3683 } //PFAlgo::checkCleaning
runTheMatrix.ret
ret
prodAgent to be discontinued
Definition: runTheMatrix.py:373
PDWG_BPHSkim_cff.muons
muons
Definition: PDWG_BPHSkim_cff.py:47
reco::PFBlockElement::isTimeValid
bool isTimeValid() const
do we have a valid time information
Definition: PFBlockElement.h:128
PFAlgo::goodPixelTrackDeadHcal_maxLost3Hit_
int goodPixelTrackDeadHcal_maxLost3Hit_
Definition: PFAlgo.h:311
reco::PFBlockElement::trackType
virtual bool trackType(TrackType trType) const
Definition: PFBlockElement.h:72
reco::PFBlockElement::HO
Definition: PFBlockElement.h:42
PFEGammaFilters
Definition: PFEGammaFilters.h:16
HLT_FULL_cff.postHFCleaning
postHFCleaning
Definition: HLT_FULL_cff.py:13641
PFAlgo::nSigmaEConstHFHAD
const double nSigmaEConstHFHAD
Definition: PFAlgo.h:336
PFEGammaFilters::passElectronSelection
bool passElectronSelection(const reco::GsfElectron &, const reco::PFCandidate &, const int &) const
Definition: PFEGammaFilters.cc:119
PFAlgo::muonECAL_
std::vector< double > muonECAL_
Definition: PFAlgo.h:294
mps_fire.i
i
Definition: mps_fire.py:428
PFAlgo::calibration_
PFEnergyCalibration & calibration_
Definition: PFAlgo.h:257
PFMuonAlgo::isIsolatedMuon
static bool isIsolatedMuon(const reco::PFBlockElement &elt)
Definition: PFMuonAlgo.cc:94
PFAlgo::useEGammaFilters_
bool useEGammaFilters_
Variables for NEW EGAMMA selection.
Definition: PFAlgo.h:265
HLT_FULL_cff.track
track
Definition: HLT_FULL_cff.py:11713
PFAlgo::nSigmaHFEM
double nSigmaHFEM(double clusterEnergy) const
Definition: PFAlgo.cc:3384
l1ParticleFlow_cff.resol
resol
Definition: l1ParticleFlow_cff.py:17
PFAlgo::reconstructParticles
void reconstructParticles(const reco::PFBlockHandle &blockHandle, PFEGammaFilters const *pfegamma)
reconstruct particles
Definition: PFAlgo.cc:132
MessageLogger.h
PFAlgo::setDisplacedVerticesParameters
void setDisplacedVerticesParameters(bool rejectTracks_Bad, bool rejectTracks_Step45, bool usePFNuclearInteractions, bool usePFConversions, bool usePFDecays, double dptRel_DispVtx)
Definition: PFAlgo.cc:98
PFMuonAlgo.h
PFAlgo::pfmu_
std::unique_ptr< PFMuonAlgo > pfmu_
Definition: PFAlgo.h:262
PFAlgo::goodTrackDeadHcal_validFr_
float goodTrackDeadHcal_validFr_
Definition: PFAlgo.h:304
PFAlgo::goodPixelTrackDeadHcal_maxLost4Hit_
int goodPixelTrackDeadHcal_maxLost4Hit_
Definition: PFAlgo.h:312
hit::y
double y
Definition: SiStripHitEffFromCalibTree.cc:90
HLT_FULL_cff.postMuonCleaning
postMuonCleaning
Definition: HLT_FULL_cff.py:13638
PFAlgo::decideType
int decideType(const edm::OwnVector< reco::PFBlockElement > &elements, const reco::PFBlockElement::Type type, std::vector< bool > &active, ElementIndices &inds, std::vector< bool > &deadArea, unsigned int iEle)
Definition: PFAlgo.cc:1195
PFAlgo::goodPixelTrackDeadHcal_chi2n_
float goodPixelTrackDeadHcal_chi2n_
Definition: PFAlgo.h:310
reco::PFCandidate::e
Definition: PFCandidate.h:47
PFAlgo::nVtx_
int nVtx_
Definition: PFAlgo.h:286
PFAlgo::goodTrackDeadHcal_chi2n_
float goodTrackDeadHcal_chi2n_
Definition: PFAlgo.h:302
custom_jme_cff.nMuons
nMuons
Definition: custom_jme_cff.py:148
PFAlgo::useVertices_
bool useVertices_
Definition: PFAlgo.h:329
muon
Definition: MuonCocktails.h:17
PFAlgo::goodPixelTrackDeadHcal_dz_
float goodPixelTrackDeadHcal_dz_
Definition: PFAlgo.h:314
PFAlgo::postCleaning
void postCleaning()
Definition: PFAlgo.cc:3493
DiDispStaMuonMonitor_cfi.pt
pt
Definition: DiDispStaMuonMonitor_cfi.py:39
reco::TrackBase::p
double p() const
momentum vector magnitude
Definition: TrackBase.h:631
muons2muons_cfi.chargedHadron
chargedHadron
Definition: muons2muons_cfi.py:26
reco::PFCluster::layer
PFLayer::Layer layer() const
cluster layer, see PFLayer.h in this directory
Definition: PFCluster.cc:56
X
#define X(str)
Definition: MuonsGrabber.cc:38
PFTrackAlgoTools::step45
bool step45(const reco::TrackBase::TrackAlgorithm &)
Definition: PFTrackAlgoTools.cc:221
PFAlgo::isFromSecInt
bool isFromSecInt(const reco::PFBlockElement &eTrack, std::string order) const
Definition: PFAlgo.cc:3464
min
T min(T a, T b)
Definition: MathUtil.h:58
reco::Vertex::z
double z() const
z coordinate
Definition: Vertex.h:120
reco::PFCandidate::mva_e_pi
float mva_e_pi() const
mva for electron-pion discrimination
Definition: PFCandidate.h:317
edm::View::refAt
RefToBase< value_type > refAt(size_type i) const
PFTrackAlgoTools::step5
bool step5(const reco::TrackBase::TrackAlgorithm &)
Definition: PFTrackAlgoTools.cc:232
HLT_FULL_cff.useProtectionsForJetMET
useProtectionsForJetMET
Definition: HLT_FULL_cff.py:13734
reco::PFBlockElement::time
float time() const
Definition: PFBlockElement.h:130
PFAlgo::valueMapGedElectrons_
const edm::ValueMap< reco::GsfElectronRef > * valueMapGedElectrons_
Definition: PFAlgo.h:268
hcal
Definition: ConfigurationDatabase.cc:13
edm::Ref::isNull
bool isNull() const
Checks for null.
Definition: Ref.h:235
multPhiCorr_741_25nsDY_cfi.py
py
Definition: multPhiCorr_741_25nsDY_cfi.py:12
PFAlgo::PFAlgo
PFAlgo(double nSigmaECAL, double nSigmaHCAL, double nSigmaHFEM, double nSigmaHFHAD, std::vector< double > resolHF_square, PFEnergyCalibration &calibration, PFEnergyCalibrationHF &thepfEnergyCalibrationHF, const edm::ParameterSet &pset)
constructor
Definition: PFAlgo.cc:15
AlCaHLTBitMon_ParallelJobs.p
p
Definition: AlCaHLTBitMon_ParallelJobs.py:153
reco::PFBlock
Block of elements.
Definition: PFBlock.h:26
reco::PFBlockElement::T_TO_DISP
Definition: PFBlockElement.h:47
ElementIndices::hfHadIs
std::vector< unsigned > hfHadIs
Definition: PFAlgo.h:50
PFAlgo::rejectTracks_Step45_
bool rejectTracks_Step45_
Definition: PFAlgo.h:277
reco::VertexCollection
std::vector< Vertex > VertexCollection
collection of Vertex objects
Definition: VertexFwd.h:9
ElementIndices::ecalIs
std::vector< unsigned > ecalIs
Definition: PFAlgo.h:44
PFEnergyCalibration::energyEmHad
void energyEmHad(double t, double &e, double &h, double eta, double phi) const
Definition: PFEnergyCalibration.cc:195
HLT_FULL_cff.usePFConversions
usePFConversions
Definition: HLT_FULL_cff.py:13632
DetId::Hcal
Definition: DetId.h:28
PFAlgo::createCandidatesHCAL
void createCandidatesHCAL(const reco::PFBlock &block, reco::PFBlock::LinkData &linkData, const edm::OwnVector< reco::PFBlockElement > &elements, std::vector< bool > &active, const reco::PFBlockRef &blockref, ElementIndices &inds, std::vector< bool > &deadArea)
Definition: PFAlgo.cc:1696
PFLayer::HCAL_ENDCAP
Definition: PFLayer.h:37
reco::PFCluster::recHitFractions
const std::vector< reco::PFRecHitFraction > & recHitFractions() const
vector of rechit fractions
Definition: PFCluster.h:65
reco::PFCandidate::h
Definition: PFCandidate.h:46
PFAlgo::nSigmaEConstHFEM
const double nSigmaEConstHFEM
Definition: PFAlgo.h:335
reco::PFCandidate::h_HF
Definition: PFCandidate.h:51
XYZVector
math::XYZVector XYZVector
Definition: RawParticle.h:26
cms::cuda::assert
assert(be >=bs)
particleBasedIsoProducer_cfi.pfEgammaCandidates
pfEgammaCandidates
Definition: particleBasedIsoProducer_cfi.py:12
CustomPhysics_cfi.gamma
gamma
Definition: CustomPhysics_cfi.py:17
edm::second
U second(std::pair< T, U > const &p)
Definition: ParameterSet.cc:222
PFAlgo::dptRel_DispVtx_
double dptRel_DispVtx_
Definition: PFAlgo.h:285
HLT_FULL_cff.resolHF_square
resolHF_square
Definition: HLT_FULL_cff.py:13635
reco::TrackBase::px
double px() const
x coordinate of momentum vector
Definition: TrackBase.h:640
reco::Vertex::position
const Point & position() const
position
Definition: Vertex.h:114
reco::PFBlockElement::HCAL
Definition: PFBlockElement.h:36
PFAlgo.h
DDAxes::x
PFAlgo::pfEgammaCandidates_
const edm::View< reco::PFCandidate > * pfEgammaCandidates_
Definition: PFAlgo.h:267
reco::LeafCandidate::pt
double pt() const final
transverse momentum
Definition: LeafCandidate.h:146
PFAlgo::muonHO_
std::vector< double > muonHO_
Definition: PFAlgo.h:295
reco
fixed size matrix
Definition: AlignmentAlgorithmBase.h:45
edm::LogInfo
Log< level::Info, false > LogInfo
Definition: MessageLogger.h:125
findQualityFiles.v
v
Definition: findQualityFiles.py:179
PFAlgo::goodPixelTrackDeadHcal_ptErrRel_
float goodPixelTrackDeadHcal_ptErrRel_
Definition: PFAlgo.h:309
reco::PFCandidate::elementsInBlocks
const ElementsInBlocks & elementsInBlocks() const
Definition: PFCandidate.cc:636
createJobs.tmp
tmp
align.sh
Definition: createJobs.py:716
convertSQLiteXML.ok
bool ok
Definition: convertSQLiteXML.py:98
PFAlgo::minHFCleaningPt_
double minHFCleaningPt_
Definition: PFAlgo.h:319
edm::Handle< reco::MuonCollection >
dqmdumpme.first
first
Definition: dqmdumpme.py:55
reco::PFBlockRef
edm::Ref< PFBlockCollection > PFBlockRef
persistent reference to PFCluster objects
Definition: PFBlockFwd.h:19
edm::LogWarning
Log< level::Warning, false > LogWarning
Definition: MessageLogger.h:122
PFAlgo::createCandidatesHCALUnlinked
void createCandidatesHCALUnlinked(const reco::PFBlock &block, reco::PFBlock::LinkData &linkData, const edm::OwnVector< reco::PFBlockElement > &elements, std::vector< bool > &active, const reco::PFBlockRef &blockref, ElementIndices &inds, std::vector< bool > &deadArea)
Definition: PFAlgo.cc:2807
PFEGammaFilters::isElectronSafeForJetMET
bool isElectronSafeForJetMET(const reco::GsfElectron &, const reco::PFCandidate &, const reco::Vertex &, bool &lockTracks) const
Definition: PFEGammaFilters.cc:174
PFAlgo::nSigmaECAL_
const double nSigmaECAL_
number of sigma to judge energy excess in ECAL
Definition: PFAlgo.h:245
reco::PFCandidate::X
Definition: PFCandidate.h:45
edm::Ref< PFBlockCollection >
PFLayer::ECAL_BARREL
Definition: PFLayer.h:33
reco::PFBlockElement::Type
Type
Definition: PFBlockElement.h:30
hit::x
double x
Definition: SiStripHitEffFromCalibTree.cc:89
patCandidatesForDimuonsSequences_cff.hcal
hcal
Definition: patCandidatesForDimuonsSequences_cff.py:37
PFAlgo::nSigmaHCAL
double nSigmaHCAL(double clusterEnergy, double clusterEta) const
Definition: PFAlgo.cc:3365
reco::PFCandidate::egammaExtraRef
reco::PFCandidateEGammaExtraRef egammaExtraRef() const
return a reference to the EGamma extra
Definition: PFCandidate.cc:554
PFAlgo::minDeltaMet_
double minDeltaMet_
Definition: PFAlgo.h:324
DetId
Definition: DetId.h:17
cmsdt::algo
algo
Definition: constants.h:164
optionsL1T.skip
skip
Definition: optionsL1T.py:30
pfElectronTranslator_cfi.PFCandidate
PFCandidate
Definition: pfElectronTranslator_cfi.py:6
PFAlgo::usePFDecays_
bool usePFDecays_
Definition: PFAlgo.h:281
HLT_FULL_cff.rejectTracks_Bad
rejectTracks_Bad
Definition: HLT_FULL_cff.py:13649
ECAL
Definition: HCALResponse.h:21
RPCNoise_example.check
check
Definition: RPCNoise_example.py:71
reco::PFRecHitCollection
std::vector< PFRecHit > PFRecHitCollection
collection of PFRecHit objects
Definition: PFRecHitFwd.h:9
DivergingColor.frac
float frac
Definition: DivergingColor.py:175
PFAlgo::elementLoop
void elementLoop(const reco::PFBlock &block, reco::PFBlock::LinkData &linkData, const edm::OwnVector< reco::PFBlockElement > &elements, std::vector< bool > &active, const reco::PFBlockRef &blockref, ElementIndices &inds, std::vector< bool > &deadArea)
Definition: PFAlgo.cc:991
reco::PFCandidate::mu
Definition: PFCandidate.h:48
reco::TrackBase::py
double py() const
y coordinate of momentum vector
Definition: TrackBase.h:643
PFLayer::HF_EM
Definition: PFLayer.h:38
SiPixelRawToDigiRegional_cfi.deltaPhi
deltaPhi
Definition: SiPixelRawToDigiRegional_cfi.py:9
PFAlgo::conversionAlgo
void conversionAlgo(const edm::OwnVector< reco::PFBlockElement > &elements, std::vector< bool > &active)
Definition: PFAlgo.cc:364
Calorimetry_cff.dp
dp
Definition: Calorimetry_cff.py:157
HLT_FULL_cff.fraction
fraction
Definition: HLT_FULL_cff.py:52795
reco::LeafCandidate::py
double py() const final
y coordinate of momentum vector
Definition: LeafCandidate.h:142
PFEnergyCalibration
Definition: PFEnergyCalibration.h:42
PFAlgo::createCandidatesHF
void createCandidatesHF(const reco::PFBlock &block, reco::PFBlock::LinkData &linkData, const edm::OwnVector< reco::PFBlockElement > &elements, std::vector< bool > &active, const reco::PFBlockRef &blockref, ElementIndices &inds)
Definition: PFAlgo.cc:1253
reco::PFBlockElement::TRACK
Definition: PFBlockElement.h:32
PFAlgo::setEGammaParameters
void setEGammaParameters(bool use_EGammaFilters, bool useProtectionsForJetMET)
Definition: PFAlgo.cc:68
PVValHelper::eta
Definition: PVValidationHelpers.h:69
mathSSE::sqrt
T sqrt(T t)
Definition: SSEVec.h:19
PFLayer::HCAL_BARREL1
Definition: PFLayer.h:35
PFEnergyCalibrationHF::energyHad
double energyHad(double uncalibratedEnergyHCAL, double eta, double phi)
Definition: PFEnergyCalibrationHF.cc:63
reco::Track
Definition: Track.h:27
PFEGammaFilters::passPhotonSelection
bool passPhotonSelection(const reco::Photon &) const
Definition: PFEGammaFilters.cc:73
reco::btau::trackMomentum
Definition: TaggingVariable.h:41
PFEnergyCalibrationHF::energyEmHad
double energyEmHad(double uncalibratedEnergyECAL, double uncalibratedEnergyHCAL, double eta, double phi)
Definition: PFEnergyCalibrationHF.cc:76
reco::CaloCluster::badHcalMarker
Definition: CaloCluster.h:50
PFAlgo::checkAndReconstructSecondaryInteraction
bool checkAndReconstructSecondaryInteraction(const reco::PFBlockRef &blockref, const edm::OwnVector< reco::PFBlockElement > &elements, bool isActive, int iElement)
Definition: PFAlgo.cc:849
DQMScaleToClient_cfi.factor
factor
Definition: DQMScaleToClient_cfi.py:8
trackingPlots.other
other
Definition: trackingPlots.py:1467
HCALHighEnergyHPDFilter_cfi.energy
energy
Definition: HCALHighEnergyHPDFilter_cfi.py:5
PFAlgo::reconstructTrack
unsigned reconstructTrack(const reco::PFBlockElement &elt, bool allowLoose=false)
Definition: PFAlgo.cc:3135
PFAlgo::checkCleaning
void checkCleaning(const reco::PFRecHitCollection &cleanedHF)
Check HF Cleaning.
Definition: PFAlgo.cc:3589
HCAL
Definition: HCALResponse.h:21
DigiToRawDM_cff.HO
HO
Definition: DigiToRawDM_cff.py:23
b
double b
Definition: hdecay.h:118
PFAlgo::muonHCAL_
std::vector< double > muonHCAL_
Variables for muons and fakes.
Definition: PFAlgo.h:293
PFAlgo::minSignificanceReduction_
double minSignificanceReduction_
Definition: PFAlgo.h:322
reco::LeafCandidate::setVertex
void setVertex(const Point &vertex) override
set vertex
Definition: LeafCandidate.h:173
HLT_FULL_cff.usePFNuclearInteractions
usePFNuclearInteractions
Definition: HLT_FULL_cff.py:13727
hit::z
double z
Definition: SiStripHitEffFromCalibTree.cc:91
reco::PFCandidate::ElementsInBlocks
std::vector< ElementInBlock > ElementsInBlocks
Definition: PFCandidate.h:379
reco::PFBlockElement::T_FROM_V0
Definition: PFBlockElement.h:47
edm::View::size
size_type size() const
reco::PFCandidate::set_mva_Isolated
void set_mva_Isolated(float mvaI)
Definition: PFCandidate.h:311
PFAlgo::nSigmaHFHAD
double nSigmaHFHAD(double clusterEnergy) const
Definition: PFAlgo.cc:3389
reco::PFBlockElement::TrackType
TrackType
Definition: PFBlockElement.h:47
PFAlgo::maxDeltaPhiPt_
double maxDeltaPhiPt_
Definition: PFAlgo.h:323
PFAlgo::goodTrackDeadHcal_layers_
int goodTrackDeadHcal_layers_
Definition: PFAlgo.h:303
ALCARECOTkAlJpsiMuMu_cff.charge
charge
Definition: ALCARECOTkAlJpsiMuMu_cff.py:47
PFLayer::HF_HAD
Definition: PFLayer.h:39
reco::PFBlockElement::T_FROM_DISP
Definition: PFBlockElement.h:47
edm::View< reco::PFCandidate >
ntuplemaker.fill
fill
Definition: ntuplemaker.py:304
reco::PFCandidate::set_mva_e_pi
void set_mva_e_pi(float mvaNI)
Definition: PFCandidate.h:315
PFMuonAlgo
Definition: PFMuonAlgo.h:13
PFTrackAlgoTools.h
PFAlgo::nSigmaHFEM_
const double nSigmaHFEM_
number of sigma to judge energy excess in HF
Definition: PFAlgo.h:251
bphysicsOniaDQM_cfi.vertex
vertex
Definition: bphysicsOniaDQM_cfi.py:7
LogDebug
#define LogDebug(id)
Definition: MessageLogger.h:223
edm::ParameterSet
Definition: ParameterSet.h:47
math::XYZPoint
XYZPointD XYZPoint
point in space with cartesian internal representation
Definition: Point3D.h:12
reco::PFBlockElement::HFEM
Definition: PFBlockElement.h:39
a
double a
Definition: hdecay.h:119
RecoEcal_cff.ecalClusters
ecalClusters
Definition: RecoEcal_cff.py:26
PFAlgo::setPFVertexParameters
void setPFVertexParameters(bool useVertex, reco::VertexCollection const &primaryVertices)
Definition: PFAlgo.cc:112
Phase1L1TJetCalibrator_cfi.calibration
calibration
Definition: Phase1L1TJetCalibrator_cfi.py:2
reco::PFBlockElement::GSF
Definition: PFBlockElement.h:37
zMuMuMuonUserData.primaryVertices
primaryVertices
Definition: zMuMuMuonUserData.py:12
reco::LeafCandidate::eta
double eta() const final
momentum pseudorapidity
Definition: LeafCandidate.h:152
SiStripPI::max
Definition: SiStripPayloadInspectorHelper.h:169
PFAlgo::nSigmaHCAL_
const double nSigmaHCAL_
number of sigma to judge energy excess in HCAL
Definition: PFAlgo.h:248
reco::PFCandidate::particleId
virtual ParticleType particleId() const
Definition: PFCandidate.h:367
phase2tkutil::isPrimary
bool isPrimary(const SimTrack &simTrk, const PSimHit *simHit)
Definition: TrackerPhase2ValidationUtil.cc:2
type
type
Definition: SiPixelVCal_PayloadInspector.cc:37
reco::Vertex::x
double x() const
x coordinate
Definition: Vertex.h:116
edm::Ref::isNonnull
bool isNonnull() const
Checks for non-null.
Definition: Ref.h:238
reco::PFCandidate::T_FROM_DISP
Definition: PFCandidate.h:69
gainCalibHelper::gainCalibPI::type
type
Definition: SiPixelGainCalibHelper.h:39
PFAlgo::checkHasDeadHcal
bool checkHasDeadHcal(const std::multimap< double, unsigned > &hcalElems, const std::vector< bool > &deadArea)
Definition: PFAlgo.cc:869
reco::PFCandidate::egamma_HF
Definition: PFCandidate.h:52
HLT_FULL_cff.dptRel_DispVtx
dptRel_DispVtx
Definition: HLT_FULL_cff.py:13653
math::XYZVector
XYZVectorD XYZVector
spatial vector with cartesian internal representation
Definition: Vector3D.h:31
PFAlgo::ptError_
double ptError_
Definition: PFAlgo.h:297
reco::PFBlockElement::ECAL
Definition: PFBlockElement.h:35
PFAlgo::usePFNuclearInteractions_
bool usePFNuclearInteractions_
Definition: PFAlgo.h:279
cand
Definition: decayParser.h:32
eventshapeDQM_cfi.order
order
Definition: eventshapeDQM_cfi.py:8
PFAlgo::goodPixelTrackDeadHcal_minEta_
float goodPixelTrackDeadHcal_minEta_
Definition: PFAlgo.h:307
HLT_FULL_cff.useVertex
useVertex
Definition: HLT_FULL_cff.py:26525
PFAlgo::goodTrackDeadHcal_dxy_
float goodTrackDeadHcal_dxy_
Definition: PFAlgo.h:305
PFAlgo::checkGoodTrackDeadHcal
bool checkGoodTrackDeadHcal(const reco::TrackRef &trackRef, bool hasDeadHcal)
Definition: PFAlgo.cc:896
alignmentValidation.c1
c1
do drawing
Definition: alignmentValidation.py:1025
PFAlgo::rejectTracks_Bad_
bool rejectTracks_Bad_
Definition: PFAlgo.h:276
reco::PFBlockElement::timeError
float timeError() const
Definition: PFBlockElement.h:132
reco::PFBlockElement::T_FROM_GAMMACONV
Definition: PFBlockElement.h:47
PFAlgo::setHcalDepthInfo
void setHcalDepthInfo(reco::PFCandidate &cand, const reco::PFCluster &cluster) const
Definition: PFAlgo.cc:3325
PFAlgo::setEGammaCollections
void setEGammaCollections(const edm::View< reco::PFCandidate > &pfEgammaCandidates, const edm::ValueMap< reco::GsfElectronRef > &valueMapGedElectrons, const edm::ValueMap< reco::PhotonRef > &valueMapGedPhotons)
Definition: PFAlgo.cc:76
reco::PFCandidate::gamma
Definition: PFCandidate.h:49
photonIsolationHIProducer_cfi.ho
ho
Definition: photonIsolationHIProducer_cfi.py:10
reco::LeafCandidate::charge
int charge() const final
electric charge
Definition: LeafCandidate.h:106
HcalResponse_cfi.energyHF
energyHF
Definition: HcalResponse_cfi.py:1084
groupFilesInBlocks.block
block
Definition: groupFilesInBlocks.py:150
PFAlgo::goodTrackDeadHcal_ptErrRel_
float goodTrackDeadHcal_ptErrRel_
Variables for track cleaning in bad HCal areas.
Definition: PFAlgo.h:301
PFAlgo::createCandidatesECAL
void createCandidatesECAL(const reco::PFBlock &block, reco::PFBlock::LinkData &linkData, const edm::OwnVector< reco::PFBlockElement > &elements, std::vector< bool > &active, const reco::PFBlockRef &blockref, ElementIndices &inds, std::vector< bool > &deadArea)
Definition: PFAlgo.cc:3006
PFAlgo::maxSignificance_
double maxSignificance_
Definition: PFAlgo.h:321
HltBtagPostValidation_cff.c
c
Definition: HltBtagPostValidation_cff.py:31
reco::PFBlockElement::HFHAD
Definition: PFBlockElement.h:40
edm::LogError
Log< level::Error, false > LogError
Definition: MessageLogger.h:123
PFAlgo::primaryVertex_
reco::Vertex primaryVertex_
Definition: PFAlgo.h:328
PFAlgo::reconstructCluster
unsigned reconstructCluster(const reco::PFCluster &cluster, double particleEnergy, bool useDirection=false, double particleX=0., double particleY=0., double particleZ=0.)
Definition: PFAlgo.cc:3214
reco::PFBlock::LinkData
std::map< unsigned int, Link > LinkData
Definition: PFBlock.h:39
PFAlgo::nSigmaHFHAD_
const double nSigmaHFHAD_
Definition: PFAlgo.h:252
PFAlgo::muonHandle_
edm::Handle< reco::MuonCollection > muonHandle_
Definition: PFAlgo.h:331
PFAlgo::valueMapGedPhotons_
const edm::ValueMap< reco::PhotonRef > * valueMapGedPhotons_
Definition: PFAlgo.h:269
reco::PFBlock::LINKTEST_ALL
Definition: PFBlock.h:41
PFEGammaFilters::isElectron
bool isElectron(const reco::GsfElectron &) const
Definition: PFEGammaFilters.cc:170
PFAlgo::useProtectionsForJetMET_
bool useProtectionsForJetMET_
Definition: PFAlgo.h:266
reco::TrackBase::conversionStep
Definition: TrackBase.h:102
XYZPoint
math::XYZVector XYZPoint
Definition: CalorimetryManager.cc:69
reco::PFBlockElement
Abstract base class for a PFBlock element (track, cluster...)
Definition: PFBlockElement.h:26
PFAlgo::recoTracksNotHCAL
bool recoTracksNotHCAL(const reco::PFBlock &block, reco::PFBlock::LinkData &linkData, const edm::OwnVector< reco::PFBlockElement > &elements, const reco::PFBlockRef &blockref, std::vector< bool > &active, bool goodTrackDeadHcal, bool hasDeadHcal, unsigned int iTrack, std::multimap< double, unsigned > &ecalElems, reco::TrackRef &trackRef)
Definition: PFAlgo.cc:393
PFAlgo::setMuonHandle
void setMuonHandle(const edm::Handle< reco::MuonCollection > &)
Definition: PFAlgo.cc:86
reco::CaloCluster::position
const math::XYZPoint & position() const
cluster centroid position
Definition: CaloCluster.h:154
ElementIndices
Definition: PFAlgo.h:40
PFAlgo::pfCandidates_
std::unique_ptr< reco::PFCandidateCollection > pfCandidates_
Definition: PFAlgo.h:226
PFEnergyCalibrationHF::energyEm
double energyEm(double uncalibratedEnergyECAL, double eta, double phi)
Definition: PFEnergyCalibrationHF.cc:48
multPhiCorr_741_25nsDY_cfi.px
px
Definition: multPhiCorr_741_25nsDY_cfi.py:10
PFElectronExtraEqual.h
PFAlgo::nSigmaTRACK_
double nSigmaTRACK_
Definition: PFAlgo.h:296
reco::LeafCandidate::setP4
void setP4(const LorentzVector &p4) final
set 4-momentum
Definition: LeafCandidate.h:158
bookConverter.elements
elements
Definition: bookConverter.py:147
HLT_FULL_cff.rejectTracks_Step45
rejectTracks_Step45
Definition: HLT_FULL_cff.py:13722
std
Definition: JetResolutionObject.h:76
reco::Vertex::y
double y() const
y coordinate
Definition: Vertex.h:118
reco::LeafCandidate::phi
double phi() const final
momentum azimuthal angle
Definition: LeafCandidate.h:148
PFAlgo::factors45_
std::vector< double > factors45_
Definition: PFAlgo.h:298
qcdUeDQM_cfi.quality
quality
Definition: qcdUeDQM_cfi.py:31
PFMuonAlgo::isMuon
static bool isMuon(const reco::PFBlockElement &elt)
Definition: PFMuonAlgo.cc:48
PFAlgo::nSigmaEConstHCAL
const double nSigmaEConstHCAL
Definition: PFAlgo.h:334
PFAlgo::goodPixelTrackDeadHcal_dxy_
float goodPixelTrackDeadHcal_dxy_
Definition: PFAlgo.h:313
math::XYZTLorentzVector
XYZTLorentzVectorD XYZTLorentzVector
Lorentz vector with cylindrical internal representation using pseudorapidity.
Definition: LorentzVector.h:29
edm::ValueMap
Definition: ValueMap.h:107
PFAlgo
Definition: PFAlgo.h:53
reco::PFCandidate::T_TO_DISP
Definition: PFCandidate.h:68
relativeConstraints.empty
bool empty
Definition: relativeConstraints.py:46
PFEGammaFilters::isPhotonSafeForJetMET
bool isPhotonSafeForJetMET(const reco::Photon &, const reco::PFCandidate &) const
Definition: PFEGammaFilters.cc:328
PFAlgo::egammaFilters
void egammaFilters(const reco::PFBlockRef &blockref, std::vector< bool > &active, PFEGammaFilters const *pfegamma)
Definition: PFAlgo.cc:222
PFAlgo::processBlock
void processBlock(const reco::PFBlockRef &blockref, std::list< reco::PFBlockRef > &hcalBlockRefs, std::list< reco::PFBlockRef > &ecalBlockRefs, PFEGammaFilters const *pfegamma)
Definition: PFAlgo.cc:3055
Exception
Definition: hltDiff.cc:246
PFEnergyCalibrationHF
Definition: PFEnergyCalibrationHF.h:24
reco::PFCandidateCollection
std::vector< reco::PFCandidate > PFCandidateCollection
collection of PFCandidates
Definition: PFCandidateFwd.h:12
reco::PFCluster
Particle flow cluster, see clustering algorithm in PFClusterAlgo.
Definition: PFCluster.h:42
PFAlgo::setPostHFCleaningParameters
void setPostHFCleaningParameters(bool postHFCleaning, const edm::ParameterSet &pfHFCleaningParams)
Definition: PFAlgo.cc:88
reco::LeafCandidate::setCharge
void setCharge(Charge q) final
set electric charge
Definition: LeafCandidate.h:108
EgHLTOffHistBins_cfi.mass
mass
Definition: EgHLTOffHistBins_cfi.py:34
PFAlgo::usePFConversions_
bool usePFConversions_
Definition: PFAlgo.h:280
edm::ParameterSet::getParameter
T getParameter(std::string const &) const
Definition: ParameterSet.h:303
PFAlgo::pfCleanedCandidates_
reco::PFCandidateCollection pfCleanedCandidates_
Definition: PFAlgo.h:228
BeamSpotPI::Y
Definition: BeamSpotPayloadInspectorHelper.h:31
fftjetpileupestimator_calo_uncalib_cfi.c0
c0
Definition: fftjetpileupestimator_calo_uncalib_cfi.py:8
bsc_activity_cfg.ecal
ecal
Definition: bsc_activity_cfg.py:25
AlignmentPI::index
index
Definition: AlignmentPayloadInspectorHelper.h:46
PFAlgo::goodPixelTrackDeadHcal_maxPt_
float goodPixelTrackDeadHcal_maxPt_
Definition: PFAlgo.h:308
PFAlgo::hfEnergyResolution
double hfEnergyResolution(double clusterEnergy) const
Definition: PFAlgo.cc:3372
reco::PFRecHit
Particle flow rechit (rechit + geometry and topology information). See clustering algorithm in PFClus...
Definition: PFRecHit.h:31
reco::PFCandidate
Particle reconstructed by the particle flow algorithm.
Definition: PFCandidate.h:41
MillePedeFileConverter_cfg.out
out
Definition: MillePedeFileConverter_cfg.py:31
funct::pow
Power< A, B >::type pow(const A &a, const B &b)
Definition: Power.h:29
PFTrackAlgoTools::errorScale
double errorScale(const reco::TrackBase::TrackAlgorithm &, const std::vector< double > &)
Definition: PFTrackAlgoTools.cc:86
reco::TrackBase::pz
double pz() const
z coordinate of momentum vector
Definition: TrackBase.h:646
PFEnergyCalibrationHF::getcalibHF_use
const bool & getcalibHF_use() const
Definition: PFEnergyCalibrationHF.h:47
ElementIndices::hoIs
std::vector< unsigned > hoIs
Definition: PFAlgo.h:43
funct::abs
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
reco::PFBlockElement::PS1
Definition: PFBlockElement.h:33
LogTrace
#define LogTrace(id)
Definition: MessageLogger.h:224
ElementIndices::hfEmIs
std::vector< unsigned > hfEmIs
Definition: PFAlgo.h:49
BeamSpotPI::Z
Definition: BeamSpotPayloadInspectorHelper.h:32
PbPb_ZMuSkimMuonDPG_cff.particleType
particleType
Definition: PbPb_ZMuSkimMuonDPG_cff.py:27
reco::PFCandidate::ParticleType
ParticleType
particle types
Definition: PFCandidate.h:44
dqmiolumiharvest.j
j
Definition: dqmiolumiharvest.py:66
JetChargeProducer_cfi.exp
exp
Definition: JetChargeProducer_cfi.py:6
HLT_FULL_cff.usePFDecays
usePFDecays
Definition: HLT_FULL_cff.py:13720
operator<<
ostream & operator<<(ostream &out, const PFAlgo &algo)
Definition: PFAlgo.cc:3394
edm::HandleBase::isValid
bool isValid() const
Definition: HandleBase.h:70
PFAlgo::useHO_
bool useHO_
Definition: PFAlgo.h:260
math::XYZPointF
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< float > > XYZPointF
point in space with cartesian internal representation
Definition: Point3D.h:10
HLT_FULL_cff.flags
flags
Definition: HLT_FULL_cff.py:13150
PFLayer::ECAL_ENDCAP
Definition: PFLayer.h:32
bTagCommon_cff.ratioMax
ratioMax
Definition: bTagCommon_cff.py:34
PFAlgo::neutralHadronEnergyResolution
double neutralHadronEnergyResolution(double clusterEnergy, double clusterEta) const
todo: use PFClusterTools for this
Definition: PFAlgo.cc:3355
PFAlgo::minSignificance_
double minSignificance_
Definition: PFAlgo.h:320
ElementIndices::hcalIs
std::vector< unsigned > hcalIs
Definition: PFAlgo.h:42
reco::LeafCandidate::px
double px() const final
x coordinate of momentum vector
Definition: LeafCandidate.h:140
PFAlgo::postHFCleaning_
bool postHFCleaning_
Definition: PFAlgo.h:317
gather_cfg.blocks
blocks
Definition: gather_cfg.py:90
cuy.ii
ii
Definition: cuy.py:590
ElementIndices::trackIs
std::vector< unsigned > trackIs
Definition: PFAlgo.h:45
PFAlgo::relinkTrackToHcal
void relinkTrackToHcal(const reco::PFBlock &block, std::multimap< double, unsigned > &ecalElems, std::multimap< double, unsigned > &hcalElems, const std::vector< bool > &active, reco::PFBlock::LinkData &linkData, unsigned int iTrack)
Definition: PFAlgo.cc:935
reco::PFCandidate::setParticleType
void setParticleType(ParticleType type)
set Particle Type
Definition: PFCandidate.cc:256
PFMuonAlgo::isLooseMuon
static bool isLooseMuon(const reco::PFBlockElement &elt)
Definition: PFMuonAlgo.cc:57
PFAlgo::resolHF_square_
const std::vector< double > resolHF_square_
Definition: PFAlgo.h:255
hit
Definition: SiStripHitEffFromCalibTree.cc:88
muonDTDigis_cfi.pset
pset
Definition: muonDTDigis_cfi.py:27
reco::PFBlockElement::PS2
Definition: PFBlockElement.h:34
edm::OwnVector< reco::PFBlockElement >
met_cff.significance
significance
Definition: met_cff.py:18
PFAlgo::associatePSClusters
void associatePSClusters(unsigned iEcal, reco::PFBlockElement::Type psElementType, const reco::PFBlock &block, const edm::OwnVector< reco::PFBlockElement > &elements, const reco::PFBlock::LinkData &linkData, std::vector< bool > &active, std::vector< double > &psEne)
Associate PS clusters to a given ECAL cluster, and return their energy.
Definition: PFAlgo.cc:3419
reco::PFCandidate::set_mva_nothing_gamma
void set_mva_nothing_gamma(float mva)
set mva for gamma detection
Definition: PFCandidate.h:332
GetRecoTauVFromDQM_MC_cff.next
next
Definition: GetRecoTauVFromDQM_MC_cff.py:31
MillePedeFileConverter_cfg.e
e
Definition: MillePedeFileConverter_cfg.py:37
PFAlgo::thepfEnergyCalibrationHF_
PFEnergyCalibrationHF & thepfEnergyCalibrationHF_
Definition: PFAlgo.h:258
PFAlgo::getPFMuonAlgo
PFMuonAlgo * getPFMuonAlgo()
Definition: PFAlgo.cc:66
reco::isMuon
bool isMuon(const Candidate &part)
Definition: pdgIdUtils.h:9
reco::TrackBase::highPurity
Definition: TrackBase.h:154