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