ATLAS Offline Software
Loading...
Searching...
No Matches
CalibrationDataInterfaceROOT.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6// //
7// CalibrationDataInterfaceROOT //
8/* Begin_Html
9<h2>Purpose</h2>
10<p>This class provides the main interface to b-tagging calibration information.
11 Each instance can be used to handle a single tagger (so if multiple taggers' information
12 needs to be accessed, multiple CalibrationDataInterfaceROOT instances need to be created).<br />
13 Its action is steered by a configuration file (which is parsed using TEnv and which is
14 specified in the CalibrationDataInterfaceROOT constructor).
15</p>
16<p>
17 Further details can be found below, arranged by topic: <a href="#basic">basic usage</a>,
18 <a href="#ev">eigenvector variation usage</a>, and <a href="#configuration">configuration file
19 specification</a>.
20</p>
21<a name="basic"><h2>Basic usage</h2></a>
22<p>
23 The main functionality is to retrieve data/MC calibration scale factors and MC efficiencies,
24 on a jet-by-jet basis. This is done using the following methods:
25 <pre>
26 getScaleFactor (variables, label, OP, unc)
27 getMCEfficiency (variables, label, OP, unc, mapIndex)
28 </pre>
29 with the following meaning for the arguments:
30 <pre>
31 variables: a CalibrationDataVariables object that should be filled by the user with all
32 the information needed to extract the calibration information
33 label: flavour label. Currently recognised labels follow the Athena TruthInfo conventions
34 (see package PhysicsAnalysis/JetTagging/JetTagInfo): "B", "C", "T", "N/A", and in
35 addition "Light" (the "N/A" is internally converted to "Light")
36 OP: tagger working point. This should correspond to a documented weight cut for the
37 tagger under consideration, but converted to a string, and with any period (".")
38 replaced with an underscore for technical reasons. Alternatively, "Continuous" may
39 be used if a "continuous tagging" calibration object exists for the tagger under
40 consideration. (Note that the use of this method for "continuous tagging" is not
41 in general to be recommended, as it does not not allow for scale factor rescaling,
42 which is ne
43 unc: enum specifying what uncertainty information is to be retrieved. Recognised values
44 for normal usage are <b>None</b> (retrieval of central values only), <b>Statistical</b>
45 (statistical uncertainty), <b>Systematic</b> (systematic uncertainty), <b>Total</b>
46 (combined statistical and systematic uncertainty). (Other choices exist for more advanced
47 usage and will be described <a href="#ev">below</a>.)
48 mapIndex: index specifying the precise MC efficiency calibration to be used. This is relevant if
49 multiple "efficiency calibration" names are specified for the given flavour label (see
50 the <a href="#configuration">configuration section</a>).
51 </pre>
52 All methods return a pair&lt;double, double&gt; (the stated <b>CalibResult</b> return type is a simple typedef),
53 the first member of which contains the central value and the second the requested uncertainty (or 0 if no
54 uncertainty was requested). Note that central values are retricted to be within the physical region (between 0
55 and 1 for the MC efficiencies, and positive for the scale factors).
56</p>
57<p>
58 A few additional methods exist, based on (and internally using) the above methods:
59 <pre>
60 getEfficiency (variables, label, OP, unc, mapIndex): data efficiency calculated as MC efficiency times SF
61 getInefficiency (variables, label, OP, unc, mapIndex): 1 - data efficiency
62 getMCInefficiency (variables, label, OP, unc, mapIndex): 1 - MC efficiency
63 getInefficiencyScaleFactor (variables, label, OP, unc, mapIndex): ratio of data to MC inefficiencies
64 </pre>
65 Especially the last of these methods is likely to be of interest when data/MC scale factors are applied;
66 <a href="http://cdsweb.cern.ch/record/1269912">this note</a> provides more detail. Restrictions to the physical
67 range apply, as is the case for the data/MC scale factor and MC efficiency methods.
68</p>
69<a name="speed-up"><h3>Code speed-up using variable caching</h3></a>
70<p>
71 Internally, pointers to the calibration objects are cached. However, the string matching needed to
72 exploit this feature is slow and leads to a non-negligible CPU overhead. Since internally all pointers
73 are stored in a vector, it is possible to request the position in this vector for a given calibration
74 object. This position can then be used in subsequent alternative calls to retrieve information:
75 <pre>
76 getScaleFactor (variables, indexSF, indexEff, unc)
77 getMCEfficiency (variables, index, unc)
78 </pre>
79 Here the "index" variables replace the specification of the flavour label and operating point. They can
80 be retrieved by calling
81 <pre>
82 retrieveCalibrationIndex(label, OP, author, isSF, index)
83 </pre>
84 where the following additional variables are to be used:
85 <pre>
86 author: jet collection name
87 isSF: set to true (false) if the object concerned is a scale factor object (a MC efficiency object)
88 index: requested information
89 </pre>
90 Note that this method has a boolean return value, indicating whether or not the retrieval succeeded.
91 This return value should be checked by the user, as no checks are carried in the alternative getScaleFactor()
92 etc. methods as to the validity of the index specified. Note also that for all methods except those using
93 only the MC information, both indices need to be provided.
94</p>
95<a name="continuous"><h3>Continuous tagging</h3></a>
96<p>
97 Rather than merely inquiring whether the tag weight discriminant resulting from a given tagger satisfies a
98 given criterion, it may be useful to use more detailed information. In particular, "continuous tagging"
99 information can be made available. This is essentially a calibrated version of the (binned) tag weight
100 discriminant distribution, or to be more precise, the corresponding data/MC ratio.
101</p>
102<p>
103 The use of this information differs somewhat from the regular calibration usage, as the "transport" from
104 the calibration sample(s) to the sample used in physics becomes a non-trivial extension of the efficiency
105 and inefficiency scale factors used for the regular calibrations. Therefore, two separate methods exist
106 which should be used for such cases:
107 <pre>
108 getWeightScaleFactor (variables, label, unc, numVariation, mapIndex)
109 getWeightScaleFactor (variables, indexSF, indexEff, unc, numVariation)
110 </pre>
111 The meaning of the variables is as above.
112</p>
113<a name="ev"><h2>Eigenvector variations</h2></a>
114<p>
115 The 'basic usage' described above does not do justice to the fact that data/MC calibration scale factors
116 are derived in kinematic bins, with uncertainties not being fully correlated from bin to bin (indeed, the
117 methods themselves do not know anything about binning to start with). It is however possible to arrive at
118 a statistically more correct approach by considering 'variations'. This exploits the fact that in the case
119 of scale factor calibrations, besides statistical and total systematic uncertainties also individual
120 systematic uncertainties are stored (along with a model of their bin-to-bin correlations). The typical use
121 of this, applicable for a systematic uncertainty that is fully correlated (or anti-correlated) between bins,
122 would be to consider the effect on all calibration scale factors from a &pm; 1 standard deviation of the
123 underlying source of systematic.
124</p>
125<p>
126 In practice, this approach is a bit cumbersome due to the sometimes large number of contributions to the
127 systematic uncertainty. In addition, no infrastructure exists at present to deal with uncertainties that are
128 not, or only partially, correlated from bin to bin. An eigenvector decomposition technique allows to address
129 both of these issues at the same time.
130</p>
131<p>
132 This method starts from the scale factor covariance matrix that can be constructed on the basis of the
133 available information (uncertainties plus a model of their bin-to-bin correlations). This covariance matrix
134 can be diagonalised and its eigenvectors and corresponding eigenvalues determined. The product of an eigenvector
135 and the square root of its corresponding eigenvalue is what is referred to as an 'eigenvector variation'.
136 The number of such (statistically independent) variations equals the number of calibration bins, and the set
137 of variations is the minimal set needed to (re-)construct the covariance matrix. After this eigenvector
138 decomposition, the eigenvector variations may be used instead of the variations corresponding to the original
139 sources of uncertainty.
140</p>
141<p>
142 One further refinement can be made, related to the fact that sometimes sources of uncertainty affecting
143 the calibration analyses also affect the physics analysis in which the calibration results are used.
144 Including these sources of uncertainty in the eigenvector decomposition would not allow to correlate the
145 effect on the calibration properly with the effect on the physics analysis; therefore it is possible to
146 exclude these sources of uncertainty from being used in the construction of the covariance matrix that is
147 diagonalised, and consider their variations separately (note, per the above, that this will be done
148 correctly only in the case of full bin-to-bin correlations).
149</p>
150<p>
151 The use of the eigenvector variation is not enabled by default and must be switched on in the configuration
152 file (see <a href="#configuration">Configuration</a>).
153</p>
154<p>
155 The above can be used to obtain jet-by-jet information by a slight modification of the arguments to the
156 normal methods for information retrieval also employed for <a href="#basic">basic usage</a>:
157 <pre>
158 getScaleFactor (variables, label, OP, unc, numVariation)
159 </pre>
160 Here, the first three variables function exactly as in the basic usage case. As for the two last arguments:
161 <pre>
162 unc: this should take the value <b>SFEigen</b> or <b>SFNamed</b> for eigenvector variations or named variations, respectively.
163 numVariation: this indicates exactly which eigenvector variation or named variation is to be considered.
164 </pre>
165 In addition, it should be noticed that the methods' return values have a different meaning in this context:
166 while in normal usage they are (value, uncertainty) pairs, here they are (up, down) pairs, with up and down
167 meaning the result of an up- or downward eigenvector variation or named variation.
168</p>
169<p>
170 The number of valid variations for each type can be retrieved using
171 <pre>
172 getNumVariations(author, label, OP, Uncertainty unc)
173 </pre>
174 with "unc" set appropriately. Finally, calling
175 <pre>
176 listScaleFactorUncertainties(author, label, OP, named)
177 </pre>
178 can be used to retrieve information about uncertainties in two ways, depending on the value of the last argument:
179 <pre>
180 named = false (default): retrieve an unsorted list of all the uncertainties associated with the relevant scale factor
181 calibration object. This can be used to identify sources of uncertainty to be excluded from
182 the eigenvector decomposition.
183 named = true: the list in this case is restricted to the named uncertainties (these should have been specified
184 in the configuration file by the user) and ordered: the position in the vector constitutes the
185 link between the name and the index to be used as the "numVariation" argument to the
186 getScaleFactor() etc. method.
187 </pre>
188 Note that as in the basic usage case, the code can be sped up by replacing the jet author/label/OP specification by the appropriate
189 integer index (see the <a href="#speed-up">corresponding section</a>).
190</p>
191<a name="configuration"><h2>Configuration</h2></a>
192<p>
193 The configuration file may specify the following settings:
194</p>
195<ul>
196 <li>calibration file specification:
197 <pre>
198
199 filename: common ROOT file for calibration scale factors and MC efficiencies
200 (default: BTaggingPerformanceCalibrations.root)
201 filenameEff: separate ROOT file for MC efficiencies
202 filenameSF: separate ROOT file for calibration scale factors
203 </pre>
204 Note that it is not necessary to specify all three files. The most common use for the specification
205 of more than one file is likely to be the specification of analysis-specific MC efficiencies (the
206 efficiencies provided in the common file are expected to be sufficient for many analysis purposes,
207 but depending on the accuracy needed it may be desirable to add custom efficiencies).
208 </li>
209 <li>choice of calibration:
210 <pre>
211
212 ScaleFactorCalibrationBName: name for scale factor calibration object to be used for b-jets (default: default)
213 EfficiencyCalibrationBName: name for MC efficiencies object(s) to be used for b-jets (default: default).
214 Note that multiple (semicolon-separated) MC efficiency objects may be specified;
215 internally they will be converted to a vector. The position in the vector can
216 then be used as the 'mapIndex' argument in the methods involving MC information
217 (see e.g. the <a href="#basic">basic usage</a> section).
218 </pre>
219 Analogous keywords (with B replaced with C, T, Light) can be used to specify the objects to be used for
220 charm, tau, and light-flavour jets, respectively. Note that the "default" object should always exist and reflect
221 the recommended choice of object unless analysis-specific needs dictate otherwise.
222 </li>
223 <li>jet aliasing:
224 <pre>
225 aliases: semicolon-separated list of jet collection aliases
226 </pre>
227 The feature of jet aliasing follows the strategy also used in the CalibrationBroker Athena class.
228 It allows for the use of a calibration for different jet collection names than those actually
229 employed in physics analysis (caveat emptor!). Each alias consists of the specification
230 <pre>
231 source-&gt;target
232 </pre>
233 where source indicates the name used in physics analysis, while target is the name to be used for calibration purposes.
234 </li>
235 <li>eigenvector decomposition:
236 <pre>
237 runEigenVectorMethod: if set to true this will carry out an eigenvector decomposition, the results
238 of which can subsequently be used as calibration scale factor "variations"
239 correctly accounting for bin-to-bin correlations. For detailed usage of
240 b-tagging results (e.g. in profile likelihood fits) this is recommended (default: false)
241 excludeFromCovMatrix: semicolon-separated list of uncertainties to be excluded from the eigenvector
242 decomposition, for all flavours.
243 excludeFromBCovMatrix: semicolon-separated list of uncertainties to be excluded from the eigenvector
244 decomposition for b jets
245 excludeFromCCovMatrix: semicolon-separated list of uncertainties to be excluded from the eigenvector
246 decomposition for c jets
247 excludeFromTCovMatrix: semicolon-separated list of uncertainties to be excluded from the eigenvector
248 decomposition for tau "jets"
249 excludeFromLightCovMatrix: semicolon-separated list of uncertainties to be excluded from the eigenvector
250 decomposition for light-flavour jets
251 </pre>
252 </li>
253</ul>
254End_Html */
255//
256// CalibrationDataInterfaceROOT.cxx, (c) ATLAS Detector software //
258
261
264#include <CxxUtils/StringUtils.h>
265
266#include "TMath.h"
267#include "TEnv.h"
268#include "TFile.h"
269#include "TObjString.h"
270#include <iostream>
271#include <iomanip>
272#include <cmath>
273#include <cassert>
274#include <cstring>
275
276using std::string;
277using std::cout;
278using std::cerr;
279using std::endl;
280using std::setw;
281
287
288#ifndef __CINT__
290#endif
291
292//________________________________________________________________________________
293Analysis::CalibrationDataInterfaceROOT::CalibrationDataInterfaceROOT(const string& taggerName, const std::string& configname, const std::string& pathname) :
294 m_runEigenVectorMethod(false), m_EVStrategy(SFEigen), m_useRecommendedEVExclusions(false), m_verbose(true),
295 m_absEtaStrategy(GiveUp), m_otherStrategy(Flag)
296{
297 // Normal constructor.
298 //
299 // taggerName: this should correspond to the tagger name as used in the calibration ROOT file
300 // configname: full name of the configuration file
301 // pathname: directory specification for separate scale factor or efficiency ROOT file
302
303 m_taggerName = taggerName;
304
305 TEnv env;
306 env.ReadFile(configname.c_str(),kEnvGlobal);
307
308
309 // ROOT file containing the calibrations
310 TString filename = env.GetValue("File", "BTaggingPerformanceCalibrations.root");
311 m_filenameEff = string(env.GetValue("FileEff", "")); m_filenameEff = trimWhiteSpaces(m_filenameEff);
312 m_filenameSF = string(env.GetValue("FileSF", "")); m_filenameSF = trimWhiteSpaces(m_filenameSF);
313 if (m_filenameEff == "") {
314 m_filenameEff = pathname + filename.Data();
315 }
316 if (m_filenameSF == "") {
317 m_filenameSF = pathname + filename.Data();
318 }
319
320 if (m_verbose) {
321 cout << "=== CalibrationDataInterfaceROOT::CalibrationDataInterfaceROOT ===" << endl;
322 cout << " Config name : " << configname << endl;
323 cout << " taggerName : " << taggerName << endl;
324 cout << " Efficiency file name : " << m_filenameEff << endl
325 << " SF file name : " << m_filenameSF << endl;
326 }
327
328 m_fileEff = TFile::Open(m_filenameEff.c_str(), "READ");
329 if (m_filenameEff == m_filenameSF)
330 m_fileSF = m_fileEff;
331 else
332 m_fileSF = TFile::Open(m_filenameSF.c_str(), "READ");
333
334 if (m_verbose) {
335 TObjString* s;
336 m_fileSF->GetObject("VersionInfo/BuildNumber", s);
337 if (s) cout << " CDI file build number: " << s->GetName() << endl;
338 cout << endl;
339 }
340
341 m_flavours = { "B", "C", "T", "Light" };
342 string testPrefix(taggerName); testPrefix += ".";
343
344 // Since TEnv doesn't allow for straight retrieval of vectors of strings, expect
345 // semicolon-separated entries (semicolon because ROOT considers this as a "special"
346 // token anyway in object names).
347 string::size_type end;
348
349 // Calibration names for the efficiencies
350 std::map<string, std::vector<string> > effNames;
351 for (auto const& flavour : m_flavours) {
352 string test(testPrefix); test += "EfficiencyCalibration"; test += flavour; test += "Name";
353 effNames[flavour] = split(string(env.GetValue(test.c_str(), "default")));
354 }
355 setEffCalibrationNames(effNames);
356
357 // Calibration names for the efficiency scale factors
358 std::map<string, string> SFNames;
359 for (auto const& flavour : m_flavours) {
360 string test(testPrefix); test += "ScaleFactorCalibration"; test += flavour; test += "Name";
361 SFNames[flavour] = string(env.GetValue(test.c_str(), "default"));
362 SFNames[flavour] = trimWhiteSpaces(SFNames[flavour]);
363 }
364 setSFCalibrationNames(SFNames);
365
366 // Since TEnv doesn't allow for straight retrieval of vectors of strings, expect
367 // semicolon-separated entries (semicolon because ROOT considers this as a "special"
368 // token anyway in object names).
369 // Don't prefix this since the aliases are common to all taggers (even if they are read again for each tagger).
370 string AL(env.GetValue("aliases", ""));
371 if (AL.size() > 0) {
372 do {
373 end = AL.find(";");
374 string alias = AL.substr(0, end);
375 // Each alias specification uses an arrow ("->"). Forget about entries
376 // not properly following this specification.
377 // NB: TEnv imposes a maximum string length of 1024 characters -- is this a problem?
378 string::size_type arrow = alias.find("->");
379 if (arrow == string::npos) continue;
380 string target = alias.substr(0,arrow); target = trimWhiteSpaces(target);
381 m_aliases[target] = alias.substr(arrow+2);
382 m_aliases[target] = trimWhiteSpaces(m_aliases[target]);
383 if (end != string::npos) AL = AL.substr(end+1);
384 } while (end != string::npos);
385 }
386
387 //run egenvector method or not?
388 string test="runEigenVectorMethod";
389 m_runEigenVectorMethod=(bool)env.GetValue(test.c_str(),0);
390
391 if (m_runEigenVectorMethod) {
392 // Retrieve the list of systematic uncertainties not to be considered when building up
393 // the full covariance matrix used for the eigenvector method.
394 // We do this in two steps: first, for backward compatibility reasons, a flavour-independent list is scanned.
395 // Second, flavour-specific lists are scanned.
396 test = "excludeFromCovMatrix";
397 std::vector<std::string> to_exclude = split(env.GetValue(test.c_str(), ""));
398 // Copy the resulting list to all flavours
399 for (auto const& flavour : m_flavours) {
400 m_excludeFromCovMatrix[flavour] = to_exclude;
401 }
402 for (auto const& flavour : m_flavours) {
403 test = "excludeFrom"; test += flavour; test += "CovMatrix";
404 to_exclude = split(env.GetValue(test.c_str(), ""));
405 // Append to the existing list
406 m_excludeFromCovMatrix[flavour].insert(m_excludeFromCovMatrix[flavour].end(), to_exclude.begin(), to_exclude.end());
407 }
408
409 unsigned int n_excluded = 0;
410 for (auto const& flavour : m_flavours) {
411 n_excluded += m_excludeFromCovMatrix[flavour].size();
412 }
413 if (m_verbose) {
414 cout << " List of uncertainties to exclude:";
415 if (n_excluded == 0) cout << " none";
416 for (auto const& flavour : m_flavours) {
417 if (m_excludeFromCovMatrix[flavour].size() > 0) {
418 cout << "\n\t" << flavour << ":\t";
419 for (unsigned int i = 0; i < m_excludeFromCovMatrix[flavour].size(); ++i) {
420 cout << m_excludeFromCovMatrix[flavour].at(i);
421 if (i+1 == m_excludeFromCovMatrix[flavour].size()) cout << "; ";
422 }
423 cout << endl;
424 }
425 }
426 cout << endl;
427 }
428
429 // The following determines whether also pre-determined (recommended) lists of uncertainties are to be excluded from EV decomposition.
430 // These lists are stored with the CalibrationDataContainers, which have not been instantiated yet (so we cannot show them at this point).
431 m_useRecommendedEVExclusions = (bool) env.GetValue("ExcludeRecommendedFromEigenVectorTreatment", false);
432
433 // determine also the eigenvector reduction strategies
434 std::map<string, EVReductionStrategy> mappings;
435 mappings["Loose"] = Loose;
436 mappings["Medium"] = Medium;
437 mappings["Tight"] = Tight;
438 for (auto const& flavour : m_flavours) {
439 test = testPrefix; test += "EigenvectorReduction"; test += flavour;
440 std::string reduction = string(env.GetValue(test.c_str(), "Loose"));
441 reduction = trimWhiteSpaces(reduction);
442 m_EVReductions[flavour] = mappings.find(reduction) == mappings.end() ? mappings["Loose"] : mappings.find(reduction)->second;
443 }
444 }
445
446 // determine |eta| validity range
447 m_maxAbsEta = env.GetValue("MaxAbsEta", 2.5);
448 if (m_maxAbsEta < 0) m_maxAbsEta = 2.5;
449
450 // set validation / protection strategy in case an out-of-bounds eta value is specified
451 string strategy = string(env.GetValue("OutOfBoundsEta", "GiveUp"));
452 strategy = trimWhiteSpaces(strategy);
453 if (strategy == "GiveUp") m_absEtaStrategy = GiveUp;
454 else if (strategy == "Flag") m_absEtaStrategy = Flag;
455 else if (strategy == "Ignore") m_absEtaStrategy = Ignore;
456 else {
457 cerr << "unknown |eta| extrapolation strategy: " << strategy << ", setting to GiveUp" << endl;
458 m_absEtaStrategy = GiveUp;
459 }
460
461 // set validation / protection strategy in case out-of-bounds variables are specified
462 strategy = string(env.GetValue("OutOfBoundsOther", "Flag"));
463 strategy = trimWhiteSpaces(strategy);
464 if (strategy == "GiveUp") m_otherStrategy = GiveUp;
465 else if (strategy == "GiveUpExtrapolated") m_otherStrategy = GiveUpExtrapolated;
466 else if (strategy == "Flag") m_otherStrategy = Flag;
467 else if (strategy == "Ignore") m_otherStrategy = Ignore;
468 else {
469 cerr << "unknown general extrapolation strategy: " << strategy << ", setting to Flag" << endl;
470 m_otherStrategy = Flag;
471 }
472
473 // maximum tag weight to accept
474 m_maxTagWeight = env.GetValue("MaxTagWeight", 10.0);
475
476 // MC/MC (hadronisation) scale factors: making this user-steerable is intended to be *temporary* only
477 m_useMCMCSF = (bool) env.GetValue("useMCMCSF", 1);
478 // MC/MC (topology) scale factors: making this user-steerable is intended to be *temporary* only
479 m_useTopologyRescaling = (bool) env.GetValue("useTopologySF", 0);
480
481 if (m_verbose) cout << "======= end of CalibrationDataInterfaceROOT instantiation ========" << endl;
482}
483
484
485//________________________________________________________________________________
487 const char* fileSF, const char* fileEff,
488 const std::vector<std::string>& jetAliases,
489 const std::map<std::string, std::string>& SFNames,
490 const std::map<std::string, std::vector<std::string> >& EffNames,
491 const std::map<std::string, std::vector<std::string> >& excludeFromEV,
492 const std::map<std::string, EVReductionStrategy>& EVReductions,
493 bool useEV, Uncertainty strat, bool useMCMCSF, bool useTopologyRescaling,
494 bool useRecommendedEEVExclusions, bool verbose,
495 std::vector<std::string> flavours) :
496 m_filenameSF(fileSF), m_filenameEff(""), m_flavours(std::move(flavours)),
497 m_runEigenVectorMethod(useEV), m_EVStrategy(strat), m_EVReductions(EVReductions),
498 m_useRecommendedEVExclusions(useRecommendedEEVExclusions), m_verbose(verbose),
499 m_useMCMCSF(useMCMCSF), m_useTopologyRescaling(useTopologyRescaling),
502{
503 // Normal constructor avoiding the need for a .env file.
504 //
505 // taggerName: this should correspond to the tagger name as used in the calibration ROOT file
506 // fileSF: full path of the calibration ROOT file containing the calibration scale factors
507 // fileEff: optional full path name of a ROOT file containing additional MC efficiency maps
508 // (use a null pointer to disable the use of such additional file)
509 // flavours; This should correspond to the list of flavour labels that's used by a tagger, and
510 // which corresponds to the labels used in internal maps
511 // jetAliases: this can be used to convert jet collection names to the corresponding names in the
512 // calibration ROOT file (this may be useful as e.g. the collection names in the
513 // calibration ROOT file have the JVF criterion attached as a suffix).
514 // Each alias is specified as
515 // nameOrig->nameTarget,
516 // where nameOrig and nameTarget are the names of the input jet collection and the
517 // jet collection name as used in the calibration ROOT file, respectively.
518 // SFNames: map specifying for each of the calibration flavours ("B", "C", "T", "Light") the
519 // name of the scale factor calibration object
520 // EffNames: map specifying for each of the calibration flavours ("B", "C", "T", "Light") the
521 // names of the possibly relevant efficiency calibration objects
522 // excludeFromEV: map specifying for each of the calibration flavours ("B", "C", "T", "Light") the
523 // systematic uncertainties to be excluded from the Eigenvector variation treatment
524 // (this is used only if Eigenvector variations are used to begin with)
525 // EVReductions: Eigenvector variation reduction strategies for "B", "C", "Light" jets (again,
526 // this is only relevant if Eigenvector variations are used to begin with)
527 // useEV: switch specifying if Eigenvector variations will be used or not
528 // useMCMCSF: switch specifying if generator-dependent scale factors are to be applied or not
529
530 // Note: at present, the means to change the strategies and maximum values initialized above do not exist
531 // when using this constructor
532
533 if (m_verbose) {
534 cout << "=== CalibrationDataInterfaceROOT::CalibrationDataInterfaceROOT ===" << endl;
535 cout << " taggerName : " << taggerName << endl;
536 cout << " Systematic strategy : ";
537 if (m_EVStrategy == Analysis::Uncertainty::SFEigen){
538 cout << "SFEigen" << endl;
539 } else if (m_EVStrategy == Analysis::Uncertainty::SFGlobalEigen){
540 cout << "SFGlobalEigen" << endl;
541 } else {
542 cout << " Other" << endl;
543 }
544 if (fileEff) cout << " Efficiency file name : " << fileEff << endl;
545 cout << " SF file name : " << fileSF << endl
546 << endl;
547 }
548
549 m_taggerName = taggerName;
550
551 m_fileSF = TFile::Open(fileSF, "READ");
552 if (fileEff && strcmp(fileSF, fileEff) != 0) {
553 m_filenameEff = string(fileEff);
554 m_fileEff = TFile::Open(fileEff, "READ");
555 } else
557
558 if (m_verbose) {
559 TObjString* s;
560 m_fileSF->GetObject("VersionInfo/BuildNumber", s);
561 if (s) cout << " CDI file build number: " << s->GetName() << endl;
562 cout << endl;
563 }
564
565 for (unsigned int i = 0; i < jetAliases.size(); ++i) {
566 // Each alias specification uses an arrow ("->"). Forget about entries
567 // not properly following this specification.
568 string::size_type arrow = jetAliases[i].find("->");
569 if (arrow == string::npos) continue;
570 m_aliases[jetAliases[i].substr(0,arrow)] = jetAliases[i].substr(arrow+2);
571 }
572
573 setEffCalibrationNames(EffNames);
574 setSFCalibrationNames(SFNames);
575
577 // if we want to run EV, then decide which one
578 // The following should hold for both eigenvector decomposition methods (SFEigen and SFGlobalEigen)
579 // The global one simply adapts itself to using the m_excludeFromCovMatrix to perform the same task
580
581 m_excludeFromCovMatrix = excludeFromEV;
582 unsigned int n_excluded = 0;
583 for (auto const& flavour : m_flavours) {
584 n_excluded += m_excludeFromCovMatrix[flavour].size();
585 }
586 if (m_verbose) {
587 cout << " List of uncertainties to exclude:";
588 if (n_excluded == 0) cout << " none";
589 for (auto const& flavour : m_flavours) {
590 if (m_excludeFromCovMatrix[flavour].size() > 0) {
591 cout << "\n\t" << flavour << ":\t";
592 for (unsigned int i = 0; i < m_excludeFromCovMatrix[flavour].size(); ++i) {
593 cout << m_excludeFromCovMatrix[flavour].at(i);
594 if (i+1 == m_excludeFromCovMatrix[flavour].size()) cout << "; ";
595 }
596 cout << endl;
597 }
598 }
599 cout << endl;
600 }
601
602 }
603
604 if (m_verbose) cout << "======= end of CalibrationDataInterfaceROOT instantiation ========" << endl;
605}
606
607//________________________________________________________________________________
609{
610 // Default constructor for PROOF purposes
611
612 m_fileEff=0;
613 m_fileSF=0;
614}
615
616//________________________________________________________________________________
626{
627 // Copy constructor. Note that the "cacheable" items aren't copied (they will be re-created if needed)
628
629 // The TFile objects cannot be copied. Therefore, create duplicate objects starting from the filenames
630 m_fileSF = TFile::Open(m_filenameSF.c_str(), "READ");
633 else
634 m_fileEff = TFile::Open(m_filenameEff.c_str(), "READ");
635}
636
637//________________________________________________________________________________
639{
640 // Destructor
641 if ((m_fileEff!=0) && (m_fileSF!=0)) {
642 if (m_fileEff == m_fileSF) {
643 m_fileEff->Close();
644 delete m_fileEff; m_fileEff = 0;
645 } else {
646 m_fileEff->Close();
647 m_fileSF->Close();
648 delete m_fileEff; m_fileEff = 0;
649 delete m_fileSF; m_fileSF = 0;
650 }
651 }
652 // delete also the stored objects (these are owned by us)
653 for (std::vector<CalibrationDataContainer*>::iterator it = m_objects.begin(); it != m_objects.end(); ++it) {
654 if (*it) {
655 delete *it; *it = 0;
656 }
657 }
658
659 for (std::map<std::string, HadronisationReferenceHelper*>::iterator it = m_refMap.begin();
660 it != m_refMap.end(); ++it) {
661 if(it->second)
662 { delete it->second; it->second=nullptr; }
663 }
664
665 // Print summary output on out-of-bounds issues
666 if (m_absEtaStrategy == Flag && m_verbose) {
667 bool found = false;
668 cout << "\t\tCalibrationDataInterfaceROOT |eta| out-of-bounds summary:" << endl;
669 for (unsigned int index = 0; index < m_mainCounters.size(); ++index)
670 if (m_etaCounters[index] > 0) {
671 found = true;
672 cout << "\t\t\t" << nameFromIndex(index) << ": " << m_etaCounters[index] << endl;
673 }
674 if (!found) cout << "\t\t\tNo issues found" << endl;
675 }
676 if (m_otherStrategy == Flag && m_verbose) {
677 bool found = false;
678 cout << "\t\tCalibrationDataInterfaceROOT object out-of-bounds summary:" << endl;
679 for (unsigned int index = 0; index < m_mainCounters.size(); ++index)
681 found = true;
682 cout << "\t\t\t" << nameFromIndex(index)
683 << " general: " << m_mainCounters[index]
684 << ", extrapolated: " << m_extrapolatedCounters[index]
685 << endl;
686 }
687 if (!found) cout << "\t\t\tNo issues found" << endl;
688 }
689}
690
691//________________________________________________________________________________
692bool
694 const std::string& OP,
695 const std::string& author,
696 bool isSF, unsigned int& index,
697 unsigned int mapIndex)
698{
699 // Retrieve the integer index corresponding to a given combination of
700 // flavour label / tagger / working point / jet collection name, and separately
701 // for calibration scale factors and MC efficiencies (all these ingredients are needed
702 // to specify fully the calibration object).
703 // In fact this method will also trigger the retrieval of the object itself, if not already
704 // done, and will cache it internally. The absence of the requested calibration object will
705 // be flagged by a false return value.
706 // This method is used internally but should also be called by users in order to exploit the
707 // "code speed-up" features documented above.
708 //
709 // label: jet flavour label
710 // OP: tagger working point
711 // author: jet collection name
712 // isSF: set to true (false) for scale factors (MC efficiencies)
713 // index: resulting index (meaningful only for a 'true' function return value)
714 // mapIndex: index to the MC efficiency map to be used
715
716 index = 0;
717
718 // construct the full name from the label, operating point, SF/Eff choice;
719 // then look up this full name
720 string name = fullName(author, OP, label, isSF, mapIndex);
721 std::map<string, unsigned int>::const_iterator it = m_objectIndices.find(name);
722 if (it == m_objectIndices.end()) {
723 // If no container is found, attempt to retrieve it here (this is so that users won't
724 // have to call the named scale factor etc. methods once just to retrieve the container).
725 string flavour = (label == "N/A") ? "Light" : label;
726 string cntname = getContainername(flavour, isSF, mapIndex);
727 if (m_verbose) std::cout << "CalibrationDataInterfaceROOT->retrieveCalibrationIndex : container name is " << cntname << std::endl;
728 retrieveContainer(flavour, OP, author, cntname, isSF, m_verbose); // Only call this if you want to retrieve a currently not available container
729 it = m_objectIndices.find(name);
730 if (it == m_objectIndices.end()) return false;
731 } else {
732 if (m_verbose) std::cout << "CalibrationDataInterfaceROOT->retrieveCalibrationIndex : container " << name << " already cached! " << std::endl;
733 }
734
735 index = it->second;
736 return true;
737}
738
739//________________________________________________________________________________
742 const string& label, const string& OP,
743 Uncertainty unc, unsigned int numVariation,
744 unsigned int mapIndex) //const
745{
746 // Scale factor retrieval identifying the requested calibration object by name.
747 // The return value is either a (value, uncertainty) or an (up, down) variation pair, as documented
748 // above, and will be a dummy value in case an error occurs.
749 //
750 // variables: object holding kinematic (and other) information needed to compute the result
751 // label: jet flavour label
752 // OP: tagger operating point
753 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
754 // named variations are to be computed)
755 // numVariation: variation index (in case of eigenvector or named variations)
756 // mapIndex: index to the efficiency map to be used (this is needed for MC/MC scale factor
757 // application)
758 unsigned int indexEff, indexSF;
759 if (! (retrieveCalibrationIndex (label, OP, variables.jetAuthor, false, indexEff, mapIndex) && retrieveCalibrationIndex (label, OP, variables.jetAuthor, true, indexSF))) {
760 cerr << "getScaleFactor: unable to find SF calibration for object " << fullName(variables.jetAuthor, OP, label, false, mapIndex) << " or SF calibration for object " << fullName(variables.jetAuthor, OP, label, true) << endl;
761 // Return a dummy result if the object is not found
763 }
764
765 Analysis::CalibResult result; // the following is SF #3
766 return (getScaleFactor(variables, indexSF, indexEff, unc, numVariation, result, label) == Analysis::kError) ?
767 Analysis::dummyResult : result;
768}
769
770//________________________________________________________________________________
773 unsigned int indexSF, unsigned int indexEff,
774 Uncertainty unc, const std::string& flavour, unsigned int numVariation)
775{
776 // Scale factor retrieval identifying the requested calibration object by index.
777 // The return value is either a (value, uncertainty) or an (up, down) variation pair, as documented
778 // above, and will be a dummy value in case an error occurs.
779 //
780 // variables: object holding kinematic (and other) information needed to compute the result
781 // indexSF: index to scale factor calibration object
782 // indexEff: index to MC efficiency object
783 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
784 // named variations are to be computed)
785 // numVariation: variation index (in case of eigenvector or named variations)
786 Analysis::CalibResult result; // the following is SF #3
787 return (getScaleFactor(variables, indexSF, indexEff, unc, numVariation, result, flavour) == Analysis::kError) ?
788 Analysis::dummyResult : result;
789}
790
791//________________________________________________________________________________
794 unsigned int indexSF, unsigned int indexEff,
795 Uncertainty unc, unsigned int numVariation,
796 Analysis::CalibResult& result, const string& flavour)
797{
798 // Scale factor retrieval identifying the requested calibration object by index.
799 //
800 // variables: object holding kinematic (and other) information needed to compute the result
801 // indexSF: index to scale factor calibration object
802 // indexEff: index to MC efficiency object
803 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
804 // named variations are to be computed)
805 // numVariation: variation index (in case of eigenvector or named variations)
806 // result: (value, uncertainty) or (up, down) variation pair, depending on the unc value.
807 // A dummy value will be returned in case of an error.
808
809 CalibrationDataContainer* container = m_objects[indexSF];
810 if (! container) {
811 cerr << "getScaleFactor: error retrieving container!" << endl;
812 return Analysis::kError;
813 }
814
815 // perform out-of-bound check of jet eta
816 if (!checkAbsEta(variables, indexSF)) {
817 if (m_verbose)
818 cerr << "Jet |eta| is outside of the boundary!" << endl;
819 return Analysis::kRange;
820 }
821
822 // retrieve the MC/MC scale factor
823 double MCMCSF = m_useMCMCSF ? getMCMCScaleFactor(variables, indexSF, indexEff) : 1; // if we don't want to switch generator, MCMCSF = 1, as it should be
824
825 if (!m_runEigenVectorMethod && (unc == SFEigen || unc == SFNamed || unc == SFGlobalEigen))
826 {
827 cerr << " ERROR. Trying to call eigenvector method but initialization not switched on in b-tagging configuration." << endl;
828 cerr << " Please correct your configuration first. Nominal uncertainties used. " << endl;
829 }
830
831 // Procede with eigenvariations methods i.e. return the SF variations
832 if (unc == SFEigen || unc == SFNamed || unc==SFGlobalEigen) {
833 std::shared_ptr<CalibrationDataEigenVariations> eigenVariation;
834 try {
835 eigenVariation=m_eigenVariationsMap.at(container);
836 } catch (const std::out_of_range&) {
837 cerr << " Could not retrieve eigenvector variation, while it should have been there." << endl;
838 return Analysis::kError;
839 }
840 TH1* up=0;
841 TH1* down=0;
842 bool extrapolate = false; // store if the numVariation is the extrapolation named uncertainty index
843 if (unc == SFEigen || unc==SFNamed){
844 unsigned int maxVariations = (unc == SFEigen) ? eigenVariation->getNumberOfEigenVariations() : eigenVariation->getNumberOfNamedVariations();
845 if (numVariation > maxVariations-1) {
846 cerr << "Asked for " << ((unc == SFEigen) ? "eigenvariation" : "named variation") << " number: " << numVariation << " but overall number of available variations is: " << maxVariations << endl;
847 return Analysis::kError;
848 }
849 bool isOK = eigenVariation->getEigenvectorVariation(numVariation,up,down);
850 if (!isOK) {
851 cerr << "Eigenvector object is there but cannot retrieve up and down uncertainty histograms." << endl;
852 return Analysis::kError;
853 }
854 // the 'extrapolation' uncertainty (always a named one) needs a somewhat special treatment
855 extrapolate = (unc == SFNamed) ? eigenVariation->isExtrapolationVariation(numVariation) : false;
856
857 } else if (unc == SFGlobalEigen) {
858 std::shared_ptr<CalibrationDataGlobalEigenVariations> GEV = std::dynamic_pointer_cast<CalibrationDataGlobalEigenVariations>(eigenVariation); //dynamic_cast<std::shared_ptr<CalibrationDataGlobalEigenVariations> >(eigenVariation);
859 if (not GEV){
860 cerr << "Analysis::CalibrationDataInterfaceROOT::getScaleFactor: dynamic cast failed\n";
861 return Analysis::kError;
862 }
863 unsigned int maxVariations = GEV->getNumberOfEigenVariations(flavour); // <----- This gets the number of variations of the flavour
864 if (maxVariations == 0){
865 cerr << "Overall number of available variations is 0!" << endl;
866 return Analysis::kError;
867 }
868 if (numVariation > maxVariations-1) {
869 cerr << "Asked for global eigenvariation number: " << numVariation << " but overall number of available variations is: " << maxVariations << endl;
870 return Analysis::kError;
871 }
872 bool isOK = GEV->getEigenvectorVariation(flavour, numVariation,up,down);
873 if (!isOK) {
874 cerr << "Eigenvector object is there but cannot retrieve up and down uncertainty histograms." << endl;
875 return Analysis::kError;
876 }
877 // the 'extrapolation' uncertainty (always a named one) needs a somewhat special treatment
878 extrapolate = GEV->isExtrapolationVariation(numVariation, flavour);
879 } else {
880 std::cerr << "ERROR: you requested " << unc << " but that isn't in the set of (SFEigen, SFGlobalEigen, SFNamed) for eigenvariations. " << std::endl;
881 return Analysis::kError;
882 }
883
884 double valueUp;
885 double valueDown;
886 Analysis::CalibrationStatus statUp = container->getResult(variables, valueUp, up, extrapolate); // This is what actually retrieves results from the container
887 Analysis::CalibrationStatus statDown = container->getResult(variables, valueDown,down, extrapolate);
888
889 if (statUp == Analysis::kError || statDown == Analysis::kError)
890 return Analysis::kError;
891 if (m_otherStrategy == GiveUp)
892 assert (statUp != Analysis::kRange); // no need to test also statDown
894 assert (statUp != Analysis::kExtrapolatedRange); // no need to test also statDown
895 else if (m_otherStrategy == Flag) {
896 if (statUp == Analysis::kRange)
897 increaseCounter(indexSF);
898 else if (statUp == Analysis::kExtrapolatedRange)
900 }
901
902 result.first = MCMCSF*valueUp;
903 result.second = MCMCSF*valueDown;
904
905 // Prevent negative return values. Should the comparison be against a strict 0?
906 result.first = std::max(Analysis::CalibZERO, result.first);
907 result.second = std::max(Analysis::CalibZERO, result.second);
908
909 return statUp; // end of getScaleFactor if SFEigen, SFGlobalEigen, or SFNamed is set
910
911
912 } // The above returns the up/down varied scale factor
913 //Proceed with no-eigenvector result
914
915 // always retrieve the result itself
916 double value;
917 Analysis::CalibrationStatus status = container->getResult(variables, value);
918 if (status == Analysis::kError) {
919 cerr << "getScaleFactor: error retrieving result in non-EV context!" << endl;
920 return status;
921 }
922 if (m_otherStrategy == GiveUp){
923 assert (status != Analysis::kRange);
924 } else if (m_otherStrategy == GiveUpExtrapolated) {
925 assert (status != Analysis::kExtrapolatedRange);
926 } else if (m_otherStrategy == Flag) {
927 if (status == Analysis::kRange){
928 increaseCounter(indexSF);
929 } else if (status == Analysis::kExtrapolatedRange) {
931 }
932 }
933
934 // retrieve the statistical uncertainty if desired
935 double stat(0);
936 if (unc == Total || unc == Statistical) {
937 if (container->getStatUncertainty(variables, stat) == Analysis::kError) {
938 cerr << "getScaleFactor: error retrieving Scale factor parameter covariance matrix!" << endl;
939 return Analysis::kError;
940 }
941 }
942
943 Analysis::UncertaintyResult resSyst(0,0);
944 if (unc == Total || unc == Systematic) {
945 if (container->getSystUncertainty(variables, resSyst) == Analysis::kError) {
946 cerr << "getScaleFactor: error retrieving Scale factor parameter systematic uncertainty!" << endl;
947 return Analysis::kError;
948 }
949 } else if (unc == Extrapolation) {
950 // this uncertainty is special, since it is not normally to be combined into the overall systematic uncertainty
951 if (container->getUncertainty("extrapolation", variables, resSyst) == Analysis::kError)
952 cerr << "getScaleFactor: error retrieving Scale factor parameter extrapolation uncertainty!" << endl;
953 } else if (unc == TauExtrapolation) {
954 // also this uncertainty is special, since it it singles out an uncertainty relevant only for tau "jets",
955 // and some care has to be taken not to duplicate or omit uncertainties
956 if (container->getUncertainty("extrapolation from charm", variables, resSyst) == Analysis::kError)
957 cerr << "getScaleFactor: error retrieving Scale factor parameter extrapolation uncertainty!" << endl;
958 }
959
960 double uncertainty = combinedUncertainty(stat, resSyst);
961 result.first = MCMCSF*value;
962 result.second = MCMCSF*uncertainty;
963
964 // Prevent negative return values. Should the comparison be against a strict 0?
965 result.first = std::max(Analysis::CalibZERO, result.first);
966 return status;
967
968}
969
970//________________________________________________________________________________
973 const string& label, const string& OP,
974 Uncertainty unc, unsigned int mapIndex)
975{
976 // MC efficiency retrieval identifying the requested calibration object by name.
977 // The return value is a (value, uncertainty) pair, as documented above, and will
978 // be a dummy value in case an error occurs.
979 //
980 // variables: object holding kinematic (and other) information needed to compute the result
981 // label: jet flavour label
982 // OP: tagger operating point
983 // unc: keyword indicating what uncertainties to evaluate
984 // mapIndex: index to the efficiency map to be used
985
986 unsigned int index;
987 if (! retrieveCalibrationIndex (label, OP, variables.jetAuthor, false, index, mapIndex)) {
988 cerr << "getMCEfficiency: unable to find Eff calibration for object " << fullName(variables.jetAuthor, OP, label, false, mapIndex) << endl;
989 // Return a dummy result if the object is not found
991 }
992
994 return (getMCEfficiency(variables, index, unc, result) == Analysis::kError) ?
995 Analysis::dummyResult : result;
996}
997
998//________________________________________________________________________________
1001 unsigned int index, Uncertainty unc) //const
1002{
1003 // MC efficiency retrieval identifying the requested calibration object by index.
1004 // The return value is a (value, uncertainty) pair, as documented above, and will
1005 // be a dummy value in case an error occurs.
1006 //
1007 // variables: object holding kinematic (and other) information needed to compute the result
1008 // index: index to calibration object
1009 // unc: keyword indicating what uncertainties to evaluate
1010
1011 Analysis::CalibResult result;
1012 return (getMCEfficiency(variables, index, unc, result) == Analysis::kError) ?
1013 Analysis::dummyResult : result;
1014}
1015
1016//________________________________________________________________________________
1019 unsigned int index, Uncertainty unc,
1020 Analysis::CalibResult& result)
1021{
1022 // MC efficiency retrieval identifying the requested calibration object by index.
1023 //
1024 // variables: object holding kinematic (and other) information needed to compute the result
1025 // index: index to calibration object
1026 // unc: keyword indicating what uncertainties to evaluate
1027 // result: (value, uncertainty) variation pair.
1028 // A dummy value will be returned in case of an error.
1029
1031 if (! container) return Analysis::kError;
1032
1033 // perform out-of-bound check of jet eta
1034 if (!checkAbsEta(variables, index)) {
1035 if (m_verbose)
1036 cerr << "Jet |eta| is outside of the boundary!" << endl;
1037 return Analysis::kRange;
1038 }
1039
1040
1041 // always retrieve the result itself
1042 double value;
1043 Analysis::CalibrationStatus status = container->getResult(variables, value);
1044 if (status == Analysis::kError) return status;
1045 if (m_otherStrategy == GiveUp)
1046 assert (status != Analysis::kRange); // no need to test also statDown
1047 else if (m_otherStrategy == Flag)
1048 if (status == Analysis::kRange)
1049 this->increaseCounter(index);
1050
1051 // retrieve the statistical uncertainty if desired
1052 double stat(0);
1053 if (unc == Total || unc == Statistical) {
1054 if (container->getStatUncertainty(variables, stat) == Analysis::kError) {
1055 cerr << "getMCEfficiency: error retrieving MC efficiency parameter covariance matrix!" << endl;
1056 return Analysis::kError;
1057 }
1058 }
1059
1060 // Temporary(?) hack: comment this out since the present MC results don't have "systematics" contributions
1061 // Analysis::UncertaintyResult resSyst(0,0);
1062 // if (unc == Total || unc == Systematic) {
1063 // if (container->getSystUncertainty(variables, resSyst) == Analysis::kError)
1064 // cerr << "getScaleFactor: error retrieving Scale factor parameter covariance matrix!"
1065 // << endl;
1066 // }
1067
1068 // since there is no combination of stat/syst uncertainties to be made, comment this out too
1069 double uncertainty = stat; // combinedUncertainty(stat, resSyst);
1070 result.first = std::max(0., std::min(1., value));
1071 result.second = uncertainty;
1072
1073 return status;
1074}
1075
1076//====================== efficiency retrieval ==========================================
1077
1078//________________________________________________________________________________
1081 const string& label,
1082 const string& OP, Uncertainty unc, const std::string& flavour,
1083 unsigned int numVariation, unsigned int mapIndex)
1084{
1085 // Data efficiency retrieval identifying the requested calibration objects by name.
1086 // The data efficiency is computed as the product of MC efficiency and data/MC efficiency scale factor.
1087 // The return value is either a (value, uncertainty) or an (up, down) variation pair, as documented
1088 // above, and will be a dummy value in case an error occurs.
1089 //
1090 // variables: object holding kinematic (and other) information needed to compute the result
1091 // label: jet flavour label
1092 // OP: tagger operating point
1093 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1094 // named variations are to be computed)
1095 // numVariation: variation index (in case of eigenvector or named variations)
1096 // mapIndex: index to the efficiency map to be used
1097
1098 unsigned int indexSF, indexEff;
1099 if (! (retrieveCalibrationIndex (label, OP, variables.jetAuthor, false, indexEff, mapIndex) &&
1100 retrieveCalibrationIndex (label, OP, variables.jetAuthor, true, indexSF))) {
1101 cerr << "getEfficiency: unable to find Eff calibration for object " << fullName(variables.jetAuthor, OP, label, false, mapIndex) << " or SF calibration for object " << fullName(variables.jetAuthor, OP, label, true) << endl;
1102 // Return a dummy result if the object is not found
1103 return Analysis::dummyResult;
1104 }
1105
1106 Analysis::CalibResult result;
1107 return (getEfficiency(variables, indexSF, indexEff, unc, numVariation, result, flavour) == Analysis::kError) ? Analysis::dummyResult : result;
1108}
1109
1110//________________________________________________________________________________
1113 unsigned int indexSF, unsigned int indexEff,
1114 Uncertainty unc, const std::string& flavour, unsigned int numVariation)
1115{
1116 // Data efficiency retrieval identifying the requested calibration objects by index.
1117 // The data efficiency is computed as the product of MC efficiency and data/MC efficiency scale factor.
1118 // The return value is either a (value, uncertainty) or an (up, down) variation pair, as documented
1119 // above, and will be a dummy value in case an error occurs.
1120 //
1121 // variables: object holding kinematic (and other) information needed to compute the result
1122 // indexSF: index to scale factor calibration object
1123 // indexEff: index to MC efficiency object
1124 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1125 // named variations are to be computed)
1126 // numVariation: variation index (in case of eigenvector or named variations)
1127
1128 Analysis::CalibResult result;
1129 return (getEfficiency(variables, indexSF, indexEff, unc, numVariation, result, flavour) == Analysis::kError) ?
1130 Analysis::dummyResult : result;
1131}
1132
1133//________________________________________________________________________________
1136 unsigned int indexSF, unsigned int indexEff,
1137 Uncertainty unc, unsigned int numVariation,
1138 Analysis::CalibResult& result, const std::string& flavour)
1139{
1140 // Data efficiency retrieval identifying the requested calibration objects by index.
1141 //
1142 // variables: object holding kinematic (and other) information needed to compute the result
1143 // indexSF: index to scale factor calibration object
1144 // indexEff: index to MC efficiency object
1145 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1146 // named variations are to be computed)
1147 // numVariation: variation index (in case of eigenvector or named variations)
1148 // result: (value, uncertainty) or (up, down) variation pair, depending on the unc value.
1149 // A dummy value will be returned in case of an error.
1150
1151 Analysis::CalibResult sfResult;
1152 Analysis::CalibrationStatus sfStatus = getScaleFactor(variables, indexSF, indexEff, unc, numVariation, sfResult, flavour);
1153 if (sfStatus == Analysis::kError) return sfStatus;
1154 Analysis::CalibResult effResult;
1155 Analysis::CalibrationStatus effStatus= getMCEfficiency(variables, indexEff, unc, effResult);
1156 if (effStatus == Analysis::kError) return effStatus;
1157
1158 double relative = 0;
1159 double value = effResult.first;
1160 if (TMath::Abs(sfResult.first) > Analysis::CalibZERO) {
1161 value = std::min(effResult.first*sfResult.first, 1.);
1162
1163 // Treat the scale factor variation cases separately since the contents of the CalibResult are different
1164 // (e.g. 'value' above contains the upward variation)
1165 if (unc == SFEigen || unc == SFNamed) {
1166 double valueDown = effResult.first*sfResult.second;
1167 result.first = value; // up/down variataions of data-efficiency
1168 result.second = valueDown;
1169 return sfStatus;
1170 }
1171 if (value > 0.) {
1172 relative = effResult.second/effResult.first;
1173 double sfRelative = sfResult.second/sfResult.first;
1174 /*
1175 cout << "sferr=" << sfResult.second
1176 << "btag Calib relative=" << relative << " sfRelative=" << sfRelative << endl;
1177 */
1178 relative = TMath::Sqrt(sfRelative*sfRelative + relative*relative);
1179 }
1180 } else {
1181 // now never happens due to protection of SF return value:
1182 cerr << "ERROR: CalibrationDataInterfaceROOT::getEfficiency: SF null result, SF=" << sfResult.first << " MC eff=" << effResult.first << "; setting SF=1." << endl;
1183 relative = Analysis::dummyValue;
1184 }
1185
1186 result.first = value;
1187 result.second = value*relative;
1188 // "Select" the status code for the actual calibration (it is subject to more constraints)
1189 return sfStatus;
1190}
1191
1192
1193//________________________________________________________________________________
1196 const string& label,
1197 const string& OP, Uncertainty unc,
1198 unsigned int numVariation, unsigned int mapIndex)
1199{
1200 // Inefficiency scale factor retrieval identifying the requested calibration objects by name.
1201 // The data efficiency is computed as the product of MC efficiency and data/MC efficiency scale factor;
1202 // the inefficiency scale factor is then computed as the ratio of data to MC inefficiencies.
1203 // The return value is either a (value, uncertainty) or an (up, down) variation pair, as documented
1204 // above, and will be a dummy value in case an error occurs.
1205 //
1206 // variables: object holding kinematic (and other) information needed to compute the result
1207 // label: jet flavour label
1208 // OP: tagger operating point
1209 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1210 // named variations are to be computed)
1211 // numVariation: variation index (in case of eigenvector or named variations)
1212 // mapIndex: index to the efficiency map to be used
1213
1214 unsigned int indexSF, indexEff;
1215 if (! (retrieveCalibrationIndex (label, OP, variables.jetAuthor, false, indexEff, mapIndex) &&
1216 retrieveCalibrationIndex (label, OP, variables.jetAuthor, true, indexSF))) {
1217 cerr << "getInefficiencyScaleFactor: unable to find Eff calibration for object "
1218 << fullName(variables.jetAuthor, OP, label, false, mapIndex)
1219 << " or SF calibration for object "
1220 << fullName(variables.jetAuthor, OP, label, true) << endl;
1221 // Return a dummy result if the object is not found
1222 return Analysis::dummyResult;
1223 }
1224
1225 Analysis::CalibResult result;
1226 return (getInefficiencyScaleFactor(variables, indexSF, indexEff, unc, numVariation, result, label) == Analysis::kError) ?
1227 Analysis::dummyResult : result;
1228}
1229
1230//________________________________________________________________________________
1233 unsigned int indexSF, unsigned int indexEff,
1234 Uncertainty unc, const std::string& flavour, unsigned int numVariation)
1235{
1236 // Inefficiency scale factor retrieval identifying the requested calibration objects by index.
1237 // The data efficiency is computed as the product of MC efficiency and data/MC efficiency scale factor;
1238 // the inefficiency scale factor is then computed as the ratio of data to MC inefficiencies.
1239 // The return value is either a (value, uncertainty) or an (up, down) variation pair, as documented
1240 // above, and will be a dummy value in case an error occurs.
1241 //
1242 // variables: object holding kinematic (and other) information needed to compute the result
1243 // indexSF: index to scale factor calibration object
1244 // indexEff: index to MC efficiency object
1245 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1246 // named variations are to be computed)
1247 // numVariation: variation index (in case of eigenvector or named variations)
1248
1249 Analysis::CalibResult result;
1250 return (getInefficiencyScaleFactor(variables, indexSF, indexEff, unc, numVariation, result, flavour) == Analysis::kError) ?
1251 Analysis::dummyResult : result;
1252}
1253
1254//________________________________________________________________________________
1257 unsigned int indexSF, unsigned int indexEff,
1258 Uncertainty unc, unsigned int numVariation,
1259 Analysis::CalibResult& result, const std::string& flavour)
1260{
1261 // Inefficiency scale factor retrieval identifying the requested calibration objects by index.
1262 // The data efficiency is computed as the product of MC efficiency and data/MC efficiency scale factor;
1263 // the inefficiency scale factor is then computed as the ratio of data to MC inefficiencies.
1264 //
1265 // variables: object holding kinematic (and other) information needed to compute the result
1266 // indexSF: index to scale factor calibration object
1267 // indexEff: index to MC efficiency object
1268 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1269 // named variations are to be computed)
1270 // numVariation: variation index (in case of eigenvector or named variations)
1271 // result: (value, uncertainty) or (up, down) variation pair, depending on the unc value.
1272 // A dummy value will be returned in case of an error.
1273
1274 Analysis::CalibResult sfResult;
1275 Analysis::CalibrationStatus sfStatus = getScaleFactor(variables, indexSF, indexEff, unc, numVariation, sfResult, flavour);
1276 if (sfStatus == Analysis::kError) return sfStatus;
1277 Analysis::CalibResult effResult;
1278 Analysis::CalibrationStatus effStatus= getMCEfficiency(variables, indexEff, unc, effResult);
1279 if (effStatus == Analysis::kError) return effStatus;
1280
1281 double eff = std::min(effResult.first, 1.);
1282 // double efferr = effResult.second; // not needed as (per the code change indicated below) we are not doing anything with MC statistical uncertainties
1283 double sf = sfResult.first;
1284 double sferr = sfResult.second;
1285
1286 double val = 0.; // Analysis::dummyValue;
1287 double err = 0.; // Analysis::dummyValue;
1288 if (1. - eff > CalibZERO) {
1289 // Protect against negative scale factors
1290 val = std::max((1. - eff*sf), CalibZERO) / (1. - eff);
1291 // Treat the scale factor variation cases separately since the contents of the CalibResult are different
1292 // ('sf' and 'sferr' above contain the upward and downward variations, respectively).
1293 if (unc == SFEigen || unc == SFNamed) {
1294 double valDown = std::max((1. - eff*sferr), CalibZERO) / (1. - eff);
1295 result.first = val;
1296 result.second = valDown;
1297 return sfStatus;
1298 }
1299 // When using eigenvector (or named) variations (as above), only scale factor variations are considered.
1300 // For the sake of consistency, it has been decided (see https://its.cern.ch/jira/browse/AFT-350) to remove them also when EV variations aren't used
1301 //err = pow((1. - sf) / (1. - eff) * efferr, 2) + pow(eff*sferr, 2);
1302 err = pow(eff*sferr, 2);
1303 if (err > 0.)
1304 err = 1./(1. - eff) * TMath::Sqrt(err);
1305 // cout << "btag Calib Ineff err=" << err << endl;
1306 }
1307
1308 result.first = std::max(CalibZERO, val);
1309 result.second = err;
1310 // "Select" the status code for the actual calibration (it is subject to more constraints)
1311 return sfStatus;
1312}
1313
1314//________________________________________________________________________________
1317 const string& label,
1318 const string& OP, Uncertainty unc,
1319 unsigned int numVariation, unsigned int mapIndex)
1320{
1321 // Data inefficiency retrieval identifying the requested calibration objects by name.
1322 // The data efficiency is computed as the product of MC efficiency and data/MC efficiency scale factor;
1323 // the inefficiency is then computed as the 1 minus the efficiency.
1324 // The return value is either a (value, uncertainty) or an (up, down) variation pair, as documented
1325 // above, and will be a dummy value in case an error occurs.
1326 //
1327 // variables: object holding kinematic (and other) information needed to compute the result
1328 // label: jet flavour label
1329 // OP: tagger operating point
1330 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1331 // named variations are to be computed)
1332 // numVariation: variation index (in case of eigenvector or named variations)
1333 // mapIndex: index to the efficiency map to be used
1334
1335 unsigned int indexSF, indexEff;
1336 if (! (retrieveCalibrationIndex (label, OP, variables.jetAuthor, false, indexEff, mapIndex) &&
1337 retrieveCalibrationIndex (label, OP, variables.jetAuthor, true, indexSF))) {
1338 cerr << "getInefficiency: unable to find Eff calibration for object "
1339 << fullName(variables.jetAuthor, OP, label, false, mapIndex)
1340 << " or SF calibration for object "
1341 << fullName(variables.jetAuthor, OP, label, true) << endl;
1342 // Return a dummy result if the object is not found
1343 return Analysis::dummyResult;
1344 }
1345
1346 Analysis::CalibResult result;
1347 return (getInefficiency(variables, indexSF, indexEff, unc, numVariation, result, label) == Analysis::kError) ?
1348 Analysis::dummyResult : result;
1349}
1350
1351//________________________________________________________________________________
1354 unsigned int indexSF, unsigned int indexEff,
1355 Uncertainty unc, const std::string& flavour, unsigned int numVariation)
1356{
1357 // Data inefficiency retrieval identifying the requested calibration objects by index.
1358 // The data efficiency is computed as the product of MC efficiency and data/MC efficiency scale factor;
1359 // the inefficiency is then computed as the 1 minus the efficiency.
1360 // The return value is either a (value, uncertainty) or an (up, down) variation pair, as documented
1361 // above, and will be a dummy value in case an error occurs.
1362 //
1363 // variables: object holding kinematic (and other) information needed to compute the result
1364 // indexSF: index to scale factor calibration object
1365 // indexEff: index to MC efficiency object
1366 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1367 // named variations are to be computed)
1368 // numVariation: variation index (in case of eigenvector or named variations)
1369
1370 Analysis::CalibResult result;
1371 return (getInefficiency(variables, indexSF, indexEff, unc, numVariation, result, flavour) == Analysis::kError) ?
1372 Analysis::dummyResult : result;
1373}
1374
1375//________________________________________________________________________________
1378 unsigned int indexSF, unsigned int indexEff,
1379 Uncertainty unc, unsigned int numVariation,
1380 Analysis::CalibResult& result, const std::string& flavour)
1381{
1382 // Data inefficiency retrieval identifying the requested calibration objects by index.
1383 // The data efficiency is computed as the product of MC efficiency and data/MC efficiency scale factor;
1384 // the inefficiency is then computed as the 1 minus the efficiency.
1385 //
1386 // variables: object holding kinematic (and other) information needed to compute the result
1387 // indexSF: index to scale factor calibration object
1388 // indexEff: index to MC efficiency object
1389 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1390 // named variations are to be computed)
1391 // numVariation: variation index (in case of eigenvector or named variations)
1392 // result: (value, uncertainty) or (up, down) variation pair, depending on the unc value.
1393 // A dummy value will be returned in case of an error.
1394
1395 Analysis::CalibResult sfResult;
1396 Analysis::CalibrationStatus sfStatus = getScaleFactor(variables, indexSF, indexEff, unc, numVariation, sfResult, flavour);
1397 if (sfStatus == Analysis::kError) return sfStatus;
1398 Analysis::CalibResult effResult;
1399 Analysis::CalibrationStatus effStatus= getMCEfficiency(variables, indexEff, unc, effResult);
1400 if (effStatus == Analysis::kError) return effStatus;
1401
1402 double val = std::max(0., 1. - effResult.first * sfResult.first);
1403 double err = 0.; // Analysis::dummyValue;
1404
1405 // Bail out here if not both results are strictly positive
1406 if (effResult.first <= 0. || sfResult.first <= 0.) return Analysis::kError;
1407
1408 // Treat the scale factor variation cases separately since the contents of the CalibResult are different
1409 // (e.g. 'val' above contains the upward variation)
1410 if (unc == SFEigen || unc == SFNamed) {
1411 double valDown = std::max(0., 1. - effResult.first*sfResult.second);
1412
1413 result.first = val;
1414 result.second = valDown;
1415 } else {
1416 // safer than pow(x, 2):
1417 err = effResult.second/effResult.first*effResult.second/effResult.first
1418 + sfResult.second/sfResult.first*sfResult.second/sfResult.first;
1419 err = val*TMath::Sqrt(err);
1420
1421 result.first = std::max(0., std::min(1., val));
1422 result.second = err;
1423 }
1424
1425 // "Select" the status code for the actual calibration (it is subject to more constraints)
1426 return sfStatus;
1427}
1428
1429//________________________________________________________________________________
1432 const string& label, const string& OP,
1433 Uncertainty unc, unsigned int mapIndex)
1434{
1435 // Data inefficiency retrieval identifying the requested calibration objects by name.
1436 // The inefficiency is computed as the 1 minus the efficiency.
1437 // The return value is a (value, uncertainty), as documented above, and will be a dummy value
1438 // in case an error occurs.
1439 //
1440 // variables: object holding kinematic (and other) information needed to compute the result
1441 // label: jet flavour label
1442 // OP: tagger operating point
1443 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1444 // named variations are to be computed)
1445 // numVariation: variation index (in case of eigenvector or named variations)
1446 // mapIndex: index to the efficiency map to be used
1447
1448 Analysis::CalibResult effResult = getMCEfficiency(variables, label, OP, unc, mapIndex);
1449 return std::make_pair(std::max(0., 1. - effResult.first), effResult.second);
1450}
1451
1452//________________________________________________________________________________
1455 unsigned int index, Uncertainty unc)
1456{
1457 // MC inefficiency retrieval identifying the requested calibration object by index.
1458 // The inefficiency is computed as the 1 minus the efficiency.
1459 // The return value is a (value, uncertainty), as documented above, and will be a dummy value
1460 // in case an error occurs.
1461 //
1462 // variables: object holding kinematic (and other) information needed to compute the result
1463 // index: index to MC efficiency object
1464 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1465 // named variations are to be computed)
1466 // numVariation: variation index (in case of eigenvector or named variations)
1467
1468 Analysis::CalibResult effResult = getMCEfficiency(variables, index, unc);
1469 return std::make_pair(std::max(0., 1. - effResult.first), effResult.second);
1470}
1471
1472//________________________________________________________________________________
1473double
1475 unsigned indexSF, unsigned int indexEff) const
1476{
1477 // Retrieve the MC/MC scale factor given the set of scale factor and efficiency indices.
1478 // variables: object holding kinematic (and other) information needed to compute the result
1479 // indexSF: index to scale factor calibration object
1480 // indexEff: index to MC efficiency object
1481
1482 // If either reference doesn't exist, or if they are the same, nothing can / needs to be done.
1483 int indexSFRef = m_hadronisationReference[indexSF], indexEffRef = m_hadronisationReference[indexEff];
1484 if (indexSFRef < 0 || indexEffRef < 0 || indexSFRef == indexEffRef) return 1;
1485
1486 // Verify also that the individual efficiencies are physically meaningful.
1487 double effSFRef; m_objects[indexSFRef]->getResult(variables, effSFRef);
1488 double effEffRef; m_objects[indexEffRef]->getResult(variables, effEffRef);
1489 return (effSFRef > 0 && effEffRef > 0) ? effSFRef/effEffRef : 1;
1490}
1491
1492//________________________________________________________________________________
1495 const string& label, Uncertainty unc,
1496 unsigned int numVariation, unsigned int mapIndex)
1497{
1498 // #1
1499 // Tag weight fraction scale factor retrieval identifying the requested calibration object by name.
1500 // The return value is either a (value, uncertainty) or (if eigenvector or named variations are specified)
1501 // an (up, down) variation pair, and will be a dummy value in case an error occurs.
1502 // Note that in contrast to the "regular" (non-continuous) case, the computation of the scale factor in
1503 // general needs the (selection- or even process-specific) MC tag weight fractions, in order to rescale
1504 // scale factors. This is used to ensure that the tag weight fractions (both in data and in MC) sum up to
1505 // unity for each given kinematic bin.
1506 //
1507 // variables: object holding kinematic (and other) information needed to compute the result
1508 // label: jet flavour label
1509 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1510 // named variations are to be computed)
1511 // numVariation: variation index (in case of eigenvector or named variations)
1512 // mapIndex: index to the MC efficiency map to be used for scale factor rescaling
1513
1514 static const string cont("Continuous");
1515
1516 unsigned int indexSF, indexEff;
1517 if (! (retrieveCalibrationIndex (label, cont, variables.jetAuthor, false, indexEff, mapIndex) &&
1518 retrieveCalibrationIndex (label, cont, variables.jetAuthor, true, indexSF))) {
1519 cerr << "getWeightScaleFactor: unable to find Eff calibration for object "
1520 << fullName(variables.jetAuthor, cont, label, false, mapIndex)
1521 << " or SF calibration for object "
1522 << fullName(variables.jetAuthor, cont, label, true) << endl;
1523 return Analysis::dummyResult;
1524 }
1525
1526 Analysis::CalibResult result;
1527 return (getWeightScaleFactor(variables, indexSF, indexEff, unc, numVariation, result) == Analysis::kError) ? Analysis::dummyResult : result;
1528}
1529
1530//________________________________________________________________________________
1533 unsigned int indexSF, unsigned int indexEff,
1534 Uncertainty unc, unsigned int numVariation)
1535{
1536 // #2
1537 // Tag weight fraction scale factor retrieval identifying the requested calibration object by index.
1538 // The return value is either a (value, uncertainty) or (if eigenvector or named variations are specified)
1539 // an (up, down) variation pair, and will be a dummy value in case an error occurs.
1540 // Note that in contrast to the "regular" (non-continuous) case, the computation of the scale factor in
1541 // general needs the (selection- or even process-specific) MC tag weight fractions, in order to rescale
1542 // scale factors. This is used to ensure that the tag weight fractions (both in data and in MC) sum up to
1543 // unity for each given kinematic bin.
1544 //
1545 // variables: object holding kinematic (and other) information needed to compute the result
1546 // indexSF: index to calibration object
1547 // indexEff: index to MC tag weight
1548 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1549 // named variations are to be computed)
1550 // numVariation: variation index (in case of eigenvector or named variations)
1551
1552 Analysis::CalibResult result;
1553 return (getWeightScaleFactor(variables, indexSF, indexEff, unc, numVariation, result) == Analysis::kError) ?
1554 Analysis::dummyResult : result;
1555}
1556
1557//________________________________________________________________________________
1560 unsigned int indexSF, unsigned int indexEff,
1561 Uncertainty unc, unsigned int numVariation,
1562 Analysis::CalibResult& result)
1563{
1564 // #3
1565 // Tag weight fraction scale factor retrieval identifying the requested calibration object by index.
1566 // Note that in contrast to the "regular" (non-continuous) case, the computation of the scale factor in
1567 // general needs the (selection- or even process-specific) MC tag weight fractions, in order to rescale
1568 // scale factors. This is used to ensure that the tag weight fractions (both in data and in MC) sum up to
1569 // unity for each given kinematic bin.
1570 //
1571 // variables: object holding kinematic (and other) information needed to compute the result
1572 // indexSF: index to calibration object
1573 // indexEff: index to MC tag weight
1574 // unc: keyword indicating what uncertainties to evaluate (or whether eigenvector or
1575 // named variations are to be computed)
1576 // numVariation: variation index (in case of eigenvector or named variations)
1577 // result: (value, uncertainty) or (up, down) variation pair, depending on the unc value.
1578 // A dummy value will be returned in case of an error.
1579 CalibrationDataContainer* container = m_objects[indexSF];
1580 if (! container) return Analysis::kError;
1581 CalibrationDataContainer* effContainer = m_objects[indexEff];
1582 if (! effContainer) return Analysis::kError;
1583
1584 // the first time this combination of scale factor and "efficiency" objects is given, check on the
1585 // scale factors that will result from their combination (where the computations reproduce those
1586 // shown below)
1587 checkWeightScaleFactors(indexSF, indexEff);
1588
1589 // perform out-of-bound check of jet eta
1590 if (!checkAbsEta(variables, indexSF)) {
1591 if (m_verbose)
1592 cerr << "Jet |eta| is outside of the boundary!" << endl;
1593 return Analysis::kRange;
1594 }
1595
1596 // Always retrieve the result itself
1597 double value;
1598 Analysis::CalibrationStatus status = container->getResult(variables, value);
1599 if (status == Analysis::kError) return status;
1600 if (m_otherStrategy == GiveUp) assert (status != Analysis::kRange);
1601 else if (m_otherStrategy == GiveUpExtrapolated) assert (status != Analysis::kExtrapolatedRange);
1602 else if (m_otherStrategy == Flag) {
1603 if (status == Analysis::kRange)
1604 increaseCounter(indexSF);
1605 else if (status == Analysis::kExtrapolatedRange)
1606 increaseCounter(indexSF, Extrapolated);
1607 }
1608
1609 // Retrieve the reference MC tag weight fraction (corresponding to the calibration scale factors)
1610 Analysis::UncertaintyResult refMCResult(0,0);
1611 if (container->getUncertainty("MCreference", variables, refMCResult) == Analysis::kError)
1612 return Analysis::kError;
1613 double fracMCref = refMCResult.first;
1614 // Retrieve the MC reference information, if requested (the initialisation below is to make sure
1615 // that no exceptions in the code will be needed)
1616 double fracSFref = fracMCref, fracEffref = fracMCref;
1617 if (m_useMCMCSF) {
1618 int indexSFref = m_hadronisationReference[indexSF], indexEffref = m_hadronisationReference[indexEff];
1619 if (indexSFref < 0 || indexEffref < 0) {
1620 cerr << "getWeightScaleFactor: error: generator-specific corrections requested but necessary reference containers lacking " << endl;
1621 return Analysis::kError;
1622 } else {
1623 m_objects[indexSFref]->getResult(variables, fracSFref);
1624 m_objects[indexEffref]->getResult(variables, fracEffref);
1625 if (! (fracSFref > 0. && fracEffref > 0.)) {
1626 cerr << "getWeightScaleFactor: error: invalid reference tag weight fraction " <<fracSFref <<" " <<fracEffref << std::endl;
1627 return Analysis::kError;
1628 }
1629 }
1630 }
1631
1632 // Retrieve the MC tag weight fraction for the sample we need to reweight to
1633 double fracMCnew;
1634 Analysis::CalibrationStatus effStatus = effContainer->getResult(variables, fracMCnew);
1635 if (effStatus == Analysis::kError) return effStatus;
1636 if (m_otherStrategy == GiveUp) assert (effStatus != Analysis::kRange);
1637 else if (m_otherStrategy == Flag)
1638 if (effStatus == Analysis::kRange) increaseCounter(indexEff);
1639 // since we need to divide by this quantity, check that it is well-defined
1640 if (!(fracMCnew > 0.) and m_useTopologyRescaling) {// but we only care if using topology rescaling
1641 cerr << "getWeightScaleFactor: error: null fracMCnew would lead to invalid operation" << endl;
1642 return Analysis::kError;
1643 }
1644
1645 if (!m_runEigenVectorMethod && (unc == SFEigen || unc == SFNamed)) {
1646 cerr << "getWeightScaleFactor: ERROR. Trying to call eigenvector method but initialization not switched on in b-tagging .env config file." << endl;
1647 cerr << " Please correct your .env config file first. Nominal uncertainties used. " << endl;
1648 }
1649
1650 if (unc == SFEigen || unc == SFNamed) {
1651 std::shared_ptr<CalibrationDataEigenVariations> eigenVariation;
1652 try {
1653 eigenVariation = m_eigenVariationsMap.at(container);
1654 } catch (const std::out_of_range&) {
1655 cerr << "getWeightScaleFactor: could not retrieve eigenvector variation, while it should have been there." << endl;
1656 return Analysis::kError;
1657 }
1658 unsigned int maxVariations = (unc == SFEigen) ? eigenVariation->getNumberOfEigenVariations() : eigenVariation->getNumberOfNamedVariations();
1659 if (numVariation > maxVariations-1) {
1660 cerr << "getWeightScaleFactor: asked for " << ((unc == SFEigen) ? "eigenvariation" : "named variation") << " number: " << numVariation << " but overall number of available variations is: " << maxVariations << endl;
1661 return Analysis::kError;
1662 }
1663 TH1* up=0;
1664 TH1* down=0;
1665 bool isOK = (unc == SFEigen) ? eigenVariation->getEigenvectorVariation(numVariation,up,down) : eigenVariation->getNamedVariation(numVariation,up,down);
1666 if (!isOK) {
1667 cerr << "getWeightScaleFactor: Eigenvector object is there but cannot retrieve up and down uncertainty histograms." << endl;
1668 return Analysis::kError;
1669 }
1670 // the 'extrapolation' uncertainty (always a named one) needs a somewhat special treatment
1671 bool extrapolate = ( unc == SFNamed ) ? eigenVariation->isExtrapolationVariation(numVariation) : false;
1672
1673 double valueUp;
1674 double valueDown;
1675 Analysis::CalibrationStatus statusUp = container->getResult(variables, valueUp, up, extrapolate);
1676 Analysis::CalibrationStatus statusDown = container->getResult(variables, valueDown,down, extrapolate);
1677 if (statusUp == Analysis::kError || statusDown == Analysis::kError) return Analysis::kError;
1678
1679 // now carry out the rescaling. Protect against unphysical or suspiciously large scale factors
1680 double variationUp = valueUp - value;
1681 double variationDown = valueDown - value;
1682 // First step: from the calibration sample to its reference sample
1683 if (m_useTopologyRescaling) value = 1.0 + (value - 1.0) * (fracMCref / fracSFref);
1684 // Second step: from the calibration reference sample to the MC object's reference sample
1685 if (m_useMCMCSF) value *= (fracSFref / fracEffref);
1686 // Third step: from the MC object's reference sample to the MC sample itself
1687 if (m_useTopologyRescaling) value = 1.0 + (value - 1.0) * (fracEffref / fracMCnew);
1688 // Since all transformations of the scale factor itself are linear, the transformation of the variations is simpler.
1690 double f = (fracMCref / fracMCnew);
1691 variationUp *= f;
1692 variationDown *= f;
1693 } else if (m_useMCMCSF) {
1694 double f = (fracSFref / fracEffref);
1695 variationUp *= f;
1696 variationDown *= f;
1697 }
1698 valueUp = value + variationUp;
1699 valueDown = value + variationDown;
1700 if (valueUp < 0) {
1701 valueUp = 0; increaseCounter(indexSF, TagWeight);
1702 } else if (valueUp > m_maxTagWeight) {
1703 valueUp = m_maxTagWeight; increaseCounter(indexSF, TagWeight);
1704 }
1705 if (valueDown < 0) {
1706 valueDown = 0; increaseCounter(indexSF, TagWeight);
1707 } else if (valueDown > m_maxTagWeight) {
1708 valueDown = m_maxTagWeight; increaseCounter(indexSF, TagWeight);
1709 }
1710
1711 result.first = valueUp;
1712 result.second = valueDown;
1713 return statusUp;
1714 } //end eigenvector method
1715
1716 //Proceed with no-eigenvector result
1717
1718 // retrieve the statistical uncertainty if desired
1719 double stat(0);
1720 if (unc == Total || unc == Statistical) {
1721 if (container->getStatUncertainty(variables, stat) == Analysis::kError) {
1722 cerr << "getWeightScaleFactor: error retrieving Scale factor parameter covariance matrix!" << endl;
1723 return Analysis::kError;
1724 }
1725 }
1726 Analysis::UncertaintyResult uncertaintyResult(0,0);
1727 if (unc == Total || unc == Systematic) {
1728 if (container->getSystUncertainty(variables, uncertaintyResult) == Analysis::kError) {
1729 cerr << "getWeightScaleFactor: error retrieving Scale factor parameter systematic uncertainty!" << endl;
1730 return Analysis::kError;
1731 }
1732 } else if (unc == Extrapolation) {
1733 // this uncertainty is special, since it is not normally to be combined into the overall systematic uncertainty
1734 if (container->getUncertainty("extrapolation", variables, uncertaintyResult) == Analysis::kError)
1735 cerr << "getWeightScaleFactor: error retrieving Scale factor parameter extrapolation uncertainty!" << endl;
1736 } else if (unc == TauExtrapolation) {
1737 // also this uncertainty is special, since it it singles out an uncertainty relevant only for tau "jets",
1738 // and some care has to be taken not to duplicate or omit uncertainties
1739 if (container->getUncertainty("extrapolation from charm", variables, uncertaintyResult) == Analysis::kError)
1740 cerr << "getWeightScaleFactor: error retrieving Scale factor parameter extrapolation uncertainty!" << endl;
1741 }
1742
1743 double uncertainty = combinedUncertainty(stat, uncertaintyResult);
1744
1745 // Now carry out the rescaling. Again protect against unphysical or suspiciously large scale factors
1746 // First step: from the calibration sample to its reference sample
1747 if (m_useTopologyRescaling) value = 1.0 + (value - 1.0) * (fracMCref / fracSFref);
1748 // Second step: from the calibration reference sample to the MC object's reference sample
1749 if (m_useMCMCSF) value *= (fracSFref / fracEffref);
1750 // Third step: from the MC object's reference sample to the MC sample itself
1751 if (m_useTopologyRescaling) value = 1.0 + (value - 1.0) * (fracEffref / fracMCnew);
1752 if (value < 0) {
1753 value = 0; increaseCounter(indexSF, TagWeight);
1754 } else if (value > m_maxTagWeight) {
1755 value = m_maxTagWeight; increaseCounter(indexSF, TagWeight);
1756 }
1757 // Since all transformations of the scale factor itself are linear, the transformation of the uncertainty is simpler.
1759 uncertainty *= (fracMCref / fracMCnew);
1760 } else if (m_useMCMCSF) {
1761 uncertainty *= (fracSFref / fracEffref);
1762 }
1763
1764 result.first = std::max(0., value);
1765 result.second = uncertainty;
1766 // "Select" the status code for the actual calibration object (it is subject to more constraints)
1767 return status;
1768}
1769
1770//________________________________________________________________________________
1771void
1773 unsigned int indexEff)
1774{
1775 // Check the tag weight scale factors that would result from the combination of
1776 // the provided scale factor and MC tag weight objects.
1777 // The way this is done is by determining the binning that would apply to the
1778 // combination of the two individual inputs, and then by explicitly computing
1779 // the scale factors in each of these resulting bins.
1780
1781 std::vector<std::pair<unsigned int, unsigned int> >::const_iterator it = std::find(m_checkedWeightScaleFactors.begin(), m_checkedWeightScaleFactors.end(), std::make_pair(indexSF, indexEff));
1782 if (it != m_checkedWeightScaleFactors.end()) return;
1783
1784
1785 // Assume that only histogram containers are involved here (this should be the case
1786 // as at least a strict tag weight binning should be applied).
1788 if (! container) {
1789 cerr << "CalibrationDataInterfaceROOT::checkWeightScaleFactors: error: container for object " << nameFromIndex(indexSF) << " not found!" << endl;
1790 return;
1791 } else if (! container->GetValue("MCreference")) {
1792 cerr << "CalibrationDataInterfaceROOT::checkWeightScaleFactors: error: no MCreference histogram for object " << nameFromIndex(indexSF) << "!" << endl;
1793 return;
1794 }
1795 CalibrationDataHistogramContainer* effContainer = dynamic_cast<CalibrationDataHistogramContainer*>(m_objects[indexEff]);
1796 if (! effContainer) {
1797 cerr << "CalibrationDataInterfaceROOT::checkWeightScaleFactors: error: container for object " << nameFromIndex(indexEff) << " not found!" << endl;
1798 return;
1799 }
1800
1801 // Retrieve the variable types and corresponding bin boundaries
1802 std::vector<unsigned int> vars = container->getVariableTypes();
1803 std::vector<unsigned int> effVars = effContainer->getVariableTypes();
1804 // Retrieve the corresponding bin boundaries
1805 std::map<unsigned int, std::vector<double> > boundaries, effBoundaries, mergedBoundaries;
1806 for (unsigned int t = 0; t < vars.size(); ++t)
1807 boundaries[vars[t]] = container->getBinBoundaries(vars[t]);
1808 for (unsigned int t = 0; t < effVars.size(); ++t)
1809 effBoundaries[effVars[t]] = effContainer->getBinBoundaries(effVars[t]);
1810
1811 // Special case: handle |eta| versus eta differences, by transforming to the latter
1812 if (boundaries.find(CalibrationDataContainer::kEta) == boundaries.end() && boundaries.find(CalibrationDataContainer::kAbsEta) != boundaries.end()) {
1814 boundaries.erase(CalibrationDataContainer::kAbsEta);
1815 }
1816 if (effBoundaries.find(CalibrationDataContainer::kEta) == effBoundaries.end() && effBoundaries.find(CalibrationDataContainer::kAbsEta) != effBoundaries.end()) {
1818 effBoundaries.erase(CalibrationDataContainer::kAbsEta);
1819 }
1820 if (boundaries.find(CalibrationDataContainer::kEta) != boundaries.end() && effBoundaries.find(CalibrationDataContainer::kEta) != effBoundaries.end()) {
1821 std::vector<double>& v = boundaries[CalibrationDataContainer::kEta];
1822 std::vector<double>& vEff = effBoundaries[CalibrationDataContainer::kEta];
1823 if (v[0] < 0 && vEff[0] >= 0) {
1824 // in this case, supplement the positive entries in vEff with their negative analogues
1825 std::vector<double> vtmp(vEff);
1826 for (std::vector<double>::iterator it = vtmp.begin(); it != vtmp.end(); ++it)
1827 if (*it > 0) vEff.insert(vEff.begin(), -(*it));
1828 } else if (v[0] >= 0 && vEff[0] < 0) {
1829 // in this case, supplement the positive entries in v with their negative analogues
1830 std::vector<double> vtmp(v);
1831 for (std::vector<double>::iterator it = vtmp.begin(); it != vtmp.end(); ++it)
1832 if (*it > 0) v.insert(v.begin(), -(*it));
1833 }
1834 }
1835
1836 // Now that the individual sets of boundaries have been determined, merge these
1837 for (unsigned int t = 0; t < vars.size(); ++t) {
1838 if (effBoundaries.find(vars[t]) == effBoundaries.end())
1839 // Variables not present in the efficiency object can go in unmodified
1840 mergedBoundaries[vars[t]] = boundaries[vars[t]];
1841 else {
1842 // Merge the boundaries for variables existing in both objects.
1843 // Take the MC array as a starting point, as it's likely to be the longest.
1844 mergedBoundaries[vars[t]] = effBoundaries[vars[t]];
1845
1846 for (std::vector<double>::iterator it = boundaries[vars[t]].begin(); it != boundaries[vars[t]].end(); ++it) {
1847 std::vector<double>::iterator itcmp = mergedBoundaries[vars[t]].begin();
1848 // Iterate until we've found a value in the target array equal to
1849 // or larger than the given element
1850 while (itcmp != mergedBoundaries[vars[t]].end() &&
1851 (! CalibrationDataContainer::isNearlyEqual(*itcmp, *it)) &&
1852 *itcmp < *it) ++itcmp;
1853 // Nothing needs to be done if the values are "nearly identical"
1854 // (or if we don't find such an element).
1855 if (itcmp == mergedBoundaries[vars[t]].end() || CalibrationDataContainer::isNearlyEqual(*itcmp, *it)) continue;
1856 // Otherwise insert the given element (this can mean adding to the end)
1857 mergedBoundaries[vars[t]].insert(itcmp, *it);
1858 }
1859 }
1860 }
1861 // Variables not present in the scale factor object still need to go in
1862 for (unsigned int t = 0; t < effVars.size(); ++t)
1863 if (boundaries.find(effVars[t]) == boundaries.end())
1864 mergedBoundaries[effVars[t]] = effBoundaries[effVars[t]];
1865
1866 // Carry out a rudimentary cross-check of the tag weight bin
1867 // (the binning used for the scale factor and MC objects should be identical).
1868 if (boundaries.find(CalibrationDataContainer::kTagWeight) == boundaries.end()) {
1869 cerr << "CalibrationDataInterfaceROOT::checkWeightScaleFactors: " << "no tag weight axis found for object " << nameFromIndex(indexSF) << endl;
1870 } else if (effBoundaries.find(CalibrationDataContainer::kTagWeight) == effBoundaries.end()) {
1871 cerr << "CalibrationDataInterfaceROOT::checkWeightScaleFactors: " << "no tag weight axis found for object " << nameFromIndex(indexEff) << endl;
1872 } else if (boundaries[CalibrationDataContainer::kTagWeight].size() != effBoundaries[CalibrationDataContainer::kTagWeight].size()) {
1873 cerr << "CalibrationDataInterfaceROOT::checkWeightScaleFactors: " << "different tag weight binning for objects " << nameFromIndex(indexSF) << " (";
1874 std::vector<double>& v = boundaries[CalibrationDataContainer::kTagWeight];
1875 for (unsigned int ib = 0; ib < v.size()-1; ++ib) cerr << v[ib] << ",";
1876 cerr << v[v.size()-1] << ") and " << nameFromIndex(indexEff) << " (";
1877 v = effBoundaries[CalibrationDataContainer::kTagWeight];
1878 for (unsigned int ib = 0; ib < v.size()-1; ++ib) cerr << v[ib] << ",";
1879 cerr << v[v.size()-1] << ") do not match!" << endl;
1880 } else {
1881 // Make sure that (possibly) dummy vectors exist for _all_ known variables
1882 // (this is a mere technicality allowing to loop over all variables explicitly).
1883 mergedBoundaries.try_emplace(CalibrationDataContainer::kPt, std::vector<double>{20.,300.});
1884 mergedBoundaries.try_emplace(CalibrationDataContainer::kEta, std::vector<double>{-2.5, 2.5});
1885
1886 // Finally, carry out the cross-check that all this is about: recompute the scale factor
1887 // in each pseudo-bin
1888 if (m_verbose){
1889 cout << "CalibrationDataInterfaceROOT::checkWeightScaleFactors: cross-checking scale factors for objects " << nameFromIndex(indexSF) << " and " << nameFromIndex(indexEff) << "\n" << std::setfill('-') << std::setw(100) << "-" << endl;
1890 cout << std::setfill(' ');
1891 }
1893 std::vector<double>& vPt = mergedBoundaries[CalibrationDataContainer::kPt], vEta = mergedBoundaries[CalibrationDataContainer::kEta], vTagWeight = mergedBoundaries[CalibrationDataContainer::kTagWeight];
1894 const std::string mcRefStr{"MCreference"};
1895 for (unsigned int ipt = 0; ipt < vPt.size()-1; ++ipt) {
1896 x.jetPt = (vPt[ipt] + vPt[ipt+1]) * 500.; // account for MeV -> GeV conversion
1897 for (unsigned int ieta = 0; ieta < vEta.size()-1; ++ieta) {
1898 x.jetEta = (vEta[ieta] + vEta[ieta+1]) / 2.;
1899 for (unsigned int iwt = 0; iwt < vTagWeight.size()-1; ++iwt) {
1900 x.jetTagWeight = (vTagWeight[iwt] + vTagWeight[iwt+1]) / 2.;
1901 // Retrieve the central scale factor value and the old and new MC tag weight fractions
1902 double value;
1903 container->getResult(x, value);
1904 Analysis::UncertaintyResult uncertaintyResult(0,0);
1905 container->getUncertainty(mcRefStr, x, uncertaintyResult);
1906 double fracMCref = uncertaintyResult.first;
1907 double fracMCnew;
1908 effContainer->getResult(x, fracMCnew);
1909 // Compute the new scale factor value
1910 if (!(fracMCnew > 0.)) {
1911 cout << "\tfor (pt=" << x.jetPt << ",eta=" << x.jetEta << ",tagweight=" << x.jetTagWeight << "): invalid new MC fraction: " << fracMCnew << endl;
1912 } else {
1913 double newvalue = 1.0 + (value - 1.0) * fracMCref/fracMCnew;
1914 if (newvalue <= 0 || newvalue > m_maxTagWeight) cout << "\tfor (pt=" << x.jetPt << ",eta=" << x.jetEta << ",tagweight=" << x.jetTagWeight << "): old (value=" << value << ",MC=" << fracMCref << "), new (value=" << newvalue << ",MC=" << fracMCnew << ")" << endl;
1915 }
1916 }
1917 }
1918 }
1919 }
1920
1921 m_checkedWeightScaleFactors.push_back(std::make_pair(indexSF, indexEff));
1922}
1923
1924//________________________________________________________________________________
1925bool
1927 unsigned int index)
1928{
1929 // Check whether the jet eta value is outside the range of validity, subject to the strategy
1930 // specified in the configuration file.
1931 bool pass = true;
1932 if (m_absEtaStrategy == Ignore) return pass;
1933
1934 switch (m_absEtaStrategy) {
1935 case GiveUp:
1936 if (std::fabs(variables.jetEta) > m_maxAbsEta) {
1937 pass = false;
1938 }
1939 break;
1940 case Flag:
1941 default:
1942 if (std::fabs(variables.jetEta) > m_maxAbsEta) {
1944 }
1945
1946 }
1947 return pass;
1948}
1949
1950//________________________________________________________________________________
1951std::string
1953{
1954 // Return the object name corresponding to the given index.
1955
1956 for (std::map<std::string, unsigned int>::const_iterator it = m_objectIndices.begin();
1957 it != m_objectIndices.end(); ++it)
1958 if (it->second == index) return it->first;
1959
1960 // This should never happen..
1961 return string("");
1962}
1963
1964//________________________________________________________________________________
1965void
1967 OutOfBoundsType oob)
1968{
1969 // Internal method bumping the relevant counter out-of-bounds counter for the specified object.
1970 //
1971 // oob: further classification of out-of-bounds case
1972 // index: object index
1973
1974 // make sure the vectors are appropriately dimensioned
1975 if (index >= m_mainCounters.size()) {
1976 unsigned int minsize = (index == 0) ? 2 : 2*index;
1977 m_mainCounters.resize(minsize, 0);
1978 m_etaCounters.resize(minsize, 0);
1979 m_extrapolatedCounters.resize(minsize, 0);
1980 }
1981 switch (oob) {
1982 case Main:
1983 m_mainCounters[index]++; break;
1984 case Eta:
1985 m_etaCounters[index]++; break;
1986 case Extrapolated:
1987 default:
1989 }
1990}
1991
1992//________________________________________________________________________________
1993std::vector<string>
1995 const string& label,
1996 const string& OP,
1997 bool named)
1998{
1999 // Retrieve the sources of uncertainty relevant for the given scale factor calibration object,
2000 // identifying the object by name.
2001 //
2002 // author: jet collection name
2003 // label: jet flavour label
2004 // OP: tagger working point
2005 // named: if false, an unsorted list of sources of uncertainties will be returned.
2006 // if true, only 'named' uncertainties will be returned, and the position in
2007 // the vector that is the return value determines the 'numVariation' index
2008 // that is to be used if named variations are to be retrieved.
2009
2010 unsigned int index;
2011 if (! retrieveCalibrationIndex (label, OP, author, true, index)) {
2012 // Return a dummy result if the object is not found
2013 cerr << "listScaleFactorUncertainties: unable to find SF calibration for object " << fullName(author, OP, label, true) << endl;
2014 std::vector<string> dummy;
2015 return dummy;
2016 }
2018}
2019
2020//________________________________________________________________________________
2021std::vector<string>
2023 const std::string& flavour, bool named)
2024{
2025 // Note: this method already works on a per-flavour basis, so passing flavour in is a simple addition
2026 // Method is called primarily from within the BTaggingEfficiencyTool - W.L.
2027 // Retrieve the sources of uncertainty relevant for the given scale factor calibration object,
2028 // identifying the object by index.
2029 //
2030 // index: index to scale factor calibration object
2031 // named: if false, an unsorted list of sources of uncertainties will be returned.
2032 // if true, only 'named' uncertainties will be returned, and the position in
2033 // the vector that is the return value determines the 'numVariation' index
2034 // that is to be used if named variations are to be retrieved.
2035
2036 std::vector<string> dummy;
2038
2039 if (container) {
2040 if (named) {
2041 // Find out which uncertainties are excluded from eigenvector construction
2042 if (! m_runEigenVectorMethod) return dummy;
2043 std::shared_ptr<CalibrationDataEigenVariations> eigenVariation=m_eigenVariationsMap.at(container);
2044 if (m_EVStrategy == Analysis::Uncertainty::SFEigen){
2045 std::vector<string> unordered = eigenVariation->listNamedVariations(); // this is for the regular EV
2046 std::vector<string> ordered(unordered.size());
2047 for (unsigned int i = 0; i < unordered.size(); ++i) {
2048 ordered[eigenVariation->getNamedVariationIndex(unordered[i])] = unordered[i];
2049 }
2050 return ordered;
2051 } else if (m_EVStrategy == Analysis::Uncertainty::SFGlobalEigen){
2052 // here we want to get the named uncertainties from the global eigenvariations flavour container specifically...
2053 std::shared_ptr<CalibrationDataGlobalEigenVariations> GEV = std::dynamic_pointer_cast<CalibrationDataGlobalEigenVariations>(eigenVariation);
2054 std::vector<std::string> unordered = GEV->listNamedVariations(flavour);
2055 std::vector<std::string> ordered(unordered.size()); // ordered by the NAMED VARIATION (internal) ORDERING
2056 for (unsigned int i = 0; i < unordered.size(); ++i) {
2057 ordered[GEV->getNamedVariationIndex(unordered[i], flavour)] = unordered[i];
2058 }
2059 return ordered;
2060 }
2061 }
2062 return container->listUncertainties(); // return this if not named
2063 }
2064
2065 return dummy;
2066}
2067
2068//________________________________________________________________________________
2069unsigned int
2071 const std::string& label,
2072 const std::string& OP,
2073 Uncertainty unc)
2074{
2075 // Retrieve the number of eigenvector variations or named variations relevant for
2076 // the given scale factor calibration object, identifying the object by name.
2077 //
2078 // author: jet collection name
2079 // label: jet flavour label
2080 // OP: tagger working point
2081 // unc: should be set to SFEigen or SFNamed for the cases of
2082 // eigenvector variations or named variations, respectively
2083
2084 unsigned int index;
2085
2086 if (! retrieveCalibrationIndex (label, OP, author, true, index)) return 0;
2087 return getNumVariations(index, unc, label);
2088}
2089
2090//________________________________________________________________________________
2091unsigned int
2093 Uncertainty unc, const std::string& flavour)
2094{
2095 // Retrieve the number of eigenvector variations or named variations relevant for
2096 // the given scale factor calibration object, identifying the object by index.
2097 //
2098 // index: index to calibration scale factor object
2099 // unc: should be set to SFEigen or SFNamed for the cases of
2100 // eigenvector variations or named variations, respectively
2101
2102 if (! (unc == SFEigen || unc == SFNamed || unc == SFGlobalEigen)) return 0;
2104 if (! container) return 0;
2105 std::shared_ptr<CalibrationDataEigenVariations> eigenVariation=m_eigenVariationsMap.at(container);
2106 if (unc == SFGlobalEigen){
2107 std::shared_ptr<CalibrationDataGlobalEigenVariations> GEV = std::dynamic_pointer_cast<CalibrationDataGlobalEigenVariations>(eigenVariation);
2108 return GEV->getNumberOfEigenVariations(flavour);
2109 }
2110 return (unc == SFEigen) ? eigenVariation->getNumberOfEigenVariations() : eigenVariation->getNumberOfNamedVariations();
2111}
2112
2113//________________________________________________________________________________
2114const TH1*
2116 const std::string& label,
2117 const std::string& OP)
2118{
2119 // Retrieve the actual histogrammed calibration scale factors, identifying the object by name.
2120 //
2121 // author: jet collection name
2122 // label: jet flavour label
2123 // OP: tagger working point
2124
2125 unsigned int index;
2126 if (! retrieveCalibrationIndex (label, OP, author, true, index)) {
2127 // Return a dummy result if the object is not found
2128 cerr << "getBinnedScaleFactors: unable to find SF calibration for object " << fullName(author, OP, label, true) << endl;
2129 return 0;
2130 }
2132 return (container) ? dynamic_cast<TH1*>(container->GetValue("result")) : 0;
2133}
2134
2135//________________________________________________________________________________
2136const TObject*
2138 const std::string& label,
2139 const std::string& OP,
2140 unsigned int mapIndex)
2141{
2142 // Retrieve the actual central values object for the MC efficiences, identifying the object by name.
2143 // The object returned can be either a TH1 or a TF1; it is up to the user to determine which.
2144 //
2145 // author: jet collection name
2146 // label: jet flavour label
2147 // OP: tagger working point
2148 // mapIndex: index to the efficiency map to be used
2149
2150 unsigned int index;
2151 if (! retrieveCalibrationIndex (label, OP, author, false, index, mapIndex)) {
2152 // Return a dummy result if the object is not found
2153 cerr << "getMCEfficiencyObject: unable to find efficiency calibration for object "
2154 << fullName(author, OP, label, false, mapIndex) << endl;
2155 return 0;
2156 }
2158 return (container) ? container->GetValue("result") : 0;
2159}
2160
2161//====================== retrieval of shifted calibration object ===========================
2162
2163//________________________________________________________________________________
2164const TH1*
2166 const std::string& label,
2167 const std::string& OP,
2168 const std::string& unc,
2169 double sigmas)
2170{
2171 // Retrieve the actual histogrammed calibration scale factors, identifying the object by name
2172 // and with the scale factors shifted by the uncertainties due to the given source of uncertainty
2173 // (where bin-to-bin correlations are accounted for, i.e., shifts may be either positive or negative).
2174 //
2175 // author: jet collection name
2176 // label: jet flavour label
2177 // OP: tagger working point
2178 // unc: source of uncertainty to consider
2179 // sigmas: number of standard deviations by which to shift the scale factor central values
2180
2181 // quick sanity check
2182 if (unc == "comment" || unc == "result" || unc == "combined" || unc == "statistics") return 0;
2183
2184 unsigned int index;
2185 if (! retrieveCalibrationIndex (label, OP, author, true, index)) {
2186 // Return a null result if the object is not found
2187 cerr << "getShiftedScaleFactors: unable to find SF calibration for object " << fullName(author, OP, label, true) << endl;
2188 return nullptr;
2189 }
2191 if (! container) return nullptr;
2192
2193 TH1* result = dynamic_cast<TH1*>(container->GetValue("result"));
2194 TH1* hunc = dynamic_cast<TH1*>(container->GetValue(unc.c_str()));
2195 // another sanity check...
2196 if ((! hunc) || (! result)) return nullptr;
2197 if (hunc->GetDimension() != result->GetDimension() || hunc->GetNbinsX() != result->GetNbinsX() ||
2198 hunc->GetNbinsX() != result->GetNbinsX() || hunc->GetNbinsX() != result->GetNbinsX())
2199 return nullptr;
2200 // also check that the uncertainty is to be treated as correlated from bin to bin
2201 // (for the variation is applied coherently, which isn't appropriate for uncertainties
2202 // that aren't correlated from bin to bin)
2203 if (! container->isBinCorrelated(unc)) return 0;
2204
2205 // if everything is consistent, the actual operation simply consists of adding histograms...
2206 std::string name(container->GetName()); name += "_"; name += unc; name += "_";
2207 TH1* shifted = dynamic_cast<TH1*>(result->Clone(name.c_str()));
2208 if (not shifted) return nullptr;
2209 shifted->Add(hunc, sigmas);
2210 return shifted;
2211}
2212//====================== run EigenVectorRecomposition method ===========================
2215 const std::string& label,
2216 const std::string& OP,
2217 unsigned int mapIndex){
2218 // run eigen vector recomposition method. If success, stored the retrieved coefficient map
2219 // in m_coefficientMap and return success. Otherwise return error and keep m_coefficientMap
2220 // untouched.
2221 // author: jet collection name
2222 // label: jet flavour label
2223 // OP: tagger working point
2224 // mapIndex: index to the MC efficiency map to be used. Should be 0?
2225 // Todo: What is mapindex?
2226 // Todo: Check the way xAODBTaggingTool initialize CDI. Check if that is the as how we are initialize CDI.
2228 cerr << "runEigenVectorRecomposition: Recomposition need to be ran with CalibrationDataInterfaceRoot initialized in eigenvector mode" << endl;
2229 return Analysis::kError;
2230 }
2231
2232 unsigned int indexSF;
2233 if (! retrieveCalibrationIndex (label, OP, author, true, indexSF, mapIndex)) {
2234 cerr << "runEigenVectorRecomposition: unable to find SF calibration for object "
2235 << fullName(author, OP, label, true) << endl;
2236 return Analysis::kError;
2237 }
2238
2239 return runEigenVectorRecomposition (label, indexSF);
2240}
2241
2244 unsigned int indexSF){
2245 // run eigen vector recomposition method. If success, stored the retrieved coefficient map
2246 // in m_coefficientMap and return success. Otherwise return error and keep m_coefficientMap
2247 // untouched.
2248 // label: jet flavour label
2249 // indexSF: index to scale factor calibration object
2250 CalibrationDataContainer* container = m_objects[indexSF];
2251 if (! container) {
2252 cerr << "runEigenVectorRecomposition: error retrieving container!" << endl;
2253 return Analysis::kError;
2254 }
2255
2256 // Retrieve eigenvariation
2257 std::shared_ptr<CalibrationDataEigenVariations> eigenVariation;
2258 try {
2259 eigenVariation = m_eigenVariationsMap.at(container);
2260 } catch (const std::out_of_range&) {
2261 cerr << "runEigenVectorRecomposition: Could not retrieve eigenvector variation, while it should have been there." << endl;
2262 return Analysis::kError;
2263 }
2264 // Doing eigenvector recomposition
2265 std::map<std::string, std::map<std::string, float>> coefficientMap;
2266 if(!eigenVariation->EigenVectorRecomposition(label, coefficientMap))
2267 return Analysis::kError;
2268
2269 m_coefficientMap = std::move(coefficientMap);
2270 return Analysis::kSuccess;
2271}
2272
2273std::map<std::string, std::map<std::string, float>>
2275 if(m_coefficientMap.empty())
2276 cerr << "getCoefficientMap: Call runEigenVectorRecomposition() before retrieving coefficient map! " <<endl;
2277 return m_coefficientMap;
2278}
2279
2280
2281//====================== put some utility functions here ===================================
2282
2283namespace {
2284 // Construct the (diagonal) covariance matrix for the statistical uncertainties on the "ref" results
2285 TMatrixDSym getStatCovarianceMatrix(const TH1* hist) {
2286 Int_t nbinx = hist->GetNbinsX()+2, nbiny = hist->GetNbinsY()+2, nbinz = hist->GetNbinsZ()+2;
2287 Int_t rows = nbinx;
2288 if (hist->GetDimension() > 1) rows *= nbiny;
2289 if (hist->GetDimension() > 2) rows *= nbinz;
2290 TMatrixDSym stat(rows);
2291 for (Int_t binx = 1; binx < nbinx; ++binx){
2292 for (Int_t biny = 1; biny < nbiny; ++biny){
2293 for (Int_t binz = 1; binz < nbinz; ++binz) {
2294 Int_t bin = hist->GetBin(binx, biny, binz);
2295 double err = hist->GetBinError(bin);
2296 stat(bin, bin) = err*err;
2297 }
2298 }
2299 }
2300 return stat;
2301 }
2302
2303 // Construct the covariance matrix assuming that histogram "unc" contains systematic uncertainties
2304 // pertaining to the "ref" results, and that the uncertainties are fully correlated from bin to bin
2305 // (unless option "doCorrelated" is false, in which bins are assumed uncorrelated)
2306 TMatrixDSym getSystCovarianceMatrix(const TH1* ref, const TH1* unc, bool doCorrelated, const std::string& uncname, int tagWeightAxis) {
2307 Int_t nbinx = ref->GetNbinsX()+2, nbiny = ref->GetNbinsY()+2, nbinz = ref->GetNbinsZ()+2;
2308 Int_t rows = nbinx;
2309 if (ref->GetDimension() > 1) rows *= nbiny;
2310 if (ref->GetDimension() > 2) rows *= nbinz;
2311 TMatrixDSym cov(rows);
2312
2313 for(int i=0 ; i<10 ; i++){
2314 Int_t bin = unc->GetBin(1,i,1);
2315 double uncval = unc->GetBinContent(bin);
2316 cout << uncval << ", ";
2317 } cout << endl;
2318
2319 // Carry out a minimal consistency check
2320 if (unc->GetNbinsX()+2 != nbinx || unc->GetNbinsY()+2 != nbiny || unc->GetNbinsZ()+2 != nbinz || unc->GetDimension() != ref->GetDimension()) {
2321 std::cout << "getSystCovarianceMatrix: inconsistency found in histograms " << ref->GetName() << " and " << unc->GetName() << " : " << uncname << std::endl;
2322 return cov;
2323 }
2324
2325 // // the "2" below doesn't actually imply that two bins are used...
2326 // // this is just to make the loops below work
2327 // if (ref->GetDimension() <= 1) nbiny = 2;
2328 // if (ref->GetDimension() <= 2) nbinz = 2;
2329
2330 // Special case: uncertainties not correlated from bin to bin.
2331 // The exception here is for tag weight bins, which are always assumed to be fully correlated.
2332 if (! doCorrelated) {
2333 if (tagWeightAxis < 0) {
2334 // truly uncorrelated uncertainties
2335 for (Int_t binx = 1; binx < nbinx-1; ++binx){
2336 for (Int_t biny = 1; biny < nbiny-1; ++biny){
2337 for (Int_t binz = 1; binz < nbinz-1; ++binz) {
2338 Int_t bin = ref->GetBin(binx, biny, binz);
2339 double err = unc->GetBinContent(bin);
2340 cov(bin,bin) = err*err;
2341 }
2342 }
2343 }
2344 return cov;
2345 } else if (tagWeightAxis == 0) {
2346 // continuous histogram with tag weight X axis
2347 for (Int_t biny = 1; biny < nbiny-1; ++biny){
2348 for (Int_t binz = 1; binz < nbinz-1; ++binz){
2349 for (Int_t binx = 1; binx < nbinx-1; ++binx) {
2350 Int_t bin = ref->GetBin(binx, biny, binz);
2351 double err = unc->GetBinContent(bin);
2352 for (Int_t binx2 = 1; binx2 < nbinx-1; ++binx2) {
2353 Int_t bin2 = ref->GetBin(binx2, biny, binz);
2354 double err2 = unc->GetBinContent(bin2);
2355 cov(bin,bin2) = err*err2;
2356 }
2357 }
2358 }
2359 }
2360 return cov;
2361 } else if (tagWeightAxis == 1) {
2362 // continuous histogram with tag weight Y axis
2363 for (Int_t binx = 1; binx < nbinx-1; ++binx){
2364 for (Int_t binz = 1; binz < nbinz-1; ++binz){
2365 for (Int_t biny = 1; biny < nbiny-1; ++biny) {
2366 Int_t bin = ref->GetBin(binx, biny, binz);
2367 double err = unc->GetBinContent(bin);
2368 for (Int_t biny2 = 1; biny2 < nbiny-1; ++biny2) {
2369 Int_t bin2 = ref->GetBin(binx, biny2, binz);
2370 double err2 = unc->GetBinContent(bin2);
2371 cov(bin,bin2) = err*err2;
2372 }
2373 }
2374 }
2375 }
2376 return cov;
2377 } else if (tagWeightAxis == 2) {
2378 // continuous histogram with tag weight Z axis
2379 for (Int_t binx = 1; binx < nbinx-1; ++binx){
2380 for (Int_t biny = 1; biny < nbiny-1; ++biny){
2381 for (Int_t binz = 1; binz < nbinz-1; ++binz) {
2382 Int_t bin = ref->GetBin(binx, biny, binz);
2383 double err = unc->GetBinContent(bin);
2384 for (Int_t binz2 = 1; binz2 < nbinz-1; ++binz2) {
2385 Int_t bin2 = ref->GetBin(binx, biny, binz2);
2386 double err2 = unc->GetBinContent(bin2);
2387 cov(bin,bin2) = err*err2;
2388 }
2389 }
2390 }
2391 }
2392 return cov;
2393 }
2394 }
2395
2396 for (Int_t binx = 1; binx < nbinx-1; ++binx){
2397 for (Int_t biny = 1; biny < nbiny-1; ++biny){
2398 for (Int_t binz = 1; binz < nbinz-1; ++binz) {
2399 Int_t bin = ref->GetBin(binx, biny, binz);
2400 double err = unc->GetBinContent(bin); // <------------- For every bin in the "ref" ("result") TH1*, GetBinContents of the corresponding uncertainty bin
2401 for (Int_t binx2 = 1; binx2 < nbinx-1; ++binx2){
2402 for (Int_t biny2 = 1; biny2 < nbiny-1; ++biny2){
2403 for (Int_t binz2 = 1; binz2 < nbinz-1; ++binz2) {
2404 Int_t bin2 = ref->GetBin(binx2, biny2, binz2);
2405 double err2 = unc->GetBinContent(bin2); // <------- Grab the unc contents of every bin, and compute the covariance matrix element
2406 cov(bin, bin2) = err*err2; // <------- err1 and err2 are the uncertainty content of the bins, so "cov" is real, symmetric
2407 } // <------- "cov" would imply that the "hunc" histogram stores "x - E[x]" differences from the mean. So in the end, it computes the covariance (as a sum of these)
2408 }
2409 }
2410 }
2411 }
2412 }
2413 return cov;
2414 }
2415
2416}
2417
2418//====================== retrieval of calibration covariance matrix ========================
2419
2420//________________________________________________________________________________
2421TMatrixDSym
2423 const std::string& label,
2424 const std::string& OP,
2425 const std::string& unc)
2426{
2427 // Return the scale factor covariance matrix for the given calibration object.
2428 // This function is deprecated since its functionality is duplicated in the
2429 // CalibrationDataEigenVariations class.
2430 //
2431 // author: jet collection name
2432 // label: jet flavour label
2433 // OP: tagger working point
2434 // unc: source of uncertainty to consider
2435 // Catch issues with the specified input as early as possible
2436 TMatrixDSym dummy;
2437 if (unc == "comment" || unc == "result" || unc == "combined") return dummy;
2438
2439 unsigned int index;
2440 if (! retrieveCalibrationIndex (label, OP, author, true, index)) {
2441 // Return a dummy result if the object is not found
2442 cerr << "getScaleFactorCovarianceMatrix: unable to find SF calibration for object " << fullName(author, OP, label, true) << endl;
2443 return dummy;
2444 }
2446 if (!container) return dummy;
2447
2448 // retrieve the central calibration and its axes
2449 TH1* result = dynamic_cast<TH1*>(container->GetValue("result"));
2450 if (! result) return dummy;
2451 // "normal" case: single source of uncertainty
2452 if (unc != "all") {
2453 if (unc == "statistics") {
2454 return getStatCovarianceMatrix(result);
2455 } else {
2456 TH1* hunc = dynamic_cast<TH1*>(container->GetValue(unc.c_str()));
2457 if (! hunc) {
2458 cout << "getScaleFactorCovarianceMatrix: no uncertainty object found "
2459 << "corresponding to name " << unc << endl;
2460 return dummy;
2461 }
2462 return getSystCovarianceMatrix(result, hunc, container->isBinCorrelated(unc), unc, container->getTagWeightAxis());
2463 }
2464 }
2465
2466 // special case: complete covariance matrix. This is to be constructed
2467 // as the sum over all individual contributions.
2468 // First, treat the statistics separately (as above)
2469 TMatrixDSym cov = getStatCovarianceMatrix(result);
2470
2471 // Then loop through the list of (other) uncertainties
2472 std::vector<string> uncs = container->listUncertainties();
2473 for (unsigned int t = 0; t < uncs.size(); ++t) {
2474 if (uncs[t] == "comment" || uncs[t] == "result" || uncs[t] == "combined" ||
2475 uncs[t] == "statistics" || uncs[t]=="extrapolation" || uncs[t]=="MChadronisation" ||
2476 uncs[t]=="ReducedSets" || uncs[t]=="systematics") continue;
2477 TH1* hunc = dynamic_cast<TH1*>(container->GetValue(uncs[t].c_str()));
2478 if (not hunc) {
2479 std::cerr<<"Analysis::CalibrationDataInterfaceROOT::getScaleFactorCovarianceMatrix : dynamic cast failed\n";
2480 continue;
2481 }
2482 TMatrixDSym syst_cov = getSystCovarianceMatrix(result, hunc, container->isBinCorrelated(uncs[t]), uncs[t], container->getTagWeightAxis());
2483 cov += syst_cov;
2484 }
2485
2486 return cov;
2487}
2488
2489//________________________________________________________________________________
2490void
2491Analysis::CalibrationDataInterfaceROOT::initialize(const string& jetauthor, const string& OP, Uncertainty unc)
2492{
2493 // Preload objects necessary so that the input calibration file can be closed.
2494 // This functionality is only needed when using PROOF.
2495
2496 if((!m_fileEff)||(!m_fileSF)) {
2497 cerr << "initialize can only be called once per CalibrationDataInterfaceROOT object" << endl;
2498 return;
2499 } else {
2500 cout << "initializing BTagCalibrationDataInterfaceROOT for PROOF with jetAuthor = " << jetauthor << ", tagger = " << m_taggerName << ", operating point = " << OP << ", uncertainty = " << unc << endl;
2501 }
2502
2503 CalibrationDataVariables BTagVars;
2504 BTagVars.jetAuthor = jetauthor;
2505 BTagVars.jetPt = 100000.; //Irrelevant, just has to be valid to retrieve objects
2506 BTagVars.jetEta = 1.5; //Irrelevant, just has to be valid to retrieve objects
2507
2508 for(const auto& flavour : m_flavours){
2509 std::pair<double, double> BTagCalibResult;
2510 BTagCalibResult = getScaleFactor(BTagVars, flavour, OP, unc);
2511 std::cout << "CalibrationDataInterfaceROOT->initialize : BTagCalibResult " << std::endl;
2512
2513 std::pair<double, double> BTagCalibMCEff;
2514 BTagCalibMCEff = getMCEfficiency(BTagVars, flavour, OP, unc);
2515 std::cout << "CalibrationDataInterfaceROOT->initialize : BTagCalibMCEff " << std::endl;
2516 }
2517
2518 if (m_fileEff != m_fileSF) {
2519 m_fileEff->Close();
2520 delete m_fileEff;
2521 }
2522 m_fileSF->Close();
2523 delete m_fileSF;
2524 m_fileEff = 0; //prevents repeat deletion in destructor
2525 m_fileSF = 0; //prevents repeat deletion in destructor
2526}
2527
2528//________________________________________________________________________________
2530Analysis::CalibrationDataInterfaceROOT::retrieveContainer(const string& label, const string& OP, const string& author, const string& cntname, bool isSF, bool doPrint)
2531{
2532 // Attempt to retrieve the given container from file. Note that also the corresponding
2533 // "hadronisation" reference is retrieved (if possible and not yet done).
2534 //
2535 // dir: name of the directory containing the requested container
2536 // cntname: name of the requested container itself (not including the full path)
2537 // isSF: set to false (true) if the object is to be retrieved from the MC efficiencies
2538 // file (the calibration scale factor file). Note that it is assumed that scale
2539 // factor objects will always be retrieved from the calibration scale factor file.
2540 // doPrint: if true, print out some basic information about the successfully retrieved container
2541 // (note that this is typically steered by the m_verbose setting;
2542 // only for the retrieval of the maps used for MC/MC SF calculations, this printout is always switched off)
2543
2544 string dir = m_taggerName + "/" + getAlias(author) + "/" + OP + "/" + label;
2545 // construct the full object name
2546 string name = dir + "/" + cntname;
2547
2548 // If the object cannot be found, then each call will result in a new attempt to
2549 // retrieve the object from the ROOT file. Hopefully this will not happen too often...
2550 unsigned int idx = m_objectIndices[name] = m_objects.size();
2551 // CalibrationDataContainer* cnt =
2552 // dynamic_cast<CalibrationDataContainer*>((isSF ? m_fileSF : m_fileEff) ->Get(name.c_str()));
2554 (isSF ? m_fileSF : m_fileEff)->GetObject(name.c_str(), cnt);
2555 // If the requested object is a MC efficiency container and is not found, make a second attempt
2556 // to retrieve it from the calibration scale factor file. This will avoid the need to duplicate
2557 // efficiency containers so that the MC efficiency file needs to store only those containers
2558 // not already present in the calibration scale factor file. Of course this is meaningful only
2559 // if separate files are used to begin with.
2560 if (!isSF && !cnt && m_fileSF != m_fileEff) m_fileSF->GetObject(name.c_str(), cnt);
2561 m_objects.push_back(cnt);
2562 if (!cnt) {
2563 cerr << "btag Calib: retrieveContainer: failed to retrieve container named " << name << " from file" << endl;
2564 return 0;
2565 }
2566
2567 // For successfully retrieved containers, also print some more information (implemented on user request)
2568 if (doPrint) {
2569 cout << "CalibrationDataInterface: retrieved container " << name << " (with comment: '" << cnt->getComment() << "' and hadronisation setting '" << cnt->getHadronisation() << "')" << endl;
2570 }
2571
2572
2573 // If the requested object is a MC efficiency container, make sure to retrieve the corresponding
2574 // calibration scale factor container first (a feature first thought to be necessary, erroneously,
2575 // but left in since this ordering should not hurt in any case).
2576 if (m_refMap.find(dir) == m_refMap.end()) {
2577 if (isSF) {
2578 // Retrieve the mapping objects from both files and merge their information using the 'helper' class.
2579 // The map resulting from this is used to retrieve the information required to compute MC/MC scale factors.
2580 string hadronisationRefs(dir + "/MChadronisation_ref");
2581 TMap* mapSF = 0; m_fileSF->GetObject(hadronisationRefs.c_str(), mapSF);
2582 TMap* mapEff = 0; if (m_fileEff != m_fileSF) m_fileEff->GetObject(hadronisationRefs.c_str(), mapEff);
2583 m_refMap[dir] = new HadronisationReferenceHelper(mapSF, mapEff);
2584 delete mapSF;
2585 delete mapEff;
2586 } else {
2587 string SFCalibName = getContainername(getBasename(dir), true);
2588 if (m_objectIndices.find(SFCalibName) == m_objectIndices.end()) retrieveContainer(label, OP, author, SFCalibName, true, doPrint);
2589 }
2590 }
2591
2592 // Attempt to find the corresponding hadronisation reference container needed for the application of
2593 // MC/MC scale factors.
2594 if (idx+1 > m_hadronisationReference.size()) m_hadronisationReference.resize(idx+1, -1);
2595 m_hadronisationReference[idx] = -1;
2596 string spec = cnt->getHadronisation();
2597 if (spec != "") {
2598 std::map<string, HadronisationReferenceHelper*>::const_iterator mapit = m_refMap.find(dir);
2599 if (mapit != m_refMap.end()) {
2600 string ref;
2601 if (mapit->second->getReference(spec, ref)) {
2602 // Retrieve the hadronisation reference if not already done. Note that the "isSF" is left unchanged:
2603 // this allows to retrieve the reference from the same file as the scale factor object. An exception
2604 // is the reference for the calibration scale factor object, which should always be obtained from
2605 // the scale factor file.
2606 // An efficiency container can be its own hadronisation reference (this is not "protected" against).
2607 string refname(dir + "/" + ref);
2608 std::map<string, unsigned int>::const_iterator it = m_objectIndices.find(refname);
2609 // If the reference cannot be found, assume that it hasn't yet been retrieved so attempt it now.
2610 if (it == m_objectIndices.end()) {
2611 // Omit the printout of container information here (the idea being that showing MC/MC SF information would confuse rather than help)
2612 retrieveContainer(label, OP, author, ref, isSF, false);
2613 it = m_objectIndices.find(refname);
2614 }
2615 if (it != m_objectIndices.end()) {
2616 m_hadronisationReference[idx] = it->second;
2617 }
2618 }
2619 } else if (m_useMCMCSF) {
2620 cerr << "btag Calib: retrieveContainer: MC hadronisation reference map not found -- this should not happen!" << endl;
2621 }
2622 }
2624 // Not being able to construct the MC/MC scale factors will lead to a potential bias.
2625 // However, this is not considered sufficiently severe that we will flag it as an error.
2626 if (m_useMCMCSF){
2627 cerr << "btag Calib: retrieveContainer: warning: unable to apply MC/MC scale factors for container " << name << " with hadronisation reference = '" << spec << "'" << endl;
2628 }
2629 }
2630
2631 // Initialize the Eigenvector variation object corresponding to this object, if applicable. Notes:
2632 // - the dual use of "isSF" (both referring to the file and to the object, see above) requires another protection here
2633 // - the constructor's second argument is used to determine whether to exclude a pre-determined set of uncertainties from the EV decomposition
2634 //
2635 // We also want to separate behavior between SFEigen and SFGlobalEigen systematic strategies
2636 // The former requires a CalibrationDataEigenVariations object to be made per flavour.
2637 // The latter combines all corresponding flavours, so once it's been made for a single flavour, it's cached under all the corresponding "flavour containers"
2638 // simulataneously in m_eigenVariationsMap, and is checked for on each subsequent call to this method.
2639 if (m_runEigenVectorMethod && isSF && name.find("_SF") != string::npos) {
2640 CalibrationDataHistogramContainer* histoContainer=dynamic_cast<CalibrationDataHistogramContainer*>(cnt);
2641 if (histoContainer==0) {
2642 cerr << "Could not cast Container to a HistogramContainer. " << endl;
2643 return 0;
2644 }
2645 if (m_EVStrategy == Analysis::Uncertainty::SFEigen){
2647 std::shared_ptr<CalibrationDataEigenVariations> newEigenVariation(new CalibrationDataEigenVariations(m_filenameSF, m_taggerName, OP, author, histoContainer, m_useRecommendedEVExclusions));
2648 newEigenVariation->setVerbose(m_verbose);
2649
2650 // At this point we may also want to reduce the number of eigenvector variations.
2651 // The choices are stored with the container object; but first we need to know what flavour we are dealing with.
2652 string flavour = dir.substr(dir.find_last_of("/")+1);
2653
2654 for (const auto & entry : m_excludeFromCovMatrix[flavour]) {
2655 newEigenVariation->excludeNamedUncertainty(entry, cnt);
2656 }
2657 newEigenVariation->initialize();
2658 int to_retain = histoContainer->getEigenvectorReduction(m_EVReductions[flavour]); // returns the number of eigenvariations to retain as per the EV reduction strategy
2659 if (to_retain > -1) {
2660 if (m_verbose) cout << "btag Calib: reducing number of eigenvector variations for flavour " << flavour << " to " << to_retain << endl;
2661 // The merged variations will end up as the first entry in the specified list, i.e., as the last of the variations to be "retained"
2662 newEigenVariation->mergeVariationsFrom(size_t(to_retain-1)); // All variations stored with indices larger than this are merged
2663 } else if (m_EVReductions[flavour] != Loose) {
2664 cerr << "btag Calib: unable to retrieve eigenvector reduction information for flavour " << flavour << " and scheme " << m_EVReductions[flavour] << "; not applying any reduction" << endl;
2665 }
2666 m_eigenVariationsMap[cnt]=std::move(newEigenVariation);
2667
2669 } else if (m_EVStrategy == Analysis::Uncertainty::SFGlobalEigen) {
2671 std::map<const CalibrationDataContainer*, std::shared_ptr<CalibrationDataEigenVariations> >::iterator evit = m_eigenVariationsMap.find(cnt);
2672 // The global implementation internally combines all the "flavour containers" (containers that correspond to each other, only with different flavours)
2673 // But the CalibrationDataInterfaceROOT object doesn't need to know that, so we want to get all the flavour containers in one go here
2674 // and map them (with m_eigenVariationsMap) to the same CalibrationDataGlobalEigenVariations pointer.
2675 // Then, in methods like getScaleFactor, we call the virtual methods which will give the proper result e.g. if you want the SF for a b-jet, it'll call the
2676
2677 if (evit == m_eigenVariationsMap.end()){
2678 // now to see if it's completely empty or not
2679 if (m_eigenVariationsMap.empty()){
2680 std::shared_ptr<CalibrationDataGlobalEigenVariations> newEigenVariation(new CalibrationDataGlobalEigenVariations(m_filenameSF, m_taggerName, OP, author, m_flavours, histoContainer, m_useRecommendedEVExclusions));
2681 for (const auto & entry : m_excludeFromCovMatrix[label]) {
2682 newEigenVariation->excludeNamedUncertainty(entry, label); // <---- custom exclude named uncertainties method for global variations
2683 }
2684
2685 newEigenVariation->initialize();
2686
2687 // flavour loop to get the flavour reduction schemes and apply them
2688 for (std::string& flavour : m_flavours){
2689 int to_retain = histoContainer->getEigenvectorReduction(m_EVReductions[flavour]); // returns the number of eigenvariations to retain as per the EV reduction strategy
2690 if (to_retain > -1) {
2691 if (m_verbose) cout << "btag Calib: reducing number of eigenvector variations for flavour " << flavour << " to " << to_retain << endl;
2692 // The merged variations will end up as the first entry in the specified list, i.e., as the last of the variations to be "retained"
2693 newEigenVariation->mergeVariationsFrom(size_t(to_retain-1), flavour); // All variations stored with indices larger than this are merged
2694 } else if (m_EVReductions[flavour] != Loose) {
2695 cerr << "btag Calib: unable to retrieve eigenvector reduction information for flavour " << flavour << " and scheme " << m_EVReductions[flavour] << "; not applying any reduction" << endl;
2696 }
2697 }
2698
2699 m_eigenVariationsMap.insert({cnt, newEigenVariation});
2700 } else {
2701 // Need to point to the CDGEV object four times in the m_eigenVariationsMap to appease the CDIROOT backend design...
2702 // Ok, turns out I can't retrieve the containers from CDGEV and insert them directly, because I'd have to use the containers directly instead..
2703 // So the strategy is to just take the CGEV objects that are already in the map, and mpa the present container to it
2704 std::shared_ptr<CalibrationDataEigenVariations> previous_eigenvariation = m_eigenVariationsMap.begin()->second;
2705 m_eigenVariationsMap.insert({cnt, previous_eigenvariation});
2706 }
2707
2708 } else {
2709 std::cout << "CalibrationDataInterfaceROOT->retrieveContainer : the CDGEV object for " << name << " already exists! " << std::endl;
2710 }
2712 }
2713 }
2714
2715 return cnt;
2716}
2717
2718//________________________________________________________________________________
2719string
2721{
2722 // Return the alias for the given jet collection name, if an alias exists.
2723 // If this is not the case, the return value will simply equal the input jet collection name.
2724
2725 std::map<string,string>::const_iterator it = m_aliases.find(author);
2726 return (it == m_aliases.end()) ? author : it->second;
2727}
2728
2729//________________________________________________________________________________
2730string
2731Analysis::CalibrationDataInterfaceROOT::fullName(const string& author, const string& OP,
2732 const string& label, bool isSF,
2733 unsigned mapIndex) const
2734{
2735 // Construct the full calibration object's pathname within the calibration ROOT file.
2736 //
2737 // author: jet collection name
2738 // OP: tagger working point
2739 // label: jet flavour label
2740 // isSF: set to true (false) for scale factors (MC efficiencies)
2741 // mapIndex: index in the list of MC efficiency calibration objects
2742
2743 string flavour = (label == "N/A") ? "Light" : label;
2744 string full(m_taggerName + "/" + getAlias(author) + "/" + OP + "/" + flavour + "/");
2745 full += getContainername(flavour, isSF, mapIndex);
2746 // full += getAlias(author); full += "/";
2747 // string name = (isSF) ?
2748 // getBasename(OP, label, "_SF", true) :
2749 // getBasename(OP, label, "_Eff", false, mapIndex);
2750 // full += name;
2751 return full;
2752}
2753
2754//________________________________________________________________________________
2756{
2757 // Create the map from hadronisation specifications to reference container names for
2758 // a given ROOT file directory.
2759 //
2760 // mapSF: reference specification as extracted from calibration scale factor file
2761 // mapEff: reference specification as extracted from MC efficiency file
2762 // (null if the two files are identical)
2763
2764 // First take the scale factor file's map
2765 if (mapSF) {
2766 TMapIter next(mapSF); TObjString* spec;
2767 while ((spec = (TObjString*) next())) {
2768 TObjString* ref = (TObjString*) mapSF->GetValue(spec);
2769 m_refs[string(spec->GetName())] = string(ref->GetName());
2770 }
2771 }
2772 // Then do the same with the efficiency file's map. The result will be to override any
2773 // items from the SF file's map. An exception is made for the scale factor calibration object,
2774 // for which (for the sake of consistency) the SF reference must be retained.
2775 if (mapEff) {
2776 TMapIter next(mapEff); TObjString* spec;
2777 while ((spec = (TObjString*) next())) {
2778 TObjString* ref = (TObjString*) mapEff->GetValue(spec);
2779 m_refs[string(spec->GetName())] = string(ref->GetName());
2780 }
2781 }
2782}
2783
2784//________________________________________________________________________________
2785bool
2787 string& ref) const
2788{
2789 // Extract the reference histogram name corresponding to the given hadronisation specification (if existing).
2790 // The return value is used to indicate whether the specification could be found.
2791 //
2792 // spec: hadronisation specification
2793 // ref: container name corresponding to this specification
2794
2795 std::map<string, string>::const_iterator it = m_refs.find(spec);
2796 if (it == m_refs.end()) return false;
2797
2798 ref = it->second;
2799 return true;
2800}
const std::regex ref(r_ef)
static const std::string hadronisationRefs("MChadronisation_ref")
std::string_view trimWhiteSpaces(std::string_view str) noexcept
Removes all trailing and starting whitespaces from a string.
ClassImp(Analysis::CalibrationDataInterfaceROOT) Analysis
std::vector< std::string > split(const std::string &str, const char token=';')
local utility function: split string into a vector of substrings separated by a specified separator,...
#define GEV
size_t size() const
Number of registered mappings.
#define x
This is the interface for the objects to be stored in the calibration ROOT file.
static bool isNearlyEqual(double a, double b)
utility for comparison of doubles
std::vector< unsigned int > getVariableTypes()
utility to retrieve variable types
virtual CalibrationStatus getResult(const CalibrationDataVariables &x, double &result, TObject *obj=0, bool extrapolate=false)=0
retrieve the calibration result.
This is the class holding information for histogram-based calibration results.
virtual int getEigenvectorReduction(unsigned int choice) const
Retrieve the number of eigenvectors to be retained for the purpose of eigenvector variation reduction...
virtual std::vector< double > getBinBoundaries(unsigned int vartype)
Retrieve the bin boundaries for the specified variable type (which should be a CalibrationParametriza...
virtual CalibrationStatus getResult(const CalibrationDataVariables &x, double &result, TObject *obj=0, bool extrapolate=false)
retrieve the calibration result.
std::string m_taggerName
tagging algorithm name
void setEffCalibrationNames(const std::map< std::string, std::vector< std::string > > &names)
std::string getContainername(const std::string &flavour, bool SF, unsigned int mapIndex=0) const
auxiliary function for retrieval of container name
void setSFCalibrationNames(const std::map< std::string, std::string > &names)
std::string getBasename(const std::string &name) const
auxiliary function for retrieval of name within the directory
double combinedUncertainty(double stat, const std::pair< double, double > &syst) const
utility function for combination of statistical and (a priori asymmetric) systematic uncertainty.
bool getReference(const std::string &spec, std::string &ref) const
Retrieve the (full) name of the reference histogram, given the hadronisation specification.
std::map< std::string, std::string > m_refs
map from hadronisation specification to container name
This tool provides an interface to flavour tagging performance estimates.
bool m_verbose
if true, allow also for some informational (and not only error/warning) messages
std::vector< std::pair< unsigned int, unsigned int > > m_checkedWeightScaleFactors
bool m_useTopologyRescaling
specify whether or not to use MC/MC (topology) scale factors (also this steering option may be remove...
CalibResult getWeightScaleFactor(const CalibrationDataVariables &variables, const std::string &label, Uncertainty unc, unsigned int numVariation=0, unsigned int mapIndex=0)
efficiency scale factor retrieval by name
std::map< std::string, unsigned int > m_objectIndices
std::map< std::string, std::string > m_aliases
Do not attempt to persistify (PROOF).
TFile * m_fileSF
Do not attempt to persistify (PROOF).
void increaseCounter(unsigned int index, OutOfBoundsType oob=Main)
bool m_useRecommendedEVExclusions
if true, exclude pre-recommended lists of uncertainties from the covariance matrix building,...
std::map< std::string, HadronisationReferenceHelper * > m_refMap
the following maps (one for each directory) specify the name of the container serving as the 'hadroni...
TFile * m_fileEff
pointer to the TFile object providing access to the calibrations
CalibResult getInefficiencyScaleFactor(const CalibrationDataVariables &variables, const std::string &label, const std::string &OP, Uncertainty unc, unsigned int numVariation=0, unsigned int mapIndex=0)
"MC" inefficiency scale factor retrieval by name
std::string nameFromIndex(unsigned int index) const
Retrieve the name of the calibration object (container) given its index.
bool retrieveCalibrationIndex(const std::string &label, const std::string &OP, const std::string &author, bool isSF, unsigned int &index, unsigned int mapIndex=0)
Retrieve the index of the calibration object (container) starting from the label and operating point.
std::string fullName(const std::string &author, const std::string &OP, const std::string &label, bool isSF, unsigned mapIndex=0) const
@ brief construct the full object pathname from its individual components
const TH1 * getBinnedScaleFactors(const std::string &author, const std::string &label, const std::string &OP)
retrieve the binned calibration object for the given flavour label and operating point.
unsigned int getNumVariations(const std::string &author, const std::string &label, const std::string &OP, Uncertainty unc)
retrieve the number of variations relevant to the calibration object.
CalibrationDataContainer * retrieveContainer(const std::string &label, const std::string &OP, const std::string &author, const std::string &cntname, bool isSF, bool doPrint=true)
utility function taking care of object retrieval
bool m_runEigenVectorMethod
decide whether to run the eigenvector method or not
CalibrationDataInterfaceROOT()
default constructor for PROOF object retrieval
std::map< std::string, std::map< std::string, float > > m_coefficientMap
CalibrationStatus runEigenVectorRecomposition(const std::string &author, const std::string &label, const std::string &OP, unsigned int mapindex=0)
run EigenVector Recomposition method
CalibResult getInefficiency(const CalibrationDataVariables &variables, const std::string &label, const std::string &OP, Uncertainty unc, unsigned int numVariation=0, unsigned int mapIndex=0)
inefficiency retrieval by name
std::map< std::string, Analysis::EVReductionStrategy > m_EVReductions
Eigenvector reduction strategy (per flavour).
const TObject * getMCEfficiencyObject(const std::string &author, const std::string &label, const std::string &OP, unsigned int mapIndex=0)
retrieve the MC efficiency (central values) object for the given flavour label and operating point.
bool m_useMCMCSF
specify whether or not to use MC/MC (hadronisation) scale factors (the fact that this is steerable is...
std::vector< std::string > listScaleFactorUncertainties(const std::string &author, const std::string &label, const std::string &OP, bool named=false)
retrieve the list of "uncertainties" relevant to the calibration object.
CalibResult getScaleFactor(const CalibrationDataVariables &variables, const std::string &label, const std::string &OP, Uncertainty unc, unsigned int numVariation=0, unsigned int mapIndex=0)
efficiency scale factor retrieval by name.
std::map< std::string, std::map< std::string, float > > getEigenVectorRecompositionCoefficientMap()
Get Eigenvector recomposition map after running runEigenVectorRecomposition().
std::vector< int > m_hadronisationReference
store the 'hadronisation' reference for each object (-1 means no reference found)
TMatrixDSym getScaleFactorCovarianceMatrix(const std::string &author, const std::string &label, const std::string &OP, const std::string &unc="all")
retrieve the named covariance matrix element corresponding to the binned calibration object.
CalibResult getMCInefficiency(const CalibrationDataVariables &variables, const std::string &label, const std::string &OP, Uncertainty unc=None, unsigned int mapIndex=0)
"MC" inefficiency retrieval by name
const TH1 * getShiftedScaleFactors(const std::string &author, const std::string &label, const std::string &OP, const std::string &unc, double sigmas)
retrieve the binned calibration object for the given flavour label and operating point,...
CalibResult getMCEfficiency(const CalibrationDataVariables &variables, const std::string &label, const std::string &OP, Uncertainty unc=None, unsigned int mapIndex=0)
"MC" efficiency retrieval by name
std::string getAlias(const std::string &author) const
associated alias retrieval method
std::map< std::string, std::vector< std::string > > m_excludeFromCovMatrix
store the uncertainties which should be excluded from building the full covariance matrix
double getMCMCScaleFactor(const CalibrationDataVariables &variables, unsigned indexSF, unsigned int indexEff) const
MC/MC scale factor retrieval.
std::vector< CalibrationDataContainer * > m_objects
cache the objects themselves (so that the user will not have to delete them after each call etc....
double m_maxAbsEta
|eta| bounds and strategy for dealing with out-of-bounds conditions
CalibResult getEfficiency(const CalibrationDataVariables &variables, const std::string &label, const std::string &OP, Uncertainty unc, const std::string &flavour, unsigned int numVariation=0, unsigned int mapIndex=0)
efficiency retrieval by name
void initialize(const std::string &jetauthor, const std::string &OP, Uncertainty unc)
initialization for PROOF usage
std::vector< unsigned int > m_etaCounters
counters for flagging out-of-bound cases
std::map< const CalibrationDataContainer *, std::shared_ptr< CalibrationDataEigenVariations > > m_eigenVariationsMap
store the eigenvector class and associate to its CalibrationDataContainer
bool checkAbsEta(const CalibrationDataVariables &variables, unsigned int index)
void checkWeightScaleFactors(unsigned int indexSF, unsigned int indexEff)
std::string m_filenameSF
in addition, store also the filenames themselves (needed for the copy constructor)
This class (struct, actually) is nothing but a light-weight container of (kinematic or other) variabl...
bool verbose
Definition hcg.cxx:75
std::string label(const std::string &format, int i)
Definition label.h:19
std::vector< std::string > split(const std::string &str, const char token=';')
local utility function: split string into a vector of substrings separated by a specified separator,...
The namespace of all packages in PhysicsAnalysis/JetTagging.
OutOfBoundsType
counter types (to be used when flagging out-of-bounds cases)
const CalibResult dummyResult(dummyValue, dummyValue)
std::pair< double, double > CalibResult
std::pair< double, double > UncertaintyResult
The following typedef is for convenience: most uncertainties can be asymmetric.
Uncertainty
specification of type information requested by the user
std::string_view trimWhiteSpaces(std::string_view str) noexcept
Removes all trailing and starting whitespaces from a string.
Definition index.py:1
STL namespace.