CMS 3D CMS Logo

PFEGammaAlgo.cc
Go to the documentation of this file.
22 
23 #include <TFile.h>
24 #include <TVector2.h>
25 #include <iomanip>
26 #include <algorithm>
27 #include <functional>
28 #include <numeric>
29 #include <TMath.h>
30 
31 // include combinations header (was never incorporated in boost)
32 #include "combination.hpp"
33 
34 // just for now do this
35 //#define PFLOW_DEBUG
36 
37 #ifdef PFLOW_DEBUG
38 #define docast(x, y) dynamic_cast<x>(y)
39 #define LOGVERB(x) edm::LogVerbatim(x)
40 #define LOGWARN(x) edm::LogWarning(x)
41 #define LOGERR(x) edm::LogError(x)
42 #define LOGDRESSED(x) edm::LogInfo(x)
43 #else
44 #define docast(x, y) reinterpret_cast<x>(y)
45 #define LOGVERB(x) LogTrace(x)
46 #define LOGWARN(x) edm::LogWarning(x)
47 #define LOGERR(x) edm::LogError(x)
48 #define LOGDRESSED(x) LogDebug(x)
49 #endif
50 
51 using namespace std;
52 using namespace reco;
53 using namespace std::placeholders; // for _1, _2, _3...
54 
55 namespace {
56  typedef PFEGammaAlgo::PFSCElement SCElement;
57  typedef PFEGammaAlgo::EEtoPSAssociation EEtoPSAssociation;
58  typedef std::pair<CaloClusterPtr::key_type, CaloClusterPtr> EEtoPSElement;
59  typedef PFEGammaAlgo::PFClusterElement ClusterElement;
60 
61  class SeedMatchesToProtoObject {
62  public:
63  SeedMatchesToProtoObject(const reco::ElectronSeedRef& s)
64  : scfromseed_(s->caloCluster().castTo<reco::SuperClusterRef>()) {
65  ispfsc_ = false;
66  if (scfromseed_.isNonnull()) {
67  const edm::Ptr<reco::PFCluster> testCast(scfromseed_->seed());
68  ispfsc_ = testCast.isNonnull();
69  }
70  }
71  bool operator()(const PFEGammaAlgo::ProtoEGObject& po) {
72  if (scfromseed_.isNull() || !po.parentSC)
73  return false;
74  if (ispfsc_) {
75  return (scfromseed_->seed() == po.parentSC->superClusterRef()->seed());
76  }
77  return (scfromseed_->seed()->seed() == po.parentSC->superClusterRef()->seed()->seed());
78  }
79 
80  private:
81  const reco::SuperClusterRef scfromseed_;
82  bool ispfsc_;
83  };
84 
85  template <bool useConvs = false>
86  bool elementNotCloserToOther(const reco::PFBlockRef& block,
87  const PFBlockElement::Type& keytype,
88  const size_t key,
89  const PFBlockElement::Type& valtype,
90  const size_t test,
91  const float EoPin_cut = 1.0e6) {
93  // this is inside out but I just want something that works right now
94  switch (keytype) {
96  const reco::PFBlockElementGsfTrack* elemasgsf =
97  docast(const reco::PFBlockElementGsfTrack*, &(block->elements()[key]));
98  if (elemasgsf && valtype == PFBlockElement::ECAL) {
99  const ClusterElement* elemasclus = reinterpret_cast<const ClusterElement*>(&(block->elements()[test]));
100  float cluster_e = elemasclus->clusterRef()->correctedEnergy();
101  float trk_pin = elemasgsf->Pin().P();
102  if (cluster_e / trk_pin > EoPin_cut) {
103  LOGDRESSED("elementNotCloserToOther") << "GSF track failed EoP cut to match with cluster!";
104  return false;
105  }
106  }
107  } break;
109  const reco::PFBlockElementTrack* elemaskf = docast(const reco::PFBlockElementTrack*, &(block->elements()[key]));
110  if (elemaskf && valtype == PFBlockElement::ECAL) {
111  const ClusterElement* elemasclus = reinterpret_cast<const ClusterElement*>(&(block->elements()[test]));
112  float cluster_e = elemasclus->clusterRef()->correctedEnergy();
113  float trk_pin = std::sqrt(elemaskf->trackRef()->innerMomentum().mag2());
114  if (cluster_e / trk_pin > EoPin_cut) {
115  LOGDRESSED("elementNotCloserToOther") << "KF track failed EoP cut to match with cluster!";
116  return false;
117  }
118  }
119  } break;
120  default:
121  break;
122  }
123 
124  const float dist = block->dist(key, test, block->linkData(), reco::PFBlock::LINKTEST_ALL);
125  if (dist == -1.0f)
126  return false; // don't associate non-linked elems
127  std::multimap<double, unsigned> dists_to_val;
128  block->associatedElements(test, block->linkData(), dists_to_val, keytype, reco::PFBlock::LINKTEST_ALL);
129 
130  for (const auto& valdist : dists_to_val) {
131  const size_t idx = valdist.second;
132  // check track types for conversion info
133  switch (keytype) {
135  const reco::PFBlockElementGsfTrack* elemasgsf =
136  docast(const reco::PFBlockElementGsfTrack*, &(block->elements()[idx]));
137  if (!useConvs && elemasgsf->trackType(ConvType))
138  return false;
139  if (elemasgsf && valtype == PFBlockElement::ECAL) {
140  const ClusterElement* elemasclus = docast(const ClusterElement*, &(block->elements()[test]));
141  float cluster_e = elemasclus->clusterRef()->correctedEnergy();
142  float trk_pin = elemasgsf->Pin().P();
143  if (cluster_e / trk_pin > EoPin_cut)
144  continue;
145  }
146  } break;
148  const reco::PFBlockElementTrack* elemaskf =
149  docast(const reco::PFBlockElementTrack*, &(block->elements()[idx]));
150  if (!useConvs && elemaskf->trackType(ConvType))
151  return false;
152  if (elemaskf && valtype == PFBlockElement::ECAL) {
153  const ClusterElement* elemasclus = reinterpret_cast<const ClusterElement*>(&(block->elements()[test]));
154  float cluster_e = elemasclus->clusterRef()->correctedEnergy();
155  float trk_pin = std::sqrt(elemaskf->trackRef()->innerMomentum().mag2());
156  if (cluster_e / trk_pin > EoPin_cut)
157  continue;
158  }
159  } break;
160  default:
161  break;
162  }
163  if (valdist.first < dist && idx != key) {
164  LOGDRESSED("elementNotCloserToOther")
165  << "key element of type " << keytype << " is closer to another element of type" << valtype << std::endl;
166  return false; // false if closer element of specified type found
167  }
168  }
169  return true;
170  }
171 
172  template <class Element1, class Element2>
173  bool compatibleEoPOut(const Element1& e, const Element2& comp) {
174  if (PFBlockElement::ECAL != e.type()) {
175  return false;
176  }
177  const ClusterElement& elemascluster = docast(ClusterElement const&, e);
178  const float gsf_eta_diff = std::abs(comp.positionAtECALEntrance().eta() - comp.Pout().eta());
179  const reco::PFClusterRef& cRef = elemascluster.clusterRef();
180  return (gsf_eta_diff <= 0.3 && cRef->energy() / comp.Pout().t() <= 5);
181  }
182 
184 
185  template <PFBlockElement::Type keytype, PFBlockElement::Type valtype, bool useConv = false>
186 
187  struct NotCloserToOther {
188  const reco::PFBlockElement* comp;
189  const reco::PFBlockRef& block;
190  const float EoPin_cut;
191  NotCloserToOther(const reco::PFBlockRef& b, const reco::PFBlockElement* e, const float EoPcut = 1.0e6)
192  : comp(e), block(b), EoPin_cut(EoPcut) {}
193  template <class T>
194  bool operator()(const T& e) {
195  if (!e.flag() || valtype != e->type())
196  return false;
197  return elementNotCloserToOther<useConv>(block, keytype, comp->index(), valtype, e->index(), EoPin_cut);
198  }
199  };
200 
201  struct LesserByDistance {
202  const reco::PFBlockElement* comp;
203  const reco::PFBlockRef& block;
205  LesserByDistance(const reco::PFBlockRef& b, const reco::PFBlock::LinkData& l, const reco::PFBlockElement* e)
206  : comp(e), block(b), links(l) {}
207  bool operator()(FlaggedPtr<const reco::PFBlockElement> const& e1,
209  double dist1 = block->dist(comp->index(), e1->index(), links, reco::PFBlock::LINKTEST_ALL);
210  double dist2 = block->dist(comp->index(), e2->index(), links, reco::PFBlock::LINKTEST_ALL);
211  dist1 = (dist1 == -1.0 ? 1e6 : dist1);
212  dist2 = (dist2 == -1.0 ? 1e6 : dist2);
213  return dist1 < dist2;
214  }
215  };
216 
217  bool isROLinkedByClusterOrTrack(const PFEGammaAlgo::ProtoEGObject& RO1, const PFEGammaAlgo::ProtoEGObject& RO2) {
218  // also don't allow ROs where both have clusters
219  // and GSF tracks to merge (10 Dec 2013)
220  if (!RO1.primaryGSFs.empty() && !RO2.primaryGSFs.empty()) {
221  LOGDRESSED("isROLinkedByClusterOrTrack") << "cannot merge, both have GSFs!" << std::endl;
222  return false;
223  }
224  // don't allow EB/EE to mix (11 Sept 2013)
225  if (!RO1.ecalclusters.empty() && !RO2.ecalclusters.empty()) {
226  if (RO1.ecalclusters.front()->clusterRef()->layer() != RO2.ecalclusters.front()->clusterRef()->layer()) {
227  LOGDRESSED("isROLinkedByClusterOrTrack") << "cannot merge, different ECAL types!" << std::endl;
228  return false;
229  }
230  }
231  const reco::PFBlockRef& blk = RO1.parentBlock;
232  bool not_closer;
233  // check links track -> cluster
234  for (const auto& cluster : RO1.ecalclusters) {
235  for (const auto& primgsf : RO2.primaryGSFs) {
236  not_closer = elementNotCloserToOther(blk, cluster->type(), cluster->index(), primgsf->type(), primgsf->index());
237  if (not_closer) {
238  LOGDRESSED("isROLinkedByClusterOrTrack") << "merged by cluster to primary GSF" << std::endl;
239  return true;
240  } else {
241  LOGDRESSED("isROLinkedByClusterOrTrack") << "cluster to primary GSF failed since"
242  << " cluster closer to another GSF" << std::endl;
243  }
244  }
245  for (const auto& primkf : RO2.primaryKFs) {
246  not_closer = elementNotCloserToOther(blk, cluster->type(), cluster->index(), primkf->type(), primkf->index());
247  if (not_closer) {
248  LOGDRESSED("isROLinkedByClusterOrTrack") << "merged by cluster to primary KF" << std::endl;
249  return true;
250  }
251  }
252  for (const auto& secdkf : RO2.secondaryKFs) {
253  not_closer = elementNotCloserToOther(blk, cluster->type(), cluster->index(), secdkf->type(), secdkf->index());
254  if (not_closer) {
255  LOGDRESSED("isROLinkedByClusterOrTrack") << "merged by cluster to secondary KF" << std::endl;
256  return true;
257  }
258  }
259  // check links brem -> cluster
260  for (const auto& brem : RO2.brems) {
261  not_closer = elementNotCloserToOther(blk, cluster->type(), cluster->index(), brem->type(), brem->index());
262  if (not_closer) {
263  LOGDRESSED("isROLinkedByClusterOrTrack") << "merged by cluster to brem KF" << std::endl;
264  return true;
265  }
266  }
267  }
268  // check links primary gsf -> secondary kf
269  for (const auto& primgsf : RO1.primaryGSFs) {
270  for (const auto& secdkf : RO2.secondaryKFs) {
271  not_closer = elementNotCloserToOther(blk, primgsf->type(), primgsf->index(), secdkf->type(), secdkf->index());
272  if (not_closer) {
273  LOGDRESSED("isROLinkedByClusterOrTrack") << "merged by GSF to secondary KF" << std::endl;
274  return true;
275  }
276  }
277  }
278  // check links primary kf -> secondary kf
279  for (const auto& primkf : RO1.primaryKFs) {
280  for (const auto& secdkf : RO2.secondaryKFs) {
281  not_closer = elementNotCloserToOther(blk, primkf->type(), primkf->index(), secdkf->type(), secdkf->index());
282  if (not_closer) {
283  LOGDRESSED("isROLinkedByClusterOrTrack") << "merged by primary KF to secondary KF" << std::endl;
284  return true;
285  }
286  }
287  }
288  // check links secondary kf -> secondary kf
289  for (const auto& secdkf1 : RO1.secondaryKFs) {
290  for (const auto& secdkf2 : RO2.secondaryKFs) {
291  not_closer =
292  elementNotCloserToOther<true>(blk, secdkf1->type(), secdkf1->index(), secdkf2->type(), secdkf2->index());
293  if (not_closer) {
294  LOGDRESSED("isROLinkedByClusterOrTrack") << "merged by secondary KF to secondary KF" << std::endl;
295  return true;
296  }
297  }
298  }
299  return false;
300  }
301 
302  bool testIfROMergableByLink(const PFEGammaAlgo::ProtoEGObject& ro, PFEGammaAlgo::ProtoEGObject& comp) {
303  const bool result = (isROLinkedByClusterOrTrack(comp, ro) || isROLinkedByClusterOrTrack(ro, comp));
304  return result;
305  }
306 
307  std::vector<const ClusterElement*> getSCAssociatedECALsSafe(
308  const reco::SuperClusterRef& scref, std::vector<FlaggedPtr<const reco::PFBlockElement>>& ecals) {
309  std::vector<const ClusterElement*> cluster_list;
310  auto sccl = scref->clustersBegin();
311  auto scend = scref->clustersEnd();
312  auto pfc = ecals.begin();
313  auto pfcend = ecals.end();
314  for (; sccl != scend; ++sccl) {
315  std::vector<const ClusterElement*> matched_pfcs;
316  const double eg_energy = (*sccl)->energy();
317 
318  for (pfc = ecals.begin(); pfc != pfcend; ++pfc) {
319  const ClusterElement* pfcel = docast(const ClusterElement*, pfc->get());
320  const bool matched = ClusterClusterMapping::overlap(**sccl, *(pfcel->clusterRef()));
321  // need to protect against high energy clusters being attached
322  // to low-energy SCs
323  if (matched && pfcel->clusterRef()->energy() < 1.2 * scref->energy()) {
324  matched_pfcs.push_back(pfcel);
325  }
326  }
327  std::sort(matched_pfcs.begin(), matched_pfcs.end());
328 
329  double min_residual = 1e6;
330  std::vector<const ClusterElement*> best_comb;
331  for (size_t i = 1; i <= matched_pfcs.size(); ++i) {
332  //now we find the combination of PF-clusters which
333  //has the smallest energy residual with respect to the
334  //EG-cluster we are looking at now
335  do {
336  double energy = std::accumulate(
337  matched_pfcs.begin(), matched_pfcs.begin() + i, 0.0, [](const double a, const ClusterElement* c) {
338  return a + c->clusterRef()->energy();
339  });
340  const double resid = std::abs(energy - eg_energy);
341  if (resid < min_residual) {
342  best_comb.clear();
343  best_comb.reserve(i);
344  min_residual = resid;
345  best_comb.insert(best_comb.begin(), matched_pfcs.begin(), matched_pfcs.begin() + i);
346  }
347  } while (notboost::next_combination(matched_pfcs.begin(), matched_pfcs.begin() + i, matched_pfcs.end()));
348  }
349  for (const auto& clelem : best_comb) {
350  if (std::find(cluster_list.begin(), cluster_list.end(), clelem) == cluster_list.end()) {
351  cluster_list.push_back(clelem);
352  }
353  }
354  }
355  return cluster_list;
356  }
357  bool addPFClusterToROSafe(const ClusterElement* cl, PFEGammaAlgo::ProtoEGObject& RO) {
358  if (RO.ecalclusters.empty()) {
359  RO.ecalclusters.emplace_back(cl, true);
360  return true;
361  } else {
362  const PFLayer::Layer clayer = cl->clusterRef()->layer();
363  const PFLayer::Layer blayer = RO.ecalclusters.back()->clusterRef()->layer();
364  if (clayer == blayer) {
365  RO.ecalclusters.emplace_back(cl, true);
366  return true;
367  }
368  }
369  return false;
370  }
371 
372  // sets the cluster best associated to the GSF track
373  // leave it null if no GSF track
374  void setROElectronCluster(PFEGammaAlgo::ProtoEGObject& RO) {
375  if (RO.ecalclusters.empty())
376  return;
377  RO.lateBrem = -1;
378  RO.firstBrem = -1;
379  RO.nBremsWithClusters = -1;
380  const reco::PFBlockElementBrem *firstBrem = nullptr, *lastBrem = nullptr;
381  const reco::PFBlockElementCluster *bremCluster = nullptr, *gsfCluster = nullptr, *kfCluster = nullptr,
382  *gsfCluster_noassc = nullptr;
383  const reco::PFBlockRef& parent = RO.parentBlock;
384  int nBremClusters = 0;
385  constexpr float maxDist = 1e6;
386  float mDist_gsf(maxDist), mDist_gsf_noassc(maxDist), mDist_kf(maxDist);
387  for (const auto& cluster : RO.ecalclusters) {
388  for (const auto& gsf : RO.primaryGSFs) {
389  const bool hasclu =
390  elementNotCloserToOther(parent, gsf->type(), gsf->index(), cluster->type(), cluster->index());
391  const float deta = std::abs(cluster->clusterRef()->positionREP().eta() - gsf->positionAtECALEntrance().eta());
392  const float dphi = std::abs(
393  TVector2::Phi_mpi_pi(cluster->clusterRef()->positionREP().phi() - gsf->positionAtECALEntrance().phi()));
394  const float dist = std::hypot(deta, dphi);
395  if (hasclu && dist < mDist_gsf) {
396  gsfCluster = cluster.get();
397  mDist_gsf = dist;
398  } else if (dist < mDist_gsf_noassc) {
399  gsfCluster_noassc = cluster.get();
400  mDist_gsf_noassc = dist;
401  }
402  }
403  for (const auto& kf : RO.primaryKFs) {
404  const bool hasclu = elementNotCloserToOther(parent, kf->type(), kf->index(), cluster->type(), cluster->index());
405  const float dist = parent->dist(cluster->index(), kf->index(), parent->linkData(), reco::PFBlock::LINKTEST_ALL);
406  if (hasclu && dist < mDist_kf) {
407  kfCluster = cluster.get();
408  mDist_kf = dist;
409  }
410  }
411  for (const auto& brem : RO.brems) {
412  const bool hasclu =
413  elementNotCloserToOther(parent, brem->type(), brem->index(), cluster->type(), cluster->index());
414  if (hasclu) {
415  ++nBremClusters;
416  if (!firstBrem || (firstBrem->indTrajPoint() - 2 > brem->indTrajPoint() - 2)) {
417  firstBrem = brem;
418  }
419  if (!lastBrem || (lastBrem->indTrajPoint() - 2 < brem->indTrajPoint() - 2)) {
420  lastBrem = brem;
421  bremCluster = cluster.get();
422  }
423  }
424  }
425  }
426  if (!gsfCluster && !kfCluster && !bremCluster) {
427  gsfCluster = gsfCluster_noassc;
428  }
429  RO.nBremsWithClusters = nBremClusters;
430  RO.lateBrem = 0;
431  if (gsfCluster) {
432  RO.electronClusters.push_back(gsfCluster);
433  } else if (kfCluster) {
434  RO.electronClusters.push_back(kfCluster);
435  }
436  if (bremCluster && !gsfCluster && !kfCluster) {
437  RO.electronClusters.push_back(bremCluster);
438  }
439  if (firstBrem && RO.ecalclusters.size() > 1) {
440  RO.firstBrem = firstBrem->indTrajPoint() - 2;
441  if (bremCluster == gsfCluster)
442  RO.lateBrem = 1;
443  }
444  }
445 } // namespace
446 
448  : gbrForests_(gbrForests), cfg_(cfg) {}
449 
451  const reco::Vertex& primaryVtx,
452  unsigned int trackIndex) {
453  const reco::PFBlock& block = *blockRef;
455  //use this to store linkdata in the associatedElements function below
456  const PFBlock::LinkData& linkData = block.linkData();
457  //calculate MVA Variables
458  const float chi2 = elements[trackIndex].trackRef()->chi2() / elements[trackIndex].trackRef()->ndof();
459  const float nlost = elements[trackIndex].trackRef()->hitPattern().numberOfLostHits(HitPattern::MISSING_INNER_HITS);
460  const float nLayers = elements[trackIndex].trackRef()->hitPattern().trackerLayersWithMeasurement();
461  const float trackPt = elements[trackIndex].trackRef()->pt();
462  const float stip = elements[trackIndex].trackRefPF()->STIP();
463 
464  float linkedE = 0;
465  float linkedH = 0;
466  std::multimap<double, unsigned int> ecalAssoTrack;
467  block.associatedElements(
468  trackIndex, linkData, ecalAssoTrack, reco::PFBlockElement::ECAL, reco::PFBlock::LINKTEST_ALL);
469  std::multimap<double, unsigned int> hcalAssoTrack;
470  block.associatedElements(
471  trackIndex, linkData, hcalAssoTrack, reco::PFBlockElement::HCAL, reco::PFBlock::LINKTEST_ALL);
472  if (!ecalAssoTrack.empty()) {
473  for (auto& itecal : ecalAssoTrack) {
474  linkedE = linkedE + elements[itecal.second].clusterRef()->energy();
475  }
476  }
477  if (!hcalAssoTrack.empty()) {
478  for (auto& ithcal : hcalAssoTrack) {
479  linkedH = linkedH + elements[ithcal.second].clusterRef()->energy();
480  }
481  }
482  const float eOverPt = linkedE / elements[trackIndex].trackRef()->pt();
483  const float hOverPt = linkedH / elements[trackIndex].trackRef()->pt();
484  GlobalVector rvtx(elements[trackIndex].trackRef()->innerPosition().X() - primaryVtx.x(),
485  elements[trackIndex].trackRef()->innerPosition().Y() - primaryVtx.y(),
486  elements[trackIndex].trackRef()->innerPosition().Z() - primaryVtx.z());
487  double vtxPhi = rvtx.phi();
488  //delta Phi between conversion vertex and track
489  float delPhi = fabs(deltaPhi(vtxPhi, elements[trackIndex].trackRef()->innerMomentum().Phi()));
490 
491  float vars[] = {delPhi, nLayers, chi2, eOverPt, hOverPt, trackPt, stip, nlost};
492 
493  return gbrForests_.singleLeg_->GetAdaBoostClassifier(vars);
494 }
495 
497  switch (pfbe.type()) {
499  auto& elements = _currentblock->elements();
500  std::multimap<double, unsigned> tks;
501  _currentblock->associatedElements(
503  for (const auto& tk : tks) {
504  if (PFMuonAlgo::isMuon(elements[tk.second])) {
505  return true;
506  }
507  }
508  } break;
510  return PFMuonAlgo::isMuon(pfbe);
511  break;
512  default:
513  break;
514  }
515  return false;
516 }
517 
519  LOGVERB("PFEGammaAlgo") << "Resetting PFEGammaAlgo for new block and running!" << std::endl;
520 
521  // candidate collections:
522  // this starts off as an inclusive list of prototype objects built from
523  // supercluster/ecal-driven seeds and tracker driven seeds in a block
524  // it is then refined through by various cleanings, determining the energy
525  // flow.
526  // use list for constant-time removals
527  std::list<ProtoEGObject> refinableObjects;
528 
529  _splayedblock.clear();
530  _splayedblock.resize(13); // make sure that we always have the HGCAL entry
531 
533  _currentlinks = block->linkData();
534  //LOGDRESSED("PFEGammaAlgo") << *_currentblock << std::endl;
535  LOGVERB("PFEGammaAlgo") << "Splaying block" << std::endl;
536  //unwrap the PF block into a fast access map
537  for (const auto& pfelement : _currentblock->elements()) {
538  if (isMuon(pfelement))
539  continue; // don't allow muons in our element list
540  if (pfelement.type() == PFBlockElement::HCAL && pfelement.clusterRef()->flags() & reco::CaloCluster::badHcalMarker)
541  continue; // skip also dead area markers for now
542  const size_t itype = (size_t)pfelement.type();
543  if (itype >= _splayedblock.size())
544  _splayedblock.resize(itype + 1);
545  _splayedblock[itype].emplace_back(&pfelement, true);
546  }
547 
548  // show the result of splaying the tree if it's really *really* needed
549 #ifdef PFLOW_DEBUG
550  std::stringstream splayout;
551  for (size_t itype = 0; itype < _splayedblock.size(); ++itype) {
552  splayout << "\tType: " << itype << " indices: ";
553  for (const auto& flaggedelement : _splayedblock[itype]) {
554  splayout << flaggedelement->index() << ' ';
555  }
556  if (itype != _splayedblock.size() - 1)
557  splayout << std::endl;
558  }
559  LOGVERB("PFEGammaAlgo") << splayout.str();
560 #endif
561 
562  // precleaning of the ECAL clusters with respect to primary KF tracks
563  // we don't allow clusters in super clusters to be locked out this way
565 
566  initializeProtoCands(refinableObjects);
567  LOGDRESSED("PFEGammaAlgo") << "Initialized " << refinableObjects.size() << " proto-EGamma objects" << std::endl;
569 
570  //
571  // now we start the refining steps
572  //
573  //
574 
575  // --- Primary Linking Step ---
576  // since this is particle flow and we try to work from the pixels out
577  // we start by linking the tracks together and finding the ECAL clusters
578  for (auto& RO : refinableObjects) {
579  // find the KF tracks associated to GSF primary tracks
581  // do the same for HCAL clusters associated to the GSF
583  // link secondary KF tracks associated to primary KF tracks
585  // pick up clusters that are linked to the GSF primary
587  // link associated KF to ECAL (ECAL part grabs PS clusters too if able)
589  // now finally look for clusters associated to brem tangents
591  }
592 
593  LOGDRESSED("PFEGammaAlgo") << "Dumping after GSF and KF Track (Primary) Linking : " << std::endl;
595 
596  // merge objects after primary linking
597  mergeROsByAnyLink(refinableObjects);
598 
599  LOGDRESSED("PFEGammaAlgo") << "Dumping after first merging operation : " << std::endl;
601 
602  // --- Secondary Linking Step ---
603  // after this we go through the ECAL clusters on the remaining tracks
604  // and try to link those in...
605  for (auto& RO : refinableObjects) {
606  // look for conversion legs
609  // look for tracks that complement conversion legs
611  // look again for ECAL clusters (this time with an e/p cut)
613  }
614 
615  LOGDRESSED("PFEGammaAlgo") << "Dumping after ECAL to Track (Secondary) Linking : " << std::endl;
617 
618  // merge objects after primary linking
619  mergeROsByAnyLink(refinableObjects);
620 
621  LOGDRESSED("PFEGammaAlgo") << "There are " << refinableObjects.size() << " after the 2nd merging step." << std::endl;
623 
624  // -- unlinking and proto-object vetos, final sorting
625  for (auto& RO : refinableObjects) {
626  // remove secondary KFs (and possibly ECALs) matched to HCAL clusters
628  // remove secondary KFs and ECALs linked to them that have bad E/p_in
629  // and spoil the resolution
631  // put things back in order after partitioning
632  std::sort(RO.ecalclusters.begin(), RO.ecalclusters.end(), [](auto const& a, auto const& b) {
633  return (a->clusterRef()->correctedEnergy() > b->clusterRef()->correctedEnergy());
634  });
635  setROElectronCluster(RO);
636  }
637 
638  LOGDRESSED("PFEGammaAlgo") << "There are " << refinableObjects.size() << " after the unlinking and vetos step."
639  << std::endl;
641 
642  // fill the PF candidates and then build the refined SC
643  return fillPFCandidates(refinableObjects);
644 }
645 
646 void PFEGammaAlgo::initializeProtoCands(std::list<PFEGammaAlgo::ProtoEGObject>& egobjs) {
647  // step 1: build SC based proto-candidates
648  // in the future there will be an SC Et requirement made here to control
649  // block size
650  for (auto& element : _splayedblock[PFBlockElement::SC]) {
651  LOGDRESSED("PFEGammaAlgo") << "creating SC-based proto-object" << std::endl
652  << "\tSC at index: " << element->index() << " has type: " << element->type()
653  << std::endl;
654  element.setFlag(false);
655  ProtoEGObject fromSC;
656  fromSC.nBremsWithClusters = -1;
657  fromSC.firstBrem = -1;
658  fromSC.lateBrem = -1;
659  fromSC.parentBlock = _currentblock;
660  fromSC.parentSC = docast(const PFSCElement*, element.get());
661  // splay the supercluster so we can knock out used elements
662  bool sc_success = unwrapSuperCluster(fromSC.parentSC, fromSC.ecalclusters, fromSC.ecal2ps);
663  if (sc_success) {
664  /*
665  auto ins_pos = std::lower_bound(refinableObjects.begin(),
666  refinableObjects.end(),
667  fromSC,
668  [&](const ProtoEGObject& a,
669  const ProtoEGObject& b){
670  const double a_en =
671  a.parentSC->superClusterRef()->energy();
672  const double b_en =
673  b.parentSC->superClusterRef()->energy();
674  return a_en < b_en;
675  });
676  */
677  egobjs.insert(egobjs.end(), fromSC);
678  }
679  }
680  // step 2: build GSF-seed-based proto-candidates
681  reco::GsfTrackRef gsfref_forextra;
682  reco::TrackExtraRef gsftrk_extra;
683  reco::ElectronSeedRef theseedref;
684  for (auto& element : _splayedblock[PFBlockElement::GSF]) {
685  LOGDRESSED("PFEGammaAlgo") << "creating GSF-based proto-object" << std::endl
686  << "\tGSF at index: " << element->index() << " has type: " << element->type()
687  << std::endl;
688  const PFGSFElement* elementAsGSF = docast(const PFGSFElement*, element.get());
690  continue; // for now, do not allow dedicated brems to make proto-objects
691  }
692  element.setFlag(false);
693 
694  ProtoEGObject fromGSF;
695  fromGSF.nBremsWithClusters = -1;
696  fromGSF.firstBrem = -1;
697  fromGSF.lateBrem = 0;
698  gsfref_forextra = elementAsGSF->GsftrackRef();
699  gsftrk_extra = (gsfref_forextra.isAvailable() ? gsfref_forextra->extra() : reco::TrackExtraRef());
700  theseedref = (gsftrk_extra.isAvailable() ? gsftrk_extra->seedRef().castTo<reco::ElectronSeedRef>()
702  fromGSF.electronSeed = theseedref;
703  // exception if there's no seed
704  if (fromGSF.electronSeed.isNull() || !fromGSF.electronSeed.isAvailable()) {
705  std::stringstream gsf_err;
706  elementAsGSF->Dump(gsf_err, "\t");
707  throw cms::Exception("PFEGammaAlgo::initializeProtoCands()")
708  << "Found a GSF track with no seed! This should not happen!" << std::endl
709  << gsf_err.str() << std::endl;
710  }
711  // flag this GSF element as globally used and push back the track ref
712  // into the protocand
713  element.setFlag(false);
714  fromGSF.parentBlock = _currentblock;
715  fromGSF.primaryGSFs.push_back(elementAsGSF);
716  // add the directly matched brem tangents
717  for (auto& brem : _splayedblock[PFBlockElement::BREM]) {
718  float dist =
719  _currentblock->dist(elementAsGSF->index(), brem->index(), _currentlinks, reco::PFBlock::LINKTEST_ALL);
720  if (dist == 0.001f) {
721  const PFBremElement* eAsBrem = docast(const PFBremElement*, brem.get());
722  fromGSF.brems.push_back(eAsBrem);
723  fromGSF.localMap.insert(eAsBrem, elementAsGSF);
724  brem.setFlag(false);
725  }
726  }
727  // if this track is ECAL seeded reset links or import cluster
728  // tracker (this is pixel only, right?) driven seeds just get the GSF
729  // track associated since this only branches for ECAL Driven seeds
730  if (fromGSF.electronSeed->isEcalDriven()) {
731  // step 2a: either merge with existing ProtoEG object with SC or add
732  // SC directly to this proto EG object if not present
733  LOGDRESSED("PFEGammaAlgo") << "GSF-based proto-object is ECAL driven, merging SC-cand" << std::endl;
734  LOGVERB("PFEGammaAlgo") << "ECAL Seed Ptr: " << fromGSF.electronSeed.get()
735  << " isAvailable: " << fromGSF.electronSeed.isAvailable()
736  << " isNonnull: " << fromGSF.electronSeed.isNonnull() << std::endl;
737  SeedMatchesToProtoObject sctoseedmatch(fromGSF.electronSeed);
738  auto objsbegin = egobjs.begin();
739  auto objsend = egobjs.end();
740  // this auto is a std::list<ProtoEGObject>::iterator
741  auto clusmatch = std::find_if(objsbegin, objsend, sctoseedmatch);
742  if (clusmatch != objsend) {
743  fromGSF.parentSC = clusmatch->parentSC;
744  fromGSF.ecalclusters = std::move(clusmatch->ecalclusters);
745  fromGSF.ecal2ps = std::move(clusmatch->ecal2ps);
746  egobjs.erase(clusmatch);
747  } else if (fromGSF.electronSeed.isAvailable() && fromGSF.electronSeed.isNonnull()) {
748  // link tests in the gap region can current split a gap electron
749  // HEY THIS IS A WORK AROUND FOR A KNOWN BUG IN PFBLOCKALGO
750  // MAYBE WE SHOULD FIX IT??????????????????????????????????
751  LOGDRESSED("PFEGammaAlgo") << "Encountered the known GSF-SC splitting bug "
752  << " in PFBlockAlgo! We should really fix this!" << std::endl;
753  } else { // SC was not in a earlier proto-object
754  std::stringstream gsf_err;
755  elementAsGSF->Dump(gsf_err, "\t");
756  throw cms::Exception("PFEGammaAlgo::initializeProtoCands()")
757  << "Expected SuperCluster from ECAL driven GSF seed "
758  << "was not found in the block!" << std::endl
759  << gsf_err.str() << std::endl;
760  } // supercluster in block
761  } // is ECAL driven seed?
762 
763  egobjs.insert(egobjs.end(), fromGSF);
764  } // end loop on GSF elements of block
765 }
766 
768  std::vector<FlaggedPtr<const PFClusterElement>>& ecalclusters,
769  ClusterMap& ecal2ps) {
770  ecalclusters.clear();
771  ecal2ps.clear();
772  LOGVERB("PFEGammaAlgo") << "Pointer to SC element: 0x" << std::hex << thesc << std::dec << std::endl
773  << "cleared ecalclusters and ecal2ps!" << std::endl;
774  auto ecalbegin = _splayedblock[reco::PFBlockElement::ECAL].begin();
775  auto ecalend = _splayedblock[reco::PFBlockElement::ECAL].end();
776  auto hgcalbegin = _splayedblock[reco::PFBlockElement::HGCAL].begin();
777  auto hgcalend = _splayedblock[reco::PFBlockElement::HGCAL].end();
778  if (ecalbegin == ecalend && hgcalbegin == hgcalend) {
779  LOGERR("PFEGammaAlgo::unwrapSuperCluster()") << "There are no ECAL elements in a block with imported SC!"
780  << " This is a bug we should fix this!" << std::endl;
781  return false;
782  }
783  reco::SuperClusterRef scref = thesc->superClusterRef();
784  const bool is_pf_sc = thesc->fromPFSuperCluster();
785  if (!(scref.isAvailable() && scref.isNonnull())) {
786  throw cms::Exception("PFEGammaAlgo::unwrapSuperCluster()")
787  << "SuperCluster pointed to by block element is null!" << std::endl;
788  }
789  LOGDRESSED("PFEGammaAlgo") << "Got a valid super cluster ref! 0x" << std::hex << scref.get() << std::dec << std::endl;
790  const size_t nscclusters = scref->clustersSize();
791  const size_t nscpsclusters = scref->preshowerClustersSize();
792  size_t npfpsclusters = 0;
793  size_t npfclusters = 0;
794  LOGDRESSED("PFEGammaAlgo") << "Precalculated cluster multiplicities: " << nscclusters << ' ' << nscpsclusters
795  << std::endl;
796  NotCloserToOther<reco::PFBlockElement::SC, reco::PFBlockElement::ECAL> ecalClustersInSC(_currentblock, thesc);
797  NotCloserToOther<reco::PFBlockElement::SC, reco::PFBlockElement::HGCAL> hgcalClustersInSC(_currentblock, thesc);
798  auto ecalfirstnotinsc = std::partition(ecalbegin, ecalend, ecalClustersInSC);
799  auto hgcalfirstnotinsc = std::partition(hgcalbegin, hgcalend, hgcalClustersInSC);
800  //reset the begin and end iterators
801  ecalbegin = _splayedblock[reco::PFBlockElement::ECAL].begin();
802  ecalend = _splayedblock[reco::PFBlockElement::ECAL].end();
803 
804  hgcalbegin = _splayedblock[reco::PFBlockElement::HGCAL].begin();
805  hgcalend = _splayedblock[reco::PFBlockElement::HGCAL].end();
806 
807  //get list of associated clusters by det id and energy matching
808  //(only needed when using non-pf supercluster)
809  std::vector<const ClusterElement*> safePFClusters =
810  is_pf_sc ? std::vector<const ClusterElement*>()
811  : getSCAssociatedECALsSafe(scref, _splayedblock[reco::PFBlockElement::ECAL]);
812 
813  if (ecalfirstnotinsc == ecalbegin && hgcalfirstnotinsc == hgcalbegin) {
814  LOGERR("PFEGammaAlgo::unwrapSuperCluster()") << "No associated block elements to SuperCluster!"
815  << " This is a bug we should fix!" << std::endl;
816  return false;
817  }
818  npfclusters = std::distance(ecalbegin, ecalfirstnotinsc) + std::distance(hgcalbegin, hgcalfirstnotinsc);
819  // ensure we have found the correct number of PF ecal clusters in the case
820  // that this is a PF supercluster, otherwise all bets are off
821  if (is_pf_sc && nscclusters != npfclusters) {
822  std::stringstream sc_err;
823  thesc->Dump(sc_err, "\t");
824  throw cms::Exception("PFEGammaAlgo::unwrapSuperCluster()")
825  << "The number of found ecal elements (" << nscclusters << ") in block is not the same as"
826  << " the number of ecal PF clusters reported by the PFSuperCluster"
827  << " itself (" << npfclusters << ")! This should not happen!" << std::endl
828  << sc_err.str() << std::endl;
829  }
830  for (auto ecalitr = ecalbegin; ecalitr != ecalfirstnotinsc; ++ecalitr) {
831  const PFClusterElement* elemascluster = docast(const PFClusterElement*, ecalitr->get());
832 
833  // reject clusters that really shouldn't be associated to the SC
834  // (only needed when using non-pf-supercluster)
835  if (!is_pf_sc && std::find(safePFClusters.begin(), safePFClusters.end(), elemascluster) == safePFClusters.end())
836  continue;
837 
838  //add cluster
839  ecalclusters.emplace_back(elemascluster, true);
840  //mark cluster as used
841  ecalitr->setFlag(false);
842 
843  // process the ES elements
844  // auto is a pair<Iterator,bool> here, bool is false when placing fails
845  auto emplaceresult = ecal2ps.emplace(elemascluster, ClusterMap::mapped_type());
846  if (!emplaceresult.second) {
847  std::stringstream clus_err;
848  elemascluster->Dump(clus_err, "\t");
849  throw cms::Exception("PFEGammaAlgo::unwrapSuperCluster()")
850  << "List of pointers to ECAL block elements contains non-unique items!"
851  << " This is very bad!" << std::endl
852  << "cluster ptr = 0x" << std::hex << elemascluster << std::dec << std::endl
853  << clus_err.str() << std::endl;
854  }
855  ClusterMap::mapped_type& eslist = emplaceresult.first->second;
856  npfpsclusters += attachPSClusters(elemascluster, eslist);
857  } // loop over ecal elements
858 
859  for (auto hgcalitr = hgcalbegin; hgcalitr != hgcalfirstnotinsc; ++hgcalitr) {
860  const PFClusterElement* elemascluster = docast(const PFClusterElement*, hgcalitr->get());
861 
862  // reject clusters that really shouldn't be associated to the SC
863  // (only needed when using non-pf-supercluster)
864  if (!is_pf_sc && std::find(safePFClusters.begin(), safePFClusters.end(), elemascluster) == safePFClusters.end())
865  continue;
866 
867  //add cluster
868  ecalclusters.emplace_back(elemascluster, true);
869  //mark cluster as used
870  hgcalitr->setFlag(false);
871  } // loop over ecal elements
872 
873  /*
874  if( is_pf_sc && nscpsclusters != npfpsclusters) {
875  std::stringstream sc_err;
876  thesc->Dump(sc_err,"\t");
877  throw cms::Exception("PFEGammaAlgo::unwrapSuperCluster()")
878  << "The number of found PF preshower elements ("
879  << npfpsclusters << ") in block is not the same as"
880  << " the number of preshower clusters reported by the PFSuperCluster"
881  << " itself (" << nscpsclusters << ")! This should not happen!"
882  << std::endl
883  << sc_err.str() << std::endl;
884  }
885  */
886 
887  LOGDRESSED("PFEGammaAlgo") << " Unwrapped SC has " << npfclusters << " ECAL sub-clusters"
888  << " and " << npfpsclusters << " PreShower layers 1 & 2 clusters!" << std::endl;
889  return true;
890 }
891 
892 int PFEGammaAlgo::attachPSClusters(const ClusterElement* ecalclus, ClusterMap::mapped_type& eslist) {
893  if (ecalclus->clusterRef()->layer() == PFLayer::ECAL_BARREL)
894  return 0;
895  edm::Ptr<reco::PFCluster> clusptr = refToPtr(ecalclus->clusterRef());
896  EEtoPSElement ecalkey(clusptr.key(), clusptr);
897  auto assc_ps =
898  std::equal_range(eetops_->cbegin(), eetops_->cend(), ecalkey, [](const EEtoPSElement& a, const EEtoPSElement& b) {
899  return a.first < b.first;
900  });
901  for (const auto& ps1 : _splayedblock[reco::PFBlockElement::PS1]) {
902  edm::Ptr<reco::PFCluster> temp = refToPtr(ps1->clusterRef());
903  for (auto pscl = assc_ps.first; pscl != assc_ps.second; ++pscl) {
904  if (pscl->second == temp) {
905  const ClusterElement* pstemp = docast(const ClusterElement*, ps1.get());
906  eslist.emplace_back(pstemp);
907  }
908  }
909  }
910  for (const auto& ps2 : _splayedblock[reco::PFBlockElement::PS2]) {
911  edm::Ptr<reco::PFCluster> temp = refToPtr(ps2->clusterRef());
912  for (auto pscl = assc_ps.first; pscl != assc_ps.second; ++pscl) {
913  if (pscl->second == temp) {
914  const ClusterElement* pstemp = docast(const ClusterElement*, ps2.get());
915  eslist.emplace_back(pstemp);
916  }
917  }
918  }
919  return eslist.size();
920 }
921 
923 #ifdef PFLOW_DEBUG
924  edm::LogVerbatim("PFEGammaAlgo")
925  //<< "Dumping current block: " << std::endl << *_currentblock << std::endl
926  << "Dumping " << refinableObjects.size() << " refinable objects for this block: " << std::endl;
927  for (const auto& ro : refinableObjects) {
928  std::stringstream info;
929  info << "Refinable Object:" << std::endl;
930  if (ro.parentSC) {
931  info << "\tSuperCluster element attached to object:" << std::endl << '\t';
932  ro.parentSC->Dump(info, "\t");
933  info << std::endl;
934  }
935  if (ro.electronSeed.isNonnull()) {
936  info << "\tGSF element attached to object:" << std::endl;
937  ro.primaryGSFs.front()->Dump(info, "\t");
938  info << std::endl;
939  info << "firstBrem : " << ro.firstBrem << " lateBrem : " << ro.lateBrem
940  << " nBrems with cluster : " << ro.nBremsWithClusters << std::endl;
941  ;
942  if (ro.electronClusters.size() && ro.electronClusters[0]) {
943  info << "electron cluster : ";
944  ro.electronClusters[0]->Dump(info, "\t");
945  info << std::endl;
946  } else {
947  info << " no electron cluster." << std::endl;
948  }
949  }
950  if (ro.primaryKFs.size()) {
951  info << "\tPrimary KF tracks attached to object: " << std::endl;
952  for (const auto& kf : ro.primaryKFs) {
953  kf->Dump(info, "\t");
954  info << std::endl;
955  }
956  }
957  if (ro.secondaryKFs.size()) {
958  info << "\tSecondary KF tracks attached to object: " << std::endl;
959  for (const auto& kf : ro.secondaryKFs) {
960  kf->Dump(info, "\t");
961  info << std::endl;
962  }
963  }
964  if (ro.brems.size()) {
965  info << "\tBrem tangents attached to object: " << std::endl;
966  for (const auto& brem : ro.brems) {
967  brem->Dump(info, "\t");
968  info << std::endl;
969  }
970  }
971  if (ro.ecalclusters.size()) {
972  info << "\tECAL clusters attached to object: " << std::endl;
973  for (const auto& clus : ro.ecalclusters) {
974  clus->Dump(info, "\t");
975  info << std::endl;
976  if (ro.ecal2ps.find(clus) != ro.ecal2ps.end()) {
977  for (const auto& psclus : ro.ecal2ps.at(clus)) {
978  info << "\t\t Attached PS Cluster: ";
979  psclus->Dump(info, "");
980  info << std::endl;
981  }
982  }
983  }
984  }
985  edm::LogVerbatim("PFEGammaAlgo") << info.str();
986  }
987 #endif
988 }
989 
990 // look through our KF tracks in this block and match
992  typedef std::multimap<double, unsigned> MatchedMap;
993  typedef const reco::PFBlockElementGsfTrack* GsfTrackElementPtr;
995  return;
996  MatchedMap matchedGSFs, matchedECALs;
997  std::unordered_map<GsfTrackElementPtr, MatchedMap> gsf_ecal_cache;
998  for (auto& kftrack : _splayedblock[reco::PFBlockElement::TRACK]) {
999  matchedGSFs.clear();
1000  _currentblock->associatedElements(
1001  kftrack->index(), _currentlinks, matchedGSFs, reco::PFBlockElement::GSF, reco::PFBlock::LINKTEST_ALL);
1002  if (matchedGSFs.empty()) { // only run this if we aren't associated to GSF
1003  LesserByDistance closestTrackToECAL(_currentblock, _currentlinks, kftrack.get());
1004  auto ecalbegin = _splayedblock[reco::PFBlockElement::ECAL].begin();
1005  auto ecalend = _splayedblock[reco::PFBlockElement::ECAL].end();
1006  std::partial_sort(ecalbegin, ecalbegin + 1, ecalend, closestTrackToECAL);
1007  auto& closestECAL = _splayedblock[reco::PFBlockElement::ECAL].front();
1008  const float dist =
1009  _currentblock->dist(kftrack->index(), closestECAL->index(), _currentlinks, reco::PFBlock::LINKTEST_ALL);
1010  bool inSC = false;
1011  for (auto& sc : _splayedblock[reco::PFBlockElement::SC]) {
1012  float dist_sc =
1013  _currentblock->dist(sc->index(), closestECAL->index(), _currentlinks, reco::PFBlock::LINKTEST_ALL);
1014  if (dist_sc != -1.0f) {
1015  inSC = true;
1016  break;
1017  }
1018  }
1019 
1020  if (dist != -1.0f && closestECAL.flag()) {
1021  bool gsflinked = false;
1022  // check that this cluster is not associated to a GSF track
1023  for (const auto& gsfflag : _splayedblock[reco::PFBlockElement::GSF]) {
1024  const reco::PFBlockElementGsfTrack* elemasgsf = docast(const reco::PFBlockElementGsfTrack*, gsfflag.get());
1026  continue; // keep clusters that have a found conversion GSF near
1027  }
1028  // make sure cache exists
1029  if (!gsf_ecal_cache.count(elemasgsf)) {
1030  matchedECALs.clear();
1031  _currentblock->associatedElements(elemasgsf->index(),
1032  _currentlinks,
1033  matchedECALs,
1036  gsf_ecal_cache.emplace(elemasgsf, matchedECALs);
1037  MatchedMap().swap(matchedECALs);
1038  }
1039  const MatchedMap& ecal_matches = gsf_ecal_cache[elemasgsf];
1040  if (!ecal_matches.empty()) {
1041  if (ecal_matches.begin()->second == closestECAL->index()) {
1042  gsflinked = true;
1043  break;
1044  }
1045  }
1046  } // loop over primary GSF tracks
1047  if (!gsflinked && !inSC) {
1048  // determine if we should remove the matched cluster
1049  const reco::PFBlockElementTrack* kfEle = docast(const reco::PFBlockElementTrack*, kftrack.get());
1050  const reco::TrackRef& trackref = kfEle->trackRef();
1051 
1052  const int nexhits = trackref->hitPattern().numberOfLostHits(HitPattern::MISSING_INNER_HITS);
1053  bool fromprimaryvertex = false;
1054  for (auto vtxtks = primaryVertex_->tracks_begin(); vtxtks != primaryVertex_->tracks_end(); ++vtxtks) {
1055  if (trackref == vtxtks->castTo<reco::TrackRef>()) {
1056  fromprimaryvertex = true;
1057  break;
1058  }
1059  } // loop over tracks in primary vertex
1060  // if associated to good non-GSF matched track remove this cluster
1061  if (PFTrackAlgoTools::isGoodForEGMPrimary(trackref->algo()) && nexhits == 0 && fromprimaryvertex) {
1062  closestECAL.setFlag(false);
1063  }
1064  }
1065  } // found a good closest ECAL match
1066  } // no GSF track matched to KF
1067  } // loop over KF elements
1068 }
1069 
1070 void PFEGammaAlgo::mergeROsByAnyLink(std::list<PFEGammaAlgo::ProtoEGObject>& ROs) {
1071  if (ROs.size() < 2)
1072  return; // nothing to do with one or zero ROs
1073  bool check_for_merge = true;
1074  while (check_for_merge) {
1075  // bugfix for early termination merging loop (15 April 2014)
1076  // check all pairwise combinations in the list
1077  // if one has a merge shuffle it to the front of the list
1078  // if there are no merges left to do we can terminate
1079  for (auto it1 = ROs.begin(); it1 != ROs.end(); ++it1) {
1080  auto find_start = it1;
1081  ++find_start;
1082  auto has_merge = std::find_if(find_start, ROs.end(), std::bind(testIfROMergableByLink, _1, *it1));
1083  if (has_merge != ROs.end() && it1 != ROs.begin()) {
1084  std::swap(*(ROs.begin()), *it1);
1085  break;
1086  }
1087  } // ensure mergables are shuffled to the front
1088  ProtoEGObject& thefront = ROs.front();
1089  auto mergestart = ROs.begin();
1090  ++mergestart;
1091  auto nomerge = std::partition(mergestart, ROs.end(), std::bind(testIfROMergableByLink, _1, thefront));
1092  if (nomerge != mergestart) {
1093  LOGDRESSED("PFEGammaAlgo::mergeROsByAnyLink()")
1094  << "Found objects " << std::distance(mergestart, nomerge) << " to merge by links to the front!" << std::endl;
1095  for (auto roToMerge = mergestart; roToMerge != nomerge; ++roToMerge) {
1096  //bugfix! L.Gray 14 Jan 2016
1097  // -- check that the front is still mergeable!
1098  if (!thefront.ecalclusters.empty() && !roToMerge->ecalclusters.empty()) {
1099  if (thefront.ecalclusters.front()->clusterRef()->layer() !=
1100  roToMerge->ecalclusters.front()->clusterRef()->layer()) {
1101  LOGWARN("PFEGammaAlgo::mergeROsByAnyLink") << "Tried to merge EB and EE clusters! Skipping!";
1102  ROs.push_back(*roToMerge);
1103  continue;
1104  }
1105  }
1106  //end bugfix
1107  thefront.ecalclusters.insert(
1108  thefront.ecalclusters.end(), roToMerge->ecalclusters.begin(), roToMerge->ecalclusters.end());
1109  thefront.ecal2ps.insert(roToMerge->ecal2ps.begin(), roToMerge->ecal2ps.end());
1110  thefront.secondaryKFs.insert(
1111  thefront.secondaryKFs.end(), roToMerge->secondaryKFs.begin(), roToMerge->secondaryKFs.end());
1112 
1113  thefront.localMap.concatenate(roToMerge->localMap);
1114  // TO FIX -> use best (E_gsf - E_clustersum)/E_GSF
1115  if (!thefront.parentSC && roToMerge->parentSC) {
1116  thefront.parentSC = roToMerge->parentSC;
1117  }
1118  if (thefront.electronSeed.isNull() && roToMerge->electronSeed.isNonnull()) {
1119  thefront.electronSeed = roToMerge->electronSeed;
1120  thefront.primaryGSFs.insert(
1121  thefront.primaryGSFs.end(), roToMerge->primaryGSFs.begin(), roToMerge->primaryGSFs.end());
1122  thefront.primaryKFs.insert(
1123  thefront.primaryKFs.end(), roToMerge->primaryKFs.begin(), roToMerge->primaryKFs.end());
1124  thefront.brems.insert(thefront.brems.end(), roToMerge->brems.begin(), roToMerge->brems.end());
1125  thefront.electronClusters = roToMerge->electronClusters;
1126  thefront.nBremsWithClusters = roToMerge->nBremsWithClusters;
1127  thefront.firstBrem = roToMerge->firstBrem;
1128  thefront.lateBrem = roToMerge->lateBrem;
1129  } else if (thefront.electronSeed.isNonnull() && roToMerge->electronSeed.isNonnull()) {
1130  LOGDRESSED("PFEGammaAlgo::mergeROsByAnyLink")
1131  << "Need to implement proper merging of two gsf candidates!" << std::endl;
1132  }
1133  }
1134  ROs.erase(mergestart, nomerge);
1135  // put the merged element in the back of the cleaned list
1136  ROs.push_back(ROs.front());
1137  ROs.pop_front();
1138  } else {
1139  check_for_merge = false;
1140  }
1141  }
1142  LOGDRESSED("PFEGammaAlgo::mergeROsByAnyLink()")
1143  << "After merging by links there are: " << ROs.size() << " refinable EGamma objects!" << std::endl;
1144 }
1145 
1146 // pull in KF tracks associated to the RO but not closer to another
1147 // NB: in initializeProtoCands() we forced the GSF tracks not to be
1148 // from a conversion, but we will leave a protection here just in
1149 // case things change in the future
1153  return;
1154  auto KFbegin = _splayedblock[reco::PFBlockElement::TRACK].begin();
1155  auto KFend = _splayedblock[reco::PFBlockElement::TRACK].end();
1156  for (auto& gsfflagged : RO.primaryGSFs) {
1157  const PFGSFElement* seedtk = gsfflagged;
1158  // don't process SC-only ROs or secondary seeded ROs
1159  if (RO.electronSeed.isNull() || seedtk->trackType(convType))
1160  continue;
1161  NotCloserToOther<reco::PFBlockElement::GSF, reco::PFBlockElement::TRACK> gsfTrackToKFs(_currentblock, seedtk);
1162  // get KF tracks not closer to another and not already used
1163  auto notlinked = std::partition(KFbegin, KFend, gsfTrackToKFs);
1164  // attach tracks and set as used
1165  for (auto kft = KFbegin; kft != notlinked; ++kft) {
1166  const PFKFElement* elemaskf = docast(const PFKFElement*, kft->get());
1167  // don't care about things that aren't primaries or directly
1168  // associated secondary tracks
1169  if (isPrimaryTrack(*elemaskf, *seedtk) && !elemaskf->trackType(convType)) {
1170  kft->setFlag(false);
1171  RO.primaryKFs.push_back(elemaskf);
1172  RO.localMap.insert(seedtk, elemaskf);
1173  } else if (elemaskf->trackType(convType)) {
1174  kft->setFlag(false);
1175  RO.secondaryKFs.push_back(elemaskf);
1176  RO.localMap.insert(seedtk, elemaskf);
1177  }
1178  } // loop on closest KFs not closer to other GSFs
1179  } // loop on GSF primaries on RO
1180 }
1181 
1185  return;
1186  auto KFbegin = _splayedblock[reco::PFBlockElement::TRACK].begin();
1187  auto KFend = _splayedblock[reco::PFBlockElement::TRACK].end();
1188  for (auto& primkf : RO.primaryKFs) {
1189  // don't process SC-only ROs or secondary seeded ROs
1190  if (primkf->trackType(convType)) {
1191  throw cms::Exception("PFEGammaAlgo::linkRefinableObjectPrimaryKFsToSecondaryKFs()")
1192  << "A KF track from conversion has been assigned as a primary!!" << std::endl;
1193  }
1194  NotCloserToOther<reco::PFBlockElement::TRACK, reco::PFBlockElement::TRACK, true> kfTrackToKFs(_currentblock,
1195  primkf);
1196  // get KF tracks not closer to another and not already used
1197  auto notlinked = std::partition(KFbegin, KFend, kfTrackToKFs);
1198  // attach tracks and set as used
1199  for (auto kft = KFbegin; kft != notlinked; ++kft) {
1200  const PFKFElement* elemaskf = docast(const PFKFElement*, kft->get());
1201  // don't care about things that aren't primaries or directly
1202  // associated secondary tracks
1203  if (elemaskf->trackType(convType)) {
1204  kft->setFlag(false);
1205  RO.secondaryKFs.push_back(elemaskf);
1206  RO.localMap.insert(primkf, elemaskf);
1207  }
1208  } // loop on closest KFs not closer to other KFs
1209  } // loop on KF primaries on RO
1210 }
1211 
1212 // try to associate the tracks to cluster elements which are not used
1215  RO.electronClusters.push_back(nullptr);
1216  return;
1217  }
1218  auto ECALbegin = _splayedblock[reco::PFBlockElement::ECAL].begin();
1219  auto ECALend = _splayedblock[reco::PFBlockElement::ECAL].end();
1220  for (auto& primgsf : RO.primaryGSFs) {
1221  NotCloserToOther<reco::PFBlockElement::GSF, reco::PFBlockElement::ECAL> gsfTracksToECALs(_currentblock, primgsf);
1222  // get set of matching ecals not already in SC
1223  auto notmatched_blk = std::partition(ECALbegin, ECALend, gsfTracksToECALs);
1224  notmatched_blk =
1225  std::partition(ECALbegin, notmatched_blk, [&primgsf](auto const& x) { return compatibleEoPOut(*x, *primgsf); });
1226  // get set of matching ecals already in the RO
1227  auto notmatched_sc = std::partition(RO.ecalclusters.begin(), RO.ecalclusters.end(), gsfTracksToECALs);
1228  notmatched_sc = std::partition(
1229  RO.ecalclusters.begin(), notmatched_sc, [&primgsf](auto const& x) { return compatibleEoPOut(*x, *primgsf); });
1230  // look inside the SC for the ECAL cluster
1231  for (auto ecal = RO.ecalclusters.begin(); ecal != notmatched_sc; ++ecal) {
1232  const PFClusterElement* elemascluster = docast(const PFClusterElement*, ecal->get());
1233  FlaggedPtr<const PFClusterElement> temp(elemascluster, true);
1234  LOGDRESSED("PFEGammaAlgo::linkGSFTracktoECAL()") << "Found a cluster already in RO by GSF extrapolation"
1235  << " at ECAL surface!" << std::endl
1236  << *elemascluster << std::endl;
1237 
1238  RO.localMap.insert(primgsf, temp.get());
1239  }
1240  // look outside the SC for the ecal cluster
1241  for (auto ecal = ECALbegin; ecal != notmatched_blk; ++ecal) {
1242  const PFClusterElement* elemascluster = docast(const PFClusterElement*, ecal->get());
1243  LOGDRESSED("PFEGammaAlgo::linkGSFTracktoECAL()") << "Found a cluster not already in RO by GSF extrapolation"
1244  << " at ECAL surface!" << std::endl
1245  << *elemascluster << std::endl;
1246  if (addPFClusterToROSafe(elemascluster, RO)) {
1247  attachPSClusters(elemascluster, RO.ecal2ps[elemascluster]);
1248  RO.localMap.insert(primgsf, elemascluster);
1249  ecal->setFlag(false);
1250  }
1251  }
1252  }
1253 }
1254 
1255 // try to associate the tracks to cluster elements which are not used
1258  return;
1259  auto HCALbegin = _splayedblock[reco::PFBlockElement::HCAL].begin();
1260  auto HCALend = _splayedblock[reco::PFBlockElement::HCAL].end();
1261  for (auto& primgsf : RO.primaryGSFs) {
1262  NotCloserToOther<reco::PFBlockElement::GSF, reco::PFBlockElement::HCAL> gsfTracksToHCALs(_currentblock, primgsf);
1263  auto notmatched = std::partition(HCALbegin, HCALend, gsfTracksToHCALs);
1264  for (auto hcal = HCALbegin; hcal != notmatched; ++hcal) {
1265  const PFClusterElement* elemascluster = docast(const PFClusterElement*, hcal->get());
1266  FlaggedPtr<const PFClusterElement> temp(elemascluster, true);
1267  LOGDRESSED("PFEGammaAlgo::linkGSFTracktoECAL()")
1268  << "Found an HCAL cluster associated to GSF extrapolation" << std::endl;
1269  RO.hcalClusters.push_back(temp.get());
1270  RO.localMap.insert(primgsf, temp.get());
1271  hcal->setFlag(false);
1272  }
1273  }
1274 }
1275 
1276 // try to associate the tracks to cluster elements which are not used
1279  return;
1280  for (auto& primkf : RO.primaryKFs)
1281  linkKFTrackToECAL(primkf, RO);
1282  for (auto& secdkf : RO.secondaryKFs)
1283  linkKFTrackToECAL(secdkf, RO);
1284 }
1285 
1287  std::vector<FlaggedPtr<const PFClusterElement>>& currentECAL = RO.ecalclusters;
1288  auto ECALbegin = _splayedblock[reco::PFBlockElement::ECAL].begin();
1289  auto ECALend = _splayedblock[reco::PFBlockElement::ECAL].end();
1290  NotCloserToOther<reco::PFBlockElement::TRACK, reco::PFBlockElement::ECAL> kfTrackToECALs(_currentblock, kfflagged);
1291  NotCloserToOther<reco::PFBlockElement::GSF, reco::PFBlockElement::ECAL> kfTrackGSFToECALs(_currentblock, kfflagged);
1292  //get the ECAL elements not used and not closer to another KF
1293  auto notmatched_sc = std::partition(currentECAL.begin(), currentECAL.end(), kfTrackToECALs);
1294  //get subset ECAL elements not used or closer to another GSF of any type
1295  notmatched_sc = std::partition(currentECAL.begin(), notmatched_sc, kfTrackGSFToECALs);
1296  for (auto ecalitr = currentECAL.begin(); ecalitr != notmatched_sc; ++ecalitr) {
1297  const PFClusterElement* elemascluster = docast(const PFClusterElement*, ecalitr->get());
1298  FlaggedPtr<const PFClusterElement> flaggedclus(elemascluster, true);
1299 
1300  LOGDRESSED("PFEGammaAlgo::linkKFTracktoECAL()") << "Found a cluster already in RO by KF extrapolation"
1301  << " at ECAL surface!" << std::endl
1302  << *elemascluster << std::endl;
1303  RO.localMap.insert(elemascluster, kfflagged);
1304  }
1305  //get the ECAL elements not used and not closer to another KF
1306  auto notmatched_blk = std::partition(ECALbegin, ECALend, kfTrackToECALs);
1307  //get subset ECAL elements not used or closer to another GSF of any type
1308  notmatched_blk = std::partition(ECALbegin, notmatched_blk, kfTrackGSFToECALs);
1309  for (auto ecalitr = ECALbegin; ecalitr != notmatched_blk; ++ecalitr) {
1310  const PFClusterElement* elemascluster = docast(const PFClusterElement*, ecalitr->get());
1311  if (addPFClusterToROSafe(elemascluster, RO)) {
1312  attachPSClusters(elemascluster, RO.ecal2ps[elemascluster]);
1313  ecalitr->setFlag(false);
1314 
1315  LOGDRESSED("PFEGammaAlgo::linkKFTracktoECAL()") << "Found a cluster not in RO by KF extrapolation"
1316  << " at ECAL surface!" << std::endl
1317  << *elemascluster << std::endl;
1318  RO.localMap.insert(elemascluster, kfflagged);
1319  }
1320  }
1321 }
1322 
1324  if (RO.brems.empty())
1325  return;
1326  int FirstBrem = -1;
1327  int TrajPos = -1;
1328  int lastBremTrajPos = -1;
1329  for (auto& brem : RO.brems) {
1330  bool has_clusters = false;
1331  TrajPos = (brem->indTrajPoint()) - 2;
1332  auto ECALbegin = _splayedblock[reco::PFBlockElement::ECAL].begin();
1333  auto ECALend = _splayedblock[reco::PFBlockElement::ECAL].end();
1334  NotCloserToOther<reco::PFBlockElement::BREM, reco::PFBlockElement::ECAL> BremToECALs(_currentblock, brem);
1335  // check for late brem using clusters already in the SC
1336  auto RSCBegin = RO.ecalclusters.begin();
1337  auto RSCEnd = RO.ecalclusters.end();
1338  auto notmatched_rsc = std::partition(RSCBegin, RSCEnd, BremToECALs);
1339  for (auto ecal = RSCBegin; ecal != notmatched_rsc; ++ecal) {
1340  float deta = std::abs((*ecal)->clusterRef()->positionREP().eta() - brem->positionAtECALEntrance().eta());
1341  if (deta < 0.015) {
1342  has_clusters = true;
1343  if (lastBremTrajPos == -1 || lastBremTrajPos < TrajPos) {
1344  lastBremTrajPos = TrajPos;
1345  }
1346  if (FirstBrem == -1 || TrajPos < FirstBrem) { // set brem information
1347  FirstBrem = TrajPos;
1348  RO.firstBrem = TrajPos;
1349  }
1350  LOGDRESSED("PFEGammaAlgo::linkBremToECAL()") << "Found a cluster already in SC linked to brem extrapolation"
1351  << " at ECAL surface!" << std::endl;
1352  RO.localMap.insert(ecal->get(), brem);
1353  }
1354  }
1355  // grab new clusters from the block (ensured to not be late brem)
1356  auto notmatched_block = std::partition(ECALbegin, ECALend, BremToECALs);
1357  for (auto ecal = ECALbegin; ecal != notmatched_block; ++ecal) {
1358  float deta = std::abs((*ecal)->clusterRef()->positionREP().eta() - brem->positionAtECALEntrance().eta());
1359  if (deta < 0.015) {
1360  has_clusters = true;
1361  if (lastBremTrajPos == -1 || lastBremTrajPos < TrajPos) {
1362  lastBremTrajPos = TrajPos;
1363  }
1364  if (FirstBrem == -1 || TrajPos < FirstBrem) { // set brem information
1365 
1366  FirstBrem = TrajPos;
1367  RO.firstBrem = TrajPos;
1368  }
1369  const PFClusterElement* elemasclus = docast(const PFClusterElement*, ecal->get());
1370  if (addPFClusterToROSafe(elemasclus, RO)) {
1371  attachPSClusters(elemasclus, RO.ecal2ps[elemasclus]);
1372 
1373  RO.localMap.insert(ecal->get(), brem);
1374  ecal->setFlag(false);
1375  LOGDRESSED("PFEGammaAlgo::linkBremToECAL()") << "Found a cluster not already associated by brem extrapolation"
1376  << " at ECAL surface!" << std::endl;
1377  }
1378  }
1379  }
1380  if (has_clusters) {
1381  if (RO.nBremsWithClusters == -1)
1382  RO.nBremsWithClusters = 0;
1383  ++RO.nBremsWithClusters;
1384  }
1385  }
1386 }
1387 
1389  auto KFbegin = _splayedblock[reco::PFBlockElement::TRACK].begin();
1390  auto KFend = _splayedblock[reco::PFBlockElement::TRACK].end();
1391  auto BeginROskfs = RO.secondaryKFs.begin();
1392  auto EndROskfs = RO.secondaryKFs.end();
1393  auto ronotconv = std::partition(BeginROskfs, EndROskfs, [](auto const& x) { return x->trackType(ConvType); });
1394  size_t convkfs_end = std::distance(BeginROskfs, ronotconv);
1395  for (size_t idx = 0; idx < convkfs_end; ++idx) {
1396  auto const& secKFs =
1397  RO.secondaryKFs; //we want the entry at the index but we allocate to secondaryKFs in loop which invalidates all iterators, references and pointers, hence we need to get the entry fresh each time
1398  NotCloserToOther<reco::PFBlockElement::TRACK, reco::PFBlockElement::TRACK, true> TracksToTracks(_currentblock,
1399  secKFs[idx]);
1400  auto notmatched = std::partition(KFbegin, KFend, TracksToTracks);
1401  notmatched = std::partition(KFbegin, notmatched, [](auto const& x) { return x->trackType(ConvType); });
1402  for (auto kf = KFbegin; kf != notmatched; ++kf) {
1403  const reco::PFBlockElementTrack* elemaskf = docast(const reco::PFBlockElementTrack*, kf->get());
1404  RO.secondaryKFs.push_back(elemaskf);
1405  RO.localMap.insert(secKFs[idx], kf->get());
1406  kf->setFlag(false);
1407  }
1408  }
1409 }
1410 
1412  auto KFbegin = _splayedblock[reco::PFBlockElement::TRACK].begin();
1413  auto KFend = _splayedblock[reco::PFBlockElement::TRACK].end();
1414  for (auto& ecal : RO.ecalclusters) {
1415  NotCloserToOther<reco::PFBlockElement::ECAL, reco::PFBlockElement::TRACK, true> ECALToTracks(_currentblock,
1416  ecal.get());
1417  auto notmatchedkf = std::partition(KFbegin, KFend, ECALToTracks);
1418  auto notconvkf = std::partition(KFbegin, notmatchedkf, [](auto const& x) { return x->trackType(ConvType); });
1419  // add identified KF conversion tracks
1420  for (auto kf = KFbegin; kf != notconvkf; ++kf) {
1421  const reco::PFBlockElementTrack* elemaskf = docast(const reco::PFBlockElementTrack*, kf->get());
1422  RO.secondaryKFs.push_back(elemaskf);
1423  RO.localMap.insert(ecal.get(), elemaskf);
1424  kf->setFlag(false);
1425  }
1426  // go through non-conv-identified kfs and check MVA to add conversions
1427  for (auto kf = notconvkf; kf != notmatchedkf; ++kf) {
1428  float mvaval = evaluateSingleLegMVA(_currentblock, *primaryVertex_, (*kf)->index());
1429  if (mvaval > cfg_.mvaConvCut) {
1430  const reco::PFBlockElementTrack* elemaskf = docast(const reco::PFBlockElementTrack*, kf->get());
1431  RO.secondaryKFs.push_back(elemaskf);
1432  RO.localMap.insert(ecal.get(), elemaskf);
1433  kf->setFlag(false);
1434 
1435  RO.singleLegConversionMvaMap.emplace(elemaskf, mvaval);
1436  }
1437  }
1438  }
1439 }
1440 
1442  auto ECALbegin = _splayedblock[reco::PFBlockElement::ECAL].begin();
1443  auto ECALend = _splayedblock[reco::PFBlockElement::ECAL].end();
1444  for (auto& skf : RO.secondaryKFs) {
1445  NotCloserToOther<reco::PFBlockElement::TRACK, reco::PFBlockElement::ECAL, false> TracksToECALwithCut(
1446  _currentblock, skf, 1.5f);
1447  auto notmatched = std::partition(ECALbegin, ECALend, TracksToECALwithCut);
1448  for (auto ecal = ECALbegin; ecal != notmatched; ++ecal) {
1449  const reco::PFBlockElementCluster* elemascluster = docast(const reco::PFBlockElementCluster*, ecal->get());
1450  if (addPFClusterToROSafe(elemascluster, RO)) {
1451  attachPSClusters(elemascluster, RO.ecal2ps[elemascluster]);
1452  RO.localMap.insert(skf, elemascluster);
1453  ecal->setFlag(false);
1454  }
1455  }
1456  }
1457 }
1458 
1459 PFEGammaAlgo::EgammaObjects PFEGammaAlgo::fillPFCandidates(const std::list<PFEGammaAlgo::ProtoEGObject>& ROs) {
1461 
1462  // reserve output collections
1463  output.candidates.reserve(ROs.size());
1464  output.candidateExtras.reserve(ROs.size());
1465  output.refinedSuperClusters.reserve(ROs.size());
1466 
1467  for (auto& RO : ROs) {
1468  if (RO.ecalclusters.empty() && !cfg_.produceEGCandsWithNoSuperCluster)
1469  continue;
1470 
1473  if (!RO.primaryGSFs.empty() || !RO.primaryKFs.empty()) {
1474  cand.setPdgId(-11); // anything with a primary track is an electron
1475  } else {
1476  cand.setPdgId(22); // anything with no primary track is a photon
1477  }
1478  if (!RO.primaryKFs.empty()) {
1479  cand.setCharge(RO.primaryKFs[0]->trackRef()->charge());
1480  xtra.setKfTrackRef(RO.primaryKFs[0]->trackRef());
1481  cand.setTrackRef(RO.primaryKFs[0]->trackRef());
1482  cand.addElementInBlock(_currentblock, RO.primaryKFs[0]->index());
1483  }
1484  if (!RO.primaryGSFs.empty()) {
1485  cand.setCharge(RO.primaryGSFs[0]->GsftrackRef()->chargeMode());
1486  xtra.setGsfTrackRef(RO.primaryGSFs[0]->GsftrackRef());
1487  cand.setGsfTrackRef(RO.primaryGSFs[0]->GsftrackRef());
1488  cand.addElementInBlock(_currentblock, RO.primaryGSFs[0]->index());
1489  }
1490  if (RO.parentSC) {
1491  xtra.setSuperClusterPFECALRef(RO.parentSC->superClusterRef());
1492  // we'll set to the refined supercluster back up in the producer
1493  cand.setSuperClusterRef(RO.parentSC->superClusterRef());
1494  xtra.setSuperClusterRef(RO.parentSC->superClusterRef());
1495  cand.addElementInBlock(_currentblock, RO.parentSC->index());
1496  }
1497  // add brems
1498  for (const auto& brem : RO.brems) {
1499  cand.addElementInBlock(_currentblock, brem->index());
1500  }
1501  // add clusters and ps clusters
1502  for (const auto& ecal : RO.ecalclusters) {
1503  const PFClusterElement* clus = ecal.get();
1504  cand.addElementInBlock(_currentblock, clus->index());
1505  if (RO.ecal2ps.count(clus)) {
1506  for (auto& psclus : RO.ecal2ps.at(clus)) {
1507  cand.addElementInBlock(_currentblock, psclus->index());
1508  }
1509  }
1510  }
1511  // add secondary tracks
1512  for (const auto& kf : RO.secondaryKFs) {
1513  cand.addElementInBlock(_currentblock, kf->index());
1514  const reco::ConversionRefVector& convrefs = kf->convRefs();
1515  bool no_conv_ref = true;
1516  for (const auto& convref : convrefs) {
1517  if (convref.isNonnull() && convref.isAvailable()) {
1518  xtra.addConversionRef(convref);
1519  no_conv_ref = false;
1520  }
1521  }
1522  if (no_conv_ref) {
1523  //single leg conversions
1524 
1525  //look for stored mva value in map or else recompute
1526  const auto& mvavalmapped = RO.singleLegConversionMvaMap.find(kf);
1527  //FIXME: Abuse single mva value to store both provenance and single leg mva score
1528  //by storing 3.0 + mvaval
1529  float mvaval = (mvavalmapped != RO.singleLegConversionMvaMap.end()
1530  ? mvavalmapped->second
1532 
1533  xtra.addSingleLegConvTrackRefMva(std::make_pair(kf->trackRef(), mvaval));
1534  }
1535  }
1536 
1537  // build the refined supercluster from those clusters left in the cand
1538  output.refinedSuperClusters.push_back(buildRefinedSuperCluster(RO));
1539 
1540  //*TODO* cluster time is not reliable at the moment, so only use track timing
1541  float trkTime = 0, trkTimeErr = -1;
1542  if (!RO.primaryGSFs.empty() && RO.primaryGSFs[0]->isTimeValid()) {
1543  trkTime = RO.primaryGSFs[0]->time();
1544  trkTimeErr = RO.primaryGSFs[0]->timeError();
1545  } else if (!RO.primaryKFs.empty() && RO.primaryKFs[0]->isTimeValid()) {
1546  trkTime = RO.primaryKFs[0]->time();
1547  trkTimeErr = RO.primaryKFs[0]->timeError();
1548  }
1549  if (trkTimeErr >= 0) {
1550  cand.setTime(trkTime, trkTimeErr);
1551  }
1552 
1553  const reco::SuperCluster& the_sc = output.refinedSuperClusters.back();
1554  // with the refined SC in hand we build a naive candidate p4
1555  // and set the candidate ECAL position to either the barycenter of the
1556  // supercluster (if super-cluster present) or the seed of the
1557  // new SC generated by the EGAlgo
1558  const double scE = the_sc.energy();
1559  if (scE != 0.0) {
1560  const math::XYZPoint& seedPos = the_sc.seed()->position();
1561  math::XYZVector egDir = the_sc.position() - primaryVertex_->position();
1562  egDir = egDir.Unit();
1563  cand.setP4(math::XYZTLorentzVector(scE * egDir.x(), scE * egDir.y(), scE * egDir.z(), scE));
1564  math::XYZPointF ecalPOS_f(seedPos.x(), seedPos.y(), seedPos.z());
1565  cand.setPositionAtECALEntrance(ecalPOS_f);
1566  cand.setEcalEnergy(the_sc.rawEnergy(), the_sc.energy());
1567  } else if (cfg_.produceEGCandsWithNoSuperCluster && !RO.primaryGSFs.empty()) {
1568  const PFGSFElement* gsf = RO.primaryGSFs[0];
1569  const reco::GsfTrackRef& gref = gsf->GsftrackRef();
1570  math::XYZTLorentzVector p4(gref->pxMode(), gref->pyMode(), gref->pzMode(), gref->pMode());
1571  cand.setP4(p4);
1573  } else if (cfg_.produceEGCandsWithNoSuperCluster && !RO.primaryKFs.empty()) {
1574  const PFKFElement* kf = RO.primaryKFs[0];
1575  reco::TrackRef kref = RO.primaryKFs[0]->trackRef();
1576  math::XYZTLorentzVector p4(kref->px(), kref->py(), kref->pz(), kref->p());
1577  cand.setP4(p4);
1579  }
1580  const float eleMVAValue = calculateEleMVA(RO, xtra);
1581  fillExtraInfo(RO, xtra);
1582  //std::cout << "PFEG eleMVA: " << eleMVAValue << std::endl;
1583  xtra.setMVA(eleMVAValue);
1584  cand.set_mva_e_pi(eleMVAValue);
1585  output.candidates.push_back(cand);
1586  output.candidateExtras.push_back(xtra);
1587  }
1588 
1589  return output;
1590 }
1591 
1593  if (ro.primaryGSFs.empty()) {
1594  return -2.0f;
1595  }
1596  const PFGSFElement* gsfElement = ro.primaryGSFs.front();
1597  const PFKFElement* kfElement = nullptr;
1598  if (!ro.primaryKFs.empty()) {
1599  kfElement = ro.primaryKFs.front();
1600  }
1601  auto const& refGsf = gsfElement->GsftrackRef();
1602  reco::TrackRef refKf;
1603  constexpr float mEl = 0.000511;
1604  const double eInGsf = std::hypot(refGsf->pMode(), mEl);
1605  double dEtGsfEcal = 1e6;
1606  double sigmaEtaEta = 1e-14;
1607  const double eneHcalGsf =
1608  std::accumulate(ro.hcalClusters.begin(), ro.hcalClusters.end(), 0.0, [](const double a, auto const& b) {
1609  return a + b->clusterRef()->energy();
1610  });
1611  if (!ro.primaryKFs.empty()) {
1612  refKf = ro.primaryKFs.front()->trackRef();
1613  }
1614  const double eOutGsf = gsfElement->Pout().t();
1615  const double etaOutGsf = gsfElement->positionAtECALEntrance().eta();
1616  double firstEcalGsfEnergy{0.0};
1617  double otherEcalGsfEnergy{0.0};
1618  double ecalBremEnergy{0.0};
1619  //shower shape of cluster closest to gsf track
1620  std::vector<const reco::PFCluster*> gsfCluster;
1621  for (const auto& ecal : ro.ecalclusters) {
1622  const double cenergy = ecal->clusterRef()->correctedEnergy();
1623  bool hasgsf = ro.localMap.contains(gsfElement, ecal.get());
1624  bool haskf = ro.localMap.contains(kfElement, ecal.get());
1625  bool hasbrem = false;
1626  for (const auto& brem : ro.brems) {
1627  if (ro.localMap.contains(brem, ecal.get())) {
1628  hasbrem = true;
1629  }
1630  }
1631  if (hasbrem && ecal.get() != ro.electronClusters[0]) {
1632  ecalBremEnergy += cenergy;
1633  }
1634  if (!hasbrem && ecal.get() != ro.electronClusters[0]) {
1635  if (hasgsf)
1636  otherEcalGsfEnergy += cenergy;
1637  if (haskf)
1638  ecalBremEnergy += cenergy; // from conv. brem!
1639  if (!(hasgsf || haskf))
1640  otherEcalGsfEnergy += cenergy; // stuff from SC
1641  }
1642  }
1643 
1644  if (ro.electronClusters[0]) {
1645  reco::PFClusterRef cref = ro.electronClusters[0]->clusterRef();
1647  firstEcalGsfEnergy = cref->correctedEnergy();
1648  dEtGsfEcal = cref->positionREP().eta() - etaOutGsf;
1649  gsfCluster.push_back(cref.get());
1650  PFClusterWidthAlgo pfwidth(gsfCluster);
1651  sigmaEtaEta = pfwidth.pflowSigmaEtaEta();
1652  }
1653 
1654  // brem sequence information
1655  float firstBrem{-1.0f};
1656  float earlyBrem{-1.0f};
1657  float lateBrem{-1.0f};
1658  if (ro.nBremsWithClusters > 0) {
1659  firstBrem = ro.firstBrem;
1660  earlyBrem = ro.firstBrem < 4 ? 1.0f : 0.0f;
1661  lateBrem = ro.lateBrem == 1 ? 1.0f : 0.0f;
1662  }
1663  xtra.setEarlyBrem(earlyBrem);
1664  xtra.setLateBrem(lateBrem);
1665  if (firstEcalGsfEnergy > 0.0) {
1666  if (refGsf.isNonnull()) {
1667  xtra.setGsfTrackPout(gsfElement->Pout());
1668  // normalization observables
1669  const float ptGsf = refGsf->ptMode();
1670  const float etaGsf = refGsf->etaMode();
1671  // tracking observables
1672  const double ptModeErrorGsf = refGsf->ptModeError();
1673  float ptModeErrOverPtGsf = (ptModeErrorGsf > 0. ? ptModeErrorGsf / ptGsf : 1.0);
1674  float chi2Gsf = refGsf->normalizedChi2();
1675  float dPtOverPtGsf = (ptGsf - gsfElement->Pout().pt()) / ptGsf;
1676  // kalman filter vars
1677  float nHitKf = refKf.isNonnull() ? refKf->hitPattern().trackerLayersWithMeasurement() : 0;
1678  float chi2Kf = refKf.isNonnull() ? refKf->normalizedChi2() : -0.01;
1679 
1680  //tracker + calorimetry observables
1681  float eTotPinMode = (firstEcalGsfEnergy + otherEcalGsfEnergy + ecalBremEnergy) / eInGsf;
1682  float eGsfPoutMode = firstEcalGsfEnergy / eOutGsf;
1683  float eTotBremPinPoutMode = (ecalBremEnergy + otherEcalGsfEnergy) / (eInGsf - eOutGsf);
1684  float dEtaGsfEcalClust = std::abs(dEtGsfEcal);
1685  float logSigmaEtaEta = std::log(sigmaEtaEta);
1686  float hOverHe = eneHcalGsf / (eneHcalGsf + firstEcalGsfEnergy);
1687 
1688  xtra.setDeltaEta(dEtaGsfEcalClust);
1689  xtra.setSigmaEtaEta(sigmaEtaEta);
1690  xtra.setHadEnergy(eneHcalGsf);
1691 
1692  // Apply bounds to variables and calculate MVA
1693  dPtOverPtGsf = std::clamp(dPtOverPtGsf, -0.2f, 1.0f);
1694  ptModeErrOverPtGsf = std::min(ptModeErrOverPtGsf, 0.3f);
1695  chi2Gsf = std::min(chi2Gsf, 10.0f);
1696  chi2Kf = std::min(chi2Kf, 10.0f);
1697  eTotPinMode = std::clamp(eTotPinMode, 0.0f, 5.0f);
1698  eGsfPoutMode = std::clamp(eGsfPoutMode, 0.0f, 5.0f);
1699  eTotBremPinPoutMode = std::clamp(eTotBremPinPoutMode, 0.0f, 5.0f);
1700  dEtaGsfEcalClust = std::min(dEtaGsfEcalClust, 0.1f);
1701  logSigmaEtaEta = std::max(logSigmaEtaEta, -14.0f);
1702 
1703  // not used for moment, weird behavior of variable
1704  //float dPtOverPtKf = refKf.isNonnull() ? (refKf->pt() - refKf->outerPt())/refKf->pt() : -0.01;
1705  //dPtOverPtKf = std::clamp(dPtOverPtKf,-0.2f, 1.0f);
1706 
1707  /*
1708  * To be used for debugging:
1709  * pretty-print the PFEgamma electron MVA input variables
1710  *
1711  * std::cout << " **** PFEG BDT observables ****" << endl;
1712  * std::cout << " < Normalization > " << endl;
1713  * std::cout << " ptGsf " << ptGsf << " Pin " << eInGsf
1714  * << " Pout " << eOutGsf << " etaGsf " << etaGsf << endl;
1715  * std::cout << " < PureTracking > " << endl;
1716  * std::cout << " ptModeErrOverPtGsf " << ptModeErrOverPtGsf
1717  * << " dPtOverPtGsf " << dPtOverPtGsf
1718  * << " chi2Gsf " << chi2Gsf
1719  * << " nhit_gsf " << nhit_gsf
1720  * << " dPtOverPtKf " << dPtOverPtKf
1721  * << " chi2Kf " << chi2Kf
1722  * << " nHitKf " << nHitKf << endl;
1723  * std::cout << " < track-ecal-hcal-ps " << endl;
1724  * std::cout << " eTotPinMode " << eTotPinMode
1725  * << " eGsfPoutMode " << eGsfPoutMode
1726  * << " eTotBremPinPoutMode " << eTotBremPinPoutMode
1727  * << " dEtaGsfEcalClust " << dEtaGsfEcalClust
1728  * << " logSigmaEtaEta " << logSigmaEtaEta
1729  * << " hOverHe " << hOverHe << " Hcal energy " << eneHcalGsf
1730  * << " lateBrem " << lateBrem
1731  * << " firstBrem " << firstBrem << endl;
1732  */
1733 
1734  float vars[] = {std::log(ptGsf),
1735  etaGsf,
1736  ptModeErrOverPtGsf,
1737  dPtOverPtGsf,
1738  chi2Gsf,
1739  nHitKf,
1740  chi2Kf,
1741  eTotPinMode,
1742  eGsfPoutMode,
1743  eTotBremPinPoutMode,
1744  dEtaGsfEcalClust,
1745  logSigmaEtaEta,
1746  hOverHe,
1747  lateBrem,
1748  firstBrem};
1749 
1750  return gbrForests_.ele_->GetAdaBoostClassifier(vars);
1751  }
1752  }
1753  return -2.0f;
1754 }
1755 
1757  // add tracks associated to clusters that are not T_FROM_GAMMACONV
1758  // info about single-leg convs is already save, so just veto in loops
1759  auto KFbegin = _splayedblock[reco::PFBlockElement::TRACK].begin();
1760  auto KFend = _splayedblock[reco::PFBlockElement::TRACK].end();
1761  for (auto& ecal : RO.ecalclusters) {
1762  NotCloserToOther<reco::PFBlockElement::ECAL, reco::PFBlockElement::TRACK, true> ECALToTracks(_currentblock,
1763  ecal.get());
1764  auto notmatchedkf = std::partition(KFbegin, KFend, ECALToTracks);
1765  auto notconvkf = std::partition(KFbegin, notmatchedkf, [](auto const& x) { return x->trackType(ConvType); });
1766  // go through non-conv-identified kfs and check MVA to add conversions
1767  for (auto kf = notconvkf; kf != notmatchedkf; ++kf) {
1768  const reco::PFBlockElementTrack* elemaskf = docast(const reco::PFBlockElementTrack*, kf->get());
1769  xtra.addExtraNonConvTrack(_currentblock, *elemaskf);
1770  }
1771  }
1772 }
1773 
1774 // currently stolen from PFECALSuperClusterAlgo, we should
1775 // try to factor this correctly since the operation is the same in
1776 // both places...
1778  if (RO.ecalclusters.empty()) {
1779  return reco::SuperCluster(0.0, math::XYZPoint(0, 0, 0));
1780  }
1781 
1782  bool isEE = false;
1783  edm::Ptr<reco::PFCluster> clusptr;
1784  // need the vector of raw pointers for a PF width class
1785  std::vector<const reco::PFCluster*> bare_ptrs;
1786  // calculate necessary parameters and build the SC
1787  double posX(0), posY(0), posZ(0), rawSCEnergy(0), corrSCEnergy(0), corrPSEnergy(0), PS1_clus_sum(0), PS2_clus_sum(0),
1788  ePS1(0), ePS2(0), ps1_energy(0.0), ps2_energy(0.0);
1789  int condP1(1), condP2(1);
1790  for (auto& clus : RO.ecalclusters) {
1791  ePS1 = 0;
1792  ePS2 = 0;
1793  isEE = PFLayer::ECAL_ENDCAP == clus->clusterRef()->layer();
1794  clusptr = edm::refToPtr<reco::PFClusterCollection>(clus->clusterRef());
1795  bare_ptrs.push_back(clusptr.get());
1796 
1797  const double cluseraw = clusptr->energy();
1798  double cluscalibe = clusptr->correctedEnergy();
1799  const math::XYZPoint& cluspos = clusptr->position();
1800  posX += cluseraw * cluspos.X();
1801  posY += cluseraw * cluspos.Y();
1802  posZ += cluseraw * cluspos.Z();
1803  // update EE calibrated super cluster energies
1804  if (isEE && RO.ecal2ps.count(clus.get())) {
1805  ePS1 = 0;
1806  ePS2 = 0;
1807  condP1 = condP2 = 1;
1808 
1809  const auto& psclusters = RO.ecal2ps.at(clus.get());
1810 
1811  for (auto i_ps = psclusters.begin(); i_ps != psclusters.end(); ++i_ps) {
1812  const PFClusterRef& psclus = (*i_ps)->clusterRef();
1813 
1814  auto const& recH_Frac = psclus->recHitFractions();
1815 
1816  switch (psclus->layer()) {
1817  case PFLayer::PS1:
1818  for (auto const& recH : recH_Frac) {
1819  ESDetId strip1 = recH.recHitRef()->detId();
1820  if (strip1 != ESDetId(0)) {
1822  //getStatusCode() == 0 => active channel
1823  // apply correction if all recHits are dead
1824  if (status_p1->getStatusCode() == 0)
1825  condP1 = 0;
1826  }
1827  }
1828  break;
1829  case PFLayer::PS2:
1830  for (auto const& recH : recH_Frac) {
1831  ESDetId strip2 = recH.recHitRef()->detId();
1832  if (strip2 != ESDetId(0)) {
1834  if (status_p2->getStatusCode() == 0)
1835  condP2 = 0;
1836  }
1837  }
1838  break;
1839  default:
1840  break;
1841  }
1842  }
1843 
1844  auto sumPSEnergy = [](double a, const ClusterElement* b, PFLayer::Layer layer) {
1845  return a + (layer == b->clusterRef()->layer()) * b->clusterRef()->energy();
1846  };
1847 
1848  PS1_clus_sum =
1849  std::accumulate(psclusters.begin(), psclusters.end(), 0.0, std::bind(sumPSEnergy, _1, _2, PFLayer::PS1));
1850  PS2_clus_sum =
1851  std::accumulate(psclusters.begin(), psclusters.end(), 0.0, std::bind(sumPSEnergy, _1, _2, PFLayer::PS2));
1852 
1853  if (condP1 == 1)
1854  ePS1 = -1.;
1855  if (condP2 == 1)
1856  ePS2 = -1.;
1857 
1858  cluscalibe = thePFEnergyCalibration_.energyEm(
1859  *clusptr, PS1_clus_sum, PS2_clus_sum, ePS1, ePS2, cfg_.applyCrackCorrections);
1860  }
1861  if (ePS1 == -1.)
1862  ePS1 = 0;
1863  if (ePS2 == -1.)
1864  ePS2 = 0;
1865 
1866  rawSCEnergy += cluseraw;
1867  corrSCEnergy += cluscalibe;
1868  ps1_energy += ePS1;
1869  ps2_energy += ePS2;
1870  corrPSEnergy += ePS1 + ePS2;
1871  }
1872  posX /= rawSCEnergy;
1873  posY /= rawSCEnergy;
1874  posZ /= rawSCEnergy;
1875 
1876  // now build the supercluster
1877  reco::SuperCluster new_sc(corrSCEnergy, math::XYZPoint(posX, posY, posZ));
1878 
1879  clusptr = edm::refToPtr<reco::PFClusterCollection>(RO.ecalclusters.front()->clusterRef());
1880  new_sc.setCorrectedEnergy(corrSCEnergy);
1881  new_sc.setSeed(clusptr);
1882  new_sc.setPreshowerEnergyPlane1(ps1_energy);
1883  new_sc.setPreshowerEnergyPlane2(ps2_energy);
1884  new_sc.setPreshowerEnergy(corrPSEnergy);
1885  for (const auto& clus : RO.ecalclusters) {
1886  clusptr = edm::refToPtr<reco::PFClusterCollection>(clus->clusterRef());
1887  new_sc.addCluster(clusptr);
1888  auto& hits_and_fractions = clusptr->hitsAndFractions();
1889  for (auto& hit_and_fraction : hits_and_fractions) {
1890  new_sc.addHitAndFraction(hit_and_fraction.first, hit_and_fraction.second);
1891  }
1892  // put the preshower stuff back in later
1893  if (RO.ecal2ps.count(clus.get())) {
1894  const auto& cluspsassociation = RO.ecal2ps.at(clus.get());
1895  // EE rechits should be uniquely matched to sets of pre-shower
1896  // clusters at this point, so we throw an exception if otherwise
1897  // now wrapped in EDM debug flags
1898  for (const auto& pscluselem : cluspsassociation) {
1899  edm::Ptr<reco::PFCluster> psclus = edm::refToPtr<reco::PFClusterCollection>(pscluselem->clusterRef());
1900 #ifdef PFFLOW_DEBUG
1901  auto found_pscluster =
1903  if (found_pscluster == new_sc.preshowerClustersEnd()) {
1904 #endif
1905  new_sc.addPreshowerCluster(psclus);
1906 #ifdef PFFLOW_DEBUG
1907  } else {
1908  throw cms::Exception("PFECALSuperClusterAlgo::buildSuperCluster")
1909  << "Found a PS cluster matched to more than one EE cluster!" << std::endl
1910  << std::hex << psclus.get() << " == " << found_pscluster->get() << std::dec << std::endl;
1911  }
1912 #endif
1913  }
1914  }
1915  }
1916 
1917  // calculate linearly weighted cluster widths
1918  PFClusterWidthAlgo pfwidth(bare_ptrs);
1919  new_sc.setEtaWidth(pfwidth.pflowEtaWidth());
1920  new_sc.setPhiWidth(pfwidth.pflowPhiWidth());
1921 
1922  // cache the value of the raw energy
1923  new_sc.rawEnergy();
1924 
1925  return new_sc;
1926 }
1927 
1929  // this only means something for ROs with a primary GSF track
1930  if (RO.primaryGSFs.empty())
1931  return;
1932  // need energy sums to tell if we've added crap or not
1933  const double Pin_gsf = RO.primaryGSFs.front()->GsftrackRef()->pMode();
1934  const double gsfOuterEta = RO.primaryGSFs.front()->positionAtECALEntrance().Eta();
1935  double tot_ecal = 0.0;
1936  std::vector<double> min_brem_dists;
1937  std::vector<double> closest_brem_eta;
1938  // first get the total ecal energy (we should replace this with a cache)
1939  for (const auto& ecal : RO.ecalclusters) {
1940  tot_ecal += ecal->clusterRef()->correctedEnergy();
1941  // we also need to look at the minimum distance to brems
1942  // since energetic brems will be closer to the brem than the track
1943  double min_brem_dist = 5000.0;
1944  double eta = -999.0;
1945  for (const auto& brem : RO.brems) {
1946  const float dist = _currentblock->dist(brem->index(), ecal->index(), _currentlinks, reco::PFBlock::LINKTEST_ALL);
1947  if (dist < min_brem_dist && dist != -1.0f) {
1948  min_brem_dist = dist;
1949  eta = brem->positionAtECALEntrance().Eta();
1950  }
1951  }
1952  min_brem_dists.push_back(min_brem_dist);
1953  closest_brem_eta.push_back(eta);
1954  }
1955 
1956  // loop through the ECAL clusters and remove ECAL clusters matched to
1957  // secondary track either in *or* out of the SC if the E/pin is bad
1958  for (auto secd_kf = RO.secondaryKFs.begin(); secd_kf != RO.secondaryKFs.end(); ++secd_kf) {
1959  reco::TrackRef trkRef = (*secd_kf)->trackRef();
1960  const float secpin = (*secd_kf)->trackRef()->p();
1961  bool remove_this_kf = false;
1962  for (auto ecal = RO.ecalclusters.begin(); ecal != RO.ecalclusters.end(); ++ecal) {
1963  size_t bremidx = std::distance(RO.ecalclusters.begin(), ecal);
1964  const float minbremdist = min_brem_dists[bremidx];
1965  const double ecalenergy = (*ecal)->clusterRef()->correctedEnergy();
1966  const double Epin = ecalenergy / secpin;
1967  const double detaGsf = std::abs(gsfOuterEta - (*ecal)->clusterRef()->positionREP().Eta());
1968  const double detaBrem = std::abs(closest_brem_eta[bremidx] - (*ecal)->clusterRef()->positionREP().Eta());
1969 
1970  bool kf_matched = RO.localMap.contains(ecal->get(), *secd_kf);
1971 
1972  const float tkdist =
1973  _currentblock->dist((*secd_kf)->index(), (*ecal)->index(), _currentlinks, reco::PFBlock::LINKTEST_ALL);
1974 
1975  // do not reject this track if it is closer to a brem than the
1976  // secondary track, or if it lies in the delta-eta plane with the
1977  // gsf track or if it is in the dEta plane with the brems
1978  if (Epin > 3 && kf_matched && tkdist != -1.0f && tkdist < minbremdist && detaGsf > 0.05 && detaBrem > 0.015) {
1979  double res_with = std::abs((tot_ecal - Pin_gsf) / Pin_gsf);
1980  double res_without = std::abs((tot_ecal - ecalenergy - Pin_gsf) / Pin_gsf);
1981  if (res_without < res_with) {
1982  LOGDRESSED("PFEGammaAlgo") << " REJECTED_RES totenergy " << tot_ecal << " Pin_gsf " << Pin_gsf
1983  << " cluster to secondary " << ecalenergy << " res_with " << res_with
1984  << " res_without " << res_without << std::endl;
1985  tot_ecal -= ecalenergy;
1986  remove_this_kf = true;
1987  ecal = RO.ecalclusters.erase(ecal);
1988  if (ecal == RO.ecalclusters.end())
1989  break;
1990  }
1991  }
1992  }
1993  if (remove_this_kf) {
1994  secd_kf = RO.secondaryKFs.erase(secd_kf);
1995  if (secd_kf == RO.secondaryKFs.end())
1996  break;
1997  }
1998  }
1999 }
2000 
2002  bool removeFreeECAL,
2003  bool removeSCEcal) {
2004  std::vector<bool> cluster_in_sc;
2005  auto ecal_begin = RO.ecalclusters.begin();
2006  auto ecal_end = RO.ecalclusters.end();
2007  auto hcal_begin = _splayedblock[reco::PFBlockElement::HCAL].begin();
2008  auto hcal_end = _splayedblock[reco::PFBlockElement::HCAL].end();
2009  for (auto secd_kf = RO.secondaryKFs.begin(); secd_kf != RO.secondaryKFs.end(); ++secd_kf) {
2010  bool remove_this_kf = false;
2011  NotCloserToOther<reco::PFBlockElement::TRACK, reco::PFBlockElement::HCAL> tracksToHCALs(_currentblock, *secd_kf);
2012  reco::TrackRef trkRef = (*secd_kf)->trackRef();
2013 
2014  bool goodTrack = PFTrackAlgoTools::isGoodForEGM(trkRef->algo());
2015  const float secpin = trkRef->p();
2016 
2017  for (auto ecal = ecal_begin; ecal != ecal_end; ++ecal) {
2018  const double ecalenergy = (*ecal)->clusterRef()->correctedEnergy();
2019  // first check if the cluster is in the SC (use dist calc for fastness)
2020  const size_t clus_idx = std::distance(ecal_begin, ecal);
2021  if (cluster_in_sc.size() < clus_idx + 1) {
2022  float dist = -1.0f;
2023  if (RO.parentSC) {
2024  dist = _currentblock->dist((*secd_kf)->index(), (*ecal)->index(), _currentlinks, reco::PFBlock::LINKTEST_ALL);
2025  }
2026  cluster_in_sc.push_back(dist != -1.0f);
2027  }
2028 
2029  // if we've found a secondary KF that matches this ecal cluster
2030  // now we see if it is matched to HCAL
2031  // if it is matched to an HCAL cluster we take different
2032  // actions if the cluster was in an SC or not
2033  if (RO.localMap.contains(ecal->get(), *secd_kf)) {
2034  auto hcal_matched = std::partition(hcal_begin, hcal_end, tracksToHCALs);
2035  for (auto hcalclus = hcal_begin; hcalclus != hcal_matched; ++hcalclus) {
2036  const reco::PFBlockElementCluster* clusthcal =
2037  dynamic_cast<const reco::PFBlockElementCluster*>(hcalclus->get());
2038  const double hcalenergy = clusthcal->clusterRef()->energy();
2039  const double hpluse = ecalenergy + hcalenergy;
2040  const bool isHoHE = ((hcalenergy / hpluse) > 0.1 && goodTrack);
2041  const bool isHoE = (hcalenergy > ecalenergy);
2042  const bool isPoHE = (secpin > hpluse);
2043  if (cluster_in_sc[clus_idx]) {
2044  if (isHoE || isPoHE) {
2045  LOGDRESSED("PFEGammaAlgo") << "REJECTED TRACK FOR H/E or P/(H+E), CLUSTER IN SC"
2046  << " H/H+E " << (hcalenergy / hpluse) << " H/E " << (hcalenergy > ecalenergy)
2047  << " P/(H+E) " << (secpin / hpluse) << " HCAL ENE " << hcalenergy
2048  << " ECAL ENE " << ecalenergy << " secPIN " << secpin << " Algo Track "
2049  << trkRef->algo() << std::endl;
2050  remove_this_kf = true;
2051  }
2052  } else {
2053  if (isHoHE) {
2054  LOGDRESSED("PFEGammaAlgo") << "REJECTED TRACK FOR H/H+E, CLUSTER NOT IN SC"
2055  << " H/H+E " << (hcalenergy / hpluse) << " H/E " << (hcalenergy > ecalenergy)
2056  << " P/(H+E) " << (secpin / hpluse) << " HCAL ENE " << hcalenergy
2057  << " ECAL ENE " << ecalenergy << " secPIN " << secpin << " Algo Track "
2058  << trkRef->algo() << std::endl;
2059  remove_this_kf = true;
2060  }
2061  }
2062  }
2063  }
2064  }
2065  if (remove_this_kf) {
2066  secd_kf = RO.secondaryKFs.erase(secd_kf);
2067  if (secd_kf == RO.secondaryKFs.end())
2068  break;
2069  }
2070  }
2071 }
2072 
2074  bool isPrimary = false;
2075 
2076  const GsfPFRecTrackRef& gsfPfRef = GsfEl.GsftrackRefPF();
2077 
2078  if (gsfPfRef.isNonnull()) {
2079  const PFRecTrackRef& kfPfRef = KfEl.trackRefPF();
2080  PFRecTrackRef kfPfRef_fromGsf = (*gsfPfRef).kfPFRecTrackRef();
2081  if (kfPfRef.isNonnull() && kfPfRef_fromGsf.isNonnull()) {
2082  reco::TrackRef kfref = (*kfPfRef).trackRef();
2083  reco::TrackRef kfref_fromGsf = (*kfPfRef_fromGsf).trackRef();
2084  if (kfref.isNonnull() && kfref_fromGsf.isNonnull()) {
2085  if (kfref == kfref_fromGsf)
2086  isPrimary = true;
2087  }
2088  }
2089  }
2090 
2091  return isPrimary;
2092 }
const SuperClusterRef & superClusterRef() const
const math::XYZTLorentzVector & Pout() const
std::vector< PFGSFElement const * > primaryGSFs
Definition: PFEGammaAlgo.h:94
Abstract base class for a PFBlock element (track, cluster...)
const math::XYZPoint & position() const
cluster centroid position
Definition: CaloCluster.h:153
CaloCluster_iterator preshowerClustersBegin() const
fist iterator over PreshowerCluster constituents
Definition: SuperCluster.h:92
float evaluateSingleLegMVA(const reco::PFBlockRef &blockref, const reco::Vertex &primaryVtx, unsigned int trackIndex)
constexpr int nLayers
Definition: Common.h:13
reco::PFCandidateEGammaExtraCollection candidateExtras
Definition: PFEGammaAlgo.h:119
const reco::GsfTrackRef & GsftrackRef() const
static const TGPicture * info(bool iBackgroundIsBlack)
GBRForests const & gbrForests_
Definition: PFEGammaAlgo.h:140
static bool overlap(const reco::CaloCluster &sc1, const reco::CaloCluster &sc, float minfrac=0.01, bool debug=false)
bool isNonnull() const
Checks for non-null.
Definition: Ref.h:238
void setSuperClusterRef(reco::SuperClusterRef sc)
set reference to the corresponding supercluster
bool unwrapSuperCluster(const reco::PFBlockElementSuperCluster *, std::vector< FlaggedPtr< const PFClusterElement >> &, ClusterMap &)
Ptr< typename C::value_type > refToPtr(Ref< C, typename C::value_type, refhelper::FindUsingAdvance< C, typename C::value_type > > const &ref)
Definition: RefToPtr.h:18
double correctedEnergy() const
Definition: CaloCluster.h:149
void addHitAndFraction(DetId id, float fraction)
Definition: CaloCluster.h:202
reco::PFBlockRef parentBlock
Definition: PFEGammaAlgo.h:85
void Dump(std::ostream &out=std::cout, const char *tab=" ") const override
print the object inside the element
void unlinkRefinableObjectKFandECALWithBadEoverP(ProtoEGObject &)
key_type key() const
Definition: Ptr.h:163
trackRef_iterator tracks_end() const
last iterator over tracks
Definition: Vertex.cc:73
static bool isMuon(const reco::PFBlockElement &elt)
Definition: PFMuonAlgo.cc:56
void setPositionAtECALEntrance(const math::XYZPointF &pos)
set position at ECAL entrance
Definition: PFCandidate.h:349
void setGsfElectronClusterRef(const reco::PFBlockRef &blk, const reco::PFBlockElementCluster &ref)
set gsf electron cluster ref
const PFClusterRef & clusterRef() const override
void addSingleLegConvTrackRefMva(const std::pair< reco::TrackRef, float > &trackrefmva)
add Single Leg Conversion TrackRef
const PFEGConfigInfo cfg_
Definition: PFEGammaAlgo.h:226
void setPreshowerEnergyPlane2(double preshowerEnergy2)
Definition: SuperCluster.h:72
const self & getMap() const
T const * get() const
Returns C++ pointer to the item.
Definition: Ptr.h:139
double y() const
y coordinate
Definition: Vertex.h:117
void setHadEnergy(float val)
set the had energy. The cluster energies should be entered before
const math::XYZPointF & positionAtECALEntrance() const
Type type() const
const reco::TrackRef & trackRef() const override
void Dump(std::ostream &out=std::cout, const char *tab=" ") const override
print the object inside the element
Geom::Phi< T > phi() const
Definition: PV3DBase.h:66
double pflowPhiWidth() const
bool isPrimaryTrack(const reco::PFBlockElementTrack &KfEl, const reco::PFBlockElementGsfTrack &GsfEl)
#define X(str)
Definition: MuonsGrabber.cc:38
bool trackType(TrackType trType) const override
void linkRefinableObjectPrimaryGSFTrackToECAL(ProtoEGObject &)
key_type index() const
Definition: Ref.h:253
const GsfPFRecTrackRef & GsftrackRefPF() const
reco::SuperClusterCollection refinedSuperClusters
Definition: PFEGammaAlgo.h:120
std::map< unsigned int, Link > LinkData
Definition: PFBlock.h:39
edm::Ref< TrackExtraCollection > TrackExtraRef
persistent reference to a TrackExtra
Definition: TrackExtraFwd.h:16
std::vector< const PFClusterElement * > electronClusters
Definition: PFEGammaAlgo.h:106
unsigned int index
index type
Definition: Vertex.h:53
const LinkData & linkData() const
Definition: PFBlock.h:88
void setSeed(const CaloClusterPtr &r)
list of used xtals by DetId // now inherited by CaloCluster
Definition: SuperCluster.h:107
const math::XYZTLorentzVector & Pin() const
edm::Ptr< CaloCluster > CaloClusterPtr
void linkRefinableObjectBremTangentsToECAL(ProtoEGObject &)
ROOT::Math::PositionVector3D< ROOT::Math::Cartesian3D< float > > XYZPointF
point in space with cartesian internal representation
Definition: Point3D.h:10
unsigned int indTrajPoint() const
void find(edm::Handle< EcalRecHitCollection > &hits, DetId thisDet, std::vector< EcalRecHitCollection::const_iterator > &hit, bool debug=false)
Definition: FindCaloHit.cc:19
reco::PFBlockRef _currentblock
Definition: PFEGammaAlgo.h:149
void setGsfTrackRef(const reco::GsfTrackRef &ref)
set gsftrack reference
void setPhiWidth(double pw)
Definition: SuperCluster.h:73
const Point & position() const
position
Definition: Vertex.h:113
double pflowEtaWidth() const
const math::XYZPointF & positionAtECALEntrance() const
bool isAvailable() const
Definition: Ref.h:537
const std::unique_ptr< const GBRForest > singleLeg_
Definition: PFEGammaAlgo.h:81
void linkRefinableObjectECALToSingleLegConv(ProtoEGObject &)
std::vector< PFKFElement const * > primaryKFs
Definition: PFEGammaAlgo.h:95
void setCharge(Charge q) final
set electric charge
XYZTLorentzVectorD XYZTLorentzVector
Lorentz vector with cylindrical internal representation using pseudorapidity.
Definition: LorentzVector.h:29
void set_mva_e_pi(float mvaNI)
Definition: PFCandidate.h:314
#define LOGERR(x)
Definition: PFEGammaAlgo.cc:47
static double delPhi(const double phi1, const double phi2)
void setEtaWidth(double ew)
Definition: SuperCluster.h:74
void concatenate(CommutativePairs< T > const &other)
Add the pairs from another CommutativePairs to this.
void setSigmaEtaEta(float val)
set the sigmaetaeta
#define LOGVERB(x)
Definition: PFEGammaAlgo.cc:45
#define LOGDRESSED(x)
Definition: PFEGammaAlgo.cc:48
const std::vector< std::pair< DetId, float > > & hitsAndFractions() const
Definition: CaloCluster.h:209
void dumpCurrentRefinableObjects() const
void initializeProtoCands(std::list< ProtoEGObject > &)
bool isGoodForEGM(const reco::TrackBase::TrackAlgorithm &)
void linkRefinableObjectPrimaryKFsToSecondaryKFs(ProtoEGObject &)
unsigned index() const
bool goodTrack(const reco::Track *pTrack, math::XYZPoint leadPV, trackSelectionParameters parameters, bool debug=false)
double pflowSigmaEtaEta() const
void setCorrectedEnergy(double cenergy)
Definition: CaloCluster.h:136
void addElementInBlock(const reco::PFBlockRef &blockref, unsigned elementIndex)
add an element to the current PFCandidate
Definition: PFCandidate.cc:199
void swap(edm::DataFrameContainer &lhs, edm::DataFrameContainer &rhs)
std::vector< PFKFElement const * > secondaryKFs
Definition: PFEGammaAlgo.h:99
reco::PFCluster::EEtoPSAssociation EEtoPSAssociation
Definition: PFEGammaAlgo.h:64
T sqrt(T t)
Definition: SSEVec.h:19
double p4[4]
Definition: TauolaWrapper.h:92
const PFSCElement * parentSC
Definition: PFEGammaAlgo.h:86
std::vector< FlaggedPtr< const PFClusterElement > > ecalclusters
Definition: PFEGammaAlgo.h:91
bool trackType(TrackType trType) const override
const_iterator find(uint32_t rawId) const
void setEarlyBrem(float val)
set EarlyBrem
Abs< T >::type abs(const T &t)
Definition: Abs.h:22
double z() const
z coordinate
Definition: Vertex.h:119
void setGsfTrackPout(const math::XYZTLorentzVector &pout)
set the pout (not trivial to get from the GSF track)
void insert(T const &a, T const &b)
double f[11][100]
reco::Vertex const * primaryVertex_
Definition: PFEGammaAlgo.h:227
double energy() const
cluster energy
Definition: CaloCluster.h:148
std::vector< PFBremElement const * > brems
Definition: PFEGammaAlgo.h:96
T const * get() const
Returns C++ pointer to the item.
Definition: Ref.h:232
T min(T a, T b)
Definition: MathUtil.h:58
void linkKFTrackToECAL(PFKFElement const *, ProtoEGObject &)
bool isNull() const
Checks for null.
Definition: Ref.h:235
void Dump(std::ostream &out=std::cout, const char *tab=" ") const override
print the object inside the element
double energy() const
cluster energy
Definition: PFCluster.h:78
bool isNonnull() const
Checks for non-null.
Definition: Ptr.h:146
CommutativePairs< const reco::PFBlockElement * > localMap
Definition: PFEGammaAlgo.h:103
void setEcalEnergy(float eeRaw, float eeCorr)
set corrected Ecal energy
Definition: PFCandidate.h:214
Layer
layer definition
Definition: PFLayer.h:29
reco::PFBlock::LinkData _currentlinks
Definition: PFEGammaAlgo.h:150
reco::PFCluster::EEtoPSAssociation const * eetops_
Definition: PFEGammaAlgo.h:148
double x() const
x coordinate
Definition: Vertex.h:115
double rawEnergy() const
raw uncorrected energy (sum of energies of component BasicClusters)
Definition: SuperCluster.h:58
bool isMuon(const reco::PFBlockElement &)
void fillExtraInfo(const ProtoEGObject &, reco::PFCandidateEGammaExtra &)
bool contains(T const &a, T const &b) const
void setDeltaEta(float val)
set the delta eta
PFEGammaAlgo(const PFEGConfigInfo &, GBRForests const &gbrForests)
void setGsfTrackRef(const reco::GsfTrackRef &ref)
set gsftrack reference
Definition: PFCandidate.cc:424
reco::ElectronSeedRef electronSeed
Definition: PFEGammaAlgo.h:87
float calculateEleMVA(const ProtoEGObject &, reco::PFCandidateEGammaExtra &) const
XYZVectorD XYZVector
spatial vector with cartesian internal representation
Definition: Vector3D.h:31
EgammaObjects operator()(const reco::PFBlockRef &block)
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:60
XYZPointD XYZPoint
point in space with cartesian internal representation
Definition: Point3D.h:12
void addPreshowerCluster(const CaloClusterPtr &r)
add reference to constituent BasicCluster
Definition: SuperCluster.h:128
double b
Definition: hdecay.h:118
reco::PFCandidateCollection candidates
Definition: PFEGammaAlgo.h:118
#define docast(x, y)
Definition: PFEGammaAlgo.cc:44
void linkRefinableObjectGSFTracksToKFs(ProtoEGObject &)
void setSuperClusterPFECALRef(reco::SuperClusterRef sc)
set reference to the corresponding supercluster
void addConversionRef(const reco::ConversionRef &convref)
add Conversions from PF
std::vector< Item >::const_iterator const_iterator
double energyEm(const reco::PFCluster &clusterEcal, std::vector< double > &EclustersPS1, std::vector< double > &EclustersPS2, bool crackCorrection=true) const
void addCluster(const CaloClusterPtr &r)
add reference to constituent BasicCluster
Definition: SuperCluster.h:122
void removeOrLinkECALClustersToKFTracks()
Particle reconstructed by the particle flow algorithm.
Definition: PFCandidate.h:40
fixed size matrix
#define LOGWARN(x)
Definition: PFEGammaAlgo.cc:46
reco::SuperCluster buildRefinedSuperCluster(const ProtoEGObject &)
EgammaObjects fillPFCandidates(const std::list< ProtoEGObject > &)
void unlinkRefinableObjectKFandECALMatchedToHCAL(ProtoEGObject &, bool removeFreeECAL=false, bool removeSCECAL=false)
double a
Definition: hdecay.h:119
std::vector< PFClusterElement const * > hcalClusters
Definition: PFEGammaAlgo.h:102
void mergeROsByAnyLink(std::list< ProtoEGObject > &)
std::vector< std::vector< FlaggedPtr< const reco::PFBlockElement > > > _splayedblock
Definition: PFEGammaAlgo.h:153
const PFRecTrackRef & trackRefPF() const override
PFEnergyCalibration thePFEnergyCalibration_
Definition: PFEGammaAlgo.h:142
void setSuperClusterRef(const reco::SuperClusterRef &scRef)
Definition: PFCandidate.cc:576
int attachPSClusters(const PFClusterElement *, ClusterMap::mapped_type &)
bool isGoodForEGMPrimary(const reco::TrackBase::TrackAlgorithm &)
void linkRefinableObjectKFTracksToECAL(ProtoEGObject &)
void setKfTrackRef(const reco::TrackRef &ref)
set kf track reference
const std::unique_ptr< const GBRForest > ele_
Definition: PFEGammaAlgo.h:80
const CaloClusterPtr & seed() const
seed BasicCluster
Definition: SuperCluster.h:77
void setMVA(float val)
set the result (mostly for debugging)
vars
Definition: DeepTauId.cc:158
void linkRefinableObjectSecondaryKFsToECAL(ProtoEGObject &)
void setLateBrem(float val)
set LateBrem
trackRef_iterator tracks_begin() const
first iterator over tracks
Definition: Vertex.cc:71
void linkRefinableObjectConvSecondaryKFsToSecondaryKFs(ProtoEGObject &)
std::unordered_map< PFClusterElement const *, std::vector< PFClusterElement const * >> ClusterMap
Definition: PFEGammaAlgo.h:72
const edm::OwnVector< reco::PFBlockElement > & elements() const
Definition: PFBlock.h:85
void setTrackRef(const reco::TrackRef &ref)
set track reference
Definition: PFCandidate.cc:393
long double T
bool next_combination(BidIt n_begin, BidIt n_end, BidIt r_begin, BidIt r_end)
Definition: combination.h:19
void setTime(float time, float timeError=0.f)
the timing information
Definition: PFCandidate.h:420
void setPdgId(int pdgId) final
void setPreshowerEnergyPlane1(double preshowerEnergy1)
Definition: SuperCluster.h:71
void setP4(const LorentzVector &p4) final
set 4-momentum
def move(src, dest)
Definition: eostools.py:511
#define constexpr
const ESChannelStatus * channelStatus_
Definition: PFEGammaAlgo.h:229
CaloCluster_iterator preshowerClustersEnd() const
last iterator over PreshowerCluster constituents
Definition: SuperCluster.h:95
void addExtraNonConvTrack(const reco::PFBlockRef &blk, const reco::PFBlockElementTrack &tkref)
track counting for electrons and photons
void setPreshowerEnergy(double preshowerEnergy)
Definition: SuperCluster.h:70
void linkRefinableObjectPrimaryGSFTrackToHCAL(ProtoEGObject &)
Block of elements.
Definition: PFBlock.h:26