ATLAS Offline Software
Loading...
Searching...
No Matches
AsgPhotonEfficiencyCorrectionTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
12
13// Include this class's header
15
16// STL includes
17#include <cfloat>
18#include <climits>
19#include <iostream>
20#include <string>
21
22// Include the return object
24
25// xAOD includes
26#include "xAODEgamma/Egamma.h"
30
31// ROOT includes
32#include "TSystem.h"
33#define MAXETA 2.47
34#define MIN_ET 7000.0
35
37
38
39// =============================================================================
40// Standard constructor
41// =============================================================================
43 AsgTool(myname),
44 m_rootTool_unc(nullptr),
45 m_rootTool_con(nullptr),
46 m_appliedSystematics(nullptr),
48{
49
50 // Create an instances of the underlying ROOT tools
53
54 // Declare the needed properties
55 declareProperty( "CorrectionFileNameConv", m_corrFileNameConv="",
56 "File that stores the correction factors for simulation for converted photons");
57
58 declareProperty( "CorrectionFileNameUnconv", m_corrFileNameUnconv="",
59 "File that stores the correction factors for simulation for unconverted photons");
60
61 declareProperty("MapFilePath", m_mapFile = "PhotonEfficiencyCorrection/2015_2025/rel22.2/2024_FinalRun2_Recommendation_v1/map1.txt",
62 "Full path to the map file");
63
64 declareProperty( "ForceDataType", m_dataTypeOverwrite=-1,
65 "Force the DataType of the Photon to specified value");
66
67 declareProperty( "ResultPrefix", m_resultPrefix="", "The prefix string for the result");
68 declareProperty( "ResultName", m_resultName="", "The string for the result");
69
70 // Properties needed for isolation corrections
71 declareProperty( "IsoKey", m_isoWP="", "Set isolation WP, if this string is empty the tool will return ID SF");
72
73 // Properties needed for trigger SF
74 declareProperty( "TriggerKey", m_trigger="", "Set trigger, if this string is empty the tool will return ID SF");
75
76 // Properties related to the receiving of event run number
77 declareProperty("UseRandomRunNumber", m_useRandomRunNumber = true,
78 "Set if use RandomRunNumber from eventinfo");
79 declareProperty("DefaultRandomRunNumber", m_defaultRandomRunNumber = 999999,
80 "Set default run number manually");
81 declareProperty("removeTRTConversion", m_removeTRTConversion = true,
82 "boolean to treat barrel standalone TRT conversion as unconverted for Run3 ");
83
84
85}
86
87// =============================================================================
88// Standard destructor
89// =============================================================================
95
96// =============================================================================
97// Athena initialize method
98// =============================================================================
100{
101 // Resolve the paths to the input files
102 std::vector < std::string > corrFileNameList;
103
104 // First check if the tool is initialized using the input files or map
105 if(!m_mapFile.empty()){ // using map file
106 corrFileNameList.push_back(getFileName(m_isoWP,m_trigger,true)); // converted photons input
107 corrFileNameList.push_back(getFileName(m_isoWP,m_trigger,false)); // unconverted photons input
108 }
109 else if(!m_corrFileNameConv.empty() && !m_corrFileNameUnconv.empty()){ // initialize the tool using input files (old scheme)
110 corrFileNameList.push_back(m_corrFileNameConv);
111 corrFileNameList.push_back(m_corrFileNameUnconv);
112 }
113 else{
114 ATH_MSG_ERROR ( "Fail to resolve input file name, check if you set MapFilePath or CorrectionFileName properly" );
115 return StatusCode::FAILURE ;
116 }
117
118 // once the input files are retrieved, update the path using PathResolver or TOOL/data folder
119 for (auto & i : corrFileNameList){
120
121 //Using the PathResolver to locate the file
122 std::string filename = PathResolverFindCalibFile( i );
123
124 if (filename.empty()){
125 ATH_MSG_ERROR ( "Could NOT resolve file name " << i );
126 return StatusCode::FAILURE ;
127 } else{
128 ATH_MSG_INFO(" Using path = "<<filename);
129 }
130
131 i = filename;
132
133 }
134
135 // Set prefix for sustematics if this is ISO, Trigger or ID SF
136 if( corrFileNameList[0].find(m_file_prefix_Trig) != std::string::npos) m_sysSubstring="TRIGGER_";
137 if( corrFileNameList[0].find(m_file_prefix_ID) != std::string::npos) m_sysSubstring="ID_";
138 if( corrFileNameList[0].find(m_file_prefix_ISO) != std::string::npos) m_sysSubstring="ISO_";
139 if( corrFileNameList[0].find(m_file_prefix_TrigEff) != std::string::npos) m_sysSubstring="TRIGGER_";
140 if(m_sysSubstring.empty()) {ATH_MSG_ERROR ( "Invalid input file" ); return StatusCode::FAILURE;}
141
142 // Configure the underlying Root tool
143 m_rootTool_con->addFileName( corrFileNameList[0] );
144 m_rootTool_unc->addFileName( corrFileNameList[1] );
145
146 // Forward the message level
147 m_rootTool_con->msg().setLevel(this->msg().level());
148 m_rootTool_unc->msg().setLevel(this->msg().level());
149
150 // We need to initialize the underlying ROOT TSelectorTool
151 if ( (0 == m_rootTool_con->initialize()) || (0 == m_rootTool_unc->initialize()) )
152 {
153 ATH_MSG_ERROR("Could not initialize the TElectronEfficiencyCorrectionTool!");
154 return StatusCode::FAILURE;
155 }
156
157 // get the map of pt/eta bins
158 // let's start with converted
159 m_rootTool_con->getNbins(m_pteta_bins);
160 std::map<float, std::vector<float>> pteta_bins_unc;
161 // now let's get unconverted
162 m_rootTool_unc->getNbins(pteta_bins_unc);
163 // let's loop the unconverted map and copy over to the common one
164 // in this tool we only ever care about et, so don't care if we overwrite eta information
165 for (const auto& [pt_unc, eta_unc]: pteta_bins_unc) {
166 m_pteta_bins[pt_unc] = eta_unc;
167 }
168
169 // Add the recommended systematics to the registry
170 if ( registerSystematics() != StatusCode::SUCCESS) {
171 return StatusCode::FAILURE;
172 }
173
174 return StatusCode::SUCCESS ;
175}
176
177
178// =============================================================================
179// The main accept method: the actual cuts are applied here
180// =============================================================================
182{
183 const auto& acc = *m_accessors;
184
185 // retrieve transverse energy from e/cosh(etaS2)
186 auto clusters = egam(acc.caloClusterAcc);
187 if (clusters.size()<1||!clusters[0].has_value()){
188 ATH_MSG_ERROR("No cluster associated to the Photon \n");
190 }
191 auto cluster = clusters[0].value();
192
193 // use et from cluster because it is immutable under syst variations of ele energy scale
194 const double energy = cluster(acc.clusterEAcc);
195 double et = 0.;
196 if ( std::abs(egam(acc.etaAcc)) < 999.) {
197 const double cosheta = std::cosh(egam(acc.etaAcc));
198 et = (cosheta != 0.) ? energy / cosheta : 0.;
199 }
200
201 // eta from second layer
202 double eta2 = cluster(acc.clusterEtaBEAcc,2);
203
204 // allow for a 5% margin at the lowest pT bin boundary (i.e. increase et by 5%
205 // for sub-threshold electrons). This assures that electrons that pass the
206 // threshold only under syst variations of energy get a scale factor assigned.
207 std::map<float, std::vector<float>>::const_iterator itr_pt = m_pteta_bins.begin();
208 if (itr_pt!=m_pteta_bins.end() && et<itr_pt->first) {
209 et=et*1.05;
210 }
211
212 // Check if photon in the range to get the SF
213 if (std::abs(eta2) > MAXETA) {
214 result.SF = 1;
215 result.Total = 1;
216 ATH_MSG_DEBUG("No correction factor provided for eta "
217 << eta2 << " Returning SF = 1 + / - 1");
219 }
220 if (et < MIN_ET) {
221 result.SF = 1;
222 result.Total = 1;
223 ATH_MSG_DEBUG("No correction factor provided for eT "
224 << et << " Returning SF = 1 + / - 1");
226 }
227 if (itr_pt != m_pteta_bins.end() && et < itr_pt->first) {
228 result.SF = 1;
229 result.Total = 1;
230 ATH_MSG_DEBUG("No scale factor uncertainty provided for et "
231 << et / 1e3 << "GeV Returning SF = 1 + / - 1");
233 }
234
235 // Retrieve the proper random Run Number
236 unsigned int runnumber = m_defaultRandomRunNumber;
238 if (!acc.randomRunNumberAcc.isAvailable(eventInfo)) {
240 "Pileup tool not run before using PhotonEfficiencyTool! SFs do not "
241 "reflect PU distribution in data");
243 }
244 runnumber = acc.randomRunNumberAcc(eventInfo);
245 }
246
247 /* For now the dataType must be set by the user. May be added to the IParticle
248 * class later. */
249 // probably event info should be able to tell us if it's data, fullsim, AF,..
251 PATCore::ParticleDataType::DataType::Data;
252 if (m_dataTypeOverwrite >= 0)
254
255
256 //exclude TRT
257 bool excludeTRT = false;
258 if(runnumber >= 410000 && m_removeTRTConversion) excludeTRT = true;
259 // check if converted
260 const auto [isConv, missingLinks] = acc.isConvertedPhotonAcc(egam, excludeTRT);
261 if (missingLinks) { // missing vertex or track links
262 if (!m_allowMissingLinks.value()) {
263 ATH_MSG_ERROR("Missing vertex or track links when trying to calculate the conversion type. missingLinks=" << std::hex << missingLinks);
265 }
266 static std::atomic<bool> warned {false};
267 if (warned.exchange(true) == false) {
268 ATH_MSG_WARNING("Missing vertex or track links when trying to calculate the conversion type. missingLinks=" << std::hex << missingLinks);
269 ATH_MSG_WARNING("This may indicate that the DAOD is incorrectly missing some conversion vertices.");
270 ATH_MSG_WARNING("This is your only warning, repeat occurrences will be reported at the DEBUG level.");
271 } else {
272 ATH_MSG_DEBUG("Missing vertex or track links when trying to calculate the conversion type. missingLinks=" << std::hex << missingLinks);
273 }
274 result.SF = 1;
275 result.Total = 1;
277 }
278
279
280 // Call the ROOT tool to get an answer (for photons we need just the total)
281 const int status = isConv ? m_rootTool_con->calculate(dataType, runnumber,
282 eta2, et, result, true)
283 : m_rootTool_unc->calculate(dataType, runnumber,
284 eta2, et, result, true);
285
286 // if status 0 something went wrong
287 if (!status) {
288 result.SF = 1;
289 result.Total = 1;
291 }
292
294}
295
297
298 // Get the run number
299 const xAOD::EventInfo* eventInfo =
300 evtStore()->retrieve<const xAOD::EventInfo>("EventInfo");
301 if (!eventInfo) {
302 ATH_MSG_ERROR("Could not retrieve EventInfo object!");
304 }
305
306 return getEfficiencyScaleFactor(columnar::EgammaId{inputObject}, columnar::EventInfoId{*eventInfo}, efficiencyScaleFactor);
307}
308
310
311 Result sfresult;
312 CP::CorrectionCode status = calculate(inputObject, eventInfo, sfresult);
313
314 if ( status != CP::CorrectionCode::Ok ) {
315 return status;
316 }
317
318 if(m_appliedSystematics==nullptr){
319 efficiencyScaleFactor=sfresult.SF;
321 }
322
323 //Get the result + the uncertainty
324 float sigma(0);
325 sigma=appliedSystematics().getParameterByBaseName("PH_EFF_"+m_sysSubstring+"Uncertainty");
326 efficiencyScaleFactor=sfresult.SF+sigma*sfresult.Total;
328}
329
330CP::CorrectionCode AsgPhotonEfficiencyCorrectionTool::getEfficiencyScaleFactorError(const xAOD::Egamma& inputObject, double& efficiencyScaleFactorError) const{
331
332 // Get the run number
333 const xAOD::EventInfo* eventInfo =
334 evtStore()->retrieve<const xAOD::EventInfo>("EventInfo");
335 if (!eventInfo) {
336 ATH_MSG_ERROR("Could not retrieve EventInfo object!");
338 }
339
340 return getEfficiencyScaleFactorError(columnar::EgammaId{inputObject}, columnar::EventInfoId{*eventInfo}, efficiencyScaleFactorError);
341}
342
344
345 Result sfresult;
346 CP::CorrectionCode status = calculate(inputObject, eventInfo, sfresult);
347
348 if ( status != CP::CorrectionCode::Ok ) {
349 return status;
350 }
351
352 efficiencyScaleFactorError=sfresult.Total;
354}
355
357
358 double efficiencyScaleFactor = 1.0;
359 CP::CorrectionCode result = getEfficiencyScaleFactor(inputObject, efficiencyScaleFactor);
361 dec(inputObject) = efficiencyScaleFactor;
362 return result;
363}
364
365//=======================================================================
366// Systematics Interface
367//=======================================================================
369 if(!systematic.empty()){
371 return sys.find(systematic) != sys.end();
372 }
373 return true;
374}
375
376
379 CP::SystematicSet mySysSet;
380
382 mySysSet.insert(CP::SystematicVariation("PH_EFF_"+m_sysSubstring+"Uncertainty", 1));
383 mySysSet.insert(CP::SystematicVariation("PH_EFF_"+m_sysSubstring+"Uncertainty", -1));
384
385 return mySysSet;
386}
387
391 if (registry.registerSystematics(*this) != StatusCode::SUCCESS) {
392 ATH_MSG_ERROR("Failed to add systematic to list of recommended systematics.");
393 return StatusCode::FAILURE;
394 }
395 return StatusCode::SUCCESS;
396}
397
400 CP::SystematicSet mySysSet;
401 mySysSet.insert(CP::SystematicVariation("PH_EFF_"+m_sysSubstring+"Uncertainty", 1));
402 mySysSet.insert(CP::SystematicVariation("PH_EFF_"+m_sysSubstring+"Uncertainty", -1));
403
404 return mySysSet;
405}
406
407
410{
411 // First, check if we already know this systematic configuration
412 auto itr = m_systFilter.find(systConfig);
413
414 // If it's a new input set, we need to filter it
415 if( itr == m_systFilter.end() ){
416
418 CP::SystematicSet filteredSys;
419 if (!CP::SystematicSet::filterForAffectingSystematics(systConfig, affectingSys, filteredSys)){
420 ATH_MSG_ERROR("Unsupported combination of systematics passed to the tool!");
421 return StatusCode::FAILURE;
422 }
423 // Insert filtered set into the map
424 itr = m_systFilter.insert(std::make_pair(systConfig, filteredSys)).first;
425 }
426
427 CP::SystematicSet& mySysConf = itr->second;
428 m_appliedSystematics = &mySysConf;
429 return StatusCode::SUCCESS;
430}
431
432//===============================================================================
433// Map Key Feature
434//===============================================================================
435// Gets the correction filename from map
436std::string AsgPhotonEfficiencyCorrectionTool::getFileName(const std::string& isoWP, const std::string& trigWP, bool isConv) {
437
438 // First locate the map file:
439 std::string mapFileName = PathResolverFindCalibFile( m_mapFile );
440 if(mapFileName.empty()){
441 ATH_MSG_ERROR ( "Can't read map file " << m_mapFile );
442 return mapFileName; // return an empty string
443 }
444
445 // Construct correction type:
446 std::string correction_type = "ID_Tight";
447 if(!trigWP.empty()) correction_type = "Trigger_"+trigWP+"_"+isoWP;
448 else if(!isoWP.empty()) correction_type = "ISO_"+isoWP;
449
450 // trigger SF same for con/unc photons
451 if(trigWP.empty()) {correction_type += isConv ? "_Converted" : "_Unconverted";}
452
453 std::string value;
454
455 // Read the map file to find the proper correction filename
456 std::ifstream is(mapFileName);
457 if (!is.is_open()){
458 ATH_MSG_ERROR("Couldn't read Map File" + mapFileName);
459 return "";
460 }
461 while (!is.eof()) {
462 std::string strLine;
463 getline(is,strLine);
464
465 int nPos = strLine.find('=');
466
467 if ((signed int)std::string::npos == nPos) continue; // no '=', invalid line;
468 std::string strKey = strLine.substr(0,nPos);
469 std::string strVal = strLine.substr(nPos + 1, strLine.length() - nPos + 1);
470
471 // check if this is the right key, if the right one stop the search
472 if(0==correction_type.compare(strKey)) {value=strVal; break;}
473 }
474
475 return value;
476
477}
478
480{
481 const Accessors& acc = *m_accessors;
482 for (auto photon : photons)
483 {
484 double sf = 0;
485 switch (getEfficiencyScaleFactor(photon, event, sf).code())
486 {
488 case Ok:
489 acc.sfDec(photon) = sf;
490 acc.validDec(photon) = true;
491 break;
492 case OutOfValidityRange:
493 acc.sfDec(photon) = sf;
494 acc.validDec(photon) = false;
495 break;
496 default:
497 throw std::runtime_error("Error in getEfficiencyScaleFactor");
498 }
499 }
500}
501
503{
504 const Accessors& acc = *m_accessors;
505 for (columnar::EventContextId event : events)
506 {
507 auto eventInfo = acc.eventInfoAcc(event);
508 callSingleEvent (acc.photonsAcc(event), eventInfo);
509 }
510}
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
float et(const xAOD::jFexSRJetRoI *j)
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
CP::SystematicSet * m_appliedSystematics
Currently applied systematics.
int m_dataTypeOverwrite
Force the data type to a given value.
std::string m_resultPrefix
The prefix string for the result.
std::string getFileName(const std::string &isoWP, const std::string &trigWP, bool isConv)
CP::CorrectionCode calculate(columnar::EgammaId egam, columnar::EventInfoId eventInfo, Result &result) const
I think these calculate methods are only used internally.
virtual CP::CorrectionCode applyEfficiencyScaleFactor(xAOD::Egamma &inputObject) const override
Decorate the photon with its scale factor.
std::string m_isoWP
Isolation working point.
virtual StatusCode applySystematicVariation(const CP::SystematicSet &systConfig) override
Configure this tool for the given systematics.
std::string m_corrFileNameConv
The list of input file names.
std::string m_trigger
Trigger name for trigger SF.
Root::TElectronEfficiencyCorrectionTool * m_rootTool_unc
Pointer to the underlying ROOT based tool.
virtual StatusCode initialize() override
Gaudi Service Interface method implementations.
virtual CP::SystematicSet recommendedSystematics() const override
returns: the list of all systematics this tool recommends to use
virtual CP::CorrectionCode getEfficiencyScaleFactor(const xAOD::Egamma &inputObject, double &efficiencyScaleFactor) const override
Add some method for now as a first step to move the tool to then new interface.
const CP::SystematicSet & appliedSystematics() const
returns: the currently applied systematics
void callEvents(columnar::EventContextRange events) const override
virtual CP::CorrectionCode getEfficiencyScaleFactorError(const xAOD::Egamma &inputObject, double &efficiencyScaleFactorError) const override
Get the "photon scale factor error" as a return value.
Root::TElectronEfficiencyCorrectionTool::Result Result
void callSingleEvent(columnar::EgammaRange photons, columnar::EventInfoId event) const
std::string m_resultName
The string for the result.
virtual CP::SystematicSet affectingSystematics() const override
returns: the list of all systematics this tool can be affected by
virtual ~AsgPhotonEfficiencyCorrectionTool()
Standard destructor.
virtual bool isAffectedBySystematic(const CP::SystematicVariation &systematic) const override
The methods below should notify the user of what is actually in the list , without him having to go i...
Root::TElectronEfficiencyCorrectionTool * m_rootTool_con
StatusCode registerSystematics()
Register the systematics with the registry and add them to the recommended list.
std::unordered_map< CP::SystematicSet, CP::SystematicSet > m_systFilter
Systematics filter map.
std::map< float, std::vector< float > > m_pteta_bins
AsgPhotonEfficiencyCorrectionTool(const std::string &myname)
Standard constructor.
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
ServiceHandle< StoreGateSvc > & evtStore()
Return value from object correction CP tools.
ErrorCode
Possible values for the correction code.
@ Error
Some error happened during the object correction.
@ OutOfValidityRange
Input object is out of validity range.
@ Ok
The correction was done successfully.
This module implements the central registry for handling systematic uncertainties with CP tools.
static SystematicRegistry & getInstance()
Get the singleton instance of the registry for the curren thread.
StatusCode registerSystematics(const IReentrantSystematicsTool &tool)
effects: register all the systematics from the tool
Class to wrap a set of SystematicVariations.
void insert(const SystematicVariation &systematic)
description: insert a systematic into the set
float getParameterByBaseName(const std::string &basename) const
returns: the parameter value for the given basename
static StatusCode filterForAffectingSystematics(const SystematicSet &systConfig, const SystematicSet &affectingSystematics, SystematicSet &filteredSystematics)
description: filter the systematics for the affected systematics returns: success guarantee: strong f...
bool empty() const
returns: whether this is an empty systematic, i.e.
SG::Decorator< T, ALLOC > Decorator
Definition AuxElement.h:575
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
std::string find(const std::string &s)
return a remapped string
Definition hcg.cxx:138
static std::vector< uint32_t > runnumber
Definition iLumiCalc.h:37
ObjectRange< ContainerId::egamma > EgammaRange
Definition EgammaDef.h:49
ObjectId< ContainerId::egamma > EgammaId
Definition EgammaDef.h:50
ObjectRange< ContainerId::eventContext > EventContextRange
ObjectId< ContainerId::eventContext > EventContextId
ObjectId< ContainerId::eventInfo > EventInfoId
EventInfo_v1 EventInfo
Definition of the latest event info version.
Egamma_v1 Egamma
Definition of the current "egamma version".
Definition Egamma.h:17
Extra patterns decribing particle interation process.
MsgStream & msg
Definition testRead.cxx:32