ATLAS Offline Software
Loading...
Searching...
No Matches
MuonTriggerScaleFactors.cxx File Reference
#include "TRandom3.h"
#include "TROOT.h"
#include "TH1.h"
#include "TH2.h"
#include "TFile.h"
#include "TKey.h"
#include "xAODMuon/MuonContainer.h"
#include "MuonEfficiencyCorrections/MuonTriggerScaleFactors.h"
#include "AsgMessaging/StatusCode.h"
#include "PATInterfaces/SystematicRegistry.h"
#include "PATInterfaces/SystematicVariation.h"
#include "PathResolver/PathResolver.h"
#include "AsgDataHandles/ReadHandle.h"
#include <iostream>
#include <functional>
#include <sstream>
#include <string>
#include <cmath>

Go to the source code of this file.

Functions

namespace ADD_CMODE (CP)

Function Documentation

◆ ADD_CMODE()

namespace ADD_CMODE ( CP )

returns: the list of all systematics this tool can be affected by

returns: the list of all systematics this tool recommends to use

Definition at line 1 of file MuonTriggerScaleFactors.cxx.

27 {
28 constexpr double muon_barrel_endcap_boundary = 1.05;
29
30 struct RunYear {
31 unsigned int lastRun{};
32 unsigned int year{};
33 };
34
35 constexpr std::array runYears{
36 RunYear{284484, 2015},
37 RunYear{311481, 2016},
38 RunYear{340453, 2017},
39 RunYear{364292, 2018},
40 RunYear{440613, 2022},
41 RunYear{456749, 2023},
42 RunYear{486706, 2024},
43 RunYear{509849, 2025}
44 };
45
46 const std::map<unsigned short int, std::vector< std::tuple<unsigned int,unsigned int,std::string_view> > > MuonTriggerScaleFactors::m_year_periods = {
47 {2015, {
48 {266904,272531,"AC"},
49 {276073,276954,"D"},
50 {278727,279928,"E"},
51 {279932,280422,"F"},
52 {280423,281075,"G"},
53 {281130,281411,"H"},
54 {281662,282482,"I"},
55 {282625,284484,"J"}}
56 },
57 {2016, {
58 {296939,300287,"A"},
59 {300345,300908,"B"},
60 {301912,302393,"C"},
61 {302737,302872,"D1D3"},
62 {302919,303560,"D4D8"},
63 {303638,303892,"E"},
64 {303943,304494,"F"},
65 {305291,306714,"G"},
66 {307124,308084,"I"},
67 {309311,309759,"K"},
68 {310015,311481,"L"}}
69 },
70 {2017, {
71 {324320,325558,"A"},
72 {325713,328393,"B"},
73 {329385,330470,"C"},
74 {330857,332304,"D"},
75 {332720,334779,"E"},
76 {334842,335290,"F"},
77 {336497,336782,"H"},
78 {336832,337833,"I"},
79 {338183,340453,"K"}}
80 },
81 {2018, {
82 {348197,348836,"A"},
83 {348885,349533,"B"},
84 {349534,350220,"C"},
85 {350310,352107,"D"},
86 {352123,352137,"E"},
87 {352274,352514,"F"},
88 {354107,354494,"G"},
89 {354826,355224,"H"},
90 {355261,355273,"I"},
91 {355331,355468,"J"},
92 {355529,356259,"K"},
93 {357050,359171,"L"},
94 {359191,360414,"M"},
95 {361635,361696,"N"},
96 {361738,363400,"O"},
97 {363664,364292,"Q"}}
98 },
99 {2022, {
100 {430536,432180,"F"},
101 {435816,439927,"H"},
102 {440199,440613,"J"}}
103 },
104 {2023, {
105 {451094,455924,"F"},
106 {455975,456749,"G"}}
107 },
108 {2024, {
109 {473235,473400,"E"},
110 {473617,474271,"F"},
111 {474441,474602,"G"},
112 {474657,475522,"H"},
113 {476060,477048,"I"},
114 {479103,480032,"K"},
115 {480188,482221,"M"},
116 {482374,484799,"N"},
117 {484909,486706,"O"}}
118 },
119 {2025, {
120 {497924,498434,"D"},
121 {498515,499592,"E"},
122 {499618,500348,"F"},
123 {502557,505458,"K"},
124 {506231,507775,"M"},
125 {508160,509452,"O"},
126 {509803,509849,"Q"}}
127 }
128 };
129
130 MuonTriggerScaleFactors::MuonTriggerScaleFactors(const std::string& name) :
131 asg::AsgTool(name) {}
132
133 MuonTriggerScaleFactors::~MuonTriggerScaleFactors() { }
134
135 StatusCode MuonTriggerScaleFactors::LoadTriggerMap(unsigned int year) {
136 std::string fileName;
137 if (m_customInputFilePerYear.value().contains(year)) {
138 fileName = m_customInputFilePerYear.value().at(year);
139 if (fileName.empty()) {
140 ANA_MSG_INFO("Skipping loading trigger SF for year " << year << " since the provided file name is empty");
141 return StatusCode::SUCCESS;
142 } else {
143 ATH_MSG_INFO("Loading trigger SF from user specified file " << fileName << " for year " << year);
144 }
145 }
146
147 if (fileName.empty()) {
148 if (year == 2015) fileName = "muontrigger_sf_2015_mc20a_v3.root";
149 else if (year == 2016) fileName = "muontrigger_sf_2016_mc20a_v3.root";
150 else if (year == 2017) fileName = "muontrigger_sf_2017_mc20d_v3.root";
151 else if (year == 2018) fileName = "muontrigger_sf_2018_mc20e_v3.root";
152 else if (year == 2022) fileName = "muontrigger_sf_2022_mc23a_v5.root";
153 else if (year == 2023) fileName = "muontrigger_sf_2023_mc23d_v5.root";
154 else if (year == 2024) fileName = "muontrigger_sf_2024_mc23e_v5.root";
155 else if (year == 2025) fileName = "muontrigger_sf_2025_mc23g_v2.root";
156 else {
157 ATH_MSG_WARNING("There is no SF file for year " << year << " yet");
158 return StatusCode::SUCCESS;
159 }
160 }
161
162 TDirectory* origDir = gDirectory;
163
164 std::string filePath;
165
166 if (m_customInputFolder.empty()) {
167 filePath = PathResolverFindCalibFile(Form("MuonEfficiencyCorrections/%s/%s", m_calibrationVersion.value().c_str(), fileName.c_str()));
168 if (filePath.empty()) {
169 ATH_MSG_ERROR("Unable to resolve the input file " << fileName << " via PathResolver.");
170 }
171 }
172 else {
173 ATH_MSG_INFO("Note: setting up with user specified input file location " << m_customInputFolder.value() << " - this is not encouraged!");
174 filePath = PathResolverFindCalibFile(Form("%s/%s", m_customInputFolder.value().c_str(), fileName.c_str()));
175 }
176
177 TFile* file = TFile::Open(filePath.c_str());
178
179 if (file == nullptr || !file->IsOpen()) {
180 ATH_MSG_FATAL("MuonTriggerScaleFactors::initialize Couldn't open file " << filePath);
181 return StatusCode::FAILURE;
182 }
183 TDirectory* tempDir = getTemporaryDirectory();
184 tempDir->cd();
185
186 static const std::vector<std::string> type { "data", "mc" };
187 static const std::vector<std::string> region { "barrel", "endcap" };
188 static const std::vector<std::string> systematic { "nominal", "stat_up", "stat_down", "syst_up", "syst_down" };
189 if (m_muonQuality.value().compare("LowPt") == 0) {
190 m_muonQuality = "Medium";
191 }
192 TDirectory* qualityDirectory = file->GetDirectory(m_muonQuality.value().c_str());
193 if (qualityDirectory == nullptr) {
194 ATH_MSG_FATAL("MuonTriggerScaleFactors::initialize cannot find directory with selected quality");
195 return StatusCode::FAILURE;
196 }
197 TKey* periodKey;
198 TIter nextPeriod(qualityDirectory->GetListOfKeys());
199 while ((periodKey = (TKey*) nextPeriod())) {
200 if (not periodKey->IsFolder()) continue;
201 TDirectory* periodDirectory = qualityDirectory->GetDirectory(periodKey->GetName());
202 std::string periodName = std::string(periodKey->GetName());
203 std::string periodName_InMap = periodName.substr(std::string("Period").size(), periodName.size());
204 YearPeriod period = YearPeriod(year, periodName_InMap);
205 TKey* triggerKey;
206 TIter nextTrigger(periodDirectory->GetListOfKeys());
207 while ((triggerKey = (TKey*) nextTrigger())) {
208 if (not triggerKey->IsFolder()) continue;
209 TDirectory* triggerDirectory = periodDirectory->GetDirectory(triggerKey->GetName());
210 std::string triggerName = std::string(triggerKey->GetName());
211 if(!std::set<std::string>{"HLT_mu26_ivarmedium", "HLT_mu50", "HLT_mu26_ivarmedium_OR_HLT_mu50"}.count(triggerName) && m_binning == "coarse"){
212 ATH_MSG_DEBUG("Coarse binning not supported for di-muon trigger legs at the moment");
213 continue;
214 }
215 for (const auto& iregion : region) {
216 bool isBarrel = iregion.find("barrel") != std::string::npos;
217 for (const auto& itype : type) {
218 bool isData = itype.find("data") != std::string::npos;
219 std::string histname = ("_MuonTrigEff_" + periodName + "_" + triggerName + "_" + m_muonQuality.value() + "_" + "_EtaPhi_" + m_binning + "_" + iregion + "_" + itype);
220 for (const auto& isys : systematic) {
221 if (itype.find("data") != std::string::npos && isys.find("syst") != std::string::npos) continue;
222 std::string path = "eff_etaphi_" + m_binning + "_" + iregion + "_" + itype + "_" + isys;
223 TH2* hist = dynamic_cast<TH2*>(triggerDirectory->Get(path.c_str()));
224 if (not hist) {
225
226 ATH_MSG_FATAL("MuonTriggerScaleFactors::initialize " << path << " not found under trigger " << triggerName << " and period " << periodName << " for year: " << year);
227 continue;
228 }
229 hist->SetDirectory(0);
230
231 EffiHistoIdent HistoId = EffiHistoIdent(period, encodeHistoName(periodName_InMap, triggerName, isData, isys, isBarrel));
232 if (m_efficiencyMap.find(HistoId) != m_efficiencyMap.end()) {
233 ATH_MSG_FATAL("MuonTriggerScaleFactors::initialize(): histogram " << path << " is duplicated for year" << year << " in period " << periodName);
234 return StatusCode::FAILURE;
235 }
236 m_efficiencyMap.insert(std::pair<EffiHistoIdent, TH1_Ptr>(HistoId, std::shared_ptr < TH1 > (hist)));
237
238 }
239 //If the trigger is chosen for toy evaluation, generate all the replicas from
240 // NOMINAL with STAT variations stored in the data hist, load them in corresponding vector
241 if (m_replicaSet.find(triggerName) != m_replicaSet.end() && itype.find("data") != std::string::npos) {
242
243 TH1_Ptr Nominal_H = getEfficiencyHistogram(year, periodName, triggerName, isData, "nominal", isBarrel);
244 TH1_Ptr StatUp_H = getEfficiencyHistogram(year, periodName, triggerName, isData, "stat_up", isBarrel);
245
246 TH1_Ptr tmp_h2 = TH1_Ptr(dynamic_cast<TH2F*>(Nominal_H->Clone(Form("tmp_h2_%s", Nominal_H->GetName()))));
247 const int xbins = tmp_h2->GetNbinsX(), ybins = tmp_h2->GetNbinsY();
248 for (int x_i = 0; x_i <= xbins; ++x_i) {
249 for (int y_i = 0; y_i <= ybins; ++y_i) {
250 double statErr = std::abs(tmp_h2->GetBinContent(x_i, y_i) - StatUp_H->GetBinContent(x_i, y_i));
251 tmp_h2->SetBinError(x_i, y_i, statErr);
252 }
253 }
254 m_efficiencyMapReplicaArray[EffiHistoIdent(period, encodeHistoName(periodName, triggerName, isData, "repl", isBarrel))] = generateReplicas(tmp_h2, m_nReplicas, m_ReplicaRandomSeed);
255 }
256 }
257 }
258 }
259 }
260 file->Close();
261 delete file;
262 origDir->cd();
263 return StatusCode::SUCCESS;
264 }
265 // ==================================================================================
266 // == MuonTriggerScaleFactors::initialize()
267 // ==================================================================================
268 StatusCode MuonTriggerScaleFactors::initialize() {
269
270 ATH_MSG_INFO("MuonQuality = '" << m_muonQuality.value() << "'");
271 ATH_MSG_INFO("Binning = '" << m_binning << "'");
272 ATH_MSG_INFO("Campaign = '" << m_campaign << "'");
273 ATH_MSG_INFO("CalibrationRelease = '" << m_calibrationVersion.value() << "'");
274 ATH_MSG_INFO("CustomInputFolder = '" << m_customInputFolder.value() << "'");
275 ATH_MSG_INFO("AllowZeroSF = " << m_allowZeroSF);
276 ATH_MSG_INFO("experimental = " << m_experimental);
277
278 ATH_CHECK(m_eventInfo.initialize());
279
280 if (registerSystematics() != StatusCode::SUCCESS) {
281 return StatusCode::FAILURE;
282 }
283
284 if (applySystematicVariation(CP::SystematicSet()) != StatusCode::SUCCESS) {
285 ATH_MSG_ERROR("Could not configure for nominal settings");
286 return StatusCode::FAILURE;
287 }
288 // Initialize indexes of replicas for trigges which are asked
289 for (auto trigToy : m_replicaTriggerList)
290 m_replicaSet.insert(trigToy);
291
292
293 if (m_campaign.empty()) {
294 constexpr auto years_to_run = std::to_array<int>({2015, 2016, 2017, 2018, 2022, 2023, 2024, 2025});
295 for (const int &year: years_to_run) {
296 ATH_CHECK(LoadTriggerMap(year));
297 }
298 } else if (m_campaign.value() == "mc20a") {
299 ATH_CHECK(LoadTriggerMap(2015));
300 ATH_CHECK(LoadTriggerMap(2016));
301 } else if (m_campaign.value() == "mc20d") {
302 ATH_CHECK(LoadTriggerMap(2017));
303 } else if (m_campaign.value() == "mc20e") {
304 ATH_CHECK(LoadTriggerMap(2018));
305 } else if (m_campaign.value() == "mc23a") {
306 ATH_CHECK(LoadTriggerMap(2022));
307 } else if (m_campaign.value() == "mc23d") {
308 ATH_CHECK(LoadTriggerMap(2023));
309 } else if (m_campaign.value() == "mc23e") {
310 ATH_CHECK(LoadTriggerMap(2024));
311 } else if (m_campaign.value() == "mc23g") {
312 ATH_CHECK(LoadTriggerMap(2025));
313 } else {
314 ATH_MSG_ERROR("Campaign " << m_campaign.value() << " is not supported. Please choose a valid campaign or leave empty to load all years.");
315 }
316 return StatusCode::SUCCESS;
317 }
318
319 CP::CorrectionCode MuonTriggerScaleFactors::getTriggerScaleFactor(const xAOD::Muon& muon, Double_t& triggersf, const std::string& trigger) const {
320 if(!m_experimental){
321 ATH_MSG_ERROR("MuonTriggerScaleFactors::getTriggerScaleFactor This is an experimental function. If you really know what you are doing set UseExperimental property.");
323 }
324
325 if (trigger.empty()) {
326 ATH_MSG_ERROR("MuonTriggerScaleFactors::getTriggerScaleFactor Trigger must have value.");
328 }
329
330 TrigMuonEff::Configuration configuration;
331
332 if (trigger == "HLT_mu8noL1")
333 ATH_MSG_WARNING("What you are trying to do is not correct. For di-muon triggers you should get the efficiency with getTriggerEfficiency and compute the SF by yourself.");
334 else if (trigger.find("HLT_2mu10") != std::string::npos || trigger.find("HLT_2mu14") != std::string::npos)
335 ATH_MSG_WARNING("Di-muon trigger scale factors for single reco muons are not supported!");
336 else
337 return GetTriggerSF(triggersf, configuration, muon, trigger);
339 }
340
341 CP::CorrectionCode MuonTriggerScaleFactors::getTriggerScaleFactor(const xAOD::MuonContainer& mucont, Double_t& triggersf, const std::string& trigger) const{
342 if (trigger.empty()) {
343 ATH_MSG_ERROR("MuonTriggerScaleFactors::getTriggerScaleFactor Trigger must have value.");
345 }
346
347 TrigMuonEff::Configuration configuration;
348
349 if (trigger == "HLT_mu8noL1") {
350 ATH_MSG_WARNING("What you are trying to do is not correct. For di-muon triggers you should get the efficiency with getTriggerEfficiency and compute the SF by yourself.");
351 } else if (trigger.find("HLT_2mu10") != std::string::npos || trigger.find("HLT_2mu14") != std::string::npos) {
352 CP::CorrectionCode cc = GetTriggerSF_dimu(triggersf, configuration, mucont, trigger);
353 return cc;
354 } else {
355 CP::CorrectionCode cc = GetTriggerSF(triggersf, configuration, mucont, trigger);
356 return cc;
357 }
359 }
360
361 // ==================================================================================
362 // == MuonTriggerScaleFactors::getReplica_index
363 // ==================================================================================
364 // Gets replica index correponding to the toy.
365 // Also checks if the sys_name contains "MCTOY" and if the trigger has replicas generated.
366 // Returns -1 if conditions are note satisfied
367 int MuonTriggerScaleFactors::getReplica_index(const std::string& sysBaseName, const std::string& trigStr) const{
368 if (m_replicaSet.find(trigStr) == m_replicaSet.end()) return -1; //No toys for this trigger
369 std::size_t pos = sysBaseName.find("MCTOY");
370 if (pos == std::string::npos) return -1; //sys variation not affected by TOYS
371 return atoi(sysBaseName.substr(pos + 5, pos + 8).c_str()); //toys for this trigger are around get the 3-digit number
372 }
373
374 CP::CorrectionCode MuonTriggerScaleFactors::getTriggerEfficiency(const xAOD::Muon& mu, Double_t& efficiency, const std::string& trigger, Bool_t dataType) const{
375 if (trigger.empty()) {
376 ATH_MSG_ERROR("MuonTriggerScaleFactors::getTriggerEfficiency Trigger must have value.");
378 }
379 TrigMuonEff::Configuration configuration;
380 configuration.isData = dataType;
381 configuration.replicaIndex = -1;
382 Int_t threshold;
384 if (result != CP::CorrectionCode::Ok) return result;
385 if (mu.pt() < threshold) {
386 efficiency = 0;
388 }
389
390 // Pre-define uncertainty variations
391 static const CP::SystematicVariation stat_up("MUON_EFF_TrigStatUncertainty", 1);
392 static const CP::SystematicVariation stat_down("MUON_EFF_TrigStatUncertainty", -1);
393 static const CP::SystematicVariation syst_up("MUON_EFF_TrigSystUncertainty", 1);
394 static const CP::SystematicVariation syst_down("MUON_EFF_TrigSystUncertainty", -1);
395
396 std::string systype = "";
397 if (appliedSystematics().matchSystematic(syst_down) && !dataType) {
398 systype = "syst_down";
399 } else if (appliedSystematics().matchSystematic(syst_up) && !dataType) {
400 systype = "syst_up";
401 } else if (appliedSystematics().matchSystematic(stat_down)) {
402 systype = "stat_down";
403 } else if (appliedSystematics().matchSystematic(stat_up)) {
404 systype = "stat_up";
405 } else {
406 systype = "nominal";
407 }
408
409 // Toys, if found, will overwrite the data hists stat with the generated toy
410 //+++++++++++++
411 // The best way is the use of filterByName with the 000MCTOY at the end. See:
412 // if( !(appliedSystematics().filterByBaseName("MUON_EFF_Trig_MCTOY000")).empty()){//The following is a hack!!!
413 //++++++++++THE FOLLOWING IS A PARTIAL HACK!!!
414 if (!appliedSystematics().empty() && configuration.isData == true) {
415 configuration.replicaIndex = getReplica_index(appliedSystematics().begin()->basename(), trigger);
416 if (configuration.replicaIndex != -1) systype = "replicas";
417 }
418 CP::CorrectionCode cc = getMuonEfficiency(efficiency, configuration, mu, trigger, systype);
419 return cc;
420 }
421
423 // Private functions //
425
426 // ==================================================================================
427 // == MuonTriggerScaleFactors::generateReplicas
428 // ==================================================================================
429 // Generate replicas of h for Toys with each bin of h varied with Gaussian distribution
430 // with mean from bin content and sigma from bin error
431 std::vector<TH1_Ptr> MuonTriggerScaleFactors::generateReplicas(TH1_Ptr h, int nrep, int seed) const {
432 TRandom3 Rndm(seed);
433 std::vector<TH1_Ptr> replica_v;
434 const int xbins = h->GetNbinsX(), ybins = h->GetNbinsY();
435
436 for (int t = 0; t < nrep; ++t) {
437 TH2* replica = dynamic_cast<TH2*>(h->Clone(Form("rep%d_%s", t, h->GetName())));
438
439 for (int x_i = 0; x_i <= xbins; ++x_i) {
440 for (int y_i = 0; y_i <= ybins; ++y_i) {
441 replica->SetBinContent(x_i, y_i, Rndm.Gaus(h->GetBinContent(x_i, y_i), h->GetBinError(x_i, y_i)));
442 }
443 }
444 replica_v.push_back(TH1_Ptr(replica));
445 }
446 return replica_v;
447 }
448
449 bool MuonTriggerScaleFactors::isTriggerSupported(const std::string& trigger) const{
450 TH1_Ptr H1 = getEfficiencyHistogram(trigger, true, "nominal");
451 return H1.get() != nullptr;
452 }
453
454 int MuonTriggerScaleFactors::getBinNumber(const xAOD::Muon& muon, const std::string& trigger) const{
455 if(!m_experimental){
456 ATH_MSG_ERROR("MuonTriggerScaleFactors::getTriggerScaleFactor This is an experimental function. If you really know what you are doing set UseExperimental property.");
458 }
459
460 const double mu_eta = muon.eta();
461 const double mu_phi = muon.phi();
462 bool isBarrel = std::abs(mu_eta) < muon_barrel_endcap_boundary;
463 TH1_Ptr cit = getEfficiencyHistogram(trigger, true, "nominal", isBarrel);
464 if(!cit.get()){
465 if(!m_allowZeroSF)
466 ATH_MSG_ERROR("Could not find efficiency map for muon with eta: " << mu_eta << " and phi: " << mu_phi << ". Something is inconsistent. Please check your settings for year, mc and trigger." );
467 return -1;
468 }
469 auto eff_h2 = cit;
470 double mu_phi_corr = mu_phi;
471 if (mu_phi_corr < eff_h2->GetYaxis()->GetXmin()) mu_phi_corr += 2.0 * M_PI;
472 if (mu_phi_corr > eff_h2->GetYaxis()->GetXmax()) mu_phi_corr -= 2.0 * M_PI;
473 return eff_h2->FindFixBin(mu_eta, mu_phi_corr);
474 }
475
476 unsigned int MuonTriggerScaleFactors::encodeHistoName(const std::string_view period, const std::string& Trigger, bool isData, const std::string& Systematic, bool isBarrel) const {
477 //keep the string as short as possible
478 const std::string histName = std::string{period} + "_" + Trigger + "_" + (isBarrel ? "b" : "e") + "_" + (isData ? "data" : "mc") + Systematic;
479 return std::hash<std::string>()(histName);
480 }
481
482 unsigned int MuonTriggerScaleFactors::encodeHistoName(const std::string& Trigger, const TrigMuonEff::Configuration& configuration, const std::string& Systematic, bool isBarrel) const {
483 //keep the string as short as possible
484 return encodeHistoName(getDataPeriod(), Trigger, configuration.isData, Systematic, isBarrel);
485
486 }
487 TH1_Ptr MuonTriggerScaleFactors::getEfficiencyHistogram(unsigned int year, const std::string_view period, const std::string& trigger, bool isData, const std::string& Systematic, bool isBarrel) const {
488 EffiHistoIdent Ident = EffiHistoIdent(YearPeriod(year, period), encodeHistoName(period, trigger, isData, Systematic, isBarrel));
489 EfficiencyMap::const_iterator Itr = m_efficiencyMap.find(Ident);
490
491 if (Itr == m_efficiencyMap.end()) {
492 return TH1_Ptr();
493 }
494 return Itr->second;
495 }
496 TH1_Ptr MuonTriggerScaleFactors::getEfficiencyHistogram(const std::string& trigger, bool isData, const std::string& Systematic, bool isBarrel) const {
497 unsigned int run = getRunNumber();
498 return getEfficiencyHistogram(getYear(run), getDataPeriod(run), trigger, isData, Systematic, isBarrel);
499 }
500
501 CP::CorrectionCode MuonTriggerScaleFactors::getMuonEfficiency(Double_t& eff, const TrigMuonEff::Configuration& configuration, const xAOD::Muon& muon, const std::string& trigger, const std::string& systematic) const{
502 const double mu_eta = muon.eta();
503 const double mu_phi = muon.phi();
504 bool isBarrel = std::abs(mu_eta) < muon_barrel_endcap_boundary;
505
506 TH1_Ptr eff_h2 = nullptr;
507 if (configuration.replicaIndex >= 0) { //Only look into the replicas if asking for them
508
509 unsigned int run = getRunNumber();
510 EffiHistoIdent Ident = EffiHistoIdent(YearPeriod(getYear(run), getDataPeriod(run)), encodeHistoName(getDataPeriod(run), trigger, configuration.isData, "repl", isBarrel));
511 std::map<EffiHistoIdent, std::vector<TH1_Ptr> >::const_iterator cit = m_efficiencyMapReplicaArray.find(Ident);
512 if (cit == m_efficiencyMapReplicaArray.end()) {
513 if (m_allowZeroSF) {
514 ATH_MSG_WARNING("Could not find what you are looking for in the efficiency map. The trigger you are looking for, year and mc are not consistent, or the trigger is unavailable in this data period. Returning efficiency = 0.");
515 eff = 0.;
517 }
518
519 else {
520 ATH_MSG_ERROR("Could not find what you are looking for in the efficiency map. The trigger you are looking for, year and mc are not consistent, or the trigger is unavailable in this data period. Please check how you set up the tool.");
522 }
523 }
524
525 if (configuration.replicaIndex >= (int) cit->second.size()) {
526 ATH_MSG_ERROR("MuonTriggerScaleFactors::getMuonEfficiency ; index for replicated histograms is out of range.");
528 }
529
530 eff_h2 = cit->second[configuration.replicaIndex];
531 } else { //Standard case, look into the usual eff map
532 TH1_Ptr cit = getEfficiencyHistogram(trigger, configuration.isData, systematic, isBarrel);
533 if (cit.get() == nullptr) {
534 if (m_allowZeroSF) {
535 ATH_MSG_WARNING("Could not find what you are looking for in the efficiency map. The trigger you are looking for, year and mc are not consistent, or the trigger is unavailable in this data period. Returning efficiency = 0.");
536 eff = 0.;
538 } else {
539 ATH_MSG_ERROR("Could not find what you are looking for in the efficiency map. The trigger you are looking for, year and mc are not consistent, or the trigger is unavailable in this data period. Please check how you set up the tool.");
541 }
542 }
543 eff_h2 = cit;
544 }
545
546 double mu_phi_corr = mu_phi;
547 if (mu_phi_corr < eff_h2->GetYaxis()->GetXmin()) mu_phi_corr += 2.0 * M_PI;
548 if (mu_phi_corr > eff_h2->GetYaxis()->GetXmax()) mu_phi_corr -= 2.0 * M_PI;
549
550 const int bin = eff_h2->FindFixBin(mu_eta, mu_phi_corr);
551 const double efficiency = eff_h2->GetBinContent(bin);
552
553 eff = efficiency;
554
555 ATH_MSG_DEBUG("getMuonEfficiency [eta,phi,phi_corr]=[" << mu_eta << "," << mu_phi << "," << mu_phi_corr << "], ibin=" << bin << " eff=" << eff);
556
558
559 }
560
561 CP::CorrectionCode MuonTriggerScaleFactors::GetTriggerSF_dimu(Double_t& TriggerSF, TrigMuonEff::Configuration& configuration, const xAOD::MuonContainer& mucont, const std::string& trigger) const{
562
563 if (mucont.size() != 2) {
564 ATH_MSG_FATAL("MuonTriggerScaleFactors::GetTriggerSF;Currently dimuon trigger chains only implemented for events with exactly 2 muons.");
565 }
566 ATH_MSG_DEBUG("The trigger that you choose : " << trigger);
567
568 Double_t eff_data = 0;
569 Double_t eff_mc = 0;
570
571 std::string data_err = "";
572 std::string mc_err = "";
573
574 if (appliedSystematics().matchSystematic(CP::SystematicVariation("MUON_EFF_TrigSystUncertainty", -1))) {
575 data_err = "nominal";
576 mc_err = "syst_up";
577 } else if (appliedSystematics().matchSystematic(CP::SystematicVariation("MUON_EFF_TrigSystUncertainty", 1))) {
578 data_err = "nominal";
579 mc_err = "syst_down";
580 } else if (appliedSystematics().matchSystematic(CP::SystematicVariation("MUON_EFF_TrigStatUncertainty", -1))) {
581 data_err = "stat_down";
582 mc_err = "nominal";
583 } else if (appliedSystematics().matchSystematic(CP::SystematicVariation("MUON_EFF_TrigStatUncertainty", 1))) {
584 data_err = "stat_up";
585 mc_err = "nominal";
586 } else {
587 data_err = "nominal";
588 mc_err = "nominal";
589 }
590
591 //Toys, if found, will overwrite the data hists with the sys generated with one toy
592 //+++++++++++++
593 //The best way is the use of filterByName with the 000MCTOY at the end. See:
594 // if( !(appliedSystematics().filterByBaseName("MUON_EFF_Trig_MCTOY000")).empty()){//The following is a hack!!!
595 //++++++++++THE FOLLOWING IS A PARTIAL HACK!!!
596 if (!appliedSystematics().empty()) {
597 configuration.replicaIndex = getReplica_index(appliedSystematics().begin()->basename(), trigger);
598 if (configuration.replicaIndex != -1) data_err = "replicas";
599 }
600
601 configuration.isData = true;
602 CP::CorrectionCode result = getDimuonEfficiency(eff_data, configuration, mucont, trigger, data_err);
603 if (result != CP::CorrectionCode::Ok) return result;
604
605 configuration.isData = false;
606 configuration.replicaIndex = -1;
607 result = getDimuonEfficiency(eff_mc, configuration, mucont, trigger, mc_err);
608 if (result != CP::CorrectionCode::Ok) return result;
609
610 double event_SF = 1.;
611
612 if (std::abs(1. - eff_mc) > 0.0001) {
613 event_SF = eff_data / eff_mc;
614 }
615
616 TriggerSF = event_SF;
618 }
619
620 CP::CorrectionCode MuonTriggerScaleFactors::GetTriggerSF(Double_t& TriggerSF, TrigMuonEff::Configuration& configuration, const xAOD::MuonContainer& mucont, const std::string& trigger) const{
621 Int_t threshold;
623 if (result != CP::CorrectionCode::Ok) return result;
624
625 double rate_not_fired_data = 1.;
626 double rate_not_fired_mc = 1.;
627
628 for (const auto mu : mucont) {
629
630 double eff_data = 0., eff_mc = 0.;
631
632 if (mu->pt() < threshold) {
633
634 eff_data = 0.;
635 eff_mc = 0.;
636
637 } else {
638
639 std::string muon_trigger_name = trigger;
640 std::string data_err = "";
641 std::string mc_err = "";
642
643 // Pre-define uncertainty variations
644 static const CP::SystematicVariation stat_up("MUON_EFF_TrigStatUncertainty", 1);
645 static const CP::SystematicVariation stat_down("MUON_EFF_TrigStatUncertainty", -1);
646 static const CP::SystematicVariation syst_up("MUON_EFF_TrigSystUncertainty", 1);
647 static const CP::SystematicVariation syst_down("MUON_EFF_TrigSystUncertainty", -1);
648
649 if (appliedSystematics().matchSystematic(syst_down)) {
650 data_err = "nominal";
651 mc_err = "syst_up";
652 } else if (appliedSystematics().matchSystematic(syst_up)) {
653 data_err = "nominal";
654 mc_err = "syst_down";
655 } else if (appliedSystematics().matchSystematic(stat_down)) {
656 data_err = "stat_down";
657 mc_err = "nominal";
658 } else if (appliedSystematics().matchSystematic(stat_up)) {
659 data_err = "stat_up";
660 mc_err = "nominal";
661 } else {
662 data_err = "nominal";
663 mc_err = "nominal";
664 }
665
666 //Toys, if found, will overwrite the data hists, on which toys for stat uncertainty have been generated
667 //+++++++++++++
668 //The best way is the use of filterByName with the 000MCTOY at the end. See:
669 // if( !(appliedSystematics().filterByBaseName("MUON_EFF_Trig_MCTOY000")).empty()){//The following is a hack!!!
670 //++++++++++The following is a hack!!!
671 if (!appliedSystematics().empty()) {
672 configuration.replicaIndex = getReplica_index(appliedSystematics().begin()->basename(), trigger);
673 if (configuration.replicaIndex != -1) data_err = "replicas";
674 }
675
676 configuration.isData = true;
677 CP::CorrectionCode result_data = getMuonEfficiency(eff_data, configuration, *mu, muon_trigger_name, data_err);
678 if (result_data != CP::CorrectionCode::Ok) return result_data;
679 configuration.isData = false;
680 configuration.replicaIndex = -1;
681 CP::CorrectionCode result_mc = getMuonEfficiency(eff_mc, configuration, *mu, muon_trigger_name, mc_err);
682 if (result_mc != CP::CorrectionCode::Ok) return result_mc;
683 }
684 rate_not_fired_data *= (1. - eff_data);
685 rate_not_fired_mc *= (1. - eff_mc);
686 }
687
688 double event_SF = 1.;
689 if (1 - rate_not_fired_data == 0) event_SF = 0;
690 if ((mucont.size()) and (std::abs(1. - rate_not_fired_mc) > 0.0001)) {
691
692 event_SF = (1. - rate_not_fired_data) / (1. - rate_not_fired_mc);
693 }
694 TriggerSF = event_SF;
695
697 }
698
699 CP::CorrectionCode MuonTriggerScaleFactors::GetTriggerSF(Double_t& TriggerSF, TrigMuonEff::Configuration& configuration, const xAOD::Muon& mu, const std::string& trigger) const{
700 Int_t threshold;
702 if (result != CP::CorrectionCode::Ok)
703 return result;
704
705 double eff_data = 0., eff_mc = 0.;
706
707 if (mu.pt() < threshold) {
708 eff_data = 0.;
709 eff_mc = 0.;
710 TriggerSF = 1.;
712 }
713
714 std::string muon_trigger_name = trigger;
715 std::string data_err = "";
716 std::string mc_err = "";
717
718 // Pre-define uncertainty variations
719 static const CP::SystematicVariation stat_up("MUON_EFF_TrigStatUncertainty", 1);
720 static const CP::SystematicVariation stat_down("MUON_EFF_TrigStatUncertainty", -1);
721 static const CP::SystematicVariation syst_up("MUON_EFF_TrigSystUncertainty", 1);
722 static const CP::SystematicVariation syst_down("MUON_EFF_TrigSystUncertainty", -1);
723
724 if (appliedSystematics().matchSystematic(syst_down)) {
725 data_err = "nominal";
726 mc_err = "syst_up";
727 } else if (appliedSystematics().matchSystematic(syst_up)) {
728 data_err = "nominal";
729 mc_err = "syst_down";
730 } else if (appliedSystematics().matchSystematic(stat_down)) {
731 data_err = "stat_down";
732 mc_err = "nominal";
733 } else if (appliedSystematics().matchSystematic(stat_up)) {
734 data_err = "stat_up";
735 mc_err = "nominal";
736 } else {
737 data_err = "nominal";
738 mc_err = "nominal";
739 }
740
741 if (!appliedSystematics().empty()) {
742 configuration.replicaIndex = getReplica_index(appliedSystematics().begin()->basename(), trigger);
743 if (configuration.replicaIndex != -1) data_err = "replicas";
744 }
745
746 configuration.isData = true;
747 CP::CorrectionCode result_data = getMuonEfficiency(eff_data, configuration, mu, muon_trigger_name, data_err);
748 if (result_data != CP::CorrectionCode::Ok)
749 return result_data;
750 configuration.isData = false;
751 configuration.replicaIndex = -1;
752 CP::CorrectionCode result_mc = getMuonEfficiency(eff_mc, configuration, mu, muon_trigger_name, mc_err);
753 if (result_mc != CP::CorrectionCode::Ok)
754 return result_mc;
755 if (eff_data == 0)
756 TriggerSF = 0;
757 if (std::abs(eff_mc) > 0.0001)
758 TriggerSF = eff_data / eff_mc;
760 }
761
762 CP::CorrectionCode MuonTriggerScaleFactors::getDimuonEfficiency(Double_t& eff, const TrigMuonEff::Configuration& configuration, const xAOD::MuonContainer& mucont, const std::string& chain, const std::string& systematic) const{
763
764 std::string trigger = getTriggerCorrespondingToDimuonTrigger(chain);
765 Int_t threshold;
767 if (result != CP::CorrectionCode::Ok) return result;
768
771
772 Double_t eff1 = 0;
773 if ((**mu1).pt() > threshold) {
774 CP::CorrectionCode result1 = getMuonEfficiency(eff1, configuration, (**mu1), trigger, systematic);
775 if (result1 != CP::CorrectionCode::Ok) return result1;
776 }
777 Double_t eff2 = 0;
778 if ((**mu2).pt() > threshold) {
779 CP::CorrectionCode result2 = getMuonEfficiency(eff2, configuration, (**mu2), trigger, systematic);
780 if (result2 != CP::CorrectionCode::Ok) return result2;
781 }
782
783 eff = eff1 * eff2;
785 }
786
787 std::string MuonTriggerScaleFactors::getTriggerCorrespondingToDimuonTrigger(const std::string& trigger) const {
788 if (trigger.find("2mu10") != std::string::npos) return "HLT_mu10";
789 if (trigger.find("2mu14") != std::string::npos) return "HLT_mu14";
790 throw std::runtime_error("Unknown dimuon trigger");
791 }
792
793 CP::CorrectionCode MuonTriggerScaleFactors::getThreshold(Int_t& threshold, const std::string& trigger) const{
794 std::size_t index = trigger.find("HLT_mu");
795 if (index != std::string::npos) {
796 std::string rawNumber = trigger.substr(index + 6);
797 if (!rawNumber.empty() && isdigit(rawNumber[0])) {
798 std::stringstream(rawNumber) >> threshold;
799 if (threshold < 10) threshold = 10000;
800 else threshold = (threshold + 1) * 1000;
802 }
803 }
804 ATH_MSG_ERROR("MuonTriggerScaleFactors::getThreshold Could not extract threshold for trigger " << trigger);
806 }
807
808 unsigned int MuonTriggerScaleFactors::getYear(unsigned int run) const {
809 if(m_forceYear != -1) return m_forceYear;
810
811 auto runCheck = RunYear{run,2050};
812 auto lower = std::ranges::lower_bound(runYears, runCheck, [](const auto &s1, const auto &s2) { return s1.lastRun < s2.lastRun; });
813 return (lower != runYears.end()) ? lower->year : runCheck.year;
814 }
815
816 const std::string_view MuonTriggerScaleFactors::getDataPeriod() const {
817 return getDataPeriod(getRunNumber());
818 }
819
820 const std::string_view MuonTriggerScaleFactors::getDataPeriod(unsigned int run) const {
821 return getDataPeriod(run, getYear(run));
822 }
823
824 const std::string_view MuonTriggerScaleFactors::getDataPeriod(unsigned int runNumber, unsigned year) const {
825 if(!m_forcePeriod.empty()) return std::string_view{m_forcePeriod};
826
827 auto yit = m_year_periods.find(year);
828 if (yit != m_year_periods.end()) {
829 auto runit = std::find_if(yit->second.begin(), yit->second.end(), [&runNumber](const auto &item){return runNumber >= std::get<0>(item) && runNumber <= std::get<1>(item);});
830 if (runit != yit->second.end()) return std::get<2>(*runit);
831 else {
832 ATH_MSG_FATAL("RunNumber " << runNumber << " does not belong to any data period in " << year);
833 throw std::invalid_argument{""};
834 }
835 }
836 else {
837 ATH_MSG_FATAL("No data were collected during " << year);
838 throw std::invalid_argument{""};
839 }
840 }
841
842 unsigned int MuonTriggerScaleFactors::getRunNumber() const {
843 static const SG::ConstAccessor<unsigned int> acc_rnd("RandomRunNumber");
845 if (info.operator->()==nullptr) {
846 ATH_MSG_FATAL("Could not retrieve the xAOD::EventInfo with name: " << m_eventInfo.key() << ". Exiting the code.");
847 throw std::invalid_argument{""};
848 }
849 if (!info->eventType(xAOD::EventInfo::IS_SIMULATION)) {
850 ATH_MSG_DEBUG("The current event is a data event. Return runNumber instead.");
851 return info->runNumber();
852 }
853 if (!acc_rnd.isAvailable(*info)) {
854 if(m_forceYear == -1 && m_forcePeriod == "")
855 ATH_MSG_FATAL("Failed to find the RandomRunNumber decoration. Please call the apply() method from the PileupReweightingTool beforehand in order to get period dependent SFs");
856 throw std::invalid_argument{""};
857 } else if (acc_rnd(*info) == 0) {
858 ATH_MSG_FATAL("Pile up tool has given runNumber 0. Exiting the code.");
859 throw std::invalid_argument{""};
860 }
861
862 // standard behaviour for MC, get the random RunNumber
863 return acc_rnd(*info);
864 }
865
866 TDirectory* MuonTriggerScaleFactors::getTemporaryDirectory(void) const {
867 gROOT->cd();
868 TDirectory* tempDir = 0;
869 int counter = 0;
870 while (not tempDir) {
871 std::stringstream dirname;
872 dirname << "MuonTriggerScaleFactorsTempDir_%i" << counter;
873 if (gROOT->GetDirectory((dirname.str()).c_str())) {
874 ++counter;
875 continue;
876 }
877 tempDir = gROOT->mkdir((dirname.str()).c_str());
878 if (not tempDir) {
879 ATH_MSG_ERROR("getTemporaryDirectory::Temporary directory could not be created");
880 }
881 }
882 return tempDir;
883 }
884
885 //=======================================================================
886 // Systematics Interface
887 //=======================================================================
888 bool MuonTriggerScaleFactors::isAffectedBySystematic(const CP::SystematicVariation& systematic) const {
889 if (!systematic.empty()) {
890 CP::SystematicSet sys = affectingSystematics();
891 return sys.find(systematic) != sys.end();
892 }
893 return true;
894 }
895
897 CP::SystematicSet MuonTriggerScaleFactors::affectingSystematics() const {
898 CP::SystematicSet mySysSet;
899
900 mySysSet.insert(CP::SystematicVariation("MUON_EFF_TrigSystUncertainty", 1));
901 mySysSet.insert(CP::SystematicVariation("MUON_EFF_TrigSystUncertainty", -1));
902
903 //Consider full statUncertainty if TOY replicas are not used
904 if (m_replicaTriggerList.size() == 0) {
905 mySysSet.insert(CP::SystematicVariation("MUON_EFF_TrigStatUncertainty", 1));
906 mySysSet.insert(CP::SystematicVariation("MUON_EFF_TrigStatUncertainty", -1));
907 } else {
908 for (int i = 0; i < m_nReplicas; ++i) { //TOFIX Hack with just up variations! needs ASG reserved words for a clean handling//+++++++
909 mySysSet.insert(CP::SystematicVariation(Form("MUON_EFF_Trig_MCTOY%03d", i), 1));
910 }
911 }
912
913 return mySysSet;
914 }
915
916 // Register the systematics with the registry and add them to the recommended list
917 StatusCode MuonTriggerScaleFactors::registerSystematics() {
919 if (registry.registerSystematics(*this) != StatusCode::SUCCESS) {
920 ATH_MSG_ERROR("Failed to add systematic to list of recommended systematics.");
921 return StatusCode::FAILURE;
922 }
923 return StatusCode::SUCCESS;
924 }
925
927 CP::SystematicSet MuonTriggerScaleFactors::recommendedSystematics() const {
928 return affectingSystematics();
929 }
930
931 StatusCode MuonTriggerScaleFactors::applySystematicVariation(const CP::SystematicSet& systConfig){
932 // First, check if we already know this systematic configuration
933 auto itr = m_systFilter.find(systConfig);
934
935 // If it's a new input set, we need to filter it
936 if (itr == m_systFilter.end()) {
937
938 // New systematic. We need to parse it.
939 static const CP::SystematicSet affectingSys = affectingSystematics();
940 CP::SystematicSet filteredSys;
941 if (!CP::SystematicSet::filterForAffectingSystematics(systConfig, affectingSys, filteredSys)) {
942 ATH_MSG_ERROR("Unsupported combination of systematics passed to the tool!");
943 return StatusCode::FAILURE;
944 }
945
946 // Insert filtered set into the map
947 itr = m_systFilter.insert(std::make_pair(systConfig, filteredSys)).first;
948 }
949
950 CP::SystematicSet& mySysConf = itr->second;
951
952 // Check to see if the set of variations tries to add in the uncertainty up and down. Since the errors
953 // are symetric this would result in 0 and so should not be done.
954 static const CP::SystematicVariation stat_up("MUON_EFF_TrigStatUncertainty", 1);
955 static const CP::SystematicVariation stat_down("MUON_EFF_TrigStatUncertainty", -1);
956 static const CP::SystematicVariation syst_up("MUON_EFF_TrigSystUncertainty", 1);
957 static const CP::SystematicVariation syst_down("MUON_EFF_TrigSystUncertainty", -1);
958
959 if ((mySysConf.matchSystematic(stat_up) && mySysConf.matchSystematic(stat_down)) || (mySysConf.matchSystematic(syst_up) && mySysConf.matchSystematic(syst_down))) {
960 return StatusCode::FAILURE;
961 }
962
963 m_appliedSystematics = &mySysConf;
964 return StatusCode::SUCCESS;
965 }
966
967} /* namespace ADD_CMODE(CP) */
#define M_PI
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_INFO(x,...)
#define ATH_MSG_FATAL(x,...)
#define ANA_MSG_INFO(xmsg,...)
Macro printing info messages.
CP::CorrectionCode getThreshold(Int_t &threshold, const std::string &trigger)
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
size_t size() const
Number of registered mappings.
static const Attributes_t empty
Header file for AthHistogramAlgorithm.
Return value from object correction CP tools.
@ Error
Some error happened during the object correction.
@ OutOfValidityRange
Input object is out of validity range.
@ Ok
The correction was done successfully.
This module implements the central registry for handling systematic uncertainties with CP tools.
static SystematicRegistry & getInstance()
Get the singleton instance of the registry for the curren thread.
Class to wrap a set of SystematicVariations.
bool matchSystematic(const SystematicVariation &systematic, MATCHTYPE type=FULL) const
void insert(const SystematicVariation &systematic)
description: insert a systematic into the set
static StatusCode filterForAffectingSystematics(const SystematicSet &systConfig, const SystematicSet &affectingSystematics, SystematicSet &filteredSystematics)
description: filter the systematics for the affected systematics returns: success guarantee: strong f...
bool empty() const
returns: whether this is an empty systematic, i.e.
DataModel_detail::const_iterator< DataVector > const_iterator
Definition DataVector.h:861
const_iterator begin() const noexcept
Return a const_iterator pointing at the beginning of the collection.
size_type size() const noexcept
Returns the number of elements in the collection.
Helper class to provide constant type-safe access to aux data.
@ IS_SIMULATION
true: simulation, false: data
void efficiency(std::vector< double > &bins, std::vector< double > &values, const std::vector< std::string > &files, const std::string &histname, const std::string &tplotname, const std::string &label="")
int atoi(std::string_view str)
Helper functions to unpack numbers decoded in string into integers and doubles The strings are requir...
::StatusCode StatusCode
StatusCode definition for legacy code.
path
python interpreter configuration --------------------------------------—
Definition athena.py:130
str filePath
Definition hancool.py:27
Definition index.py:1
TH2F(name, title, nxbins, bins_par2, bins_par3, bins_par4, bins_par5=None, bins_par6=None, path='', **kwargs)
Muon_v1 Muon
Reference the current persistent version:
Definition Muon.h:13
MuonContainer_v1 MuonContainer
Definition of the current "Muon container version".
structure to hold variables to configure behaviour of the tool
int replicaIndex
index of replicated histograms to access
TFile * file
int run(int argc, char *argv[])
std::string dirname(std::string name)
Definition utils.cxx:200
std::string basename(std::string name)
Definition utils.cxx:207