ATLAS Offline Software
Loading...
Searching...
No Matches
CommonSmearingTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5// Framework include(s):
7
8// local include(s)
12
15
16// ROOT include(s)
17#include "TROOT.h"
18#include "TFile.h"
19#include "TH1.h"
20#include "TF1.h"
21#include "TClass.h"
22#include "TKey.h"
23
24using namespace ADD_CMODE(TauAnalysisTools);
25/*
26 This tool acts as a common tool to apply tau energy smearing and
27 uncertainties. By default, only nominal smearings without systematic
28 variations are applied. Unavailable systematic variations are ignored, meaning
29 that the tool only returns the nominal value. In case the one available
30 systematic is requested, the smeared scale factor is computed as:
31 - pTsmearing = pTsmearing_nominal +/- n * uncertainty
32
33 where n is in general 1 (representing a 1 sigma smearing), but can be any
34 arbitrary value. In case multiple systematic variations are passed they are
35 added in quadrature. Note that it's currently only supported if all are up or
36 down systematics.
37
38 The tool reads in root files including TH2 histograms which need to fulfill a
39 predefined structure:
40
41 nominal smearing:
42 - sf_<workingpoint>_<prongness>p
43 uncertainties:
44 - <NP>_<up/down>_<workingpoint>_<prongness>p (for asymmetric uncertainties)
45 - <NP>_<workingpoint>_<prongness>p (for symmetric uncertainties)
46
47 where the <workingpoint> (e.g. loose/medium/tight) fields may be
48 optional. <prongness> represents either 1 or 3, whereas 3 is currently used
49 for multiprong in general. The <NP> fields are names for the type of nuisance
50 parameter (e.g. STAT or SYST), note the tool decides whether the NP is a
51 recommended or only an available systematic based on the first character:
52 - uppercase -> recommended
53 - lowercase -> available
54 This magic happens here:
55 - CommonSmearingTool::generateSystematicSets()
56
57 In addition the root input file can also contain objects of type TF1 that can
58 be used to provide kind of unbinned smearings or systematics. Currently there
59 is no usecase for tau energy smearing
60
61 The files may also include TNamed objects which is used to define how x and
62 y-axes should be treated. By default the x-axis is given in units of tau-pT in
63 GeV and the y-axis is given as tau-eta. If there is for example a TNamed
64 object with name "Yaxis" and title "|eta|" the y-axis is treated in units of
65 absolute tau eta. All this is done in:
66 - void CommonSmearingTool::ReadInputs(TFile* fFile)
67
68 Other tools for scale factors may build up on this tool and overwrite or add
69 particular functionality.
70*/
71
72//______________________________________________________________________________
73CommonSmearingTool::CommonSmearingTool(const std::string& sName)
74 : asg::AsgMetadataTool( sName )
75 , m_sSystematicSet(nullptr)
76 , m_fX(&finalTauPt)
77 , m_fY(&finalTauEta)
78 , m_bIsData(false)
79 , m_bIsConfigured(false)
80 , m_tauResolutionTool("TauResolutionTool", this)
81 , m_eCheckTruth(TauAnalysisTools::Unknown)
82{
83}
84
85/*
86 need to clear the map of histograms cause we have the ownership, not ROOT
87*/
88CommonSmearingTool::~CommonSmearingTool()
89{
90 for (auto& mEntry : m_mSF)
91 delete mEntry.second;
92}
93
94/*
95 - Find the root files with smearing inputs on cvmfs using PathResolver
96 (more info here:
97 https://twiki.cern.ch/twiki/bin/viewauth/AtlasComputing/PathResolver)
98 - Call further functions to process and define NP strings and so on
99 - Configure to provide nominal smearings by default
100*/
101StatusCode CommonSmearingTool::initialize()
102{
103 ATH_MSG_INFO( "Initializing CommonSmearingTool" );
104
105 // FIXME: do we expect initialize() to be called several times?
106 // only read in histograms once
107 if (m_mSF.empty())
108 {
109 std::string sInputFilePath = PathResolverFindCalibFile(m_sInputFilePath);
110 std::unique_ptr<TFile> fSF( TFile::Open(sInputFilePath.c_str()) );
111 if(fSF == nullptr) {
112 ATH_MSG_FATAL("Could not open file " << sInputFilePath.c_str());
113 return StatusCode::FAILURE;
114 }
115 ReadInputs(fSF.get(), m_mSF);
116 fSF->Close();
117 }
118
119 generateSystematicSets();
120
121 // load empty systematic variation by default
122 if (applySystematicVariation(CP::SystematicSet()) != StatusCode::SUCCESS )
123 return StatusCode::FAILURE;
124
125 // TauResolutionTool tool must be set up when checking compatibility between calo TES and MVA TES
126 if (m_bMVATESQualityCheck)
127 {
128 ATH_CHECK(ASG_MAKE_ANA_TOOL(m_tauResolutionTool, TauAnalysisTools::TauResolutionTool));
129 ATH_CHECK(m_tauResolutionTool.initialize());
130 }
131
132 return StatusCode::SUCCESS;
133}
134
135/*
136 Retrieve the smearing value and if requested the values for the NP's and add
137 this stuff in quadrature. Finally apply the correction to the tau pt of the
138 non-const tau.
139*/
140//______________________________________________________________________________
141CP::CorrectionCode CommonSmearingTool::applyCorrection( xAOD::TauJet& xTau ) const
142{
143 // consistency check between calo-only pt ("ptTauEnergyScale") and MVA pt ("ptFinalCalib", the default calibration)
144 // MVA TES always has better resolution than calo-only TES for true taus
145 // this check helpsts mostly to discard muons faking taus with large track momentum but little energy deposit in the calorimeter:
146
147 // check if decorations were already added to the first passed tau
148 if(!m_bIsTESCompatibilityCheckAvailable.isValid()) {
149 static const SG::ConstAccessor<char> caccTESCompatibility("TESCompatibility");
150 m_bIsTESCompatibilityCheckAvailable.set (caccTESCompatibility.isAvailable(xTau));
151 }
152
153 if (m_bMVATESQualityCheck && (!(*m_bIsTESCompatibilityCheckAvailable.ptr()))){
154 // if decoration is not available on derivations, calculate it on the fly
155 bool compatibility = true;
156 static const SG::ConstAccessor<float> accPtTauEnergyScale ("ptTauEnergyScale");
157 if(accPtTauEnergyScale.isAvailable(xTau)) {
158 const auto combinedTEStool = dynamic_cast<const TauResolutionTool*>(m_tauResolutionTool.get());
159 if (!combinedTEStool)[[unlikely]]{
160 throw std::runtime_error("CommonSmearingTool::applyCorrection: combinedTEStool is null");
161 }
162 compatibility = combinedTEStool->getTESCompatibility(xTau);
163 }
164 static const SG::Accessor<char> accTESCompatibility("TESCompatibility");
165 accTESCompatibility(xTau) = char(compatibility);
166 }
167
168 // step out here if we run on data
169 if (m_bIsData)
171
172 // check which true state is requested
173 if (!m_bSkipTruthMatchCheck and getTruthParticleType(xTau) != m_eCheckTruth) {
175 }
176
177 // skip taus which are not 1 or 3 prong
178 if( xTau.nTracks() != 1 && xTau.nTracks() != 3) {
180 }
181
182 // get prong extension for histogram name
183 std::string sProng = ConvertProngToString(xTau.nTracks());
184
185 double dCorrection = 1.;
186 CP::CorrectionCode tmpCorrectionCode;
187 if (m_bApplyInsituCorrection)
188 {
189 // get standard scale factor
190 tmpCorrectionCode = getValue("sf"+sProng,
191 xTau,
192 dCorrection);
193 // return correction code if histogram is not available
194 if (tmpCorrectionCode != CP::CorrectionCode::Ok)
195 return tmpCorrectionCode;
196 }
197
198 // skip further process if systematic set is empty
199 if (!m_sSystematicSet->empty())
200 {
201 // get uncertainties summed in quadrature
202 double dTotalSystematic2 = 0.;
203 double dDirection = 0.;
204 for (auto& syst : *m_sSystematicSet)
205 {
206 // check if systematic is available
207 auto it = m_mSystematicsHistNames.find(syst.basename());
208 if (it == m_mSystematicsHistNames.end())[[unlikely]]{
209 continue;
210 }
211 // get uncertainty value
212 double dUncertaintySyst = 0.;
213 tmpCorrectionCode = getValue(it->second+sProng,
214 xTau,
215 dUncertaintySyst);
216 // return correction code if histogram is not available
217 if (tmpCorrectionCode != CP::CorrectionCode::Ok)
218 return tmpCorrectionCode;
219
220 // needed for up/down decision
221 dDirection = syst.parameter();
222
223 // scale uncertainty with direction, i.e. +/- n*sigma
224 dUncertaintySyst *= dDirection;
225
226 // square uncertainty and add to total uncertainty
227 dTotalSystematic2 += dUncertaintySyst * dUncertaintySyst;
228 }
229
230 // now use dDirection to use up/down uncertainty
231 dDirection = (dDirection > 0.) ? 1. : -1.;
232
233 // finally apply uncertainty (eff * ( 1 +/- \sum )
234 dCorrection *= 1 + dDirection * std::sqrt(dTotalSystematic2);
235 }
236
237 // finally apply correction
238 // in-situ TES is applied w.r.t. ptFinalCalib, use explicit calibration for pt to avoid irreproducibility upon re-calibration (PHYSLITE)
239 // not required for eta/phi/m that we don't correct (only ptFinalCalib and etaFinalCalib are stored in DAODs)
240 xTau.setP4( xTau.ptFinalCalib() * dCorrection,
241 xTau.eta(), xTau.phi(), xTau.m());
243}
244
245/*
246 Create a non-const copy of the passed const xTau object and apply the
247 correction to the non-const copy.
248 */
249//______________________________________________________________________________
250CP::CorrectionCode CommonSmearingTool::correctedCopy( const xAOD::TauJet& xTau,
251 xAOD::TauJet*& xTauCopy ) const
252{
253
254 // A sanity check:
255 if( xTauCopy )
256 {
257 ATH_MSG_WARNING( "Non-null pointer received. "
258 "There's a possible memory leak!" );
259 }
260
261 // Create a new object:
262 xTauCopy = new xAOD::TauJet();
263 xTauCopy->makePrivateStore( xTau );
264
265 // Use the other function to modify this object:
266 return applyCorrection( *xTauCopy );
267}
268
269/*
270 standard check if a systematic is available
271*/
272//______________________________________________________________________________
273bool CommonSmearingTool::isAffectedBySystematic( const CP::SystematicVariation& systematic ) const
274{
275 const CP::SystematicSet& sys = affectingSystematics();
276 return sys.find(systematic) != sys.end();
277}
278
279/*
280 standard way to return systematics that are available (including recommended
281 systematics)
282*/
283//______________________________________________________________________________
284CP::SystematicSet CommonSmearingTool::affectingSystematics() const
285{
286 return m_sAffectingSystematics;
287}
288
289/*
290 standard way to return systematics that are recommended
291*/
292//______________________________________________________________________________
293CP::SystematicSet CommonSmearingTool::recommendedSystematics() const
294{
295 return m_sRecommendedSystematics;
296}
297
298/*
299 Configure the tool to use a systematic variation for further usage, until the
300 tool is reconfigured with this function. The passed systematic set is checked
301 for sanity:
302 - unsupported systematics are skipped
303 - only combinations of up or down supported systematics is allowed
304 - don't mix recommended systematics with other available systematics, cause
305 sometimes recommended are a quadratic sum of the other variations,
306 e.g. TOTAL=(SYST^2 + STAT^2)^0.5
307*/
308//______________________________________________________________________________
309StatusCode CommonSmearingTool::applySystematicVariation ( const CP::SystematicSet& sSystematicSet )
310{
311 // first check if we already know this systematic configuration
312 auto itSystematicSet = m_mSystematicSets.find(sSystematicSet);
313 if (itSystematicSet != m_mSystematicSets.end())
314 {
315 m_sSystematicSet = &itSystematicSet->first;
316 return StatusCode::SUCCESS;
317 }
318
319 // sanity checks if systematic set is supported
320 double dDirection = 0.;
321 CP::SystematicSet sSystematicSetAvailable;
322 for (auto& sSyst : sSystematicSet)
323 {
324 // check if systematic is available
325 auto it = m_mSystematicsHistNames.find(sSyst.basename());
326 if (it == m_mSystematicsHistNames.end())
327 {
328 ATH_MSG_VERBOSE("unsupported systematic variation: "<< sSyst.basename()<<"; skipping this one");
329 continue;
330 }
331
332 if (sSyst.parameter() * dDirection < 0)
333 {
334 ATH_MSG_ERROR("unsupported set of systematic variations, you should either use only \"UP\" or only \"DOWN\" systematics in one set!");
335 ATH_MSG_ERROR("systematic set will not be applied");
336 return StatusCode::FAILURE;
337 }
338 dDirection = sSyst.parameter();
339
340 if ((m_sRecommendedSystematics.find(sSyst.basename()) != m_sRecommendedSystematics.end()) and sSystematicSet.size() > 1)
341 {
342 ATH_MSG_ERROR("unsupported set of systematic variations, you should not combine \"TAUS_{TRUE|FAKE}_SME_TOTAL\" with other systematic variations!");
343 ATH_MSG_ERROR("systematic set will not be applied");
344 return StatusCode::FAILURE;
345 }
346
347 // finally add the systematic to the set of systematics to process
348 sSystematicSetAvailable.insert(sSyst);
349 }
350
351 // store this calibration for future use, and make it current
352 m_sSystematicSet = &m_mSystematicSets.insert(std::pair<CP::SystematicSet,std::string>(sSystematicSetAvailable, sSystematicSet.name())).first->first;
353
354 return StatusCode::SUCCESS;
355}
356
357//=================================PRIVATE-PART=================================
358/*
359 Executed at the beginning of each event, checks if the tool is used on data or MC.
360 This tool is mostly for MC (in-situ TES correction).
361 But the TES compatibility requirement is applied to both data and MC (when MVATESQualityCheck=true).
362*/
363//______________________________________________________________________________
364StatusCode CommonSmearingTool::beginEvent()
365{
366 if (m_bIsConfigured)
367 return StatusCode::SUCCESS;
368
369 const xAOD::EventInfo* xEventInfo = nullptr;
370 ATH_CHECK(evtStore()->retrieve(xEventInfo,"EventInfo"));
371 m_bIsData = !(xEventInfo->eventType( xAOD::EventInfo::IS_SIMULATION));
372 m_bIsConfigured = true;
373
374 return StatusCode::SUCCESS;
375}
376
377//______________________________________________________________________________
378std::string CommonSmearingTool::ConvertProngToString(const int fProngness) const
379{
380 return fProngness == 1 ? "_1p" : "_3p";
381}
382
383//______________________________________________________________________________
384template<class T>
385void CommonSmearingTool::ReadInputs(TFile* fFile, std::map<std::string, T>& mMap)
386{
387 // initialize function pointer
388 m_fX = &finalTauPt;
389 m_fY = &finalTauEta;
390
391 TKey *kKey;
392 TIter itNext(fFile->GetListOfKeys());
393 while ((kKey = (TKey*)itNext()))
394 {
395 TClass *cClass = gROOT->GetClass(kKey->GetClassName());
396
397 // parse file content for objects of type TNamed, check their title for
398 // known strings and reset funtion pointer
399 std::string sKeyName = kKey->GetName();
400 if (sKeyName == "Xaxis")
401 {
402 TNamed* tObj = (TNamed*)kKey->ReadObj();
403 std::string sTitle = tObj->GetTitle();
404 delete tObj;
405 if (sTitle == "pt")
406 {
407 m_fX = &finalTauPt;
408 ATH_MSG_DEBUG("using tau pt for x-axis");
409 }
410 }
411 if (sKeyName == "Yaxis")
412 {
413 TNamed* tObj = (TNamed*)kKey->ReadObj();
414 std::string sTitle = tObj->GetTitle();
415 delete tObj;
416 if (sTitle == "|eta|")
417 {
418 m_fY = &finalTauAbsEta;
419 ATH_MSG_DEBUG("using absolute tau eta for y-axis");
420 }
421 }
422 if (!cClass->InheritsFrom("TH1"))
423 continue;
424 T tObj = (T)kKey->ReadObj();
425 tObj->SetDirectory(0);
426 mMap[sKeyName] = tObj;
427 }
428 ATH_MSG_INFO("data loaded from " << fFile->GetName());
429}
430
431//______________________________________________________________________________
432void CommonSmearingTool::generateSystematicSets()
433{
434 std::vector<std::string> vInputFilePath;
435 split(m_sInputFilePath,'/',vInputFilePath);
436 std::string sInputFileName = vInputFilePath.back();
437
438 // creation of basic string for all NPs, e.g. "TAUS_TRUEHADTAU_SME_TES_"
439 std::vector<std::string> vSplitInputFilePath = {};
440 split(sInputFileName,'_',vSplitInputFilePath);
441 std::string sEfficiencyType = vSplitInputFilePath.at(0);
442 std::string sTruthType = vSplitInputFilePath.at(1);
443 std::transform(sEfficiencyType.begin(), sEfficiencyType.end(), sEfficiencyType.begin(), toupper);
444 std::transform(sTruthType.begin(), sTruthType.end(), sTruthType.begin(), toupper);
445 std::string sSystematicBaseString = "TAUS_" + sTruthType + "_SME_" + sEfficiencyType + "_";
446
447 // set truth type to check for in truth matching - only true hadronic tau is supported at the moment
448 if (sTruthType=="TRUEHADTAU") m_eCheckTruth = TauAnalysisTools::TruthHadronicTau;
449
450
451 for (auto& mSF : m_mSF)
452 {
453 // parse for nuisance parameter in histogram name
454 std::vector<std::string> vSplitNP = {};
455 split(mSF.first,'_',vSplitNP);
456 std::string sNP;
457 std::string sNPUppercase;
458 if (vSplitNP.size() > 2)
459 {
460 sNP = vSplitNP.at(0)+'_'+vSplitNP.at(1);
461 sNPUppercase = vSplitNP.at(0) + '_' + vSplitNP.at(1);
462 } else {
463 sNP = vSplitNP.at(0);
464 sNPUppercase = vSplitNP.at(0);
465 }
466
467 // skip nominal scale factors
468 if (sNP == "sf") continue;
469 // skip if non 1p histogram to avoid duplications (TODO: come up with a better solution)
470 if (mSF.first.find("_1p") == std::string::npos) continue;
471
472 // test if NP starts with a capital letter indicating that this should be recommended
473 bool bIsRecommended = false;
474 if (isupper(sNP.at(0)))
475 bIsRecommended = true;
476
477 // make sNP uppercase and build final NP entry name
478 std::transform(sNPUppercase.begin(), sNPUppercase.end(), sNPUppercase.begin(), toupper);
479 std::string sSystematicString = sSystematicBaseString + sNPUppercase;
480
481 // add all found systematics to the AffectingSystematics
482 m_sAffectingSystematics.insert(CP::SystematicVariation (sSystematicString, 1));
483 m_sAffectingSystematics.insert(CP::SystematicVariation (sSystematicString, -1));
484 // only add found uppercase systematics to the RecommendedSystematics
485 if (bIsRecommended)
486 {
487 m_sRecommendedSystematics.insert(CP::SystematicVariation (sSystematicString, 1));
488 m_sRecommendedSystematics.insert(CP::SystematicVariation (sSystematicString, -1));
489 }
490
491 ATH_MSG_DEBUG("connected histogram base name " << sNP << " with systematic " << sSystematicString);
492 m_mSystematicsHistNames.insert({sSystematicString,sNP});
493 }
494}
495
496//______________________________________________________________________________
497CP::CorrectionCode CommonSmearingTool::getValue(const std::string& sHistName,
498 const xAOD::TauJet& xTau,
499 double& dEfficiencyScaleFactor) const
500{
501 TH1* hHist = m_mSF.at(sHistName);
502 if (hHist == nullptr)
503 {
504 ATH_MSG_ERROR("Histogram with name " << sHistName << " was not found in input file.");
506 }
507
508 double dPt = m_fX(xTau);
509 double dEta = m_fY(xTau);
510
511 // protect values from underflow bins
512 dPt = std::max(dPt,hHist->GetXaxis()->GetXmin());
513 dEta = std::max(dEta,hHist->GetYaxis()->GetXmin());
514 // protect values from overflow bins (times .999 to keep it inside last bin)
515 dPt = std::min(dPt,hHist->GetXaxis()->GetXmax() * .999);
516 dEta = std::min(dEta,hHist->GetYaxis()->GetXmax() * .999);
517
518 int iBin = hHist->FindFixBin(dPt,dEta);
519 dEfficiencyScaleFactor = hHist->GetBinContent(iBin);
520
521 if (m_bApplyFading)
522 {
523 std::string sTitle = hHist->GetTitle();
524 if (!sTitle.empty())
525 {
526 TF1 f("",sTitle.c_str(), 0, 1000);
527 if (sHistName.find("sf_") != std::string::npos)
528 dEfficiencyScaleFactor = (dEfficiencyScaleFactor -1.) *f.Eval(m_fX(xTau)) + 1.;
529 else
530 dEfficiencyScaleFactor *= f.Eval(m_fX(xTau));
531 }
532 }
534}
#define ASG_MAKE_ANA_TOOL(handle, type)
create the tool in the given tool handle
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
#define ATH_MSG_INFO(x,...)
#define ATH_MSG_FATAL(x,...)
#define ADD_CMODE(name)
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
struct TBPatternUnitContext Unknown
Return value from object correction CP tools.
@ Error
Some error happened during the object correction.
@ Ok
The correction was done successfully.
Class to wrap a set of SystematicVariations.
void insert(const SystematicVariation &systematic)
description: insert a systematic into the set
iterator find(const SystematicVariation &sys) const
description: find an element in the set
Helper class to provide type-safe access to aux data.
Helper class to provide constant type-safe access to aux data.
bool eventType(EventType type) const
Check for one particular bitmask value.
@ IS_SIMULATION
true: simulation, false: data
virtual double phi() const
The azimuthal angle ( ) of the particle.
void setP4(double pt, double eta, double phi, double m)
Set methods for IParticle values.
double ptFinalCalib() const
virtual double m() const
The invariant mass of the particle.
virtual double eta() const
The pseudorapidity ( ) of the particle.
size_t nTracks(TauJetParameters::TauTrackFlag flag=TauJetParameters::TauTrackFlag::classifiedCharged) const
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:179
bool dEta(const xAOD::TauJet &tau, const xAOD::CaloVertexedTopoCluster &cluster, float &out)
EventInfo_v1 EventInfo
Definition of the latest event info version.
TauJet_v3 TauJet
Definition of the current "tau version".
Definition TauJet.h:17
#define unlikely(x)