ATLAS Offline Software
Loading...
Searching...
No Matches
AsgElectronLikelihoodTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3*/
4
13
19// STL includes
20#include <cmath>
21#include <cstdint>
22#include <string>
23// EDM includes
25#include "xAODEgamma/Electron.h"
26#include "xAODTracking/Vertex.h"
27// Framework includes
33#include "TEnv.h"
34
35// Standard constructor
36AsgElectronLikelihoodTool::AsgElectronLikelihoodTool(const std::string& myname)
37 : AsgTool(myname)
38 , m_configFile{ "" }
39 , m_rootTool{ nullptr }
40{
41
42 // Create an instance of the underlying ROOT tool
43 m_rootTool = new Root::TElectronLikelihoodTool(("T" + myname).c_str());
44
45 // Declare the needed properties
46 // not having a config file results in
47 // a failure
48 declareProperty("WorkingPoint", m_WorkingPoint = "", "The Working Point");
49 declareProperty("ConfigFile", m_configFile = "", "The config file to use");
50
51 // pdf file name. If specified it overrides the one in the config file
52 declareProperty("inputPDFFileName",
53 m_pdfFileName = "",
54 "The input ROOT file name that holds the PDFs");
55
56 // Addtional properties that are not part of the config files
57 declareProperty(
58 "usePVContainer", m_usePVCont = true, "Whether to use the PV container");
59 declareProperty(
60 "nPVdefault", m_nPVdefault = 0, "The default number of PVs if not counted");
61 declareProperty("useCaloSumsContainer",
62 m_useCaloSumsCont = true,
63 "Whether to use the CaloSums container");
64 declareProperty(
65 "fcalEtDefault", m_fcalEtDefault = 0, "The default FCal sum ET");
66 declareProperty("skipDeltaPoverP",
67 m_skipDeltaPoverP = false,
68 "If true, it wil skip the check of deltaPoverP");
69 declareProperty("useAverageMu",
70 m_useAverageMu=false,
71 "Whether to use average mu instead of NPV." );
72}
73
78
79StatusCode
81{
82 std::string configFile, PDFfilename, resolvedPDF; // Default
83
84 if (!m_WorkingPoint.empty()) {
87 }
88
89 if (!m_configFile.empty()) {
91 if (configFile.empty()) {
92 ATH_MSG_ERROR("Could not locate config " << m_configFile);
93 return StatusCode::FAILURE;
94 }
95
96 TEnv env;
97 if (env.ReadFile(configFile.c_str(), kEnvLocal)) {
98 ATH_MSG_ERROR("Could not open config " << configFile);
99 return StatusCode::FAILURE;
100 }
101
102 // Get the input PDFs for the tool.
103 // We need to see if the user had provided
104 // an override, if not needs to be in the input
105 // config file
106 if (!m_pdfFileName.empty()) {
107 // If the property was set by the user, take that.
108 ATH_MSG_INFO("Setting user specified PDF file " << m_pdfFileName);
109 PDFfilename = m_pdfFileName;
110 } else {
111 if (!env.Defined("inputPDFFileName")) {
112 ATH_MSG_WARNING("will use default PDF filename "
113 "since none is specified in the config "
114 << m_configFile);
115 }
116 PDFfilename = env.GetValue(
117 "inputPDFFileName",
118 "ElectronPhotonSelectorTools/v1/ElectronLikelihoodPdfs.root");
119 if (PDFfilename.empty()) {
120 ATH_MSG_ERROR("empty inputPDFFilename in " << configFile);
121 return StatusCode::FAILURE;
122 }
123 if (m_configFile.find("dev/") != std::string::npos) {
124 PDFfilename.insert(0, "dev/");
125 }
126 }
127
128 resolvedPDF = PathResolverFindCalibFile(PDFfilename);
129 if (!resolvedPDF.empty()) {
130 m_rootTool->setPDFFileName(resolvedPDF);
131 } else {
132 ATH_MSG_ERROR("Couldn't resolve PDF filename from "
133 << PDFfilename << ", config file = " << configFile);
134 return StatusCode::FAILURE;
135 }
136
138 // The following are all taken from the config
139 // file
140 m_rootTool->m_variableNames = env.GetValue("VariableNames", "");
141 m_rootTool->m_cutLikelihood =
142 AsgConfigHelper::HelperDouble("CutLikelihood", env);
143 m_rootTool->m_cutLikelihoodPileupCorrection =
144 AsgConfigHelper::HelperDouble("CutLikelihoodPileupCorrection", env);
145 m_rootTool->m_cutLikelihood4GeV =
146 AsgConfigHelper::HelperDouble("CutLikelihood4GeV", env);
147 m_rootTool->m_cutLikelihoodPileupCorrection4GeV =
148 AsgConfigHelper::HelperDouble("CutLikelihoodPileupCorrection4GeV", env);
149 // do the ambiguity cut
150 m_rootTool->m_cutAmbiguity =
151 AsgConfigHelper::HelperInt("CutAmbiguity", env);
152 // cut on b-layer
153 m_rootTool->m_cutBL = AsgConfigHelper::HelperInt("CutBL", env);
154 // cut on pixel hits
155 m_rootTool->m_cutPi = AsgConfigHelper::HelperInt("CutPi", env);
156 // cut on precision hits
157 m_rootTool->m_cutSi = AsgConfigHelper::HelperInt("CutSi", env);
158 // cut on d0
159 m_rootTool->m_cutA0 = AsgConfigHelper::HelperDouble("CutA0", env);
160 // cut on deltaEta
161 m_rootTool->m_cutDeltaEta =
162 AsgConfigHelper::HelperDouble("CutDeltaEta", env);
163 // cut on deltaPhiRes
164 m_rootTool->m_cutDeltaPhiRes =
165 AsgConfigHelper::HelperDouble("CutDeltaPhiRes", env);
166 // turn off f3 at high Et
167 m_rootTool->m_doRemoveF3AtHighEt =
168 env.GetValue("doRemoveF3AtHighEt", false);
169 // turn off TRTPID at high Et
170 m_rootTool->m_doRemoveTRTPIDAtHighEt =
171 env.GetValue("doRemoveTRTPIDAtHighEt", false);
172 // do smooth interpolation between bins
173 m_rootTool->m_doSmoothBinInterpolation =
174 env.GetValue("doSmoothBinInterpolation", false);
175 m_caloOnly = env.GetValue("caloOnly", false);
176
177 m_rootTool->m_useOneExtraHighETLHBin =
178 env.GetValue("useOneExtraHighETLHBin", false);
179 // cut on Wstot above HighETBinThreshold
180 m_rootTool->m_cutWstotAtHighET =
181 AsgConfigHelper::HelperDouble("CutWstotAtHighET", env);
182 // cut on EoverP above HighETBinThreshold
183 m_rootTool->m_cutEoverPAtHighET =
184 AsgConfigHelper::HelperDouble("CutEoverPAtHighET", env);
185 m_rootTool->m_highETBinThreshold = env.GetValue("HighETBinThreshold", 125);
186
187 m_rootTool->m_doPileupTransform = env.GetValue("doPileupTransform", false);
188 m_rootTool->m_doCentralityTransform =
189 env.GetValue("doCentralityTransform", false);
190 m_rootTool->m_discHardCutForPileupTransform =
191 AsgConfigHelper::HelperDouble("DiscHardCutForPileupTransform", env);
192 m_rootTool->m_discHardCutSlopeForPileupTransform =
193 AsgConfigHelper::HelperDouble("DiscHardCutSlopeForPileupTransform", env);
194 m_rootTool->m_discHardCutQuadForPileupTransform =
195 AsgConfigHelper::HelperDouble("DiscHardCutQuadForPileupTransform", env);
196 m_rootTool->m_discLooseForPileupTransform =
197 AsgConfigHelper::HelperDouble("DiscLooseForPileupTransform", env);
198 m_rootTool->m_discHardCutForPileupTransform4GeV =
199 AsgConfigHelper::HelperDouble("DiscHardCutForPileupTransform4GeV", env);
200 m_rootTool->m_discHardCutSlopeForPileupTransform4GeV =
201 AsgConfigHelper::HelperDouble("DiscHardCutSlopeForPileupTransform4GeV",
202 env);
203 m_rootTool->m_discHardCutQuadForPileupTransform4GeV =
204 AsgConfigHelper::HelperDouble("DiscHardCutQuadForPileupTransform4GeV",
205 env);
206 m_rootTool->m_discLooseForPileupTransform4GeV =
207 AsgConfigHelper::HelperDouble("DiscLooseForPileupTransform4GeV", env);
208 m_rootTool->m_discMaxForPileupTransform =
209 env.GetValue("DiscMaxForPileupTransform", 2.0);
210 m_rootTool->m_pileupMaxForPileupTransform =
211 env.GetValue("PileupMaxForPileupTransform", 50);
212 // if true, deltaEta1 will be corrected for the pear shape distortion of the
213 // LAr
214 m_correctDeltaEta = env.GetValue("doCorrectDeltaEta", false);
215
216 if (m_rootTool->m_doCentralityTransform && m_useAverageMu) {
217 ATH_MSG_ERROR("Cannot use centrality transform and average mu "
218 << "at the same time as they affect the same variable");
219 return StatusCode::FAILURE;
220 }
221 } else { // Error if it cant find the conf
222 ATH_MSG_ERROR("Could not find configuration file");
223 return StatusCode::FAILURE;
224 }
225
227
228 // Setup primary vertex key handle
230 // Setup average mu key handle
232
233 // Setup HI container key handle (must come after init from env)
234 bool doCentralityTransform = m_rootTool->m_doCentralityTransform;
235 ATH_CHECK(
236 m_HIESContKey.initialize(doCentralityTransform && m_useCaloSumsCont));
237
238 // Get the message level and set the underlying ROOT tool message level
239 // accordingly
240 m_rootTool->msg().setLevel(this->msg().level());
241
242 // We need to initialize the underlying ROOT TSelectorTool
243 if (m_rootTool->initialize().isFailure()) {
244 ATH_MSG_ERROR("Could not initialize the TElectronLikelihoodTool! "
245 "Configuration details: "
246 << "working point = \"" << m_WorkingPoint
247 << "\", config file = \"" << m_configFile
248 << "\", resolved file = \"" << configFile
249 << "\", PDF file = \"" << PDFfilename
250 << "\", resolved file = \"" << resolvedPDF);
251 return StatusCode::FAILURE;
252 }
253 return StatusCode::SUCCESS;
254}
255
256// return the accept info object
257const asg::AcceptInfo&
259{
260 return m_rootTool->getAcceptInfo();
261}
262
263// The main accept method: the actual cuts are applied here
265AsgElectronLikelihoodTool::accept(const EventContext& ctx,
266 const xAOD::Electron* el,
267 double mu) const
268{
269 if (!el) {
270 ATH_MSG_ERROR("Failed, no electron object.");
271 return m_rootTool->accept();
272 }
273
274 const xAOD::CaloCluster* cluster = el->caloCluster();
275 if (!cluster) {
276 ATH_MSG_ERROR("exiting because cluster is NULL " << cluster);
277 return m_rootTool->accept();
278 }
279
280 if (!cluster->hasSampling(CaloSampling::CaloSample::EMB2) &&
281 !cluster->hasSampling(CaloSampling::CaloSample::EME2)) {
282 ATH_MSG_ERROR("Failed, cluster is missing samplings EMB2 and EME2");
283 return m_rootTool->accept();
284 }
285
286 const double energy = cluster->e();
287 const float eta = (cluster->etaBE(2));
288
289 if (isForwardElectron(el, eta)) {
291 "Failed, this is a forward electron! The AsgElectronLikelihoodTool is "
292 "only suitable for central electrons!");
293 return m_rootTool->accept();
294 }
295
296 double et = 0.;
297 if (el->trackParticle() && !m_caloOnly) {
298 et = (cosh(el->trackParticle()->eta()) != 0.)
299 ? energy / cosh(el->trackParticle()->eta())
300 : 0.;
301 } else
302 et = (cosh(eta) != 0.) ? energy / cosh(eta) : 0.;
303
304 // number of track hits
305 uint8_t nSiHitsPlusDeadSensors(0);
306 uint8_t nPixHitsPlusDeadSensors(0);
307 bool passBLayerRequirement(false);
308 float d0(0.0);
309 float deltaEta = 0;
310 float deltaPhiRescaled2 = 0;
311 float wstot = 0;
312 float EoverP = 0;
313 uint8_t ambiguityBit(0);
314 double ip(0);
315
316 bool allFound = true;
317 std::string notFoundList = "";
318
319 // Wstot for use when CutWstotAtHighET vector is filled
320 if (!el->showerShapeValue(wstot, xAOD::EgammaParameters::wtots1)) {
321 allFound = false;
322 notFoundList += "wtots1 ";
323 }
324
325 // get the ambiguity type from the decoration
326 if (!m_rootTool->m_cutAmbiguity.empty()) {
327 static const SG::AuxElement::Accessor<uint8_t> ambiguityTypeAcc("ambiguityType");
328 if (ambiguityTypeAcc.isAvailable(*el)) {
329 ambiguityBit = ambiguityTypeAcc(*el);
330 } else {
331 allFound = false;
332 notFoundList += "ambiguityType ";
333 }
334 }
335
336 if (!m_caloOnly) {
337 // retrieve associated track
338 const xAOD::TrackParticle* t = el->trackParticle();
339 if (t) {
340 nSiHitsPlusDeadSensors =
342 nPixHitsPlusDeadSensors =
344 passBLayerRequirement = ElectronSelectorHelpers::passBLayerRequirement(*t);
345 d0 = t->d0();
346 EoverP = std::abs(t->qOverP()) * energy;
347 } else {
348 ATH_MSG_ERROR("Failed, no track particle. et= " << et << "eta= " << eta);
349 return m_rootTool->accept();
350 }
351
352 if (!el->trackCaloMatchValue(deltaEta, xAOD::EgammaParameters::deltaEta1)) {
353 allFound = false;
354 notFoundList += "deltaEta1 ";
355 }
356 // correction of deltaEta1 for pear shape distortion
357 else if (m_correctDeltaEta) {
358 const static SG::AuxElement::Accessor<float> acc(
359 "deltaEta1PearDistortion");
360 if (acc.isAvailable(*el)) {
361 deltaEta -= acc(*el);
362 } else {
363 allFound = false;
364 notFoundList += "deltaEta1PearDistortion ";
365 }
366 }
367
368 if (!el->trackCaloMatchValue(deltaPhiRescaled2,
370 allFound = false;
371 notFoundList += "deltaPhiRescaled2 ";
372 }
373
374 } // if not calo ONly
375
376 // Get the number of primary vertices,
377 // avg mu or FCal ET in this event,
378 // depending on user configuration
379 ip = getIpVariable(mu, ctx);
380
381 // for now don't cache.
382 double likelihood = calculate(ctx, el, ip);
383
384 if (!allFound) {
386 "Skipping LH rectangular cuts! The following variables are missing: "
387 << notFoundList);
388 return m_rootTool->accept();
389 }
390
391 // Get the answer from the underlying ROOT tool
392 return m_rootTool->accept(likelihood,
393 eta,
394 et,
395 nSiHitsPlusDeadSensors,
396 nPixHitsPlusDeadSensors,
397 passBLayerRequirement,
398 ambiguityBit,
399 d0,
400 deltaEta,
401 deltaPhiRescaled2,
402 wstot,
403 EoverP,
404 ip);
405}
406
407// Accept method for EFCaloLH in the trigger; do full LH if !CaloCutsOnly
409AsgElectronLikelihoodTool::accept(const EventContext& ctx,
410 const xAOD::Egamma* eg,
411 double mu) const
412{
413 if (!eg) {
414 ATH_MSG_ERROR("Failed, no egamma object.");
415 return m_rootTool->accept();
416 }
417 // Call the main accept if this is not a calo-only LH
418 if (!m_caloOnly) {
419 if (eg->type() == xAOD::Type::Electron) {
420 const xAOD::Electron* el = static_cast<const xAOD::Electron*>(eg);
421 return accept(ctx, el, mu);
422 }
423 ATH_MSG_ERROR("Input is not an electron and not caloOnly is set");
424 return m_rootTool->accept();
425 }
426
427 // Calo only LH
428 const xAOD::CaloCluster* cluster = eg->caloCluster();
429 if (!cluster) {
430 ATH_MSG_ERROR("Failed, no cluster.");
431 return m_rootTool->accept();
432 }
433 if (!cluster->hasSampling(CaloSampling::CaloSample::EMB2) &&
434 !cluster->hasSampling(CaloSampling::CaloSample::EME2)) {
435 ATH_MSG_ERROR("Failed, cluster is missing samplings EMB2 and EME2");
436 return m_rootTool->accept();
437 }
438
439 const double energy = cluster->e();
440 const float eta = (cluster->etaBE(2));
441 if (isForwardElectron(eg, eta)) {
443 "Failed, this is a forward electron! The AsgElectronLikelihoodTool is "
444 "only suitable for central electrons!");
445 return m_rootTool->accept();
446 }
447
448 const double et = (cosh(eta) != 0.) ? energy / cosh(eta) : 0.;
449
450 // Variables the EFCaloLH ignores
451 uint8_t nSiHitsPlusDeadSensors(0);
452 uint8_t nPixHitsPlusDeadSensors(0);
453 bool passBLayerRequirement(false);
454 uint8_t ambiguityBit(0);
455
456 // Get the pileup or centrality information
457 double ip = getIpVariable(mu, ctx);
458
459 // for now don't cache.
460 double likelihood = calculate(ctx, eg, ip);
461
462 double deltaEta = 0;
463 double deltaPhiRescaled2 = 0;
464 double d0 = 0;
465 float wstot = 0;
466 float EoverP = 0;
467
468 bool allFound = true;
469 std::string notFoundList = "";
470
471 // Wstot for use when CutWstotAtHighET vector is filled
472 if (!eg->showerShapeValue(wstot, xAOD::EgammaParameters::wtots1)) {
473 allFound = false;
474 notFoundList += "wtots1 ";
475 }
476
477 if (!allFound) {
479 "Skipping LH rectangular cuts! The following variables are missing: "
480 << notFoundList);
481 return m_rootTool->accept();
482 }
483
484 // Get the answer from the underlying ROOT tool
485 return m_rootTool->accept(likelihood,
486 eta,
487 et,
488 nSiHitsPlusDeadSensors,
489 nPixHitsPlusDeadSensors,
490 passBLayerRequirement,
491 ambiguityBit,
492 d0,
493 deltaEta,
494 deltaPhiRescaled2,
495 wstot,
496 EoverP,
497 ip);
498}
499
500// The main result method: the actual likelihood is calculated here
501double
503 const xAOD::Electron* el,
504 double mu) const
505{
506 if (!el) {
507 ATH_MSG_ERROR("Failed, no egamma object.");
508 return -999;
509 }
510 const xAOD::CaloCluster* cluster = el->caloCluster();
511 if (!cluster) {
512 ATH_MSG_ERROR("Failed, no cluster.");
513 return -999;
514 }
515 if (!cluster->hasSampling(CaloSampling::CaloSample::EMB2) &&
516 !cluster->hasSampling(CaloSampling::CaloSample::EME2)) {
517 ATH_MSG_ERROR("Failed, cluster is missing samplings EMB2 and EME2");
518 return -999;
519 }
520
521 const double energy = cluster->e();
522 const float eta = cluster->etaBE(2);
523
524 if (isForwardElectron(el, eta)) {
526 "Failed, this is a forward electron! The AsgElectronLikelihoodTool is "
527 "only suitable for central electrons!");
528 return -999;
529 }
530
531 double et = 0.;
532 if (el->trackParticle() && !m_caloOnly) {
533 et = (cosh(el->trackParticle()->eta()) != 0.)
534 ? energy / cosh(el->trackParticle()->eta())
535 : 0.;
536 } else {
537 et = (cosh(eta) != 0.) ? energy / cosh(eta) : 0.;
538 }
539
540 // number of track hits and other track quantities
541 float trackqoverp(0.0);
542 float d0(0.0);
543 float d0sigma(0.0);
544 double dpOverp(0.0);
545 float TRT_PID(0.0);
546 double trans_TRT_PID(0.0);
547 float deltaEta = 0;
548 float deltaPhiRescaled2 = 0;
549
550 bool allFound = true;
551 std::string notFoundList = "";
552
553 if (!m_caloOnly) {
554 // retrieve associated TrackParticle
555 const xAOD::TrackParticle* t = el->trackParticle();
556 if (t) {
557 trackqoverp = t->qOverP();
558 d0 = t->d0();
559 float vard0 = t->definingParametersCovMatrix()(0, 0);
560 if (vard0 > 0) {
561 d0sigma = sqrtf(vard0);
562 }
563
564 const static SG::AuxElement::Accessor<float> trans_TRT_PID_acc("transformed_e_probability_ht");
565 if (!trans_TRT_PID_acc.isAvailable(*el)) {
566 // most probable case, need to compute the variable
567
568 if (!t->summaryValue(TRT_PID, xAOD::eProbabilityHT)) {
569 allFound = false;
570 notFoundList += "eProbabilityHT ";
571 }
572
573 // Transform the TRT PID output for use in the LH tool.
574 const double tau = 15.0;
575 const double fEpsilon = 1.0e-30; // to avoid zero division
576 double pid_tmp = TRT_PID;
577 if (pid_tmp >= 1.0)
578 pid_tmp = 1.0 - 1.0e-15; // this number comes from TMVA
579 else if (pid_tmp <= fEpsilon)
580 pid_tmp = fEpsilon;
581 trans_TRT_PID = -log(1.0 / pid_tmp - 1.0) * (1. / double(tau));
582 }
583 else
584 {
585 // it means the variable have been already computed by another tool
586 // usually this is the EGammaVariableCorrection, which means that
587 // it is also fudged (only MC)
588 trans_TRT_PID = trans_TRT_PID_acc(*el);
589 }
590
591 unsigned int index;
592 if (t->indexOfParameterAtPosition(index, xAOD::LastMeasurement)) {
593
594 double refittedTrack_LMqoverp =
595 t->charge() / sqrt(std::pow(t->parameterPX(index), 2) +
596 std::pow(t->parameterPY(index), 2) +
597 std::pow(t->parameterPZ(index), 2));
598
599 dpOverp = 1 - trackqoverp / (refittedTrack_LMqoverp);
600 } else if (!m_skipDeltaPoverP) {
601 allFound = false;
602 notFoundList += "deltaPoverP ";
603 }
604
605 } else {
606 ATH_MSG_ERROR("Failed, no track particle. et= " << et << "eta= " << eta);
607 return -999;
608 }
609 } // if not calo Only
610
611 float Reta(0);
612 float Rphi(0);
613 float Rhad1(0);
614 float Rhad(0);
615 float w2(0);
616 float f1(0);
617 float Eratio(0);
618 float f3(0);
619
620 // reta = e237/e277
621 if (!el->showerShapeValue(Reta, xAOD::EgammaParameters::Reta)) {
622 allFound = false;
623 notFoundList += "Reta ";
624 }
625 // rphi e233/e237
626 if (!el->showerShapeValue(Rphi, xAOD::EgammaParameters::Rphi)) {
627 allFound = false;
628 notFoundList += "Rphi ";
629 }
630 // rhad1 = ethad1/et
631 if (!el->showerShapeValue(Rhad1, xAOD::EgammaParameters::Rhad1)) {
632 allFound = false;
633 notFoundList += "Rhad1 ";
634 }
635 // rhad = ethad/et
636 if (!el->showerShapeValue(Rhad, xAOD::EgammaParameters::Rhad)) {
637 allFound = false;
638 notFoundList += "Rhad ";
639 }
640 // shower width in 2nd sampling
641 if (!el->showerShapeValue(w2, xAOD::EgammaParameters::weta2)) {
642 allFound = false;
643 notFoundList += "weta2 ";
644 }
645 // fraction of energy reconstructed in the 1st sampling
646 if (!el->showerShapeValue(f1, xAOD::EgammaParameters::f1)) {
647 allFound = false;
648 notFoundList += "f1 ";
649 }
650 // E of 2nd max between max and min in strips
651 if (!el->showerShapeValue(Eratio, xAOD::EgammaParameters::Eratio)) {
652 allFound = false;
653 notFoundList += "Eratio ";
654 }
655 // fraction of energy reconstructed in the 3rd sampling
656 if (!el->showerShapeValue(f3, xAOD::EgammaParameters::f3)) {
657 allFound = false;
658 notFoundList += "f3 ";
659 }
660
661 if (!m_caloOnly) {
662 // deltaEta1
663 if (!el->trackCaloMatchValue(deltaEta, xAOD::EgammaParameters::deltaEta1)) {
664 allFound = false;
665 notFoundList += "deltaEta1 ";
666 }
667 // correction of deltaEta1 for pear shape distortion
668 else if (m_correctDeltaEta) {
669 const static SG::AuxElement::Accessor<float> acc(
670 "deltaEta1PearDistortion");
671 if (acc.isAvailable(*el)) {
672 deltaEta -= acc(*el);
673 } else {
674 allFound = false;
675 notFoundList += "deltaEta1PearDistortion ";
676 }
677 }
678
679 // difference between the cluster phi (sampling 2) and the eta of the track
680 // extrapolated from the last measurement point.
681 if (!el->trackCaloMatchValue(deltaPhiRescaled2,
683 allFound = false;
684 notFoundList += "deltaPhiRescaled2 ";
685 }
686 }
687
688 // Get the number of primary vertices, avg mu or FCal ET in this event,
689 // depending on user configuration
690 double ip = static_cast<double>(m_nPVdefault);
691
692 ip = getIpVariable(mu, ctx);
693
694 if (!allFound) {
696 "Skipping LH calculation! The following variables are missing: "
697 << notFoundList);
698 return -999;
699 }
700
701 // Get the answer from the underlying ROOT tool
702 return m_rootTool->calculate(eta,
703 et,
704 f3,
705 Rhad,
706 Rhad1,
707 Reta,
708 w2,
709 f1,
710 Eratio,
711 deltaEta,
712 d0,
713 d0sigma,
714 Rphi,
715 dpOverp,
716 deltaPhiRescaled2,
717 trans_TRT_PID,
718 ip);
719}
720
721// Calculate method for EFCaloLH in the trigger; do full LH if !CaloCutsOnly
722double
724 const xAOD::Egamma* eg,
725 double mu) const
726{
727 if (!eg) {
728 ATH_MSG_ERROR("Failed, no egamma object.");
729 return -999;
730 }
731
732 if (!m_caloOnly) {
733 if (eg->type() == xAOD::Type::Electron) {
734 const xAOD::Electron* el = static_cast<const xAOD::Electron*>(eg);
735 return calculate(ctx, el);
736 }
737
738 ATH_MSG_ERROR("Input is not an electron and not Calo Only is required");
739 return -999;
740 }
741
742 const xAOD::CaloCluster* cluster = eg->caloCluster();
743 if (!cluster) {
744 ATH_MSG_ERROR("Failed, no cluster.");
745 return -999;
746 }
747
748 if (!cluster->hasSampling(CaloSampling::CaloSample::EMB2) &&
749 !cluster->hasSampling(CaloSampling::CaloSample::EME2)) {
750 ATH_MSG_ERROR("Failed, cluster is missing samplings EMB2 and EME2");
751 return -999;
752 }
753
754 const double energy = cluster->e();
755 const float eta = cluster->etaBE(2);
756
757 if (isForwardElectron(eg, eta)) {
759 "Failed, this is a forward electron! The AsgElectronLikelihoodTool is "
760 "only suitable for central electrons!");
761 return -999;
762 }
763
764 const double et = (cosh(eta) != 0.) ? energy / cosh(eta) : 0.;
765
766 // Track variables that the EFCaloLH will not use
767 float d0(0.0);
768 float d0sigma(0.0);
769 double dpOverp(0.0);
770
771 float deltaEta = 0;
772 float deltaPhiRescaled2 = 0;
773 float TRT_PID(0.0);
774
775 // Calo Variables
776 float Reta(0);
777 float Rphi(0);
778 float Rhad1(0);
779 float Rhad(0);
780 float w2(0);
781 float f1(0);
782 float Eratio(0);
783 float f3(0);
784
785 bool allFound = true;
786 std::string notFoundList = "";
787
788 // reta = e237/e277
789 if (!eg->showerShapeValue(Reta, xAOD::EgammaParameters::Reta)) {
790 allFound = false;
791 notFoundList += "Reta ";
792 }
793 // rphi e233/e237
794 if (!eg->showerShapeValue(Rphi, xAOD::EgammaParameters::Rphi)) {
795 allFound = false;
796 notFoundList += "Rphi ";
797 }
798 // rhad1 = ethad1/et
799 if (!eg->showerShapeValue(Rhad1, xAOD::EgammaParameters::Rhad1)) {
800 allFound = false;
801 notFoundList += "Rhad1 ";
802 }
803 // rhad = ethad/et
804 if (!eg->showerShapeValue(Rhad, xAOD::EgammaParameters::Rhad)) {
805 allFound = false;
806 notFoundList += "Rhad ";
807 }
808 // shower width in 2nd sampling
809 if (!eg->showerShapeValue(w2, xAOD::EgammaParameters::weta2)) {
810 allFound = false;
811 notFoundList += "weta2 ";
812 }
813 // fraction of energy reconstructed in the 1st sampling
814 if (!eg->showerShapeValue(f1, xAOD::EgammaParameters::f1)) {
815 allFound = false;
816 notFoundList += "f1 ";
817 }
818 // E of 2nd max between max and min in strips
819 if (!eg->showerShapeValue(Eratio, xAOD::EgammaParameters::Eratio)) {
820 allFound = false;
821 notFoundList += "Eratio ";
822 }
823 // fraction of energy reconstructed in the 3rd sampling
824 if (!eg->showerShapeValue(f3, xAOD::EgammaParameters::f3)) {
825 allFound = false;
826 notFoundList += "f3 ";
827 }
828
829 // Get the pileup or centrality information
830 double ip = getIpVariable(mu, ctx);
831
832 if (!allFound) {
834 "Skipping LH calculation! The following variables are missing: "
835 << notFoundList);
836 return -999;
837 }
838
839 // Get the answer from the underlying ROOT tool
840 return m_rootTool->calculate(eta,
841 et,
842 f3,
843 Rhad,
844 Rhad1,
845 Reta,
846 w2,
847 f1,
848 Eratio,
849 deltaEta,
850 d0,
851 d0sigma,
852 Rphi,
853 dpOverp,
854 deltaPhiRescaled2,
855 TRT_PID,
856 ip);
857}
858
859// Get the name of the current operating point
860std::string
865
866// aceept with IParticle and no EventContext, backwards compatibility
869{
870 // Backwards compatibility
871 return accept(Gaudi::Hive::currentContext(), part);
872}
873
875AsgElectronLikelihoodTool::accept(const EventContext& ctx,
876 const xAOD::IParticle* part) const
877{
878 if (part->type() == xAOD::Type::Electron) {
879 const xAOD::Electron* el = static_cast<const xAOD::Electron*>(part);
880 return accept(ctx, el);
881 }
882 ATH_MSG_ERROR("Input is not an electron");
883 return m_rootTool->accept();
884}
885
886double
888 const xAOD::IParticle* part) const
889{
890 if (part->type() == xAOD::Type::Electron) {
891 const xAOD::Electron* el = static_cast<const xAOD::Electron*>(part);
892 return calculate(ctx, el);
893 }
894
895 ATH_MSG_ERROR("Input is not an electron");
896 return -999;
897}
898
899// Helper method to get IP variable
900// Can be either NPV, <mu> or fcal Et
901// depending on user configuration.
902double AsgElectronLikelihoodTool::getIpVariable(double mu, const EventContext& ctx) const
903{
904 if (mu < 0) { // determine variable if mu is negative (not given)
905 bool doCentralityTransform = m_rootTool->m_doCentralityTransform;
906 if (doCentralityTransform)
907 return static_cast<double>(m_useCaloSumsCont ? this->getFcalEt(ctx)
909 else if (m_useAverageMu)
910 return this->getAverageMu(ctx);
911 else
912 return static_cast<double>(m_usePVCont ? this->getNPrimVertices(ctx)
913 : m_nPVdefault);
914 }
915 return mu;
916}
917
918// Helper method to get the number of primary vertices
919// We don't want to iterate over all vertices in the event for each electron!!!
920unsigned int
922{
923 unsigned int nVtx(0);
925 if (!vtxCont.isValid()) {
926 ATH_MSG_WARNING("Cannot find " << m_primVtxContKey.key()
927 << " container, returning default nVtx");
928 return m_nPVdefault;
929 }
930 for (const auto *vxcand : *vtxCont) {
931 if (vxcand->nTrackParticles() >= 2)
932 nVtx++;
933 }
934 return nVtx;
935}
936
937// Helper method to get the average mu
938// Defined to use the same definition as
939// TrigEgammaPrecisionElectronHypoAlg
940double AsgElectronLikelihoodTool::getAverageMu(const EventContext &ctx) const
941{
943 if(!eventInfoDecor.isPresent()) {
944 ATH_MSG_WARNING("Cannot find " << m_avgMuKey.key()
945 << ", returning 0");
946 }
947 return eventInfoDecor(0);
948}
949
950// Helper method to get FCal ET for centrality determination
951double
952AsgElectronLikelihoodTool::getFcalEt(const EventContext& ctx) const
953{
954 static const SG::ConstAccessor<std::string> SummaryAcc ("Summary");
955 double fcalEt(0.);
957 xAOD::HIEventShapeContainer::const_iterator es_itr = HIESCont->begin();
958 xAOD::HIEventShapeContainer::const_iterator es_end = HIESCont->end();
959 for (; es_itr != es_end; ++es_itr) {
960 double et = (*es_itr)->et();
961 const std::string& name = SummaryAcc (**es_itr);
962 if (name == "FCal")
963 fcalEt = et * 1.e-6;
964 }
965 return fcalEt;
966}
967
968bool
970 const float eta) const
971{
972
973 static const SG::AuxElement::ConstAccessor<uint16_t> accAuthor("author");
974
975 if (accAuthor.isAvailable(*eg)) {
976
977 // cannot just do eg->author() because it isn't always filled
978 // at trigger level
979 if (accAuthor(*eg) == xAOD::EgammaParameters::AuthorFwdElectron) {
981 "Failed, this is a forward electron! The AsgElectronLikelihoodTool is "
982 "only suitable for central electrons!");
983 return true;
984 }
985 } else {
986 // Check for fwd via eta range the old logic
987 if (std::abs(eta) > 2.5) {
988 ATH_MSG_WARNING("Failed, cluster->etaBE(2) range due to "
989 << eta << " seems like a fwd electron");
990 return true;
991 }
992 }
993
994 return false;
995}
Scalar eta() const
pseudorapidity method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
Handle class for reading a decoration on an object.
Handle class for reading from StoreGate.
Helper class to provide constant type-safe access to aux data.
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
virtual double calculate(const EventContext &ctx, const xAOD::IParticle *part) const override final
calculate method: for pointer to IParticle
double getAverageMu(const EventContext &ctx) const
Get the average mu.
std::string m_pdfFileName
The input ROOT file name that holds the PDFs.
bool isForwardElectron(const xAOD::Egamma *eg, const float eta) const
check for FwdElectron
unsigned int getNPrimVertices(const EventContext &ctx) const
Get the number of primary vertices.
bool m_usePVCont
Whether to use the PV (not available for trigger)
unsigned int m_nPVdefault
defualt nPV (when not using PVCont)
SG::ReadHandleKey< xAOD::HIEventShapeContainer > m_HIESContKey
read handle key to heavy ion container
virtual const asg::AcceptInfo & getAcceptInfo() const override
Method to get the plain AcceptInfo.
bool m_skipDeltaPoverP
Flag for skip the use of deltaPoverP in LH computation (like at HLT)
virtual std::string getOperatingPointName() const override final
Get the name of the current operating point.
virtual asg::AcceptData accept(const xAOD::IParticle *part) const override final
The main accept method: using the generic interface.
bool m_caloOnly
Flag for calo only LH.
virtual ASG_TOOL_CLASS2(AsgElectronLikelihoodTool, IAsgElectronLikelihoodTool, IAsgSelectionTool) public ~AsgElectronLikelihoodTool()
Standard constructor.
double getIpVariable(double mu, const EventContext &ctx) const
Get IP variable based on user configuration.
bool m_useCaloSumsCont
Whether or not to use the CaloSums container in HI events.
SG::ReadHandleKey< xAOD::VertexContainer > m_primVtxContKey
read handle key to primary vertex container
std::string m_WorkingPoint
Working Point.
virtual StatusCode initialize() override
Gaudi Service Interface method implementations.
SG::ReadDecorHandleKey< xAOD::EventInfo > m_avgMuKey
read handle key to averager mu
double m_fcalEtDefault
defualt FCal ET (when not using CaloSums container, in HI events)
bool m_correctDeltaEta
Flag to toggle the correction of deltaEta1 for the pear shape distortion of the LAr.
double getFcalEt(const EventContext &ctx) const
Get the FCal ET for centrality determination (for HI collisions)
Root::TElectronLikelihoodTool * m_rootTool
Pointer to the underlying ROOT based tool.
DataModel_detail::const_iterator< DataVector > const_iterator
Definition DataVector.h:838
SG::ConstAccessor< T, ALLOC > ConstAccessor
Definition AuxElement.h:569
SG::Accessor< T, ALLOC > Accessor
Definition AuxElement.h:572
Helper class to provide constant type-safe access to aux data.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
Handle class for reading a decoration on an object.
bool isPresent() const
Is the referenced container present in SG?
virtual bool isValid() override final
Can the handle be successfully dereferenced?
virtual double e() const
The total energy of the particle.
bool hasSampling(const CaloSample s) const
Checks if certain smapling contributes to cluster.
float etaBE(const unsigned layer) const
Get the eta in one layer of the EM Calo.
Class providing the definition of the 4-vector interface.
std::vector< int > HelperInt(const std::string &input, TEnv &env)
std::vector< double > HelperDouble(const std::string &input, TEnv &env)
std::string findConfigFile(const std::string &input, const std::map< std::string, std::string > &configmap)
const std::map< std::string, std::string > LHPointToConfFile
std::size_t numberOfPixelHitsAndDeadSensors(const xAOD::TrackParticle &tp)
return the number of Pixel hits plus dead sensors in the track particle
bool passBLayerRequirement(const xAOD::TrackParticle &tp)
return true if effective number of BL hits + outliers is at least one
std::size_t numberOfSiliconHitsAndDeadSensors(const xAOD::TrackParticle &tp)
return the number of Silicon hits plus dead sensors in the track particle
Definition index.py:1
@ Electron
The object is an electron.
Definition ObjectType.h:46
@ deltaPhiRescaled2
difference between the cluster phi (second sampling) and the phi of the track extrapolated to the sec...
@ deltaEta1
difference between the cluster eta (first sampling) and the eta of the track extrapolated to the firs...
const uint16_t AuthorFwdElectron
Electron reconstructed by the Forward cluster-based algorithm.
Definition EgammaDefs.h:30
@ wtots1
shower width is determined in a window detaxdphi = 0,0625 ×~0,2, corresponding typically to 20 strips...
@ f3
fraction of energy reconstructed in 3rd sampling
Definition EgammaEnums.h:55
@ f1
E1/E = fraction of energy reconstructed in the first sampling, where E1 is energy in all strips belon...
Definition EgammaEnums.h:53
@ Eratio
(emaxs1-e2tsts1)/(emaxs1+e2tsts1)
@ weta2
the lateral width is calculated with a window of 3x5 cells using the energy weighted sum over all cel...
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
Egamma_v1 Egamma
Definition of the current "egamma version".
Definition Egamma.h:17
@ eProbabilityHT
Electron probability from High Threshold (HT) information [float].
Electron_v1 Electron
Definition of the current "egamma version".
@ LastMeasurement
Parameter defined at the position of the last measurement.
Extra patterns decribing particle interation process.
MsgStream & msg
Definition testRead.cxx:32