ATLAS Offline Software
Loading...
Searching...
No Matches
METEgammaAssociator.cxx
Go to the documentation of this file.
1
2
3/*
4 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
5*/
6
7// METEgammaAssociator.cxx
8// Implementation file for class METEgammaAssociator
9//
10// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
11//
12// Author: P Loch, S Resconi, TJ Khoo, AS Mete
14
15// METReconstruction includes
17
18// Egamma EDM
22
23// Tracking EDM
24#include "xAODTracking/Vertex.h"
25
26// DeltaR calculation
28
33
34namespace met {
35
36 using namespace xAOD;
37
38 //accessor for PV
39 const static SG::ConstAccessor<char> PVMatchedAcc("matchedToPV");
40
41 // Constructors
46 {
47 // 0 is delta-R geometrical matching
48 // 1 is using TopoClusterLink decoration on clusters
49 declareProperty( "TCMatchMethod", m_tcMatch_method = DeltaR );
50 declareProperty( "TCMatchMaxRat", m_tcMatch_maxRat = 1.5 );
51 declareProperty( "TCMatchDeltaR", m_tcMatch_dR = 0.1 );
52 declareProperty( "ExtraTrackMatchDeltaR", m_extraTrkMatch_dR = 0.05 );
53 declareProperty( "CheckUnmatched", m_checkUnmatched = false);
54 }
55
56 // Destructor
59 = default;
60
61 // Athena algtool's Hooks
64 {
66 ATH_MSG_VERBOSE ("Initializing " << name() << "...");
67 switch(m_tcMatch_method) {
68 case DeltaR: ATH_MSG_INFO("Egamma-topocluster matching uses DeltaR check."); break;
69 case ClusterLink: ATH_MSG_INFO("Egamma-topocluster matching uses topocluster links."); break;
70 default:
71 ATH_MSG_WARNING( "Invalid topocluster match method configured!" );
72 return StatusCode::FAILURE;
73 }
74
75 return StatusCode::SUCCESS;
76 }
77
79 {
80 ATH_MSG_VERBOSE ("Finalizing " << name() << "...");
81 return StatusCode::SUCCESS;
82 }
83
84
85 // **********************************************************************
86 // Get Egamma constituents
88 std::vector<const xAOD::IParticle*>& tclist,
89 const met::METAssociator::ConstitHolder& constits) const
90 {
91 const Egamma *eg = static_cast<const Egamma*>(obj);
92 // safe to assume a single SW cluster?
93 // will do so for now...
94 const CaloCluster* swclus = eg->caloCluster();
95
96 // the matching strategy depends on how the cluster container is sorted
97 // easier if it's sorted in descending pt order
98 // we'll worry about optimisation later
99 std::vector<const IParticle*> inputTC;
100 inputTC.reserve(10);
101
103 for(const auto *const cl : *constits.tcCont) {
104 // this can probably be done more elegantly
105 if(P4Helpers::isInDeltaR(*swclus,*cl,m_tcMatch_dR,m_useRapidity) && cl->e()>FLT_MIN) {
106 // could consider also requirements on the EM fraction or depth
107 inputTC.push_back(cl);
108 } // match TC in a cone around SW cluster
109 }
110 ATH_MSG_VERBOSE("Found " << inputTC.size() << " nearby topoclusters");
111 std::sort(inputTC.begin(),inputTC.end(),greaterPt);
112 } else if(m_tcMatch_method==ClusterLink) {
113 static const SG::AuxElement::ConstAccessor<std::vector<ElementLink<CaloClusterContainer> > > tcLinkAcc("constituentClusterLinks");
114 // Fill a vector of vectors
115 for(const auto& el : tcLinkAcc(*swclus)) {
116 if(el.isValid())
117 inputTC.push_back(*el);
118 else{
119 ATH_MSG_ERROR("Invalid constituentClusterLinks on input electron/photon!");
120 return StatusCode::FAILURE;
121 }
122 }
123 ATH_MSG_VERBOSE("Found " << inputTC.size() << " linked topoclusters");
124 } else {
125 ATH_MSG_WARNING( "Invalid topocluster match method configured!" );
126 return StatusCode::FAILURE;
127 }
128
129 ATH_CHECK( selectEgammaClusters(swclus, inputTC, tclist) );
130
131 return StatusCode::SUCCESS;
132 }
133
134
136 std::vector<const xAOD::IParticle*>& constlist,
137 const met::METAssociator::ConstitHolder& constits) const
138 {
139 const xAOD::Egamma *eg = static_cast<const xAOD::Egamma*>(obj);
140 std::set<const xAOD::TrackParticle*> trackset; // use a set for duplicate-free retrieval
141 ATH_CHECK( selectEgammaTracks(eg, constits.trkCont, trackset) );
142 for(const auto& track : trackset) {
143 if(acceptTrack(track,constits.pv) && isGoodEoverP(track)) {
144 constlist.push_back(track);
145 }
146 }
147 return StatusCode::SUCCESS;
148 }
149
151 std::vector<const xAOD::IParticle*>& pfolist,
152 const met::METAssociator::ConstitHolder& constits,
153 std::map<const IParticle*,MissingETBase::Types::constvec_t> &/*momenta*/) const
154 {
155 const xAOD::Egamma *eg = static_cast<const xAOD::Egamma*>(obj);
156
157 if (m_usePFOLinks)
158 ATH_CHECK( extractPFOsFromLinks(eg, pfolist,constits) );
159 else
160 ATH_CHECK( extractPFOs(eg, pfolist, constits) );
161
162 return StatusCode::SUCCESS;
163 }
164
166 std::vector<const xAOD::IParticle*>& pfolist,
167 const met::METAssociator::ConstitHolder& constits) const
168 {
169
170 ATH_MSG_DEBUG("Extract PFOs From Links for " << eg->type() << " with pT " << eg->pt());
171
172 std::vector<PFOLink_t> cPFOLinks;
173 std::vector<PFOLink_t> nPFOLinks;
174
175 if (eg->type() == xAOD::Type::Electron){
178 nPFOLinks=neutralPFOReadDecorHandle(*eg);
179 cPFOLinks=chargedPFOReadDecorHandle(*eg);
180 }
181 if (eg->type() == xAOD::Type::Photon) {
184 nPFOLinks=neutralPFOReadDecorHandle(*eg);
185 cPFOLinks=chargedPFOReadDecorHandle(*eg);
186 }
187
188
189 // Charged PFOs
190 for (const PFOLink_t& pfoLink : cPFOLinks) {
191 if (!pfoLink.isValid()) continue;
192 const xAOD::PFO* pfo_init = *pfoLink;
193 for (const auto *const pfo : *constits.pfoCont){
194 if (pfo->index() == pfo_init->index() && pfo->isCharged()){ //index-based match between JetETmiss and CHSParticleFlow collections
195 const static SG::AuxElement::ConstAccessor<char> PVMatchedAcc("matchedToPV");
196 if( pfo->isCharged() && PVMatchedAcc(*pfo)&& ( !m_cleanChargedPFO || isGoodEoverP(pfo->track(0)) ) ) {
197 ATH_MSG_DEBUG("Accept cPFO with pt " << pfo->pt() << ", e " << pfo->e() << ", eta " << pfo->eta() << ", phi " << pfo->phi() );
198 if (!m_checkUnmatched || !hasUnmatchedClusters(eg,pfo_init)) pfolist.push_back(pfo);
199 }
200 }
201 }
202 } // end cPFO loop
203
204 // Neutral PFOs
205 double eg_cl_e = eg->caloCluster()->e();
206 double sumE_pfo = 0.;
207
208 for (const PFOLink_t& pfoLink : nPFOLinks) {
209 if (!pfoLink.isValid()) continue;
210 const xAOD::PFO* pfo_init = *pfoLink;
211 for (const auto *const pfo : *constits.pfoCont){
212 if (pfo->index() == pfo_init->index() && !pfo->isCharged()){ //index-based match between JetETmiss and CHSParticleFlow collections
213 double pfo_e = pfo->eEM();
214 if( ( !pfo->isCharged()&& pfo->e() > FLT_MIN ) ){
215 sumE_pfo += pfo_e;
216 ATH_MSG_DEBUG("E match with new nPFO: " << fabs(sumE_pfo+pfo_e - eg_cl_e) / eg_cl_e);
217 ATH_MSG_DEBUG("Accept nPFO with pt " << pfo->pt() << ", e " << pfo->e() << ", eta " << pfo->eta() << ", phi " << pfo->phi() << " in sum.");
218 ATH_MSG_DEBUG("Energy ratio of nPFO to eg: " << pfo_e / eg_cl_e);
219 pfolist.push_back(pfo);
220 }
221 }
222 }
223 } // end nPFO links loop
224
225
226 return StatusCode::SUCCESS;
227 }
228
230 std::vector<const xAOD::IParticle*>& pfolist,
231 const met::METAssociator::ConstitHolder& constits) const
232
233 {
234 // safe to assume a single SW cluster?
235 // will do so for now...
236 const xAOD::IParticle* swclus = eg->caloCluster();
237 ANA_MSG_VERBOSE("Extract PFOs with DeltaR for " << eg->type() << " with pT " << eg->pt());
238
239 // Preselect PFOs based on proximity: dR<0.4
240 std::vector<const xAOD::PFO*> nearbyPFO;
241 nearbyPFO.reserve(20);
242 for(const auto *const pfo : *constits.pfoCont) {
243 if(P4Helpers::isInDeltaR(*pfo, *swclus, 0.4, m_useRapidity)) {
244 // We set a small -ve pt for cPFOs that were rejected
245 // by the ChargedHadronSubtractionTool
246 const static SG::AuxElement::ConstAccessor<char> PVMatchedAcc("matchedToPV");
247 if( ( !pfo->isCharged() && pfo->e() > FLT_MIN ) ||
248 ( pfo->isCharged() && PVMatchedAcc(*pfo)
249 && ( !m_cleanChargedPFO || isGoodEoverP(pfo->track(0)) ) )
250 ) {
251 nearbyPFO.push_back(pfo);
252 } // retain +ve E neutral PFOs and charged PFOs passing PV association
253 } // DeltaR check
254 } // PFO loop
255 ATH_MSG_VERBOSE("Found " << nearbyPFO.size() << " nearby pfos");
256
257 std::set<const xAOD::TrackParticle*> trackset; // use a set for duplicate-free retrieval
258 ATH_CHECK( selectEgammaTracks(eg, constits.trkCont, trackset) );
259 for(const auto& track : trackset) {
260 for(const auto& pfo : nearbyPFO) {
261 if(pfo->isCharged() && pfo->track(0) == track) {
262 pfolist.push_back(pfo);
263 } // PFO/track match
264 } // PFO loop
265 } // Track loop
266 double eg_cl_e = swclus->e();
267
268 // the matching strategy depends on how the cluster container is sorted
269 // easier if it's sorted in descending pt order
270 // ideally this should be done using cell matching, but we can't use the links from topoclusters reliably
271 // because some PFOs don't correspond to the original TC
272 bool doSum = true;
273 double sumE_pfo = 0.;
274 const IParticle* bestbadmatch = nullptr;
275 std::sort(nearbyPFO.begin(),nearbyPFO.end(),greaterPtPFO);
276 for(const auto& pfo : nearbyPFO) {
277 // Skip charged PFOs, as we already matched them
278 if(pfo->isCharged() || !P4Helpers::isInDeltaR(*pfo, *swclus, m_tcMatch_dR, m_useRapidity)) {continue;}
279 // Handle neutral PFOs like topoclusters
280 double pfo_e = pfo->eEM();
281 // skip cluster if it's above our bad match threshold or outside the matching radius
282 if(pfo_e>m_tcMatch_maxRat*eg_cl_e) {
283 ATH_MSG_VERBOSE("Reject topocluster in sum. Ratio vs eg cluster: " << (pfo_e/eg_cl_e));
284 if( !bestbadmatch || (fabs(pfo_e/eg_cl_e-1.) < fabs(bestbadmatch->e()/eg_cl_e-1.)) ) bestbadmatch = pfo;
285 continue;
286 }
287
288 ATH_MSG_VERBOSE("E match with new nPFO: " << fabs(sumE_pfo+pfo_e - eg_cl_e) / eg_cl_e);
289 if( (doSum = fabs(sumE_pfo+pfo_e-eg_cl_e) < fabs(sumE_pfo - eg_cl_e)) ) {
290 pfolist.push_back(pfo);
291 sumE_pfo += pfo_e;
292 ATH_MSG_VERBOSE("Accept pfo with pt " << pfo->pt() << ", e " << pfo->e() << " in sum.");
293 ATH_MSG_VERBOSE("Energy ratio of nPFO to eg: " << pfo_e / eg_cl_e);
294 ATH_MSG_VERBOSE("E match with new PFO: " << fabs(sumE_pfo+pfo_e - eg_cl_e) / eg_cl_e);
295 } // if we will retain the topocluster
296 else {break;}
297 } // loop over nearby clusters
298 if(sumE_pfo<FLT_MIN && bestbadmatch) {
299 ATH_MSG_VERBOSE("No better matches found -- add bad match topocluster with pt "
300 << bestbadmatch->pt() << ", e " << bestbadmatch->e() << ".");
301 pfolist.push_back(bestbadmatch);
302 }
303
304 return StatusCode::SUCCESS;
305 }
306
308 std::vector<const xAOD::IParticle*>& felist,
309 const met::METAssociator::ConstitHolder& constits,
310 std::map<const IParticle*,MissingETBase::Types::constvec_t> &/*momenta*/) const
311 {
312 const xAOD::Egamma *eg = static_cast<const xAOD::Egamma*>(obj);
313
314 if (m_useFELinks)
315 ATH_CHECK( extractFEsFromLinks(eg, felist,constits) );
316 else
317 ATH_CHECK( extractFEs(eg, felist, constits) );
318
319 return StatusCode::SUCCESS;
320 }
321
322
323 StatusCode METEgammaAssociator::extractFEsFromLinks(const xAOD::Egamma* eg, // TODO: to be tested
324 std::vector<const xAOD::IParticle*>& felist,
325 const met::METAssociator::ConstitHolder& constits) const
326 {
327
328 ATH_MSG_DEBUG("Extract FEs From Links for " << eg->type() << " with pT " << eg->pt());
329
330 std::vector<FELink_t> nFELinks;
331 std::vector<FELink_t> cFELinks;
332
333 if (eg->type() == xAOD::Type::Electron){
336 nFELinks=neutralFEReadDecorHandle(*eg);
337 cFELinks=chargedFEReadDecorHandle(*eg);
338 }
339 if (eg->type() == xAOD::Type::Photon) {
342 nFELinks=neutralFEReadDecorHandle(*eg);
343 cFELinks=chargedFEReadDecorHandle(*eg);
344 }
345
346
347 // Charged FEs
348 for (const FELink_t& feLink : cFELinks) {
349 if (!feLink.isValid()) continue;
350 const xAOD::FlowElement* fe_init = *feLink;
351 for (const auto *const fe : *constits.feCont){
352 if (fe->index() == fe_init->index() && fe->isCharged()){ //index-based match between JetETmiss and CHSFlowElements collections
353 const static SG::AuxElement::ConstAccessor<char> PVMatchedAcc("matchedToPV");
354 if( fe->isCharged() && PVMatchedAcc(*fe)&& ( !m_cleanChargedPFO || isGoodEoverP(static_cast<const xAOD::TrackParticle*>(fe->chargedObject(0))) ) ) {
355 ATH_MSG_DEBUG("Accept cFE with pt " << fe->pt() << ", e " << fe->e() << ", eta " << fe->eta() << ", phi " << fe->phi() );
356 felist.push_back(fe);
357 }
358 }
359 }
360 } // end cFE loop
361
362 // Neutral FEs
363 double eg_cl_e = eg->caloCluster()->e();
364 double sumE_fe = 0.;
365
366 for (const FELink_t& feLink : nFELinks) {
367 if (!feLink.isValid()) continue;
368 const xAOD::FlowElement* fe_init = *feLink;
369 for (const auto *const fe : *constits.feCont){
370 if (fe->index() == fe_init->index() && !fe->isCharged()){ //index-based match between JetETmiss and CHSFlowElements collections
371 double fe_e = fe->e();
372 if( ( !fe->isCharged()&& fe->e() > FLT_MIN ) ){
373 sumE_fe += fe_e;
374 ATH_MSG_DEBUG("E match with new nFE: " << fabs(sumE_fe+fe_e - eg_cl_e) / eg_cl_e);
375 ATH_MSG_DEBUG("Accept nFE with pt " << fe->pt() << ", e " << fe->e() << ", eta " << fe->eta() << ", phi " << fe->phi() << " in sum.");
376 ATH_MSG_DEBUG("Energy ratio of nFE to eg: " << fe_e / eg_cl_e);
377 felist.push_back(fe);
378 }
379 }
380 }
381 } // end nFE links loop
382
383
384 return StatusCode::SUCCESS;
385 }
386
388 std::vector<const xAOD::IParticle*>& felist,
389 const met::METAssociator::ConstitHolder& constits) const
390 {
391 ATH_MSG_VERBOSE("Extract FEs From DeltaR for " << eg->type() << " with pT " << eg->pt());
392
393 // safe to assume a single SW cluster?
394 // will do so for now...
395 const xAOD::IParticle* swclus = eg->caloCluster();
396
397 // Preselect PFOs based on proximity: dR<0.4
398 std::vector<const xAOD::FlowElement*> nearbyFE;
399 nearbyFE.reserve(20);
400 for(const xAOD::FlowElement* fe : *constits.feCont) {
402 ATH_MSG_ERROR("Attempted to extract non-PFlow FlowElements. This is not supported!");
403 return StatusCode::FAILURE;
404 }
405 if(P4Helpers::isInDeltaR(*fe, *swclus, 0.4, m_useRapidity)) {
406 // We set a small -ve pt for cPFOs that were rejected
407 // by the ChargedHadronSubtractionTool
408 const static SG::AuxElement::ConstAccessor<char> PVMatchedAcc("matchedToPV");
409 if( ( !fe->isCharged() && fe->e() > FLT_MIN ) ||
410 ( fe->isCharged() && PVMatchedAcc(*fe)
411 && ( !m_cleanChargedPFO || isGoodEoverP(static_cast<const xAOD::TrackParticle*>(fe->chargedObject(0))) ) )
412 ) {
413 nearbyFE.push_back(fe);
414 } // retain +ve E neutral PFOs and charged PFOs passing PV association
415 } // DeltaR check
416 } // PFO loop
417 ATH_MSG_VERBOSE("Found " << nearbyFE.size() << " nearby FlowElements (PFOs)");
418
419 std::set<const xAOD::TrackParticle*> trackset; // use a set for duplicate-free retrieval
420 ATH_CHECK( selectEgammaTracks(eg, constits.trkCont, trackset) );
421 for(const xAOD::TrackParticle* track : trackset) {
422 for(const xAOD::FlowElement* fe : nearbyFE) {
423 if(fe->isCharged() && fe->chargedObject(0) == track) {
424 felist.push_back(fe);
425 } // PFO/track match
426 } // PFO loop
427 } // Track loop
428 double eg_cl_e = swclus->e();
429
430 // the matching strategy depends on how the cluster container is sorted
431 // easier if it's sorted in descending pt order
432 // ideally this should be done using cell matching, but we can't use the links from topoclusters reliably
433 // because some PFOs don't correspond to the original TC
434 bool doSum = true;
435 double sumE_pfo = 0.;
436 const IParticle* bestbadmatch = nullptr;
437 std::sort(nearbyFE.begin(),nearbyFE.end(),greaterPtFE);
438 for(const xAOD::FlowElement* fe : nearbyFE) {
439 // Skip charged PFOs, as we already matched them
440 if(fe->isCharged() || !P4Helpers::isInDeltaR(*fe, *swclus, m_tcMatch_dR, m_useRapidity)) continue;
441 // Handle neutral PFOs like topoclusters
442 // TODO: Use EM-scale energy here in the future? No way to access from FlowElement in general.
443 double pfo_e = fe->e();
444 // skip cluster if it's above our bad match threshold or outside the matching radius
445 if(pfo_e > m_tcMatch_maxRat*eg_cl_e) {
446 ATH_MSG_VERBOSE("Reject topocluster in sum. Ratio vs eg cluster: " << (pfo_e/eg_cl_e));
447 if( !bestbadmatch || (fabs(pfo_e/eg_cl_e-1.) < fabs(bestbadmatch->e()/eg_cl_e-1.)) ) bestbadmatch = fe;
448 continue;
449 }
450
451 ATH_MSG_VERBOSE("E match with new nPFO: " << fabs(sumE_pfo+pfo_e - eg_cl_e) / eg_cl_e);
452 if( (doSum = fabs(sumE_pfo+pfo_e-eg_cl_e) < fabs(sumE_pfo - eg_cl_e)) ) {
453 felist.push_back(fe);
454 sumE_pfo += pfo_e;
455 ATH_MSG_VERBOSE("Accept pfo with pt " << fe->pt() << ", e " << fe->e() << " in sum.");
456 ATH_MSG_VERBOSE("Energy ratio of nPFO to eg: " << pfo_e / eg_cl_e);
457 ATH_MSG_VERBOSE("E match with new PFO: " << fabs(sumE_pfo+pfo_e - eg_cl_e) / eg_cl_e);
458 } // if we will retain the topocluster
459 else break;
460 } // loop over nearby clusters
461 if(sumE_pfo<FLT_MIN && bestbadmatch) {
462 ATH_MSG_VERBOSE("No better matches found -- add bad match topocluster with pt "
463 << bestbadmatch->pt() << ", e " << bestbadmatch->e() << ".");
464 felist.push_back(bestbadmatch);
465 }
466
467 return StatusCode::SUCCESS;
468 }
469
470
471 // add HR implementation from release 21.2
472 // extractFE for W precision-type measurements
474 std::vector<const xAOD::IParticle*> hardObjs,
475 std::vector<const xAOD::IParticle*>& felist,
476 const met::METAssociator::ConstitHolder& constits,
477 std::map<const IParticle*,MissingETBase::Types::constvec_t> & /*momenta*/,
478 float& UEcorr) const
479 {
480 // Constructing association electron-FE map
481 if(obj->type() != xAOD::Type::ObjectType::Electron){
482 UEcorr=0.0;
483 felist={};
484 return StatusCode::SUCCESS;
485 }
486 const xAOD::Egamma *eg = static_cast<const xAOD::Egamma*>(obj);
487
488 // Preselect charged and neutral FEs, based on proximity: dR < m_Drcone
489 for(const auto fe : *constits.feCont) {
490 if(eg && P4Helpers::isInDeltaR(*fe, *eg, m_Drcone, m_useRapidity)) {
491 if( ( !fe->isCharged() && fe->e() > FLT_MIN ) ||
492 ( fe->isCharged() && PVMatchedAcc(*fe) && ( !m_cleanChargedPFO || isGoodEoverP(static_cast<const xAOD::TrackParticle*>(fe->chargedObject(0))) ) ) ) {
493 felist.push_back(fe);
494 } // quality cuts
495 } // DeltaR check
496 } // FE loop
497
498 // Step 2. Calculating Uncorrected HR and UE energy correction
499 if(eg){
500 // Vectoral sum of all FEs
501 TLorentzVector HR; // uncorrected HR (initialized with 0,0,0,0 automatically)
502 for(const auto fe_itr : *constits.feCont) {
503 if( fe_itr->pt() < 0 || fe_itr->e() < 0 ) { // sanity check
504 continue;
505 }
506
507 //remove charged FE that are not matched to the PV
508 if(fe_itr->isCharged() && !PVMatchedAcc(*fe_itr)){
509 continue;
510 }
511 HR += fe_itr->p4();
512 }
513
514 // Create a vector of egamma form hardObjs (all electrons)
515 std::vector<const xAOD::Egamma*> v_eg;
516 for(const auto& obj_i : hardObjs){
517 const xAOD::Egamma* eg_curr = static_cast<const xAOD::Egamma*>(obj_i); // current egamma object
518 v_eg.push_back( eg_curr );
519 }
520
521 // Subtruct FEs which are in the cone around egamma (gives uncorrected HR)
522 for(const auto fe_i : *constits.feCont) { // charged and neutral FEs
523 if( fe_i->pt() < 0 || fe_i->e() < 0 ) { // sanity check
524 continue;
525 }
526 //std::cout << "new eg candidate" << std::endl;
527 for(const auto& eg_i : v_eg) { // loop over v_eg
528 double dR = P4Helpers::deltaR( fe_i->eta(), fe_i->phi(), eg_i->eta(), eg_i->phi() );
529 if( dR < m_Drcone ) {
530 HR -= fe_i->p4();
531 break;
532 }
533 } // over v_eg
534 } // over FEs
535
536 // Save v_eg as a vector TLV (as commonn type for electrons and muons)
537 std::vector<TLorentzVector> v_egTLV;
538 v_egTLV.reserve(v_eg.size());
539 for(const auto& eg_i : v_eg) { // loop over v_eg
540 v_egTLV.push_back( eg_i->p4() );
541 }
542
543 // Save current eg as TLV
544 TLorentzVector egTLV = eg->p4();
545
546 // Get UE correction
547 ATH_CHECK( GetUEcorr(constits, v_egTLV, egTLV, HR, m_Drcone, m_MinDistCone, UEcorr) );
548 } // eg existance requirement
549
550 return StatusCode::SUCCESS;
551 }
552
553 //**********************************************************************
554 // Select Egamma tracks & clusters
555
557 const std::vector<const IParticle*>& inputTC,
558 std::vector<const xAOD::IParticle*>& tclist) const
559 {
560 double eg_cl_e = swclus->e();
561
562 bool doSum = true;
563 double sumE_tc = 0.;
564 const IParticle* bestbadmatch = nullptr;
565 for(const auto& cl : inputTC) {
566 double tcl_e = cl->e();
567 // skip cluster if it's above our bad match threshold
568 // retain pointer of the closest matching cluster in case no better is found
569 if(tcl_e>m_tcMatch_maxRat*eg_cl_e) {
570 ATH_MSG_VERBOSE("Reject topocluster in sum. Ratio vs eg cluster: " << (tcl_e/eg_cl_e));
571 if( !bestbadmatch || (fabs(tcl_e/eg_cl_e-1.) < fabs(bestbadmatch->e()/eg_cl_e-1.)) ) bestbadmatch = cl;
572 continue;
573 }
574
575 ATH_MSG_VERBOSE("E match with new cluster: " << fabs(sumE_tc+tcl_e - eg_cl_e) / eg_cl_e);
576 if( (doSum = (fabs(sumE_tc+tcl_e - eg_cl_e) < fabs(sumE_tc - eg_cl_e))) ) {
577 ATH_MSG_VERBOSE("Accept topocluster with pt " << cl->pt() << ", e " << cl->e() << " in sum.");
578 ATH_MSG_VERBOSE("Energy ratio of nPFO to eg: " << tcl_e / eg_cl_e);
579 ATH_MSG_VERBOSE("E match with new cluster: " << fabs(sumE_tc+tcl_e - eg_cl_e) / eg_cl_e);
580 tclist.push_back(cl);
581 sumE_tc += tcl_e;
582 } // if we will retain the topocluster
583 } // loop over nearby clusters
584 if(sumE_tc<FLT_MIN && bestbadmatch) {
585 ATH_MSG_VERBOSE("No better matches found -- add bad match topocluster with pt "
586 << bestbadmatch->pt() << ", e " << bestbadmatch->e() << ".");
587 tclist.push_back(bestbadmatch);
588 }
589 return StatusCode::SUCCESS;
590 }
591
593 const xAOD::TrackParticleContainer* trkCont,
594 std::set<const xAOD::TrackParticle*>& tracklist) const
595 {
596 // switch to using egamma helpers for track extraction
597 // set ensures that there's no duplication
598 const std::set<const xAOD::TrackParticle*> egtracks = EgammaHelpers::getTrackParticles(eg);
599 for(const auto& track : egtracks) {
600 ATH_MSG_VERBOSE("Accept " << eg->type() << " track " << track << " px, py = " << track->p4().Px() << ", " << track->p4().Py());
601 tracklist.insert(track);
602 } // end initial egamma track loop
603
604 // for objects with ambiguous author, grab the tracks matched to the counterpart ambiguous object too
605 // set ensures that there's no duplication
606 if (eg->author() & xAOD::EgammaParameters::AuthorAmbiguous && eg->ambiguousObject()) {
607 const std::set<const xAOD::TrackParticle*> ambitracks = EgammaHelpers::getTrackParticles(eg->ambiguousObject());
608 for(const auto& track : egtracks) {
609 ATH_MSG_VERBOSE("Accept ambiguous " << eg->type() << " track " << track << " px, py = " << track->p4().Px() << ", " << track->p4().Py());
610 tracklist.insert(track);
611 }
612 } // end ambiguous track case
613
614 // in a small dR window, also accept tracks without an IBL hit
615 for(const auto *const track : *trkCont) {
617 // dR check should be faster than track summary retrieval
618 uint8_t expect_innermostHit(false);
619 uint8_t N_innermostHit(false);
620 uint8_t expect_nextToInnermostHit(false);
621 uint8_t N_nextToInnermostHit(false);
622 if( !track->summaryValue(expect_innermostHit, expectInnermostPixelLayerHit)
623 || !track->summaryValue(expect_nextToInnermostHit, expectNextToInnermostPixelLayerHit)) {
624 ATH_MSG_WARNING("Track summary retrieval failed for 'expect(NextTo)InnermostPixelLayerHit'");
625 return StatusCode::FAILURE;
626 }
627 if(expect_innermostHit) {
628 if( !track->summaryValue(N_innermostHit, numberOfInnermostPixelLayerHits) ) {
629 ATH_MSG_WARNING("Track summary retrieval failed for 'numberOfInnermostPixelLayerHits'");
630 return StatusCode::FAILURE;
631 if(N_innermostHit==0 ) {
632 ATH_MSG_VERBOSE("Accept nearby track w/o innermost hit");
633 tracklist.insert(track);
634 }
635 }
636 } else if(expect_nextToInnermostHit) {
637 if( !track->summaryValue(N_nextToInnermostHit, numberOfNextToInnermostPixelLayerHits) ) {
638 ATH_MSG_WARNING("Track summary retrieval failed for 'numberOfNextToInnermostPixelLayerHits'");
639 return StatusCode::FAILURE;
640 if(N_nextToInnermostHit==0 ) {
641 ATH_MSG_VERBOSE("Accept nearby track w/o next-to-innermost hit");
642 tracklist.insert(track);
643 }
644 }
645 }
646
647 } // end dR check
648 } // end extra track loop
649 return StatusCode::SUCCESS;
650 }
651
652
654
655 bool has_unmatched=false;
656 float totSumpt=0;
657 float unmatchedSumpt=0;
658 float unmatchedE=0;
659 float unmatchedTotEMFrac=0;
660 double emfrac=0;
661
662 static const SG::AuxElement::Decorator<Float_t> dec_unmatchedFrac("unmatchedFrac");
663 static const SG::AuxElement::Decorator<Float_t> dec_unmatchedFracSumpt("unmatchedFracSumpt");
664 static const SG::AuxElement::Decorator<Float_t> dec_unmatchedFracPt("unmatchedFracPt");
665 static const SG::AuxElement::Decorator<Float_t> dec_unmatchedFracE("unmatchedFracE");
666 static const SG::AuxElement::Decorator<Float_t> dec_unmatchedFracEClusterPFO("unmatchedFracEClusterPFO");
667 static const SG::AuxElement::Decorator<Float_t> dec_unmatchedFracPtClusterPFO("unmatchedFracPtClusterPFO");
668 static const SG::AuxElement::Decorator<Float_t> dec_unmatchedTotEMFrac("unmatchedTotEMFrac");
669
670 TLorentzVector totVec(0.,0.,0.,0.), unmatchedVec(0.,0.,0.,0.);
671 const std::vector<const xAOD::CaloCluster*> egClusters = xAOD::EgammaHelpers::getAssociatedTopoClusters(eg->caloCluster());
672 std::set<const xAOD::CaloCluster*> cPFOClusters;
673 int nCluscPFO = pfo->nCaloCluster();
674
675 for (int cl = 0; cl < nCluscPFO; ++cl) {
676 if (pfo->cluster(cl)) cPFOClusters.insert( pfo->cluster(cl) );
677 }
678
679 std::vector<const xAOD::CaloCluster*> unmatchedClusters;
680 for (const xAOD::CaloCluster* pfoclus : cPFOClusters) {
681 TLorentzVector tmpVec;
682 tmpVec.SetPtEtaPhiE(pfoclus->pt(),pfoclus->eta(),pfoclus->phi(),pfoclus->e());
683 totSumpt+=pfoclus->pt();
684 totVec+=tmpVec;
685 bool inEgamma = false;
686 for (const xAOD::CaloCluster* phclus : egClusters) {
687 if (pfoclus == phclus) {
688 inEgamma = true;
689 }
690 }
691 if (!inEgamma) {
692 unmatchedClusters.push_back(pfoclus);
693 unmatchedSumpt+=pfoclus->pt();
694 unmatchedE+=pfoclus->e();
695 unmatchedVec+=tmpVec;
696 pfoclus->retrieveMoment(xAOD::CaloCluster::ENG_FRAC_EM ,emfrac);
697 unmatchedTotEMFrac=unmatchedTotEMFrac+emfrac*pfoclus->e();
698 }
699
700 }
701
702 ATH_MSG_DEBUG("PFO associated to "<<nCluscPFO<< " cluster, of which " << unmatchedClusters.size() << "unmatched one and unmatched pt "<<unmatchedSumpt);
703 dec_unmatchedFrac(*pfo)=nCluscPFO>0 ? float(unmatchedClusters.size())/float(nCluscPFO) : -1;
704 dec_unmatchedFracPt(*pfo)= totVec.Pt()>0 ? float(unmatchedVec.Pt()/totVec.Pt()): -1;
705 dec_unmatchedFracSumpt(*pfo)= totSumpt>0 ? float(unmatchedSumpt/totSumpt): -1;
706 dec_unmatchedFracE(*pfo)= totVec.E()>0 ? float(unmatchedE/totVec.E()): -1;
707 dec_unmatchedTotEMFrac(*pfo)= totVec.E()>0 ? float(unmatchedTotEMFrac/totVec.E()): -1;
708 dec_unmatchedFracEClusterPFO(*pfo)= pfo->e()>0 ? float(unmatchedE/pfo->e()): -1;
709 dec_unmatchedFracPtClusterPFO(*pfo)= pfo->pt()>0 ? float(unmatchedE/pfo->pt()): -1;
710
711 return has_unmatched;
712 }
713
714
715
716}
717
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
#define ANA_MSG_VERBOSE(xmsg)
Macro printing verbose messages.
ElementLink< xAOD::PFOContainer > PFOLink_t
ElementLink< xAOD::PhotonContainer > PhotonLink_t
ElementLink< xAOD::ElectronContainer > ElectronLink_t
ElementLink< xAOD::FlowElementContainer > FELink_t
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
SG::ConstAccessor< T, ALLOC > ConstAccessor
Definition AuxElement.h:569
SG::Decorator< T, ALLOC > Decorator
Definition AuxElement.h:575
size_t index() const
Return the index of this element within its container.
Helper class to provide constant type-safe access to aux data.
Handle class for reading a decoration on an object.
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
virtual StatusCode initialize() override
Dummy implementation of the initialisation function.
static bool greaterPtPFO(const xAOD::PFO *part1, const xAOD::PFO *part2)
static bool greaterPtFE(const xAOD::FlowElement *part1, const xAOD::FlowElement *part2)
static bool greaterPt(const xAOD::IParticle *part1, const xAOD::IParticle *part2)
METAssociator(const std::string &name)
bool isGoodEoverP(const xAOD::TrackParticle *trk) const
StatusCode GetUEcorr(const met::METAssociator::ConstitHolder &constits, std::vector< TLorentzVector > &v_clus, TLorentzVector &clus, TLorentzVector &HR, const float Drcone, const float MinDistCone, float &UEcorr) const
bool acceptTrack(const xAOD::TrackParticle *trk, const xAOD::Vertex *pv) const
SG::ReadDecorHandleKey< xAOD::PhotonContainer > m_photonNeutralFEReadDecorKey
StatusCode extractTopoClusters(const xAOD::IParticle *obj, std::vector< const xAOD::IParticle * > &tclist, const met::METAssociator::ConstitHolder &constits) const final
SG::ReadDecorHandleKey< xAOD::ElectronContainer > m_electronNeutralPFOReadDecorKey
StatusCode selectEgammaClusters(const xAOD::CaloCluster *swclus, const std::vector< const xAOD::IParticle * > &inputTC, std::vector< const xAOD::IParticle * > &tclist) const
SG::ReadDecorHandleKey< xAOD::ElectronContainer > m_electronChargedFEReadDecorKey
SG::ReadDecorHandleKey< xAOD::ElectronContainer > m_electronChargedPFOReadDecorKey
StatusCode extractFE(const xAOD::IParticle *obj, std::vector< const xAOD::IParticle * > &felist, const met::METAssociator::ConstitHolder &constits, std::map< const xAOD::IParticle *, MissingETBase::Types::constvec_t > &momenta) const final
StatusCode extractFEHR(const xAOD::IParticle *obj, std::vector< const xAOD::IParticle * > hardObjs, std::vector< const xAOD::IParticle * > &felist, const met::METAssociator::ConstitHolder &constits, std::map< const xAOD::IParticle *, MissingETBase::Types::constvec_t > &momenta, float &UEcorr) const final
StatusCode extractFEsFromLinks(const xAOD::Egamma *eg, std::vector< const xAOD::IParticle * > &felist, const met::METAssociator::ConstitHolder &constits) const
StatusCode initialize()
Dummy implementation of the initialisation function.
StatusCode extractTracks(const xAOD::IParticle *obj, std::vector< const xAOD::IParticle * > &constlist, const met::METAssociator::ConstitHolder &constits) const final
SG::ReadDecorHandleKey< xAOD::PhotonContainer > m_photonNeutralPFOReadDecorKey
StatusCode selectEgammaTracks(const xAOD::Egamma *el, const xAOD::TrackParticleContainer *trkCont, std::set< const xAOD::TrackParticle * > &tracklist) const
SG::ReadDecorHandleKey< xAOD::PhotonContainer > m_photonChargedPFOReadDecorKey
bool hasUnmatchedClusters(const xAOD::Egamma *eg, const xAOD::PFO *pfo) const
SG::ReadDecorHandleKey< xAOD::ElectronContainer > m_electronNeutralFEReadDecorKey
StatusCode extractPFO(const xAOD::IParticle *obj, std::vector< const xAOD::IParticle * > &pfolist, const met::METAssociator::ConstitHolder &constits, std::map< const xAOD::IParticle *, MissingETBase::Types::constvec_t > &momenta) const final
StatusCode extractPFOsFromLinks(const xAOD::Egamma *eg, std::vector< const xAOD::IParticle * > &pfolist, const met::METAssociator::ConstitHolder &constits) const
StatusCode extractPFOs(const xAOD::Egamma *eg, std::vector< const xAOD::IParticle * > &pfolist, const met::METAssociator::ConstitHolder &constits) const
static constexpr float m_MinDistCone
static constexpr float m_Drcone
StatusCode extractFEs(const xAOD::Egamma *eg, std::vector< const xAOD::IParticle * > &felist, const met::METAssociator::ConstitHolder &constits) const
SG::ReadDecorHandleKey< xAOD::PhotonContainer > m_photonChargedFEReadDecorKey
METEgammaAssociator()
Default constructor:
virtual double e() const
The total energy of the particle.
@ ENG_FRAC_EM
Energy fraction in EM calorimeters.
virtual double pt() const override
signal_t signalType() const
const xAOD::IParticle * chargedObject(std::size_t i) const
virtual double e() const override
The total energy of the particle.
Class providing the definition of the 4-vector interface.
virtual double pt() const =0
The transverse momentum ( ) of the particle.
virtual double e() const =0
The total energy of the particle.
unsigned int nCaloCluster() const
Find out how many CaloCluster are linked.
Definition PFO_v1.cxx:659
virtual double e() const
The total energy of the particle.
Definition PFO_v1.cxx:81
virtual double pt() const
The transverse momentum ( ) of the particle.
Definition PFO_v1.cxx:52
const CaloCluster * cluster(unsigned int index) const
Retrieve a const pointer to a CaloCluster.
Definition PFO_v1.cxx:669
@ ClusterLink
static const SG::ConstAccessor< char > PVMatchedAcc("matchedToPV")
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
@ Photon
The object is a photon.
Definition ObjectType.h:47
@ Electron
The object is an electron.
Definition ObjectType.h:46
std::set< const xAOD::TrackParticle * > getTrackParticles(const xAOD::Egamma *eg, bool useBremAssoc=true, bool allParticles=true)
Return a list of all or only the best TrackParticle associated to the object.
std::vector< const xAOD::CaloCluster * > getAssociatedTopoClusters(const xAOD::CaloCluster *cluster)
Return a vector of all the topo clusters associated with the egamma cluster.
const uint16_t AuthorAmbiguous
Object Reconstructed by standard cluster-based algorithm.
Definition EgammaDefs.h:32
bool isInDeltaR(const xAOD::IParticle &p1, const xAOD::IParticle &p2, double dR, bool useRapidity=true)
Check if 2 xAOD::IParticle are in a cone.
double deltaR(double rapidity1, double phi1, double rapidity2, double phi2)
from bare bare rapidity,phi
ICaloAffectedTool is abstract interface for tools checking if 4 mom is in calo affected region.
PFO_v1 PFO
Definition of the current "pfo version".
Definition PFO.h:17
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
FlowElement_v1 FlowElement
Definition of the current "pfo version".
Definition FlowElement.h:16
TrackParticle_v1 TrackParticle
Reference the current persistent version:
Egamma_v1 Egamma
Definition of the current "egamma version".
Definition Egamma.h:17
TrackParticleContainer_v1 TrackParticleContainer
Definition of the current "TrackParticle container version".
@ expectInnermostPixelLayerHit
Do we expect a 0th-layer barrel hit for this track?
@ numberOfNextToInnermostPixelLayerHits
these are the hits in the 1st pixel barrel layer
@ expectNextToInnermostPixelLayerHit
Do we expect a 1st-layer barrel hit for this track?
@ numberOfInnermostPixelLayerHits
these are the hits in the 0th pixel barrel layer
const xAOD::IParticleContainer * tcCont
const xAOD::FlowElementContainer * feCont
const xAOD::PFOContainer * pfoCont
const xAOD::TrackParticleContainer * trkCont