CMS 3D CMS Logo

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