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
39namespace ADD_CMODE(CP)
40{
41
42// =============================================================================
43// Standard constructor
44// =============================================================================
45AsgPhotonEfficiencyCorrectionTool::AsgPhotonEfficiencyCorrectionTool( const std::string& myname ) :
46 AsgTool(myname),
47 m_rootTool_unc(nullptr),
48 m_rootTool_con(nullptr),
49 m_appliedSystematics(nullptr),
50 m_sysSubstring("")
51{
52
53 // Create an instances of the underlying ROOT tools
54 m_rootTool_unc = new Root::TElectronEfficiencyCorrectionTool();
55 m_rootTool_con = new Root::TElectronEfficiencyCorrectionTool();
56
57 // Declare the needed properties
58 declareProperty( "CorrectionFileNameConv", m_corrFileNameConv="",
59 "File that stores the correction factors for simulation for converted photons");
60
61 declareProperty( "CorrectionFileNameUnconv", m_corrFileNameUnconv="",
62 "File that stores the correction factors for simulation for unconverted photons");
63
64 declareProperty("MapFilePath", m_mapFile = "PhotonEfficiencyCorrection/2015_2025/rel22.2/2026_Run3Consolidated_Recommendation_v1/map0.txt",
65 "Full path to the map file");
66
67 declareProperty( "ForceDataType", m_dataTypeOverwrite=-1,
68 "Force the DataType of the Photon to specified value");
69
70 declareProperty( "ResultPrefix", m_resultPrefix="", "The prefix string for the result");
71 declareProperty( "ResultName", m_resultName="", "The string for the result");
72
73 // Properties needed for isolation corrections
74 declareProperty( "IsoKey", m_isoWP="", "Set isolation WP, if this string is empty the tool will return ID SF");
75
76 // Properties needed for trigger SF
77 declareProperty( "TriggerKey", m_trigger="", "Set trigger, if this string is empty the tool will return ID SF");
78
79 // Properties related to the receiving of event run number
80 declareProperty("UseRandomRunNumber", m_useRandomRunNumber = true,
81 "Set if use RandomRunNumber from eventinfo");
82 declareProperty("DefaultRandomRunNumber", m_defaultRandomRunNumber = 999999,
83 "Set default run number manually");
84 declareProperty("removeTRTConversion", m_removeTRTConversion = true,
85 "boolean to treat barrel standalone TRT conversion as unconverted for Run3 ");
86
87
88}
89
90// =============================================================================
91// Standard destructor
92// =============================================================================
93AsgPhotonEfficiencyCorrectionTool::~AsgPhotonEfficiencyCorrectionTool()
94{
95 if ( m_rootTool_unc ) delete m_rootTool_unc;
96 if ( m_rootTool_con ) delete m_rootTool_con;
97}
98
99// =============================================================================
100// Athena initialize method
101// =============================================================================
102StatusCode AsgPhotonEfficiencyCorrectionTool::initialize()
103{
104 // Resolve the paths to the input files
105 std::vector < std::string > corrFileNameList;
106
107 // First check if the tool is initialized using the input files or map
108 if(!m_mapFile.empty()){ // using map file
109 corrFileNameList.push_back(getFileName(m_isoWP,m_trigger,true)); // converted photons input
110 corrFileNameList.push_back(getFileName(m_isoWP,m_trigger,false)); // unconverted photons input
111 }
112 else if(!m_corrFileNameConv.empty() && !m_corrFileNameUnconv.empty()){ // initialize the tool using input files (old scheme)
113 corrFileNameList.push_back(m_corrFileNameConv);
114 corrFileNameList.push_back(m_corrFileNameUnconv);
115 }
116 else{
117 ATH_MSG_ERROR ( "Fail to resolve input file name, check if you set MapFilePath or CorrectionFileName properly" );
118 return StatusCode::FAILURE ;
119 }
120
121 // once the input files are retrieved, update the path using PathResolver or TOOL/data folder
122 for (auto & i : corrFileNameList){
123
124 //Using the PathResolver to locate the file
125 std::string filename = PathResolverFindCalibFile( i );
126
127 if (filename.empty()){
128 ATH_MSG_ERROR ( "Could NOT resolve file name " << i );
129 return StatusCode::FAILURE ;
130 } else{
131 ATH_MSG_INFO(" Using path = "<<filename);
132 }
133
134 i = filename;
135
136 }
137
138 // Set prefix for sustematics if this is ISO, Trigger or ID SF
139 if( corrFileNameList[0].find(m_file_prefix_Trig) != std::string::npos) m_sysSubstring="TRIGGER_";
140 if( corrFileNameList[0].find(m_file_prefix_ID) != std::string::npos) m_sysSubstring="ID_";
141 if( corrFileNameList[0].find(m_file_prefix_ISO) != std::string::npos) m_sysSubstring="ISO_";
142 if( corrFileNameList[0].find(m_file_prefix_TrigEff) != std::string::npos) m_sysSubstring="TRIGGER_";
143 if(m_sysSubstring.empty()) {ATH_MSG_ERROR ( "Invalid input file" ); return StatusCode::FAILURE;}
144
145 // Configure the underlying Root tool
146 m_rootTool_con->addFileName( corrFileNameList[0] );
147 m_rootTool_unc->addFileName( corrFileNameList[1] );
148
149 // Forward the message level
150 m_rootTool_con->msg().setLevel(this->msg().level());
151 m_rootTool_unc->msg().setLevel(this->msg().level());
152
153 // We need to initialize the underlying ROOT TSelectorTool
154 if ( (0 == m_rootTool_con->initialize()) || (0 == m_rootTool_unc->initialize()) )
155 {
156 ATH_MSG_ERROR("Could not initialize the TElectronEfficiencyCorrectionTool!");
157 return StatusCode::FAILURE;
158 }
159
160 // get the map of pt/eta bins
161 // let's start with converted
162 m_rootTool_con->getNbins(m_pteta_bins);
163 std::map<float, std::vector<float>> pteta_bins_unc;
164 // now let's get unconverted
165 m_rootTool_unc->getNbins(pteta_bins_unc);
166 // let's loop the unconverted map and copy over to the common one
167 // in this tool we only ever care about et, so don't care if we overwrite eta information
168 for (const auto& [pt_unc, eta_unc]: pteta_bins_unc) {
169 m_pteta_bins[pt_unc] = eta_unc;
170 }
171
172 // Add the recommended systematics to the registry
173 if ( registerSystematics() != StatusCode::SUCCESS) {
174 return StatusCode::FAILURE;
175 }
176
177 return StatusCode::SUCCESS ;
178}
179
180
181// =============================================================================
182// The main accept method: the actual cuts are applied here
183// =============================================================================
184CP::CorrectionCode AsgPhotonEfficiencyCorrectionTool::calculate( columnar::EgammaId egam, columnar::EventInfoId eventInfo, Result& result ) const
185{
186 const auto& acc = *m_accessors;
187
188 // retrieve transverse energy from e/cosh(etaS2)
189 auto clusters = egam(acc.caloClusterAcc);
190 if (clusters.size()<1||!clusters[0].has_value()){
191 ATH_MSG_ERROR("No cluster associated to the Photon \n");
193 }
194 auto cluster = clusters[0].value();
195
196 // use et from cluster because it is immutable under syst variations of ele energy scale
197 const double energy = cluster(acc.clusterEAcc);
198 double et = 0.;
199 if ( std::abs(egam(acc.etaAcc)) < 999.) {
200 const double cosheta = std::cosh(egam(acc.etaAcc));
201 et = (cosheta != 0.) ? energy / cosheta : 0.;
202 }
203
204 // eta from second layer
205 double eta2 = cluster(acc.clusterEtaBEAcc,2);
206
207 // allow for a 5% margin at the lowest pT bin boundary (i.e. increase et by 5%
208 // for sub-threshold electrons). This assures that electrons that pass the
209 // threshold only under syst variations of energy get a scale factor assigned.
210 std::map<float, std::vector<float>>::const_iterator itr_pt = m_pteta_bins.begin();
211 if (itr_pt!=m_pteta_bins.end() && et<itr_pt->first) {
212 et=et*1.05;
213 }
214
215 // Check if photon in the range to get the SF
216 if (std::abs(eta2) > MAXETA) {
217 result.SF = 1;
218 result.Total = 1;
219 ATH_MSG_DEBUG("No correction factor provided for eta "
220 << eta2 << " Returning SF = 1 + / - 1");
222 }
223 if (et < MIN_ET) {
224 result.SF = 1;
225 result.Total = 1;
226 ATH_MSG_DEBUG("No correction factor provided for eT "
227 << et << " Returning SF = 1 + / - 1");
229 }
230 if (itr_pt != m_pteta_bins.end() && et < itr_pt->first) {
231 result.SF = 1;
232 result.Total = 1;
233 ATH_MSG_DEBUG("No scale factor uncertainty provided for et "
234 << et / 1e3 << "GeV Returning SF = 1 + / - 1");
236 }
237
238 // Retrieve the proper random Run Number
239 unsigned int runnumber = m_defaultRandomRunNumber;
240 if (m_useRandomRunNumber) {
241 if (!acc.randomRunNumberAcc.isAvailable(eventInfo)) {
243 "Pileup tool not run before using PhotonEfficiencyTool! SFs do not "
244 "reflect PU distribution in data");
246 }
247 runnumber = acc.randomRunNumberAcc(eventInfo);
248 }
249
250 /* For now the dataType must be set by the user. May be added to the IParticle
251 * class later. */
252 // probably event info should be able to tell us if it's data, fullsim, AF,..
254 PATCore::ParticleDataType::DataType::Data;
255 if (m_dataTypeOverwrite >= 0)
256 dataType = (PATCore::ParticleDataType::DataType)m_dataTypeOverwrite;
257
258
259 //exclude TRT
260 bool excludeTRT = false;
261 if(runnumber >= 410000 && m_removeTRTConversion) excludeTRT = true;
262 // check if converted
263 const auto [isConv, missingLinks] = acc.isConvertedPhotonAcc(egam, excludeTRT);
264 if (missingLinks) { // missing vertex or track links
265 if (!m_allowMissingLinks.value()) {
266 ATH_MSG_ERROR("Missing vertex or track links when trying to calculate the conversion type. missingLinks=" << std::hex << missingLinks);
268 }
269 static std::atomic<bool> warned {false};
270 if (warned.exchange(true) == false) {
271 ATH_MSG_WARNING("Missing vertex or track links when trying to calculate the conversion type. missingLinks=" << std::hex << missingLinks);
272 ATH_MSG_WARNING("This may indicate that the DAOD is incorrectly missing some conversion vertices.");
273 ATH_MSG_WARNING("This is your only warning, repeat occurrences will be reported at the DEBUG level.");
274 } else {
275 ATH_MSG_DEBUG("Missing vertex or track links when trying to calculate the conversion type. missingLinks=" << std::hex << missingLinks);
276 }
277 result.SF = 1;
278 result.Total = 1;
280 }
281
282
283 // Call the ROOT tool to get an answer (for photons we need just the total)
284 const int status = isConv ? m_rootTool_con->calculate(dataType, runnumber,
285 eta2, et, result, true)
286 : m_rootTool_unc->calculate(dataType, runnumber,
287 eta2, et, result, true);
288
289 // if status 0 something went wrong
290 if (!status) {
291 result.SF = 1;
292 result.Total = 1;
294 }
295
297}
298
299CP::CorrectionCode AsgPhotonEfficiencyCorrectionTool::getEfficiencyScaleFactor(const xAOD::Egamma& inputObject, double& efficiencyScaleFactor) const{
300
301 // Get the run number
302 const xAOD::EventInfo* eventInfo =
303 evtStore()->retrieve<const xAOD::EventInfo>("EventInfo");
304 if (!eventInfo) {
305 ATH_MSG_ERROR("Could not retrieve EventInfo object!");
307 }
308
309 return getEfficiencyScaleFactor(columnar::EgammaId{inputObject}, columnar::EventInfoId{*eventInfo}, efficiencyScaleFactor);
310}
311
312CP::CorrectionCode AsgPhotonEfficiencyCorrectionTool::getEfficiencyScaleFactor(columnar::EgammaId inputObject, columnar::EventInfoId eventInfo, double& efficiencyScaleFactor) const{
313
314 Result sfresult;
315 CP::CorrectionCode status = calculate(inputObject, eventInfo, sfresult);
316
317 if ( status != CP::CorrectionCode::Ok ) {
318 return status;
319 }
320
321 if(m_appliedSystematics==nullptr){
322 efficiencyScaleFactor=sfresult.SF;
324 }
325
326 //Get the result + the uncertainty
327 float sigma(0);
328 sigma=appliedSystematics().getParameterByBaseName("PH_EFF_"+m_sysSubstring+"Uncertainty");
329 efficiencyScaleFactor=sfresult.SF+sigma*sfresult.Total;
331}
332
333CP::CorrectionCode AsgPhotonEfficiencyCorrectionTool::getEfficiencyScaleFactorError(const xAOD::Egamma& inputObject, double& efficiencyScaleFactorError) const{
334
335 // Get the run number
336 const xAOD::EventInfo* eventInfo =
337 evtStore()->retrieve<const xAOD::EventInfo>("EventInfo");
338 if (!eventInfo) {
339 ATH_MSG_ERROR("Could not retrieve EventInfo object!");
341 }
342
343 return getEfficiencyScaleFactorError(columnar::EgammaId{inputObject}, columnar::EventInfoId{*eventInfo}, efficiencyScaleFactorError);
344}
345
346CP::CorrectionCode AsgPhotonEfficiencyCorrectionTool::getEfficiencyScaleFactorError(columnar::EgammaId inputObject, columnar::EventInfoId eventInfo, double& efficiencyScaleFactorError) const{
347
348 Result sfresult;
349 CP::CorrectionCode status = calculate(inputObject, eventInfo, sfresult);
350
351 if ( status != CP::CorrectionCode::Ok ) {
352 return status;
353 }
354
355 efficiencyScaleFactorError=sfresult.Total;
357}
358
359CP::CorrectionCode AsgPhotonEfficiencyCorrectionTool::applyEfficiencyScaleFactor(xAOD::Egamma& inputObject) const {
360
361 double efficiencyScaleFactor = 1.0;
362 CP::CorrectionCode result = getEfficiencyScaleFactor(inputObject, efficiencyScaleFactor);
363 const static SG::Decorator<float> dec(m_resultPrefix+m_resultName+"SF");
364 dec(inputObject) = efficiencyScaleFactor;
365 return result;
366}
367
368//=======================================================================
369// Systematics Interface
370//=======================================================================
371bool AsgPhotonEfficiencyCorrectionTool::isAffectedBySystematic( const CP::SystematicVariation& systematic ) const {
372 if(!systematic.empty()){
373 CP::SystematicSet sys = affectingSystematics();
374 return sys.find(systematic) != sys.end();
375 }
376 return true;
377}
378
379
381CP::SystematicSet AsgPhotonEfficiencyCorrectionTool::affectingSystematics() const {
382 CP::SystematicSet mySysSet;
383
384 mySysSet.insert(CP::SystematicVariation("PH_EFF_"+m_sysSubstring+"Uncertainty", CP::SystematicVariation::CONTINUOUS));
385 mySysSet.insert(CP::SystematicVariation("PH_EFF_"+m_sysSubstring+"Uncertainty", 1));
386 mySysSet.insert(CP::SystematicVariation("PH_EFF_"+m_sysSubstring+"Uncertainty", -1));
387
388 return mySysSet;
389}
390
392StatusCode AsgPhotonEfficiencyCorrectionTool::registerSystematics() {
394 if (registry.registerSystematics(*this) != StatusCode::SUCCESS) {
395 ATH_MSG_ERROR("Failed to add systematic to list of recommended systematics.");
396 return StatusCode::FAILURE;
397 }
398 return StatusCode::SUCCESS;
399}
400
402CP::SystematicSet AsgPhotonEfficiencyCorrectionTool::recommendedSystematics() const {
403 CP::SystematicSet mySysSet;
404 mySysSet.insert(CP::SystematicVariation("PH_EFF_"+m_sysSubstring+"Uncertainty", 1));
405 mySysSet.insert(CP::SystematicVariation("PH_EFF_"+m_sysSubstring+"Uncertainty", -1));
406
407 return mySysSet;
408}
409
410
411StatusCode AsgPhotonEfficiencyCorrectionTool::
412applySystematicVariation ( const CP::SystematicSet& systConfig )
413{
414 // First, check if we already know this systematic configuration
415 auto itr = m_systFilter.find(systConfig);
416
417 // If it's a new input set, we need to filter it
418 if( itr == m_systFilter.end() ){
419
420 CP::SystematicSet affectingSys = affectingSystematics();
421 CP::SystematicSet filteredSys;
422 if (!CP::SystematicSet::filterForAffectingSystematics(systConfig, affectingSys, filteredSys)){
423 ATH_MSG_ERROR("Unsupported combination of systematics passed to the tool!");
424 return StatusCode::FAILURE;
425 }
426 // Insert filtered set into the map
427 itr = m_systFilter.insert(std::make_pair(systConfig, filteredSys)).first;
428 }
429
430 CP::SystematicSet& mySysConf = itr->second;
431 m_appliedSystematics = &mySysConf;
432 return StatusCode::SUCCESS;
433}
434
435//===============================================================================
436// Map Key Feature
437//===============================================================================
438// Gets the correction filename from map
439std::string AsgPhotonEfficiencyCorrectionTool::getFileName(const std::string& isoWP, const std::string& trigWP, bool isConv) {
440
441 // First locate the map file:
442 std::string mapFileName = PathResolverFindCalibFile( m_mapFile );
443 if(mapFileName.empty()){
444 ATH_MSG_ERROR ( "Can't read map file " << m_mapFile );
445 return mapFileName; // return an empty string
446 }
447
448 // Construct correction type:
449 std::string correction_type = "ID_Tight";
450 if(!trigWP.empty()) correction_type = "Trigger_"+trigWP+"_"+isoWP;
451 else if(!isoWP.empty()) correction_type = "ISO_"+isoWP;
452
453 // trigger SF same for con/unc photons
454 if(trigWP.empty()) {correction_type += isConv ? "_Converted" : "_Unconverted";}
455
456 std::string value;
457
458 // Read the map file to find the proper correction filename
459 std::ifstream is(mapFileName);
460 if (!is.is_open()){
461 ATH_MSG_ERROR("Couldn't read Map File" + mapFileName);
462 return "";
463 }
464 while (!is.eof()) {
465 std::string strLine;
466 getline(is,strLine);
467
468 int nPos = strLine.find('=');
469
470 if ((signed int)std::string::npos == nPos) continue; // no '=', invalid line;
471 std::string strKey = strLine.substr(0,nPos);
472 std::string strVal = strLine.substr(nPos + 1, strLine.length() - nPos + 1);
473
474 // check if this is the right key, if the right one stop the search
475 if(0==correction_type.compare(strKey)) {value=strVal; break;}
476 }
477
478 return value;
479
480}
481
482void AsgPhotonEfficiencyCorrectionTool::callSingleEvent (columnar::EgammaRange photons, columnar::EventInfoId event) const
483{
484 const Accessors& acc = *m_accessors;
485 for (auto photon : photons)
486 {
487 double sf = 0;
488 switch (getEfficiencyScaleFactor(photon, event, sf).code())
489 {
491 case Ok:
492 acc.sfDec(photon) = sf;
493 acc.validDec(photon) = true;
494 break;
495 case OutOfValidityRange:
496 acc.sfDec(photon) = sf;
497 acc.validDec(photon) = false;
498 break;
499 default:
500 throw std::runtime_error("Error in getEfficiencyScaleFactor");
501 }
502 }
503}
504
505void AsgPhotonEfficiencyCorrectionTool::callEvents (columnar::EventContextRange events) const
506{
507 const Accessors& acc = *m_accessors;
508 for (columnar::EventContextId event : events)
509 {
510 auto eventInfo = acc.eventInfoAcc(event);
511 callSingleEvent (acc.photonsAcc(event), eventInfo);
512 }
513}
514
515} // namespace ADD_CMODE(CP)
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_INFO(x,...)
float et(const xAOD::jFexSRJetRoI *j)
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
const_iterator begin() const
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.
Class to wrap a set of SystematicVariations.
void insert(const SystematicVariation &systematic)
description: insert a systematic into the set
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.
std::string find(const std::string &s)
return a remapped string
Definition hcg.cxx:140
static std::vector< uint32_t > runnumber
Definition iLumiCalc.h:37
SG::Decorator< T, ALLOC > Decorator
Helper class to provide type-safe access to aux data, specialized for JaggedVecElt.
Definition AuxElement.h:576
ObjectRange< EgammaDef > EgammaRange
Definition EgammaDef.h:46
ObjectRange< EventContextDef > EventContextRange
ObjectId< EventContextDef > EventContextId
ObjectId< EgammaDef > EgammaId
Definition EgammaDef.h:47
ObjectId< EventInfoDef > 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