ATLAS Offline Software
Loading...
Searching...
No Matches
CommonSmearingTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 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 "TF1.h"
18#include "TClass.h"
19#include "TKey.h"
20
21// tauRecTools include(s)
23
24using namespace 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//______________________________________________________________________________
74 : asg::AsgMetadataTool( sName )
75 , m_sSystematicSet(nullptr)
78 , m_bIsData(false)
79 , m_bIsConfigured(false)
80 , m_tTauCombinedTES("TauCombinedTES", this)
82{
83}
84
85/*
86 need to clear the map of histograms cause we have the ownership, not ROOT
87*/
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*/
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
120
121 // load empty systematic variation by default
122 if (applySystematicVariation(CP::SystematicSet()) != StatusCode::SUCCESS )
123 return StatusCode::FAILURE;
124
125 // TauCombinedTES tool must be set up when checking compatibility between calo TES and MVA TES
127 {
129 ATH_CHECK(m_tTauCombinedTES.setProperty("WeightFileName", "CombinedTES_R22_Round2.5_v2.root"));
130 ATH_CHECK(m_tTauCombinedTES.initialize());
131 }
132
133 return StatusCode::SUCCESS;
134}
135
136/*
137 Retrieve the smearing value and if requested the values for the NP's and add
138 this stuff in quadrature. Finally apply the correction to the tau pt of the
139 non-const tau.
140*/
141//______________________________________________________________________________
143{
144 // consistency check between calo-only pt ("ptTauEnergyScale") and MVA pt ("ptFinalCalib", the default calibration)
145 // MVA TES always has better resolution than calo-only TES for true taus
146 // this check helpsts mostly to discard muons faking taus with large track momentum but little energy deposit in the calorimeter:
147
148 // check if decorations were already added to the first passed tau
150 static const SG::ConstAccessor<char> caccTESCompatibility("TESCompatibility");
151 m_bIsTESCompatibilityCheckAvailable.set (caccTESCompatibility.isAvailable(xTau));
152 }
153
155 // if decoration is not available on derivations, calculate it on the fly
156 bool compatibility = true;
157 static const SG::ConstAccessor<float> accPtTauEnergyScale ("ptTauEnergyScale");
158 if(accPtTauEnergyScale.isAvailable(xTau)) {
159 const auto combinedTEStool = dynamic_cast<const TauCombinedTES*>(m_tTauCombinedTES.get());
160 compatibility = combinedTEStool->getTESCompatibility(xTau);
161 }
162 static const SG::Accessor<char> accTESCompatibility("TESCompatibility");
163 accTESCompatibility(xTau) = char(compatibility);
164 }
165
166 // step out here if we run on data
167 if (m_bIsData)
169
170 // check which true state is requested
173 }
174
175 // skip taus which are not 1 or 3 prong
176 if( xTau.nTracks() != 1 && xTau.nTracks() != 3) {
178 }
179
180 // get prong extension for histogram name
181 std::string sProng = ConvertProngToString(xTau.nTracks());
182
183 double dCorrection = 1.;
184 CP::CorrectionCode tmpCorrectionCode;
186 {
187 // get standard scale factor
188 tmpCorrectionCode = getValue("sf"+sProng,
189 xTau,
190 dCorrection);
191 // return correction code if histogram is not available
192 if (tmpCorrectionCode != CP::CorrectionCode::Ok)
193 return tmpCorrectionCode;
194 }
195
196 // skip further process if systematic set is empty
197 if (!m_sSystematicSet->empty())
198 {
199 // get uncertainties summed in quadrature
200 double dTotalSystematic2 = 0.;
201 double dDirection = 0.;
202 for (auto& syst : *m_sSystematicSet)
203 {
204 // check if systematic is available
205 auto it = m_mSystematicsHistNames.find(syst.basename());
206
207 // get uncertainty value
208 double dUncertaintySyst = 0.;
209 tmpCorrectionCode = getValue(it->second+sProng,
210 xTau,
211 dUncertaintySyst);
212 // return correction code if histogram is not available
213 if (tmpCorrectionCode != CP::CorrectionCode::Ok)
214 return tmpCorrectionCode;
215
216 // needed for up/down decision
217 dDirection = syst.parameter();
218
219 // scale uncertainty with direction, i.e. +/- n*sigma
220 dUncertaintySyst *= dDirection;
221
222 // square uncertainty and add to total uncertainty
223 dTotalSystematic2 += dUncertaintySyst * dUncertaintySyst;
224 }
225
226 // now use dDirection to use up/down uncertainty
227 dDirection = (dDirection > 0.) ? 1. : -1.;
228
229 // finally apply uncertainty (eff * ( 1 +/- \sum )
230 dCorrection *= 1 + dDirection * std::sqrt(dTotalSystematic2);
231 }
232
233 // finally apply correction
234 // in-situ TES is applied w.r.t. ptFinalCalib, use explicit calibration for pt to avoid irreproducibility upon re-calibration (PHYSLITE)
235 // not required for eta/phi/m that we don't correct (only ptFinalCalib and etaFinalCalib are stored in DAODs)
236 xTau.setP4( xTau.ptFinalCalib() * dCorrection,
237 xTau.eta(), xTau.phi(), xTau.m());
239}
240
241/*
242 Create a non-const copy of the passed const xTau object and apply the
243 correction to the non-const copy.
244 */
245//______________________________________________________________________________
247 xAOD::TauJet*& xTauCopy ) const
248{
249
250 // A sanity check:
251 if( xTauCopy )
252 {
253 ATH_MSG_WARNING( "Non-null pointer received. "
254 "There's a possible memory leak!" );
255 }
256
257 // Create a new object:
258 xTauCopy = new xAOD::TauJet();
259 xTauCopy->makePrivateStore( xTau );
260
261 // Use the other function to modify this object:
262 return applyCorrection( *xTauCopy );
263}
264
265/*
266 standard check if a systematic is available
267*/
268//______________________________________________________________________________
270{
272 return sys.find(systematic) != sys.end();
273}
274
275/*
276 standard way to return systematics that are available (including recommended
277 systematics)
278*/
279//______________________________________________________________________________
284
285/*
286 standard way to return systematics that are recommended
287*/
288//______________________________________________________________________________
293
294/*
295 Configure the tool to use a systematic variation for further usage, until the
296 tool is reconfigured with this function. The passed systematic set is checked
297 for sanity:
298 - unsupported systematics are skipped
299 - only combinations of up or down supported systematics is allowed
300 - don't mix recommended systematics with other available systematics, cause
301 sometimes recommended are a quadratic sum of the other variations,
302 e.g. TOTAL=(SYST^2 + STAT^2)^0.5
303*/
304//______________________________________________________________________________
306{
307 // first check if we already know this systematic configuration
308 auto itSystematicSet = m_mSystematicSets.find(sSystematicSet);
309 if (itSystematicSet != m_mSystematicSets.end())
310 {
311 m_sSystematicSet = &itSystematicSet->first;
312 return StatusCode::SUCCESS;
313 }
314
315 // sanity checks if systematic set is supported
316 double dDirection = 0.;
317 CP::SystematicSet sSystematicSetAvailable;
318 for (auto& sSyst : sSystematicSet)
319 {
320 // check if systematic is available
321 auto it = m_mSystematicsHistNames.find(sSyst.basename());
322 if (it == m_mSystematicsHistNames.end())
323 {
324 ATH_MSG_VERBOSE("unsupported systematic variation: "<< sSyst.basename()<<"; skipping this one");
325 continue;
326 }
327
328 if (sSyst.parameter() * dDirection < 0)
329 {
330 ATH_MSG_ERROR("unsupported set of systematic variations, you should either use only \"UP\" or only \"DOWN\" systematics in one set!");
331 ATH_MSG_ERROR("systematic set will not be applied");
332 return StatusCode::FAILURE;
333 }
334 dDirection = sSyst.parameter();
335
336 if ((m_sRecommendedSystematics.find(sSyst.basename()) != m_sRecommendedSystematics.end()) and sSystematicSet.size() > 1)
337 {
338 ATH_MSG_ERROR("unsupported set of systematic variations, you should not combine \"TAUS_{TRUE|FAKE}_SME_TOTAL\" with other systematic variations!");
339 ATH_MSG_ERROR("systematic set will not be applied");
340 return StatusCode::FAILURE;
341 }
342
343 // finally add the systematic to the set of systematics to process
344 sSystematicSetAvailable.insert(sSyst);
345 }
346
347 // store this calibration for future use, and make it current
348 m_sSystematicSet = &m_mSystematicSets.insert(std::pair<CP::SystematicSet,std::string>(sSystematicSetAvailable, sSystematicSet.name())).first->first;
349
350 return StatusCode::SUCCESS;
351}
352
353//=================================PRIVATE-PART=================================
354/*
355 Executed at the beginning of each event, checks if the tool is used on data or MC.
356 This tool is mostly for MC (in-situ TES correction).
357 But the TES compatibility requirement is applied to both data and MC (when MVATESQualityCheck=true).
358*/
359//______________________________________________________________________________
361{
362 if (m_bIsConfigured)
363 return StatusCode::SUCCESS;
364
365 const xAOD::EventInfo* xEventInfo = nullptr;
366 ATH_CHECK(evtStore()->retrieve(xEventInfo,"EventInfo"));
368 m_bIsConfigured = true;
369
370 return StatusCode::SUCCESS;
371}
372
373//______________________________________________________________________________
374std::string CommonSmearingTool::ConvertProngToString(const int fProngness) const
375{
376 return fProngness == 1 ? "_1p" : "_3p";
377}
378
379//______________________________________________________________________________
380template<class T>
381void CommonSmearingTool::ReadInputs(TFile* fFile, std::map<std::string, T>& mMap)
382{
383 // initialize function pointer
384 m_fX = &finalTauPt;
385 m_fY = &finalTauEta;
386
387 TKey *kKey;
388 TIter itNext(fFile->GetListOfKeys());
389 while ((kKey = (TKey*)itNext()))
390 {
391 TClass *cClass = gROOT->GetClass(kKey->GetClassName());
392
393 // parse file content for objects of type TNamed, check their title for
394 // known strings and reset funtion pointer
395 std::string sKeyName = kKey->GetName();
396 if (sKeyName == "Xaxis")
397 {
398 TNamed* tObj = (TNamed*)kKey->ReadObj();
399 std::string sTitle = tObj->GetTitle();
400 delete tObj;
401 if (sTitle == "pt")
402 {
403 m_fX = &finalTauPt;
404 ATH_MSG_DEBUG("using tau pt for x-axis");
405 }
406 }
407 if (sKeyName == "Yaxis")
408 {
409 TNamed* tObj = (TNamed*)kKey->ReadObj();
410 std::string sTitle = tObj->GetTitle();
411 delete tObj;
412 if (sTitle == "|eta|")
413 {
415 ATH_MSG_DEBUG("using absolute tau eta for y-axis");
416 }
417 }
418 if (!cClass->InheritsFrom("TH1"))
419 continue;
420 T tObj = (T)kKey->ReadObj();
421 tObj->SetDirectory(0);
422 mMap[sKeyName] = tObj;
423 }
424 ATH_MSG_INFO("data loaded from " << fFile->GetName());
425}
426
427//______________________________________________________________________________
429{
430 std::vector<std::string> vInputFilePath;
431 split(m_sInputFilePath,'/',vInputFilePath);
432 std::string sInputFileName = vInputFilePath.back();
433
434 // creation of basic string for all NPs, e.g. "TAUS_TRUEHADTAU_SME_TES_"
435 std::vector<std::string> vSplitInputFilePath = {};
436 split(sInputFileName,'_',vSplitInputFilePath);
437 std::string sEfficiencyType = vSplitInputFilePath.at(0);
438 std::string sTruthType = vSplitInputFilePath.at(1);
439 std::transform(sEfficiencyType.begin(), sEfficiencyType.end(), sEfficiencyType.begin(), toupper);
440 std::transform(sTruthType.begin(), sTruthType.end(), sTruthType.begin(), toupper);
441 std::string sSystematicBaseString = "TAUS_" + sTruthType + "_SME_" + sEfficiencyType + "_";
442
443 // set truth type to check for in truth matching - only true hadronic tau is supported at the moment
444 if (sTruthType=="TRUEHADTAU") m_eCheckTruth = TauAnalysisTools::TruthHadronicTau;
445
446
447 for (auto& mSF : m_mSF)
448 {
449 // parse for nuisance parameter in histogram name
450 std::vector<std::string> vSplitNP = {};
451 split(mSF.first,'_',vSplitNP);
452 std::string sNP;
453 std::string sNPUppercase;
454 if (vSplitNP.size() > 2)
455 {
456 sNP = vSplitNP.at(0)+'_'+vSplitNP.at(1);
457 sNPUppercase = vSplitNP.at(0) + '_' + vSplitNP.at(1);
458 } else {
459 sNP = vSplitNP.at(0);
460 sNPUppercase = vSplitNP.at(0);
461 }
462
463 // skip nominal scale factors
464 if (sNP == "sf") continue;
465 // skip if non 1p histogram to avoid duplications (TODO: come up with a better solution)
466 if (mSF.first.find("_1p") == std::string::npos) continue;
467
468 // test if NP starts with a capital letter indicating that this should be recommended
469 bool bIsRecommended = false;
470 if (isupper(sNP.at(0)))
471 bIsRecommended = true;
472
473 // make sNP uppercase and build final NP entry name
474 std::transform(sNPUppercase.begin(), sNPUppercase.end(), sNPUppercase.begin(), toupper);
475 std::string sSystematicString = sSystematicBaseString + sNPUppercase;
476
477 // add all found systematics to the AffectingSystematics
478 m_sAffectingSystematics.insert(CP::SystematicVariation (sSystematicString, 1));
479 m_sAffectingSystematics.insert(CP::SystematicVariation (sSystematicString, -1));
480 // only add found uppercase systematics to the RecommendedSystematics
481 if (bIsRecommended)
482 {
483 m_sRecommendedSystematics.insert(CP::SystematicVariation (sSystematicString, 1));
484 m_sRecommendedSystematics.insert(CP::SystematicVariation (sSystematicString, -1));
485 }
486
487 ATH_MSG_DEBUG("connected histogram base name " << sNP << " with systematic " << sSystematicString);
488 m_mSystematicsHistNames.insert({sSystematicString,sNP});
489 }
490}
491
492//______________________________________________________________________________
494 const xAOD::TauJet& xTau,
495 double& dEfficiencyScaleFactor) const
496{
497 TH1* hHist = m_mSF.at(sHistName);
498 if (hHist == nullptr)
499 {
500 ATH_MSG_ERROR("Histogram with name " << sHistName << " was not found in input file.");
502 }
503
504 double dPt = m_fX(xTau);
505 double dEta = m_fY(xTau);
506
507 // protect values from underflow bins
508 dPt = std::max(dPt,hHist->GetXaxis()->GetXmin());
509 dEta = std::max(dEta,hHist->GetYaxis()->GetXmin());
510 // protect values from overflow bins (times .999 to keep it inside last bin)
511 dPt = std::min(dPt,hHist->GetXaxis()->GetXmax() * .999);
512 dEta = std::min(dEta,hHist->GetYaxis()->GetXmax() * .999);
513
514 int iBin = hHist->FindFixBin(dPt,dEta);
515 dEfficiencyScaleFactor = hHist->GetBinContent(iBin);
516
517 if (m_bApplyFading)
518 {
519 std::string sTitle = hHist->GetTitle();
520 if (!sTitle.empty())
521 {
522 TF1 f("",sTitle.c_str(), 0, 1000);
523 if (sHistName.find("sf_") != std::string::npos)
524 dEfficiencyScaleFactor = (dEfficiencyScaleFactor -1.) *f.Eval(m_fX(xTau)) + 1.;
525 else
526 dEfficiencyScaleFactor *= f.Eval(m_fX(xTau));
527 }
528 }
530}
#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_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(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