6 #include "CaloGeoHelpers/CaloSampling.h"
18 #include <type_traits>
23 #define MSGSOURCE "EgEfficiencyCorr_dumpNPs"
29 std::string
str(
const bool abs_eta=
false)
const;
49 "AsgElectronEfficiencyCorrectionTool/tool"};
58 using map_t = std::map<NP, std::vector<std::size_t>>;
66 const map_t::mapped_type& affected_bins,
72 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f, 0.8f, 0.9f, 1.0f, 1.1f,
73 1.15f, 1.2f, 1.3f, 1.37f, 1.52f, 1.6f, 1.65f, 1.7f, 1.8f, 1.9f, 2.0f,
74 2.1f, 2.2f, 2.3f, 2.37f, 2.4f, 2.47f};
77 45e2f, 7e3f, 1e4f, 15e3f, 2e4f, 25e3f, 3e4f, 35e3f, 4e4f, 45e3f, 5e4f,
78 6e4f, 8e4f, 15e4f, 25e4f, 5e5f, 1e7f};
87 using namespace asg::msgUserCode;
89 #ifdef XAOD_STANDALONE
90 StatusCode::enableFailure();
101 const bool duplicates_eta{
103 const bool duplicates_pt{
105 if (duplicates_eta || duplicates_pt) {
106 Error(
MSGSOURCE,
"Duplicated values in the specified bin edges.");
119 for (
const auto&
var : allsysts) {
120 std::string
name(
var.basename());
123 if (
name.find(
"UncorrUncertainty") != std::string::npos) {
125 }
else if (
name.find(
"CorrUncertainty") != std::string::npos) {
128 throw std::invalid_argument(
"");
130 np.index = std::stoul(
name.substr(
name.find(
"NP") + 2));
133 }
catch (std::invalid_argument&) {
134 Error(
MSGSOURCE,
"The pattern of the SystematicVariation names seems to have changed.");
137 std::sort(
cfg.systematics.begin(),
cfg.systematics.end(),
138 [](
auto&
x,
auto&
y) { return std::get<NP>(x) < std::get<NP>(y); });
140 if (
cfg.analysis_mode) {
141 for (
const int year : {2015, 2016, 2017, 2018}) {
146 Info(
MSGSOURCE,
"Relevant nuisance parameters for the year %i:",
year);
161 using namespace asg::msgUserCode;
164 Info(
MSGSOURCE,
"Usage (1): EgEfficiencyCorr_dumpNPs Year=<value> IdKey=<value> <property1=value1> ... <property N=value N>");
165 Info(
MSGSOURCE,
" ++ prints eta x pt region of influence of each nuisance parameter for the chosen year");
166 Info(
MSGSOURCE,
"Usage (2): EgEfficiencyCorr_dumpNPs AnalysisEta=<list> AnalysisPt=<list> IdKey=<value> <property1=value1> ... <property N=value N>");
167 Info(
MSGSOURCE,
" ++ prints a short list of all NPs relevant in the selected eta x pt (in MeV) region");
168 Info(
MSGSOURCE,
" ++ e.g. EgEfficiencyCorr_dumpNPs AnalysisEta=-2.0..2.0,nocrack AnalysisPt=20e3..150e3");
169 Info(
MSGSOURCE,
" ++ one of the Analysis* arguments can be omitted, in which case the full range is scanned");
170 Info(
MSGSOURCE,
" ++ several intervals can be specified, separated by commas, e.g. AnalysisEta=0..0.6,0.8..1.37");
171 Info(
MSGSOURCE,
" ++ recognized aliases: 'nocrack' (crack veto), 'sym' (duplicates for eta<0), 'barrel', 'endcap'");
173 Info(
MSGSOURCE,
" ++ for other SFs, IdKey can be replaced by RecoKey/IsoKey/TriggerKey");
174 Info(
MSGSOURCE,
" ++ CorrectionFileNameList can also be used instead of keys");
175 Info(
MSGSOURCE,
" ++ optional arguments are other configurable properties of the AsgElectronEfficiencyCorrectionTool.");
176 Info(
MSGSOURCE,
" ++ e.g. EgEfficiencyCorr_dumpNPs Year=2015 IdKey=Tight CorrelationModel=SIMPLIFIED UncorrEtaBinsUser=0,1.37,2.47");
182 const std::set<std::string> string_properties{
183 "MapFilePath",
"RecoKey",
"IdKey",
"IsoKey",
"TriggerKey",
185 const std::set<std::string> int_properties{
"ForceDataType"};
186 const std::set<std::string> vector_properties{
187 "UncorrEtaBinsUser",
"UncorrEtBinsUser"};
196 std::vector<int8_t> eta_flags(
eta_edges.size()-1, 0);
197 std::vector<int8_t> pt_flags(
pt_edges.size()-1, 0);
201 const std::size_t
pos{
key.find(
'=')};
202 if (
pos == std::string::npos)
help();
206 if (
key ==
"CorrectionFileNameList") {
207 std::vector<std::string>
files{
val};
209 }
else if (
key ==
"Year") {
211 }
else if (string_properties.count(
key)) {
213 }
else if (int_properties.count(
key)) {
215 }
else if (vector_properties.count(
key)) {
216 std::vector<float>
bins;
219 }
else if (
key ==
"AnalysisEta") {
221 analysis_mode =
true;
222 }
else if (
key ==
"AnalysisPt") {
224 analysis_mode =
true;
226 Error(
MSGSOURCE,
"Unrecognized property %s",
key.c_str());
234 "When Analysis* arguments are provided, "
238 Error(
MSGSOURCE,
"Command-line arguments must include Year");
243 for (std::size_t
i{0};
i<eta_flags.size(); ++
i) {
244 if (eta_flags[
i] != 2)
continue;
247 const auto j =
static_cast<std::size_t
>(
248 std::upper_bound(eeb, eee, -
z) - eeb - 1);
249 if (
i == j)
continue;
251 eta_flags[
i] = eta_flags[j];
255 auto efb{eta_flags.begin()}, efe{eta_flags.end()};
259 auto pfb{pt_flags.begin()}, pfe{pt_flags.end()};
265 for (std::size_t
i{0};
i<eta_flags.size(); ++
i) {
266 if (eta_flags[
i] < 1)
continue;
267 for (std::size_t j{0}; j<pt_flags.size(); ++j) {
268 if (pt_flags[j] < 1)
continue;
269 domains.emplace_back(
Domain{
281 using namespace asg::msgUserCode;
283 static int display_count{-1};
284 if (!++display_count) {
286 "Scanning the eta/pt plane, this will take a bit of time...");
292 const auto *asgtool =
dynamic_cast<RealTool*
>(&*
cfg.tool);
294 const std::size_t dmax{
cfg.domains.size()};
295 for (std::size_t
d{0};
d<dmax; ++
d) {
296 const auto& dom =
cfg.domains[
d];
297 std::bitset<1024> zero_uncorr{}, nonzero_uncorr{}, expected_uncorr{};
298 std::bitset<256> zero_corr{}, nonzero_corr{};
299 bool multi_np{
false};
300 for (std::size_t j{1}; j<jmax; ++j) {
301 const float eta{((jmax-j)*dom.etamin + j*dom.etamax) / jmax};
302 for (std::size_t
k{1};
k<kmax; ++
k) {
303 const float pt{((kmax-
k)*dom.ptmin +
k*dom.ptmax) / kmax};
308 double sf_nom{},
sf{};
311 int vi{asgtool->systUncorrVariationIndex(
electron)};
312 expected_uncorr.set(vi);
313 unsigned n_nonzero_uncorr{0};
314 for (
const auto&
s :
cfg.systematics) {
315 const auto&
sys = std::get<CP::SystematicSet>(
s);
318 const double delta{
sf - sf_nom};
319 const NP np{std::get<NP>(
s)};
322 nonzero_uncorr.set(
np.index);
325 else nonzero_corr.set(
np.index);
327 if (
np.uncorr) zero_uncorr.set(
np.index);
328 else zero_corr.set(
np.index);
331 multi_np = multi_np || (n_nonzero_uncorr > 1);
336 std::string
w(
"several uncorrelated NPs seem to affect the bin at "
339 }
else if (nonzero_uncorr.count() > 1) {
340 std::string
w{
"the binning used for the scan is unadapted at " +
341 dom.str() +
" (too coarse?)."};
344 if ((zero_uncorr & nonzero_uncorr).any() ||
345 (zero_corr & nonzero_corr).any()) {
346 std::string
w(
"the binning used for the scan is unadapted at " +
347 dom.str() +
" (wrong boundaries?).");
350 if ((nonzero_uncorr!=expected_uncorr) && nonzero_uncorr.any()) {
352 for (std::size_t
i{0};
i<nonzero_uncorr.size(); ++
i) {
354 if (nonzero_uncorr[
i] && !expected_uncorr[
i]) snz +=
x;
355 if (!nonzero_uncorr[
i] && expected_uncorr[
i])
se +=
x;
357 if (snz.length()) snz.pop_back();
358 if (
se.length())
se.pop_back();
359 std::string
w{
"systUncorrVariationIndex() at " + dom.str() +
360 " indicates different NP(s) than those found in the scan, "
364 for (
const auto&
s :
cfg.systematics) {
365 const NP np{std::get<NP>(
s)};
367 np.uncorr? nonzero_uncorr.test(
np.index)
368 : nonzero_corr.test(
np.index)};
369 if (impact) affected_bins[
np].emplace_back(
d);
378 using namespace asg::msgUserCode;
379 for (
const auto&
s :
cfg.systematics) {
380 const NP np{std::get<NP>(
s)};
383 bool valid{
false},
holes{
false}, abs_eta{
false};
384 auto itr = affected_bins.find(
np);
385 if (itr != affected_bins.end()) {
389 Warning(
MSGSOURCE,
"%s seems to not affect any bin",
name.c_str());
392 std::string txt(
name +
" --> ");
393 if (
holes) txt +=
"subdomain of ";
394 txt += bounds.str(abs_eta) +
'.';
407 using namespace asg::msgUserCode;
409 std::vector<std::pair<std::string, int>>
summary;
410 NP prev{-888,
false};
411 for (
const auto& kv : affected_bins) {
412 const auto&
bins{kv.second};
413 if (
bins.empty())
continue;
414 const NP np{kv.first};
415 const bool next{prev.uncorr==
np.uncorr &&
np.index==prev.index+1};
418 std::get<int>(
summary.back()) =
np.index;
422 std::get<CP::SystematicSet>(
424 cfg.systematics.cbegin(),
cfg.systematics.cend(),
425 [=](
auto&
x){ return std::get<NP>(x) == np; }))};
426 summary.emplace_back(
sys.begin()->basename(), -1);
429 std::string
s(
" ++ " + std::get<std::string>(
x));
430 const int i{std::get<int>(
x)};
439 const map_t::mapped_type& affected_bins,
444 if (affected_bins.empty())
return false;
446 constexpr
float inf_{std::numeric_limits<float>::lowest()};
448 auto update = [&](
const int side,
const auto& dom,
449 const float etamin,
const float etamax) {
457 for (
const int bin : affected_bins) {
459 if (dom.etamax > 0.f)
update(1, dom,
std::max(0.
f, dom.etamin), dom.etamax);
460 if (dom.etamin < 0.f)
update(0, dom, dom.etamin,
std::min(0.f, dom.etamax));
467 (std::fabs(bAC[0].etamax + bAC[1].
etamin) < 1
e-4
f) &&
468 (std::fabs(bAC[0].
etamin + bAC[1].etamax) < 1
e-4
f);
478 }
else if (ac) bounds = bAC[ac - 1];
483 const std::size_t
imax{
cfg.domains.size()};
484 auto f = [=](
const float x) {
return abs_eta? std::fabs(
x) :
x; };
486 if (
std::count(affected_bins.cbegin(), affected_bins.cend(),
i))
continue;
487 const auto& dom{
cfg.domains[
i]};
489 (dom.ptmin < bounds.
ptmax) &&
490 (
f(dom.etamax) > bounds.
etamin) &&
491 (
f(dom.etamin) < bounds.
etamax);
501 const char*
eta{abs_eta?
"|eta|" :
"eta"};
507 s <<
"pt > " << 1
e-3
f *
ptmin <<
" GeV";
521 std::vector<int8_t>&
flags,
522 const std::vector<float>& edges)
524 const bool accept_aliases{&edges == &
eta_edges};
525 if (
flags.size() != edges.size() - 1)
return false;
527 if (
s ==
"nocrack")
alias = 1;
528 else if (
s ==
"barrel")
alias = 2;
529 else if (
s ==
"endcap")
alias = 3;
530 else if (
s ==
"sym")
alias = 4;
531 if (!accept_aliases &&
alias>0)
return false;
533 for (std::size_t
i{0};
i<edges.size()-1; ++
i) {
534 const float eta{std::fabs(edges[
i])};
540 }
else if (
alias == 4) {
541 for (std::size_t
i=0;
i<edges.size();++
i) {
542 if (edges[
i] < 0)
flags[
i] = 2;
546 auto pos =
s.find(
"..");
547 if (
pos == std::string::npos)
return false;
548 std::stringstream
ss{
s.replace(
pos, 2, 1,
' ')};
551 if (
ss.fail() || !(
ss >> std::ws).eof())
return false;
552 for (std::size_t
i{0};
i<edges.size()-1; ++
i) {
561 template<
typename...
Args>
565 std::stringstream
ss{
val};
567 std::conditional_t<(
sizeof...(Args)<2),
float, std::string>
x;
569 if (
ss.fail())
break;
578 using namespace asg::msgUserCode;
579 constexpr
int random_runs[] = {280423, 302737, 332720, 354826};
581 case 2015:
case 2016:
case 2017:
case 2018:
582 return random_runs[
year - 2015];
585 "Invalid year specified, it should be between 2015 and 2018.");