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}) {
 
  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.");