ATLAS Offline Software
Loading...
Searching...
No Matches
MuonSelectionTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6
11
12namespace {
13 static constexpr double const MeVtoGeV = 1. / 1000.;
14 // This function defines the order of chamber indices for the low-pT MVA,
15 // i.e. defining the meaning of "the first two segments", which are used in the BDT
16 std::vector<int> initializeChamberIdxOrder() {
17 // This vector defines the order. The current order follows the order of the enum "ChIndex"
18 // except for the CSCs, which appear first. Since the order is not strictly innermost-to-outermost,
19 // a reordering could be considered for a rel. 22 retuning, which can then easily be achieved by
20 // swapping around the elements in the below initialization.
22 using namespace Muon::MuonStationIndex;
23 const std::vector<ChIdx> orderedChIndices{
24 ChIdx::CSS, ChIdx::CSL, ChIdx::BIS, ChIdx::BIL,
25 ChIdx::BMS, ChIdx::BML, ChIdx::BOS, ChIdx::BOL,
26 ChIdx::BEE, ChIdx::EIS, ChIdx::EIL, ChIdx::EMS,
27 ChIdx::EML, ChIdx::EOS, ChIdx::EOL, ChIdx::EES,
28 ChIdx::EEL};
29
30 // This vector will hold the equivalent information in a form that can be efficiently accessed in the
31 // below function "chamberIndexCompare", using the chamber index as the vector index
32 std::vector<int> chamberIndexOrder(orderedChIndices.size());
33
34 for (unsigned int i = 0; i < orderedChIndices.size(); i++) {
35 chamberIndexOrder[toInt(orderedChIndices[i])] = i;
36 }
37 return chamberIndexOrder;
38 }
39
40 // This is the comparison function for the sorting of segments according to the chamber index
41 bool chamberIndexCompare(const xAOD::MuonSegment* first, const xAOD::MuonSegment* second) {
42 static const std::vector<int> chamberIndexOrder = initializeChamberIdxOrder();
43 return (chamberIndexOrder[toInt(first->chamberIndex())] <
44 chamberIndexOrder[toInt(second->chamberIndex())]);
45 }
46
47 static const SG::AuxElement::Accessor<float> mePt_acc("MuonSpectrometerPt");
48 static const SG::AuxElement::Accessor<float> idPt_acc("InnerDetectorPt");
49 static const SG::AuxElement::Accessor<uint8_t> eta1stgchits_acc("etaLayer1STGCHits");
50 static const SG::AuxElement::Accessor<uint8_t> eta2stgchits_acc("etaLayer2STGCHits");
51 static const SG::AuxElement::Accessor<uint8_t> mmhits_acc("MMHits");
52} // namespace
53
54namespace CP {
55
56 MuonSelectionTool::MuonSelectionTool(const std::string& tool_name) : asg::AsgTool(tool_name), m_acceptInfo("MuonSelection"){
57
58 if (!m_calculateTightNNScore) m_onnxTool.setTypeAndName("");
59
60 }
61
63
65
66 // Greet the user:
67 ATH_MSG_INFO("Initialising...");
68
69 m_geoOnTheFly ? ATH_MSG_INFO("Is Run-3 geometry: On-the-fly determination. THIS OPTION IS DEPRECATED AND WILL BE REMOVED SOON. Use IsRun3Geo property instead.")
70 : ATH_MSG_INFO("Is Run-3 geometry: " << m_isRun3.value());
71 ATH_MSG_INFO("Maximum muon |eta|: " << m_maxEta.value());
72 ATH_MSG_INFO("Muon quality: "<< m_quality.value());
73 if (m_toroidOff) ATH_MSG_INFO("!! CONFIGURED FOR TOROID-OFF COLLISIONS !!");
74 if (m_SctCutOff) ATH_MSG_WARNING("!! SWITCHING SCT REQUIREMENTS OFF !! FOR DEVELOPMENT USE ONLY !!");
75 if (m_PixCutOff) ATH_MSG_WARNING("!! SWITCHING PIXEL REQUIREMENTS OFF !! FOR DEVELOPMENT USE ONLY !!");
76 if (m_SiHolesCutOff) ATH_MSG_WARNING("!! SWITCHING SILICON HOLES REQUIREMENTS OFF !! FOR DEVELOPMENT USE ONLY !!");
77 if (m_custom_dir != "")
78 ATH_MSG_WARNING("!! SETTING UP WITH USER SPECIFIED INPUT LOCATION \"" << m_custom_dir << "\"!! FOR DEVELOPMENT USE ONLY !! ");
79 if (!m_useAllAuthors)
81 "Not using allAuthors variable as currently missing in many derivations; LowPtEfficiency working point will always return "
82 "false, but this is expected at the moment. Have a look here: "
83 "https://twiki.cern.ch/twiki/bin/view/Atlas/MuonSelectionToolR21#New_LowPtEfficiency_working_poin");
84
85 // Print message to ensure that users excluding 2-station muons in the high-pT selection are aware of this
87 ATH_MSG_INFO("You have opted to select only 3-station muons in the high-pT selection! "
88 << "Please feed 'HighPt3Layers' to the 'WorkingPoint' property to retrieve the appropriate scale-factors");
89
90 // Only an MVA-based selection is defined for segment-tagged muons for the Low-pT working point
92 ATH_MSG_WARNING("No cut-based selection is defined for segment-tagged muons in the Low-pT working point. "
93 << "Please set UseMVALowPt=true if you want to try the UseSegmentTaggedLowPt=true option.");
95 }
96 if (m_useLRT) {
97 ATH_MSG_INFO("MuonSelectionTool will assume both Standard and LRT Muons are being used, and that the necessary information is available to identify the type (standard or LRT).");
98 if (m_quality!=1) ATH_MSG_WARNING("Currently, only Medium quality is supported for LRT muons. Your chosen WP will be applied (w/o ID cuts), but no recommendations are available for this quality.");
99 }
100
101 // Set up the TAccept object:
102 m_acceptInfo.addCut("Eta", "Selection of muons according to their pseudorapidity");
103 m_acceptInfo.addCut("IDHits", "Selection of muons according to whether they passed the MCP ID Hit cuts");
104 m_acceptInfo.addCut("Preselection", "Selection of muons according to their type/author");
105 m_acceptInfo.addCut("Quality", "Selection of muons according to their tightness");
106 // Sanity check
107 if (m_quality > 5) {
109 "Invalid quality (i.e. selection WP) set: "
110 << m_quality
111 << " - it must be an integer between 0 and 5! (0=Tight, 1=Medium, 2=Loose, 3=Veryloose, 4=HighPt, 5=LowPtEfficiency)");
112 return StatusCode::FAILURE;
113 }
114 if (m_quality == 5 && !m_useAllAuthors) {
115 ATH_MSG_ERROR("Cannot use lowPt working point if allAuthors is not available!");
116 return StatusCode::FAILURE;
117 }
118
120 ATH_MSG_FATAL("CaloScoreWP property must be set to 1, 2, 3 or 4");
121 return StatusCode::FAILURE;
122 }
123
124 // Load Tight WP cut-map
125 ATH_MSG_INFO("Initialising tight working point histograms...");
126 std::string tightWP_rootFile_fullPath;
127 if (!m_custom_dir.empty()) {
128 tightWP_rootFile_fullPath = PathResolverFindCalibFile(m_custom_dir + "/muonSelection_tightWPHisto.root");
129 } else {
130 tightWP_rootFile_fullPath = PathResolverFindCalibFile(
131 Form("MuonSelectorTools/%s/muonSelection_tightWPHisto.root", m_calibration_version.value().c_str()));
132 }
133
134 ATH_MSG_INFO("Reading muon tight working point histograms from " << tightWP_rootFile_fullPath);
135 //
136 std::unique_ptr<TFile> file(TFile::Open(tightWP_rootFile_fullPath.c_str(), "READ"));
137
138 if (!file->IsOpen()) {
139 ATH_MSG_ERROR("Cannot read tight working point file from " << tightWP_rootFile_fullPath);
140 return StatusCode::FAILURE;
141 }
142
143 // Retrieve all the relevant histograms
144 ATH_CHECK(getHist(file.get(), "tightWP_lowPt_rhoCuts", m_tightWP_lowPt_rhoCuts));
145 ATH_CHECK(getHist(file.get(), "tightWP_lowPt_qOverPCuts", m_tightWP_lowPt_qOverPCuts));
146 ATH_CHECK(getHist(file.get(), "tightWP_mediumPt_rhoCuts", m_tightWP_mediumPt_rhoCuts));
147 ATH_CHECK(getHist(file.get(), "tightWP_highPt_rhoCuts", m_tightWP_highPt_rhoCuts));
148 //
149 file->Close();
150
151 // Read bad muon veto efficiency histograms
152 std::string BMVcutFile_fullPath = PathResolverFindCalibFile(m_BMVcutFile);
153
154 ATH_MSG_INFO("Reading bad muon veto cut functions from " << BMVcutFile_fullPath);
155 //
156 std::unique_ptr<TFile> BMVfile(TFile::Open(BMVcutFile_fullPath.c_str(), "READ"));
157
158 if (!BMVfile->IsOpen()) {
159 ATH_MSG_ERROR("Cannot read bad muon veto cut function file from " << BMVcutFile_fullPath);
160 return StatusCode::FAILURE;
161 }
162
163 m_BMVcutFunction_barrel = std::unique_ptr<TF1>((TF1*)BMVfile->Get("BMVcutFunction_barrel"));
164 m_BMVcutFunction_endcap = std::unique_ptr<TF1>((TF1*)BMVfile->Get("BMVcutFunction_endcap"));
165
166 BMVfile->Close();
167
169 ATH_MSG_ERROR("Cannot read bad muon veto cut functions");
170 return StatusCode::FAILURE;
171 }
172
173 if (m_useMVALowPt) {
174 if (m_isRun3) {
175 // Helper lambda to load BDT model and scaler
176 auto loadLowPtMVABDTModel = [&](const std::string& calibFile,
177 std::unique_ptr<MVAUtils::BDT>& bdt,
178 std::vector<double>& means,
179 std::vector<double>& scales) -> StatusCode {
180 const std::string modelFile = PathResolverFindCalibFile(calibFile);
181 std::unique_ptr<TFile> rootFile(TFile::Open(modelFile.c_str(), "READ"));
182
183 if (!rootFile || rootFile->IsZombie()) {
184 ATH_MSG_ERROR("Failed to open model file for Run-3 LowPtMVA: " << modelFile);
185 return StatusCode::FAILURE;
186 }
187
188 // Load BDT model
189 auto* bdt_tree = static_cast<TTree*>(rootFile->Get("xgboost"));
190 bdt = std::make_unique<MVAUtils::BDT>(bdt_tree);
191
192 // Load scaler
193 std::unique_ptr<TTree> scaler_tree(static_cast<TTree*>(rootFile->Get("scaler")));
194 std::vector<double>* mean_ptr = nullptr;
195 std::vector<double>* scale_ptr = nullptr;
196 scaler_tree->SetBranchAddress("mean", &mean_ptr);
197 scaler_tree->SetBranchAddress("scale", &scale_ptr);
198
199 for (Long64_t i = 0; i < scaler_tree->GetEntries(); ++i) {
200 scaler_tree->GetEntry(i);
201 for (size_t j = 0; j < mean_ptr->size(); ++j) {
202 means.push_back((*mean_ptr)[j]);
203 scales.push_back((*scale_ptr)[j]);
204 }
205 }
206 return StatusCode::SUCCESS;
207 };
208
209 // Load MuidCO model for Run-3 LowPtMVA
210 ATH_CHECK(loadLowPtMVABDTModel("MuonSelectorTools/260130_LowPtMVA_r24run3/xgb_lowPtMVA_MuidCO_wScaler.root",
212
213 // Load MuGirl model for Run-3 LowPtMVA
214 ATH_CHECK(loadLowPtMVABDTModel("MuonSelectorTools/260130_LowPtMVA_r24run3/xgb_lowPtMVA_MuGirl_wScaler.root",
216 }
217 else{
218 // Set up TMVA readers for MVA-based low-pT working point
219 // E and O refer to even and odd event numbers to avoid applying the MVA on events used for training
220 TString weightPath_EVEN_MuidCB = PathResolverFindCalibFile(m_MVAreaderFile_EVEN_MuidCB);
221 TString weightPath_ODD_MuidCB = PathResolverFindCalibFile(m_MVAreaderFile_ODD_MuidCB);
222 TString weightPath_EVEN_MuGirl = PathResolverFindCalibFile(m_MVAreaderFile_EVEN_MuGirl);
223 TString weightPath_ODD_MuGirl = PathResolverFindCalibFile(m_MVAreaderFile_ODD_MuGirl);
224
225 auto make_mva_reader = [](TString file_path) {
226 std::vector<std::string> mva_var_names{"momentumBalanceSignificance",
227 "scatteringCurvatureSignificance",
228 "scatteringNeighbourSignificance",
229 "EnergyLoss",
230 "middleLargeHoles+middleSmallHoles",
231 "muonSegmentDeltaEta",
232 "muonSeg1ChamberIdx",
233 "muonSeg2ChamberIdx"};
234 std::unique_ptr<TMVA::Reader> reader = std::make_unique<TMVA::Reader>(mva_var_names);
235 reader->BookMVA("BDTG", file_path);
236 return reader;
237 };
238 m_readerE_MUID = make_mva_reader(weightPath_EVEN_MuidCB);
239
240 m_readerO_MUID = make_mva_reader(weightPath_ODD_MuidCB);
241
242 m_readerE_MUGIRL = make_mva_reader(weightPath_EVEN_MuGirl);
243
244 m_readerO_MUGIRL = make_mva_reader(weightPath_ODD_MuGirl);
245
247 TString weightPath_MuTagIMO_etaBin1 = PathResolverFindCalibFile(m_MVAreaderFile_MuTagIMO_etaBin1);
248 TString weightPath_MuTagIMO_etaBin2 = PathResolverFindCalibFile(m_MVAreaderFile_MuTagIMO_etaBin2);
249 TString weightPath_MuTagIMO_etaBin3 = PathResolverFindCalibFile(m_MVAreaderFile_MuTagIMO_etaBin3);
250
251 auto make_mva_reader_MuTagIMO = [](TString file_path, bool useSeg2ChamberIndex) {
252 std::vector<std::string> mva_var_names;
253 if (useSeg2ChamberIndex) mva_var_names.push_back("muonSeg2ChamberIndex");
254 mva_var_names.push_back("muonSeg1ChamberIndex");
255 mva_var_names.push_back("muonSeg1NPrecisionHits");
256 mva_var_names.push_back("muonSegmentDeltaEta");
257 mva_var_names.push_back("muonSeg1GlobalR");
258 mva_var_names.push_back("muonSeg1Chi2OverDoF");
259 mva_var_names.push_back("muonSCS");
260
261 std::unique_ptr<TMVA::Reader> reader = std::make_unique<TMVA::Reader>(mva_var_names);
262 reader->BookMVA("BDT", file_path);
263 return reader;
264 };
265
266 m_reader_MUTAGIMO_etaBin1 = make_mva_reader_MuTagIMO(weightPath_MuTagIMO_etaBin1, false);
267 m_reader_MUTAGIMO_etaBin2 = make_mva_reader_MuTagIMO(weightPath_MuTagIMO_etaBin2, false);
268 m_reader_MUTAGIMO_etaBin3 = make_mva_reader_MuTagIMO(weightPath_MuTagIMO_etaBin3, true);
269 }
270 }
271 }
272
273 ATH_MSG_INFO("TightNNScore calculation is " << (m_calculateTightNNScore ? "enabled." : "disabled."));
274
276 if (m_onnxTool.empty()) {
277 ATH_MSG_ERROR("Cannot calculate TightNNScore: ONNX tool not configured! "
278 "Please set the ORTInferenceTool property to a valid AthOnnx::OnnxRuntimeInferenceTool instance.");
279 return StatusCode::FAILURE;
280 }
281
282 ATH_MSG_INFO("Retrieving ONNX tool: " << m_onnxTool.name());
283 ATH_CHECK(m_onnxTool.retrieve());
284 } else ATH_MSG_INFO("ONNX tool not configured — skipping retrieval.");
285
286 ATH_MSG_INFO("Finished ONNX tool setup");
287
288 ATH_CHECK(m_eventInfo.initialize());
289 // Return gracefully:
290 return StatusCode::SUCCESS;
291 }
292
293 StatusCode MuonSelectionTool::getHist(TFile* file, const std::string& histName, std::unique_ptr<TH1>& hist) const {
294 //
295 if (!file) {
296 ATH_MSG_ERROR(" getHist(...) TFile is nullptr! Check that the Tight cut map is loaded correctly");
297 return StatusCode::FAILURE;
298 }
299 TH1* h_ptr = nullptr;
300 file->GetObject(histName.c_str(), h_ptr);
301 //
302 //
303 if (!h_ptr) {
304 ATH_MSG_ERROR("Cannot retrieve histogram " << histName);
305 return StatusCode::FAILURE;
306 }
307 hist = std::unique_ptr<TH1>{h_ptr};
308 hist->SetDirectory(nullptr);
309 ATH_MSG_INFO("Successfully read tight working point histogram: " << hist->GetName());
310 //
311 return StatusCode::SUCCESS;
312 }
313
315
317 // Check if this is a muon:
318 if (p->type() != xAOD::Type::Muon) {
319 ATH_MSG_ERROR("accept(...) Function received a non-muon");
321 }
322
323 // Cast it to a muon:
324 const xAOD::Muon* mu = dynamic_cast<const xAOD::Muon*>(p);
325 if (!mu) {
326 ATH_MSG_FATAL("accept(...) Failed to cast particle to muon");
328 }
329
330 // Let the specific function do the work:
331 return accept(*mu);
332 }
333
334 //============================================================================
336
337 static std::atomic<bool> checkDone{false};
338
339 if(!checkDone) {
340 // Check that the user either set the correct geometry or enabled on-the-fly determination
341 // This can happen intentionally in developer mode. In any case don't throw an exception
342 // (we should either trust the user or ignore in the first place and force on-the-fly determination).
343 if (isRun3() != isRun3(true)) {
344 ATH_MSG_WARNING("MuonSelectionTool is configured with isRun3Geo="<<isRun3()
345 <<" while on-the fly check for runNumber "<<getRunNumber(true)<<" indicates isRun3Geo="<<isRun3(true));
346 }
347
348 // Check that the requested WP is currently supported by the MCP group.
349 if (isRun3()) {
350 if(m_quality!=0 && m_quality!=1 && m_quality!=2 && m_quality!=4 && m_quality!=5) {
351 ATH_MSG_WARNING("MuonSelectionTool currently supports Loose, Medium, Tight, HighPt, and LowPtEfficiency WPs for Run3; all other WPs can only be used in ExpertDevelopMode mode");
352 }
353
355 ATH_MSG_WARNING("For Run3, Tight WP is supported only when ExcludeNSWFromPrecisionLayers=False and RecalcPrecisionLayerswNSW=True");
356 }
357 }
358
359 checkDone = true;
360 }
361 }
362
363 //============================================================================
365 // Verbose information
366 ATH_MSG_VERBOSE("-----------------------------------");
367 ATH_MSG_VERBOSE("New muon passed to accept function:");
368 if (mu.muonType() == xAOD::Muon::Combined)
369 ATH_MSG_VERBOSE("Muon type: combined");
370 else if (mu.muonType() == xAOD::Muon::MuonStandAlone)
371 ATH_MSG_VERBOSE("Muon type: stand-alone");
372 else if (mu.muonType() == xAOD::Muon::SegmentTagged)
373 ATH_MSG_VERBOSE("Muon type: segment-tagged");
374 else if (mu.muonType() == xAOD::Muon::CaloTagged)
375 ATH_MSG_VERBOSE("Muon type: calorimeter-tagged");
376 else if (mu.muonType() == xAOD::Muon::SiliconAssociatedForwardMuon)
377 ATH_MSG_VERBOSE("Muon type: silicon-associated forward");
378 ATH_MSG_VERBOSE("Muon pT [GeV]: " << mu.pt() * MeVtoGeV);
379 ATH_MSG_VERBOSE("Muon eta: " << mu.eta());
380 ATH_MSG_VERBOSE("Muon phi: " << mu.phi());
381
382 checkSanity();
383
384 asg::AcceptData acceptData(&m_acceptInfo);
385
386 // Do the eta cut:
387 if (std::abs(mu.eta()) >= m_maxEta) {
388 ATH_MSG_VERBOSE("Failed eta cut");
389 return acceptData;
390 }
391 acceptData.setCutResult("Eta", true);
392
393 // Passes ID hit cuts
394 bool passIDCuts = passedIDCuts(mu);
395 ATH_MSG_VERBOSE("Passes ID Hit cuts " << passIDCuts);
396 acceptData.setCutResult("IDHits", passIDCuts);
397
398 // passes muon preselection
399 bool passMuonCuts = passedMuonCuts(mu);
400 ATH_MSG_VERBOSE("Passes preselection cuts " << passMuonCuts);
401 acceptData.setCutResult("Preselection", passMuonCuts);
402
403 if (!passIDCuts || !passMuonCuts) { return acceptData; }
404
405 // Passes quality requirements
406 xAOD::Muon::Quality thisMu_quality = getQuality(mu);
407 bool thisMu_highpt = false;
408 thisMu_highpt = passedHighPtCuts(mu);
409 bool thisMu_lowptE = false;
410 thisMu_lowptE = passedLowPtEfficiencyCuts(mu, thisMu_quality);
411 ATH_MSG_VERBOSE("Summary of quality information for this muon: ");
412 ATH_MSG_VERBOSE("Muon quality: " << thisMu_quality << " passes HighPt: " << thisMu_highpt
413 << " passes LowPtEfficiency: " << thisMu_lowptE);
414 if (m_quality < 4 && thisMu_quality > m_quality) { return acceptData; }
415 if (m_quality == 4 && !thisMu_highpt) { return acceptData; }
416 if (m_quality == 5 && !thisMu_lowptE) { return acceptData; }
417 acceptData.setCutResult("Quality", true);
418 // Return the result:
419 return acceptData;
420 }
421
423 mu.setQuality(getQuality(mu));
424 return;
425 }
426 void MuonSelectionTool::IdMsPt(const xAOD::Muon& mu, float& idPt, float& mePt) const {
427 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
428 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
429 if (!idtrack || !metrack) idPt = mePt = -1.;
430 else if (m_turnOffMomCorr) {
431 mePt = metrack->pt();
432 idPt = idtrack->pt();
433 } else {
434 if (!mePt_acc.isAvailable(mu) || !idPt_acc.isAvailable(mu)) {
435 ATH_MSG_FATAL("The muon with pT " << mu.pt() * MeVtoGeV << " eta: " << mu.eta() << ", phi:" << mu.phi()
436 << " q:" << mu.charge() << ", author:" << mu.author()
437 << " is not decorated with calibrated momenta. Please fix");
438 throw std::runtime_error("MuonSelectionTool() - qOverP significance calculation failed");
439 }
440 mePt = mePt_acc(mu);
441 idPt = idPt_acc(mu);
442 }
443 }
444
446 // Avoid spurious FPEs in the clang build.
448
449 if (m_disablePtCuts) {
450 ATH_MSG_VERBOSE(__FILE__ << ":"<<__LINE__
451 << " Momentum dependent cuts are disabled. Return 0.");
452 return 0.;
453 }
454 const xAOD::TrackParticle* idtrack = muon.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
455 const xAOD::TrackParticle* metrack = muon.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
456 if (!idtrack || !metrack) {
457 ATH_MSG_VERBOSE("No ID / MS track. Return dummy large value of 1 mio");
458 return 1.e6;
459 }
460 float mePt{-1.}, idPt{-1.};
461 IdMsPt(muon, idPt, mePt);
462
463 const float meP = mePt / std::sin(metrack->theta());
464 const float idP = idPt / std::sin(idtrack->theta());
465
466 float qOverPsigma = std::sqrt(idtrack->definingParametersCovMatrix()(4, 4) + metrack->definingParametersCovMatrix()(4, 4));
467 return std::abs((metrack->charge() / meP) - (idtrack->charge() / idP)) / qOverPsigma;
468 }
470 if (m_disablePtCuts) {
471 ATH_MSG_VERBOSE(__FILE__ << ":"<<__LINE__
472 << "Momentum dependent cuts are disabled. Return 0.");
473 return 0.;
474 }
475 float mePt{-1.}, idPt{-1.};
476 IdMsPt(muon, idPt, mePt);
477 return std::abs(idPt - mePt) / muon.pt();
478 }
479
480 xAOD::Muon::Quality MuonSelectionTool::getQuality(const xAOD::Muon& mu) const {
481 ATH_MSG_VERBOSE("Evaluating muon quality...");
482 if (isRun3() && mu.isAuthor(xAOD::Muon::Author::Commissioning) && !m_allowComm) {
483 ATH_MSG_VERBOSE("Reject authors from the commissioning chain");
484 return xAOD::Muon::VeryLoose;
485 }
486
487 // SegmentTagged muons
488 if (mu.muonType() == xAOD::Muon::SegmentTagged) {
489 ATH_MSG_VERBOSE("Muon is segment-tagged");
490
491 if (std::abs(mu.eta()) < 0.1) {
492 ATH_MSG_VERBOSE("Muon is loose");
493 return xAOD::Muon::Loose;
494 } else {
495 ATH_MSG_VERBOSE("Do not allow segment-tagged muon at |eta| > 0.1 - return VeryLoose");
496 return xAOD::Muon::VeryLoose;
497 }
498 }
499
500 // CaloTagged muons
501 if (mu.muonType() == xAOD::Muon::CaloTagged) {
502 ATH_MSG_VERBOSE("Muon is calorimeter-tagged");
503
504 if (std::abs(mu.eta()) < 0.1 && passedCaloTagQuality(mu)) {
505 ATH_MSG_VERBOSE("Muon is loose");
506 return xAOD::Muon::Loose;
507 }
508 }
509
510 // Combined muons
511 hitSummary summary{};
512 fillSummary(mu, summary);
513
514 if (mu.muonType() == xAOD::Muon::Combined) {
515 ATH_MSG_VERBOSE("Muon is combined");
516 if (mu.author() == xAOD::Muon::STACO) {
517 ATH_MSG_VERBOSE("Muon is STACO - return VeryLoose");
518 return xAOD::Muon::VeryLoose;
519 }
520
521 // rejection muons with out-of-bounds hits
524
526 ATH_MSG_VERBOSE("Muon has out-of-bounds precision hits - return VeryLoose");
527 return xAOD::Muon::VeryLoose;
528 }
529
530 // LOOSE / MEDIUM / TIGHT WP
531 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
532 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
533 if (idtrack && metrack && metrack->definingParametersCovMatrix()(4, 4) > 0) {
534 const float qOverPsignif = qOverPsignificance(mu);
535 const float rho = rhoPrime(mu);
536 const float reducedChi2 = mu.primaryTrackParticle()->chiSquared() / mu.primaryTrackParticle()->numberDoF();
537
538 ATH_MSG_VERBOSE("Relevant cut variables:");
539 ATH_MSG_VERBOSE("number of precision layers = " << (int)summary.nprecisionLayers);
540 ATH_MSG_VERBOSE("reduced Chi2 = " << reducedChi2);
541 ATH_MSG_VERBOSE("qOverP significance = " << qOverPsignif);
542
543 // NEW TIGHT WP
544 if (summary.nprecisionLayers > 1 && reducedChi2 < 8 && std::abs(qOverPsignif) < 7) {
545 if (passTight(mu, rho, qOverPsignif)) {
546 ATH_MSG_VERBOSE("Muon is tight");
547 return xAOD::Muon::Tight;
548 }
549 }
550
551 ATH_MSG_VERBOSE("Muon did not pass requirements for tight combined muon");
552
553 // MEDIUM WP
554 if ((std::abs(qOverPsignif) < 7 || m_toroidOff) &&
555 (summary.nprecisionLayers > 1 ||(summary.nprecisionLayers == 1 && summary.nprecisionHoleLayers < 2 && std::abs(mu.eta()) < 0.1))
556
557 ) {
558 ATH_MSG_VERBOSE("Muon is medium");
559 return xAOD::Muon::Medium;
560 }
561
562 ATH_MSG_VERBOSE("Muon did not pass requirements for medium combined muon");
563
564 } else {
565 ATH_MSG_VERBOSE("Muon is missing the ID and/or ME tracks...");
566
567 // CB muons with missing ID or ME track
568 if ((summary.nprecisionLayers > 1 ||
569 (summary.nprecisionLayers == 1 && summary.nprecisionHoleLayers < 2 && std::abs(mu.eta()) < 0.1))) {
570 // In toroid-off data ME/MS tracks often missing - need special treatment => flagging as "Medium"
571 // In toroid-on data ME/MS tracks missing only for <1% of CB muons, mostly MuGirl (to be fixed) => flagging as "Loose"
572 if (m_toroidOff) {
573 ATH_MSG_VERBOSE("...this is toroid-off data - returning medium");
574 return xAOD::Muon::Medium;
575 } else {
576 ATH_MSG_VERBOSE("...this is not toroid-off data - returning loose");
577 return xAOD::Muon::Loose;
578 }
579 }
580 }
581
582 // Improvement for Loose targeting low-pT muons (pt<7 GeV)
583 if ((m_disablePtCuts || mu.pt() * MeVtoGeV < 7.) && std::abs(mu.eta()) < 1.3 && summary.nprecisionLayers > 0 &&
584 (mu.author() == xAOD::Muon::MuGirl && mu.isAuthor(xAOD::Muon::MuTagIMO))) {
585 ATH_MSG_VERBOSE("Muon passed selection for loose working point at low pT");
586 return xAOD::Muon::Loose;
587 }
588
589 // didn't pass the set of requirements for a medium or tight combined muon
590 ATH_MSG_VERBOSE("Did not pass selections for combined muon - returning VeryLoose");
591 return xAOD::Muon::VeryLoose;
592 }
593
594 // SA muons
595 if (mu.author() == xAOD::Muon::MuidSA) {
596 ATH_MSG_VERBOSE("Muon is stand-alone");
597
598 if (std::abs(mu.eta()) > 2.5) {
599 ATH_MSG_VERBOSE("number of precision layers = " << (int)summary.nprecisionLayers);
600
601 // 3 station requirement for medium
602 if (summary.nprecisionLayers > 2 && !m_toroidOff) {
603 ATH_MSG_VERBOSE("Muon is medium");
604 return xAOD::Muon::Medium;
605 }
606 }
607
608 // didn't pass the set of requirements for a medium SA muon
609 ATH_MSG_VERBOSE("Muon did not pass selection for medium stand-alone muon - return VeryLoose");
610 return xAOD::Muon::VeryLoose;
611 }
612
613 // SiliconAssociatedForward (SAF) muons
614 if (mu.muonType() == xAOD::Muon::SiliconAssociatedForwardMuon) {
615 ATH_MSG_VERBOSE("Muon is silicon-associated forward muon");
616
617 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
618 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
619
620 if (cbtrack && metrack) {
621 if (std::abs(cbtrack->eta()) > 2.5) {
622 ATH_MSG_VERBOSE("number of precision layers = " << (int)summary.nprecisionLayers);
623
624 if (summary.nprecisionLayers > 2 && !m_toroidOff) {
625 if (mu.trackParticle(xAOD::Muon::Primary) == mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle) &&
626 !m_developMode) {
628 "SiliconForwardAssociated muon has ID track as primary track particle. "
629 << "This is a bug fixed starting with xAODMuon-00-17-07, which should be present in this release. "
630 << "Please report this to the Muon CP group!");
631 }
632 ATH_MSG_VERBOSE("Muon is medium");
633 return xAOD::Muon::Medium;
634 }
635 }
636 }
637
638 // didn't pass the set of requirements for a medium SAF muon
639 ATH_MSG_VERBOSE("Muon did not pass selection for medium silicon-associated forward muon - return VeryLoose");
640 return xAOD::Muon::VeryLoose;
641 }
642
643 ATH_MSG_VERBOSE("Muon did not pass selection for loose/medium/tight for any muon type - return VeryLoose");
644 return xAOD::Muon::VeryLoose;
645 }
646
647 void MuonSelectionTool::setPassesIDCuts(xAOD::Muon& mu) const { mu.setPassesIDCuts(passedIDCuts(mu)); }
648
650 if (m_useLRT) {
651 static const SG::AuxElement::Accessor<char> isLRTmuon("isLRT");
652 if (isLRTmuon.isAvailable(mu)) {
653 if (isLRTmuon(mu)) return true;
654 }
655 else {
656 static const SG::AuxElement::Accessor<uint64_t> patternAcc("patternRecoInfo");
657 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
658 if(idtrack) {
659 if(!patternAcc.isAvailable(*idtrack)) {
660 ATH_MSG_FATAL("No information available to tell if the muon is LRT or standard. Either run MuonLRTMergingAlg to decorate with `isLRT` flag, or supply the patternRecoInfo for the original ID track.");
661 throw std::runtime_error("MuonSelectionTool() - isLRT decor and patternRecoInfo both unavailable for a muon.");
662 }
663 std::bitset<xAOD::NumberOfTrackRecoInfo> patternBitSet(patternAcc(*idtrack));
664 if (patternBitSet.test(xAOD::SiSpacePointsSeedMaker_LargeD0)) return true;
665 }
666 }
667 }
668 // do not apply the ID hit requirements for SA muons for |eta| > 2.5
669 if (mu.author() == xAOD::Muon::MuidSA && std::abs(mu.eta()) > 2.5) {
670 return true;
671 } else if (mu.muonType() == xAOD::Muon::SiliconAssociatedForwardMuon) {
672 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
673 if (cbtrack && std::abs(cbtrack->eta()) > 2.5) { return true; }
674 return false;
675 } else {
676 if (mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle))
677 return passedIDCuts(*mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle));
678 else if (mu.primaryTrackParticle())
679 return passedIDCuts(*mu.primaryTrackParticle());
680 }
681 return false;
682 }
683
685 if (mu.muonType() != xAOD::Muon::Combined) return false;
686 // ::
687 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
688 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
689 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
690 // ::
691 // Some spurious muons are found to have negative ME track fit covariance, and are typically poorly reconstructed
692 if (metrack && metrack->definingParametersCovMatrix()(4, 4) < 0.0) return true;
693 // ::
694 bool IsBadMuon = false;
695 if (idtrack && metrack && cbtrack) {
696 // ::
697 double qOverP_ID = idtrack->qOverP();
698 double qOverPerr_ID = std::sqrt(idtrack->definingParametersCovMatrix()(4, 4));
699 double qOverP_ME = metrack->qOverP();
700 double qOverPerr_ME = std::sqrt(metrack->definingParametersCovMatrix()(4, 4));
701 double qOverP_CB = cbtrack->qOverP();
702 double qOverPerr_CB = std::sqrt(cbtrack->definingParametersCovMatrix()(4, 4));
703 // ::
704 if (m_quality == 4) {
705 // recipe for high-pt selection
706 IsBadMuon = !passedErrorCutCB(mu);
707
708 hitSummary summary{};
709 fillSummary(mu, summary);
710
711 // temporarily apply same recipe as for other working points in addition to CB error
712 // cut for 2-station muons, pending better treatment of ID/MS misalignments
713 if (m_use2stationMuonsHighPt && summary.nprecisionLayers == 2) {
714 double IdCbRatio = std::abs((qOverPerr_ID / qOverP_ID) / (qOverPerr_CB / qOverP_CB));
715 double MeCbRatio = std::abs((qOverPerr_ME / qOverP_ME) / (qOverPerr_CB / qOverP_CB));
716 IsBadMuon = (IdCbRatio < 0.8 || MeCbRatio < 0.8 || IsBadMuon);
717 }
718 } else {
719 // recipe for other WP
720 double IdCbRatio = std::abs((qOverPerr_ID / qOverP_ID) / (qOverPerr_CB / qOverP_CB));
721 double MeCbRatio = std::abs((qOverPerr_ME / qOverP_ME) / (qOverPerr_CB / qOverP_CB));
722 IsBadMuon = (IdCbRatio < 0.8 || MeCbRatio < 0.8);
723 }
724 } else {
725 return true;
726 }
727 return IsBadMuon;
728 }
729
731 xAOD::Muon::Quality thisMu_quality = getQuality(mu);
732 return passedLowPtEfficiencyCuts(mu, thisMu_quality);
733 }
734
735 bool MuonSelectionTool::passedLowPtEfficiencyCuts(const xAOD::Muon& mu, xAOD::Muon::Quality thisMu_quality) const {
736 ATH_MSG_VERBOSE("Checking whether muon passes low-pT selection...");
737
738 if (!m_useAllAuthors) { // no allAuthors, always fail the WP
739 ATH_MSG_VERBOSE("Do not have allAuthors variable - fail low-pT");
740 return false;
741 }
742
743 // requiring combined muons, unless segment-tags are included
745 if (mu.muonType() != xAOD::Muon::Combined) {
746 ATH_MSG_VERBOSE("Muon is not combined - fail low-pT");
747 return false;
748 }
749 } else {
750 if (mu.muonType() != xAOD::Muon::Combined && mu.muonType() != xAOD::Muon::SegmentTagged) {
751 ATH_MSG_VERBOSE("Muon is not combined or segment-tagged - fail low-pT");
752 return false;
753 }
754 }
755
756 // author check
758 if (mu.author() != xAOD::Muon::MuGirl && mu.author() != xAOD::Muon::MuidCo) {
759 ATH_MSG_VERBOSE("Muon is neither MuGirl nor MuidCo - fail low-pT");
760 return false;
761 }
762 } else {
763 if (mu.author() != xAOD::Muon::MuGirl && mu.author() != xAOD::Muon::MuidCo && mu.author() != xAOD::Muon::MuTagIMO) {
764 ATH_MSG_VERBOSE("Muon is neither MuGirl / MuidCo / MuTagIMO - fail low-pT");
765 return false;
766 }
767 }
768
769 // applying Medium selection above pT = 18 GeV
770 if (mu.pt() * MeVtoGeV > 18.) {
771 ATH_MSG_VERBOSE("pT > 18 GeV - apply medium selection");
772 if (thisMu_quality <= xAOD::Muon::Medium) {
773 ATH_MSG_VERBOSE("Muon passed low-pT selection");
774 return true;
775 } else {
776 ATH_MSG_VERBOSE("Muon failed low-pT selection");
777 return false;
778 }
779 }
780
781 // requiring Medium in forward regions
782 if (!m_useMVALowPt && std::abs(mu.eta()) > 1.55 && thisMu_quality > xAOD::Muon::Medium) {
783 ATH_MSG_VERBOSE("Not using MVA selection, failing low-pT selection due to medium requirement in forward region");
784 return false;
785 }
786
787 // rejection of muons with out-of-bounds hits
791 ATH_MSG_VERBOSE("Muon has out-of-bounds precision hits - fail low-pT");
792 return false;
793 }
794
795 // requiring explicitely >=1 station (2 in the |eta|>1.3 region when Medium selection is not explicitely required)
796 if (mu.muonType() == xAOD::Muon::Combined) {
797 hitSummary summary{};
798 fillSummary(mu, summary);
799 uint nStationsCut = (std::abs(mu.eta()) > 1.3 && std::abs(mu.eta()) < 1.55) ? 2 : 1;
800 if (summary.nprecisionLayers < nStationsCut) {
801 ATH_MSG_VERBOSE("number of precision layers = " << (int)summary.nprecisionLayers << " is lower than cut value " << nStationsCut
802 << " - fail low-pT");
803 return false;
804 }
805 }
806
807 // reject MuGirl muon if not found also by MuTagIMO
808 if (m_useAllAuthors) {
809 if (mu.author() == xAOD::Muon::MuGirl && !mu.isAuthor(xAOD::Muon::MuTagIMO)) {
810 ATH_MSG_VERBOSE("MuGirl muon is not confirmed by MuTagIMO - fail low-pT");
811 return false;
812 }
813 } else
814 return false;
815
816 if (m_useMVALowPt) {
817 if (isRun3()) {
818 ATH_MSG_VERBOSE("Applying Run-3 MVA-based selection");
820 } else {
821 ATH_MSG_VERBOSE("Applying Run-2 MVA-based selection");
823 }
824
825 }
826
827 ATH_MSG_VERBOSE("Applying cut-based selection");
828
829 // apply some loose quality requirements
830 float momentumBalanceSignificance{0.}, scatteringCurvatureSignificance{0.}, scatteringNeighbourSignificance{0.};
831
832 retrieveParam(mu, momentumBalanceSignificance, xAOD::Muon::momentumBalanceSignificance);
833 retrieveParam(mu, scatteringCurvatureSignificance, xAOD::Muon::scatteringCurvatureSignificance);
834 retrieveParam(mu, scatteringNeighbourSignificance, xAOD::Muon::scatteringNeighbourSignificance);
835
836 ATH_MSG_VERBOSE("momentum balance significance: " << momentumBalanceSignificance);
837 ATH_MSG_VERBOSE("scattering curvature significance: " << scatteringCurvatureSignificance);
838 ATH_MSG_VERBOSE("scattering neighbour significance: " << scatteringNeighbourSignificance);
839
840 if (std::abs(momentumBalanceSignificance) > 3. || std::abs(scatteringCurvatureSignificance) > 3. ||
841 std::abs(scatteringNeighbourSignificance) > 3.) {
842 ATH_MSG_VERBOSE("Muon failed cut-based low-pT selection");
843 return false;
844 }
845
846 // passed low pt selection
847 ATH_MSG_VERBOSE("Muon passed cut-based low-pT selection");
848 return true;
849 }
850
851 std::vector<const xAOD::MuonSegment*> MuonSelectionTool::getSegmentsSorted(const xAOD::Muon& mu) const {
852 std::vector<const xAOD::MuonSegment*> segments_sorted;
853 segments_sorted.reserve(mu.nMuonSegments());
854
855 for (unsigned int i = 0; i < mu.nMuonSegments(); i++) {
856 if (!mu.muonSegment(i))
857 ATH_MSG_WARNING("The muon reports more segments than are available. Please report this to the muon software community!");
858 else
859 segments_sorted.push_back(mu.muonSegment(i));
860 }
861
862 std::sort(segments_sorted.begin(), segments_sorted.end(), chamberIndexCompare);
863
864 return segments_sorted;
865 }
866
868 //LowPt Not supported in run3 for the time being
869 if(isRun3() && !m_developMode){
870 ATH_MSG_VERBOSE("LowPt WP currently not supported for run3 if not in expert mode");
871 return false;
872 }
873 if (!m_useMVALowPt) {
874 ATH_MSG_DEBUG("Low pt MVA disabled. Return... ");
875 return false;
876 }
877 std::lock_guard<std::mutex> guard(m_low_pt_mva_mutex);
878 // set values for all BDT input variables from the muon in question
879 float momentumBalanceSig{-1}, CurvatureSig{-1}, energyLoss{-1}, muonSegmentDeltaEta{-1}, scatteringNeigbour{-1};
880 retrieveParam(mu, momentumBalanceSig, xAOD::Muon::momentumBalanceSignificance);
881 retrieveParam(mu, CurvatureSig, xAOD::Muon::scatteringCurvatureSignificance);
882 retrieveParam(mu, scatteringNeigbour, xAOD::Muon::scatteringNeighbourSignificance);
883 retrieveParam(mu, energyLoss, xAOD::Muon::EnergyLoss);
884 retrieveParam(mu, muonSegmentDeltaEta, xAOD::Muon::segmentDeltaEta);
885
889
890 float seg1ChamberIdx{-1.}, seg2ChamberIdx{-1.}, middleHoles{-1.}, seg1NPrecisionHits{-1.}, seg1GlobalR{-1.}, seg1Chi2OverDoF{-1.};
891
892 std::vector<const xAOD::MuonSegment*> muonSegments = getSegmentsSorted(mu);
893
894 if (mu.author() == xAOD::Muon::MuTagIMO && muonSegments.size() == 0)
895 ATH_MSG_WARNING("passedLowPtEfficiencyMVACut - found segment-tagged muon with no segments!");
896
897 using namespace Muon::MuonStationIndex;
898 seg1ChamberIdx = (!muonSegments.empty()) ? toInt(muonSegments[0]->chamberIndex()) : -9;
899 seg2ChamberIdx = (muonSegments.size() > 1) ? toInt(muonSegments[1]->chamberIndex()) : -9;
900
901 // these variables are only used for MuTagIMO
902 if (mu.author() == xAOD::Muon::MuTagIMO) {
903 seg1NPrecisionHits = (!muonSegments.empty()) ? muonSegments[0]->nPrecisionHits() : -1;
904 seg1GlobalR = (!muonSegments.empty())
905 ? std::hypot(muonSegments[0]->x(), muonSegments[0]->y(), muonSegments[0]->z())
906 : 0;
907 seg1Chi2OverDoF = (!muonSegments.empty()) ? muonSegments[0]->chiSquared() / muonSegments[0]->numberDoF() : -1;
908 }
909
910 middleHoles = middleSmallHoles + middleLargeHoles;
911
912 // get event number from event info
914 //overwrite event number
915 unsigned long long eventNumber = 0;
917 else eventNumber = eventInfo->eventNumber();
918
919 // variables for the BDT
920 std::vector<float> var_vector;
921 if (mu.author() == xAOD::Muon::MuidCo || mu.author() == xAOD::Muon::MuGirl) {
922 var_vector = {momentumBalanceSig, CurvatureSig, scatteringNeigbour, energyLoss,
923 middleHoles, muonSegmentDeltaEta, seg1ChamberIdx, seg2ChamberIdx};
924 } else {
925 if (std::abs(mu.eta()) >= 1.3)
926 var_vector = {seg2ChamberIdx, seg1ChamberIdx, seg1NPrecisionHits, muonSegmentDeltaEta,
927 seg1GlobalR, seg1Chi2OverDoF, std::abs(CurvatureSig)};
928 else
929 var_vector = {seg1ChamberIdx, seg1NPrecisionHits, muonSegmentDeltaEta,
930 seg1GlobalR, seg1Chi2OverDoF, std::abs(CurvatureSig)};
931 }
932
933 // use different trainings for even/odd numbered events
934 TMVA::Reader *reader_MUID, *reader_MUGIRL;
935 if (eventNumber % 2 == 1) {
936 reader_MUID = m_readerE_MUID.get();
937 reader_MUGIRL = m_readerE_MUGIRL.get();
938 } else {
939 reader_MUID = m_readerO_MUID.get();
940 reader_MUGIRL = m_readerO_MUGIRL.get();
941 }
942
943 // BDT for MuTagIMO is binned in |eta|
944 TMVA::Reader* reader_MUTAGIMO;
945 if (std::abs(mu.eta()) < 0.7)
946 reader_MUTAGIMO = m_reader_MUTAGIMO_etaBin1.get();
947 else if (std::abs(mu.eta()) < 1.3)
948 reader_MUTAGIMO = m_reader_MUTAGIMO_etaBin2.get();
949 else
950 reader_MUTAGIMO = m_reader_MUTAGIMO_etaBin3.get();
951
952 // get the BDT discriminant response
953 float BDTdiscriminant;
954
955 if (mu.author() == xAOD::Muon::MuidCo)
956 BDTdiscriminant = reader_MUID->EvaluateMVA(var_vector, "BDTG");
957 else if (mu.author() == xAOD::Muon::MuGirl)
958 BDTdiscriminant = reader_MUGIRL->EvaluateMVA(var_vector, "BDTG");
959 else if (mu.author() == xAOD::Muon::MuTagIMO && m_useSegmentTaggedLowPt)
960 BDTdiscriminant = reader_MUTAGIMO->EvaluateMVA(var_vector, "BDT");
961 else {
962 ATH_MSG_WARNING("Invalid author for low-pT MVA, failing selection...");
963 return false;
964 }
965
966 // cut on dicriminant
967 float BDTcut = (mu.author() == xAOD::Muon::MuTagIMO) ? 0.12 : -0.6;
968
969 if (BDTdiscriminant > BDTcut) {
970 ATH_MSG_VERBOSE("Passed low-pT MVA cut");
971 return true;
972 } else {
973 ATH_MSG_VERBOSE("Failed low-pT MVA cut");
974 return false;
975 }
976 }
977
979 if (!m_useMVALowPt) {
980 ATH_MSG_DEBUG("Low pt MVA disabled. Return... ");
981 return false;
982 }
983
984 if (mu.author() == xAOD::Muon::MuidCo) {
985 ATH_MSG_VERBOSE("passedLowPtEfficiencyMVACutRun3() for MuidCO");
986
987 //-- Prepare BDT input feature variables
988 float momentumBalanceSig{-1}, CurvatureSig{-1}, scatteringNeigbour{-1};
989 int CaloMuonIDTag{-1};
990 uint8_t nPixelHits{0}, nTRTOutliers{0};
991 float reducedChi2{-1};
992 float etaBalanceSig{-1}, phiBalanceSig{-1};
993 float seg1ChamberIdx{-1};
994
995 retrieveParam(mu, momentumBalanceSig, xAOD::Muon::momentumBalanceSignificance);
996 retrieveParam(mu, CurvatureSig, xAOD::Muon::scatteringCurvatureSignificance);
997 retrieveParam(mu, scatteringNeigbour, xAOD::Muon::scatteringNeighbourSignificance);
998
1001
1002 mu.parameter(CaloMuonIDTag, xAOD::Muon::CaloMuonIDTag);
1003
1004 reducedChi2 = mu.primaryTrackParticle()->chiSquared() / mu.primaryTrackParticle()->numberDoF();
1005
1006 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
1007 const xAOD::TrackParticle* metrk = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
1008
1009 etaBalanceSig = std::abs(idtrack->eta() - metrk->eta());
1010 phiBalanceSig = std::abs(idtrack->phi() - metrk->phi());
1011
1012 std::vector<const xAOD::MuonSegment*> muonSegments = getSegmentsSorted(mu);
1013 using namespace Muon::MuonStationIndex;
1014 seg1ChamberIdx = (!muonSegments.empty()) ? toInt(muonSegments[0]->chamberIndex()) : -9;
1015
1016 hitSummary summary{};
1017 fillSummary(mu, summary);
1018
1019 //-- Apply clipping
1020 etaBalanceSig = std::min(etaBalanceSig, 1.0f);
1021 reducedChi2 = std::min(reducedChi2, 100.0f);
1022 CurvatureSig = std::clamp(CurvatureSig, -10.0f, 10.0f);
1023 scatteringNeigbour = std::clamp(scatteringNeigbour, -10.0f, 10.0f);
1024
1025 //-- Prepare BDT input feature variables vector
1026 std::vector<float> muidCO_feature_vector = {
1027 static_cast<float>(nPixelHits),
1028 static_cast<float>(nTRTOutliers),
1029 static_cast<float>(CaloMuonIDTag),
1030 static_cast<float>(mu.energyLossType()),
1031 etaBalanceSig,
1032 momentumBalanceSig,
1033 static_cast<float>(summary.nprecisionLayers),
1034 phiBalanceSig,
1035 reducedChi2,
1036 CurvatureSig,
1037 scatteringNeigbour,
1038 seg1ChamberIdx
1039 };
1040
1041 //-- Apply scaling
1042 for (size_t j=0; j<muidCO_feature_vector.size(); ++j){
1043 muidCO_feature_vector[j] = (muidCO_feature_vector[j] - m_lowPtMuidCO_means[j]) / m_lowPtMuidCO_scaler[j];
1044 }
1045
1046 //-- Get BDT response
1047 float bdt_score = m_MuidCO->GetClassification(muidCO_feature_vector);
1048 ATH_MSG_VERBOSE(" Low-pT MVA BDT score (MuidCo, Run-3) : " << bdt_score);
1049
1050 //-- cut on discriminant (value motivated by study, slide 10):
1051 // https://indico.cern.ch/event/1639238/contributions/6896745/attachments/3208593/5714230/Run-3%20LowPt%20MVA%20WP%20Update.pdf
1052 float lowPtMVARun3_MuidCO_cut_value = 0.1300;
1053 return (bdt_score > lowPtMVARun3_MuidCO_cut_value);
1054
1055 } else if (mu.author() == xAOD::Muon::MuGirl && mu.isAuthor(xAOD::Muon::MuTagIMO)) {
1056 ATH_MSG_VERBOSE("passedLowPtEfficiencyMVACutRun3() for MuGirl");
1057
1058 //-- Prepare BDT input feature variables
1060 float momentumBalanceSig{-1}, seg1ChamberIdx{-1}, energyLoss{-1};
1061 int CaloMuonIDTag{-1};
1062 float reducedChi2{-1}, etaBalanceSig{-1}, phiBalanceSig{-1};
1063 float segmentDeltaEta{-1}, etaPrime{-1}, innerHits{-1};
1064 float middleHoles{-1}, nGoodPrecLayers{-1};
1065 float nprecisionHoleLayers{-1}, outerHoles{-1};
1066
1067 retrieveParam(mu, momentumBalanceSig, xAOD::Muon::momentumBalanceSignificance);
1068 retrieveParam(mu, segmentDeltaEta, xAOD::Muon::segmentDeltaEta);
1069 retrieveParam(mu, energyLoss, xAOD::Muon::EnergyLoss);
1070
1080
1081 mu.parameter(CaloMuonIDTag, xAOD::Muon::CaloMuonIDTag);
1082
1083 middleHoles = middleSmallHoles + middleLargeHoles;
1084 outerHoles = outerSmallHoles + outerLargeHoles;
1085 reducedChi2 = mu.primaryTrackParticle()->chiSquared() / mu.primaryTrackParticle()->numberDoF();
1086
1087 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
1088 const xAOD::TrackParticle* metrk = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
1089
1090 etaBalanceSig = std::abs(idtrack->eta() - metrk->eta());
1091 etaPrime = std::abs((idtrack->eta() - metrk->eta())/mu.eta());
1092 phiBalanceSig = std::abs(idtrack->phi() - metrk->phi());
1093
1094 std::vector<const xAOD::MuonSegment*> muonSegments = getSegmentsSorted(mu);
1095 using namespace Muon::MuonStationIndex;
1096 seg1ChamberIdx = (!muonSegments.empty()) ? toInt(muonSegments[0]->chamberIndex()) : -9;
1097
1098 hitSummary summary{};
1099 fillSummary(mu, summary);
1100 innerHits = summary.innerSmallHits + summary.innerLargeHits;
1101 nGoodPrecLayers = summary.nGoodPrecLayers;
1102 nprecisionHoleLayers = summary.nprecisionHoleLayers;
1103
1104 //-- Apply clipping
1105 etaBalanceSig = std::min(etaBalanceSig, 1.0f);
1106 reducedChi2 = std::min(reducedChi2, 100.0f);
1107 etaPrime = std::clamp(etaPrime, -10.0f, 10.0f);
1108
1109 //-- Prepare BDT input feature variables vector
1110 std::vector<float> muGirl_feature_vector = {
1111 static_cast<float>(nTRTOutliers),
1112 static_cast<float>(CaloMuonIDTag),
1113 energyLoss,
1114 etaBalanceSig,
1115 etaPrime,
1116 innerHits,
1117 static_cast<float>(middleClosePrecisionHits),
1118 middleHoles,
1119 momentumBalanceSig,
1120 nGoodPrecLayers,
1121 nprecisionHoleLayers,
1122 static_cast<float>(summary.nprecisionLayers),
1123 static_cast<float>(outerClosePrecisionHits),
1124 outerHoles,
1125 phiBalanceSig,
1126 reducedChi2,
1127 seg1ChamberIdx,
1128 segmentDeltaEta
1129 };
1130
1131 //-- Apply scaling
1132 for (size_t j=0; j<muGirl_feature_vector.size(); ++j){
1133 muGirl_feature_vector[j] = (muGirl_feature_vector[j] - m_lowPtMuGirl_means[j]) / m_lowPtMuGirl_scaler[j];
1134 }
1135
1136 //-- Get BDT response
1137 float bdt_score = m_MuGirl->GetClassification(muGirl_feature_vector);
1138 ATH_MSG_VERBOSE(" Low-pT MVA BDT score (MuGirl, Run-3) : " << bdt_score);
1139
1140 //-- cut on discriminant (value motivated by study, slide 11):
1141 // https://indico.cern.ch/event/1639238/contributions/6896745/attachments/3208593/5714230/Run-3%20LowPt%20MVA%20WP%20Update.pdf
1142 float lowPtMVARun3_MuGirl_cut_value = 0.1550;
1143 return (bdt_score > lowPtMVARun3_MuGirl_cut_value);
1144 } else {
1145 ATH_MSG_WARNING("Invalid author for low-pT MVA in Run-3, failing selection...");
1146 return false;
1147 }
1148 }
1149
1151 ATH_MSG_VERBOSE("Checking whether muon passes high-pT selection...");
1152
1153 // :: Request combined muons
1154 if (mu.muonType() != xAOD::Muon::Combined) {
1155 ATH_MSG_VERBOSE("Muon is not combined - fail high-pT");
1156 return false;
1157 }
1158 if (mu.author() == xAOD::Muon::STACO) {
1159 ATH_MSG_VERBOSE("Muon is STACO - fail high-pT");
1160 return false;
1161 }
1162
1163 // :: Reject muons with out-of-bounds hits
1167 ATH_MSG_VERBOSE("Muon has out-of-bounds precision hits - fail high-pT");
1168 return false;
1169 }
1170
1171 // :: Access MS hits information
1172 hitSummary summary{};
1173 fillSummary(mu, summary);
1174
1175
1176 ATH_MSG_VERBOSE("number of precision layers: " << (int)summary.nprecisionLayers);
1177
1178 //::: Apply MS Chamber Vetoes
1179 // Given according to their eta-phi locations in the muon spectrometer
1180 // FORM: CHAMBERNAME[ array of four values ] = { eta 1, eta 2, phi 1, phi 2}
1181 // The vetoes are applied based on the MS track if available. If the MS track is not available,
1182 // the vetoes are applied according to the combined track, and runtime warning is printed to
1183 // the command line.
1184 const xAOD::TrackParticle* CB_track = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1185 const xAOD::TrackParticle* MS_track = mu.trackParticle(xAOD::Muon::MuonSpectrometerTrackParticle);
1186 if (!MS_track) {
1187 ATH_MSG_VERBOSE("passedHighPtCuts - No MS track available for muon. Using combined track.");
1188 MS_track = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1189 }
1190
1191 if (MS_track && CB_track) {
1192 float etaMS = MS_track->eta();
1193 float phiMS = MS_track->phi();
1194 float etaCB = CB_track->eta();
1195
1196 //::: no unspoiled clusters in CSC
1197 if (!isRun3() && (std::abs(etaMS) > 2.0 || std::abs(etaCB) > 2.0)) {
1198 if (summary.cscUnspoiledEtaHits == 0) {
1199 ATH_MSG_VERBOSE("Muon has only spoiled CSC clusters - fail high-pT");
1200 return false;
1201 }
1202 }
1203
1204 // veto bad CSC giving troubles with scale factors
1205 if (!isRun3() && mu.eta() < -1.899 && std::abs(mu.phi()) < 0.211) {
1206 ATH_MSG_VERBOSE("Muon is in eta/phi region vetoed due to disabled chambers in MC - fail high-pT");
1207 return false;
1208 }
1209
1210 //::: Barrel/Endcap overlap region
1211 if ((1.01 < std::abs(etaMS) && std::abs(etaMS) < 1.1) || (1.01 < std::abs(etaCB) && std::abs(etaCB) < 1.1)) {
1212 ATH_MSG_VERBOSE("Muon is in barrel/endcap overlap region - fail high-pT");
1213 return false;
1214 }
1215
1216 //::: BIS78
1217 if (isBIS78(etaMS, phiMS)) {
1218 if (!isRun3() || !m_useBEEBISInHighPtRun3) {
1219 ATH_MSG_VERBOSE("Muon is in BIS7/8 eta/phi region - fail high-pT");
1220 return false;
1221 }
1222 }
1223
1226 //if (getRunNumber(true) >= 324320) {
1227 //if (isBMG(etaMS, phiMS)) {
1228 //ATH_MSG_VERBOSE("Muon is in BMG eta/phi region - fail high-pT");
1229 //return false;
1230 //}
1231 //}
1232
1233 //::: BEE
1234 if (isBEE(etaMS, phiMS)) {
1235 // in Run3, large mis-alignment on the BEE chamber was found. temporarily mask the BEE region
1236 if (isRun3() && !m_useBEEBISInHighPtRun3) {
1237 ATH_MSG_VERBOSE("Muon is in BEE eta/phi region - fail high-pT");
1238 return false;
1239 }
1240 // Muon falls in the BEE eta-phi region: asking for 4 good precision layers
1241 // if( nGoodPrecLayers < 4 ) return false; // postponed (further studies needed)
1242 if (summary.nprecisionLayers < 4) {
1243 ATH_MSG_VERBOSE("Muon is in BEE eta/phi region and does not have 4 precision layers - fail high-pT");
1244 return false;
1245 }
1246 }
1247 if (std::abs(etaCB) > 1.4) {
1248 // Veto residual 3-station muons in BEE region due to MS eta/phi resolution effects
1249 // if( nGoodPrecLayers<4 && (extendedSmallHits>0||extendedSmallHoles>0) ) return false; // postponed (further studies
1250 // needed)
1251 if (summary.nprecisionLayers < 4 && (summary.extendedSmallHits > 0 || summary.extendedSmallHoles > 0)) {
1252 ATH_MSG_VERBOSE("Muon is in BEE eta/phi region and does not have 4 precision layers - fail high-pT");
1253 return false;
1254 }
1255 }
1256 } else {
1257 ATH_MSG_WARNING("passedHighPtCuts - MS or CB track missing in muon! Failing High-pT selection...");
1258 return false;
1259 }
1260
1261 //::: Apply 1/p significance cut
1262 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
1263 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
1264 if (idtrack && metrack && metrack->definingParametersCovMatrix()(4, 4) > 0) { const float qOverPsignif = qOverPsignificance(mu);
1265
1266 ATH_MSG_VERBOSE("qOverP significance: " << qOverPsignif);
1267
1268 if (std::abs(qOverPsignif) > 7) {
1269 ATH_MSG_VERBOSE("Muon failed qOverP significance cut");
1270 return false;
1271 }
1272 } else {
1273 ATH_MSG_VERBOSE("Muon missing ID or ME tracks - fail high-pT");
1274 return false;
1275 }
1276
1277 // Accept good 2-station muons if the user has opted to include these
1278 if (m_use2stationMuonsHighPt && summary.nprecisionLayers == 2) {
1279 // should not accept EM+EO muons due to ID/MS alignment issues
1280 if (std::abs(mu.eta()) > 1.2 && summary.extendedSmallHits < 3 && summary.extendedLargeHits < 3) {
1281 ATH_MSG_VERBOSE("2-station muon with EM+EO - fail high-pT");
1282 return false;
1283 }
1284
1285 // only select muons missing the inner precision layer
1286 // apply strict veto on overlap between small and large sectors
1287
1288 if (summary.innerLargeHits == 0 && summary.middleLargeHits == 0 && summary.outerLargeHits == 0 &&
1289 summary.extendedLargeHits == 0 && summary.middleSmallHits > 2 &&
1290 (summary.outerSmallHits > 2 || summary.extendedSmallHits > 2)) {
1291 ATH_MSG_VERBOSE("Accepted 2-station muon in small sector");
1292 return true;
1293 }
1294
1295 if (summary.innerSmallHits == 0 && summary.middleSmallHits == 0 && summary.outerSmallHits == 0 &&
1296 summary.extendedSmallHits == 0 && summary.middleLargeHits > 2 &&
1297 (summary.outerLargeHits > 2 || summary.extendedLargeHits > 2)) {
1298 ATH_MSG_VERBOSE("Accepted 2-station muon in large sector");
1299 return true;
1300 }
1301 }
1302
1303 //::: Require 3 (good) station muons
1304 if (summary.nprecisionLayers < 3) {
1305 ATH_MSG_VERBOSE("Muon has less than 3 precision layers - fail high-pT");
1306 return false;
1307 }
1308
1309 // Remove 3-station muons with small-large sectors overlap
1310 if (summary.isSmallGoodSectors) {
1311 if (!(summary.innerSmallHits > 2 && summary.middleSmallHits > 2 &&
1312 (summary.outerSmallHits > 2 || summary.extendedSmallHits > 2))) {
1313 ATH_MSG_VERBOSE("Muon has small/large sectors overlap - fail high-pT");
1314 return false;
1315 }
1316 } else {
1317 if (!(summary.innerLargeHits > 2 && summary.middleLargeHits > 2 &&
1318 (summary.outerLargeHits > 2 || summary.extendedLargeHits > 2))) {
1319 ATH_MSG_VERBOSE("Muon has small/large sectors overlap - fail high-pT");
1320 return false;
1321 }
1322 }
1323
1324 ATH_MSG_VERBOSE("Muon passed high-pT selection");
1325 return true;
1326 }
1327
1329 // ::
1330 if (mu.muonType() != xAOD::Muon::Combined) return false;
1331 // ::
1332 double start_cut = 3.0;
1333 double end_cut = 1.6;
1334 double abs_eta = std::abs(mu.eta());
1335
1336 // parametrization of expected q/p error as function of pT
1337 double p0(8.0), p1(0.), p2(0.);
1338 if(isRun3()) //MC21 optimization
1339 {
1340 if(abs_eta<=1.05){
1341 p1=0.046;
1342 p2=0.00005;
1343 }
1344 else if (abs_eta > 1.05 && abs_eta <= 1.3) {
1345 p1 = 0.052;
1346 p2 = 0.00008;
1347 } else if (abs_eta > 1.3 && abs_eta <= 1.7) {
1348 p1 = 0.068;
1349 p2 = 0.00006;
1350 } else if (abs_eta > 1.7 && abs_eta <= 2.0) {
1351 p1 = 0.048;
1352 p2 = 0.00006;
1353 } else if (abs_eta > 2.0) {
1354 p1 = 0.037;
1355 p2 = 0.00006;
1356 }
1357 }
1358 else
1359 {
1360 if(abs_eta<=1.05){
1361 p1=0.039;
1362 p2=0.00006;
1363 }
1364 else if (abs_eta > 1.05 && abs_eta <= 1.3) {
1365 p1 = 0.040;
1366 p2 = 0.00009;
1367 } else if (abs_eta > 1.3 && abs_eta <= 1.7) {
1368 p1 = 0.056;
1369 p2 = 0.00008;
1370 } else if (abs_eta > 1.7 && abs_eta <= 2.0) {
1371 p1 = 0.041;
1372 p2 = 0.00006;
1373 } else if (abs_eta > 2.0) {
1374 p1 = 0.031;
1375 p2 = 0.00006;
1376 }
1377 }
1378 // ::
1379 hitSummary summary{};
1380 fillSummary(mu, summary);
1381
1382 // independent parametrization for 2-station muons
1383 if (m_use2stationMuonsHighPt && summary.nprecisionLayers == 2) {
1384 start_cut = 1.1;
1385 end_cut=0.7;
1386 p1 = 0.0739568;
1387 p2 = 0.00012443;
1388 if (abs_eta > 1.05 && abs_eta < 1.3) {
1389 p1 = 0.0674484;
1390 p2 = 0.000119879;
1391 } else if (abs_eta >= 1.3 && abs_eta < 1.7) {
1392 p1 = 0.041669;
1393 p2 = 0.000178349;
1394 } else if (abs_eta >= 1.7 && abs_eta < 2.0) {
1395 p1 = 0.0488664;
1396 p2 = 0.000137648;
1397 } else if (abs_eta >= 2.0) {
1398 p1 = 0.028077;
1399 p2 = 0.000152707;
1400 }
1401 }
1402 // ::
1403 bool passErrorCutCB = false;
1404 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1405 if (cbtrack) {
1406 // ::
1407 double pt_CB = (cbtrack->pt() * MeVtoGeV < 5000.) ? cbtrack->pt() * MeVtoGeV : 5000.; // GeV
1408 double qOverP_CB = cbtrack->qOverP();
1409 double qOverPerr_CB = std::sqrt(cbtrack->definingParametersCovMatrix()(4, 4));
1410 // sigma represents the average expected error at the muon's pt/eta
1411 double sigma = std::sqrt(std::pow(p0 / pt_CB, 2) + std::pow(p1, 2) + std::pow(p2 * pt_CB, 2));
1412 // cutting at start_cut*sigma for pt <=1 TeV depending on eta region,
1413 // then linearly tightening until end_cut*sigma is reached at pt >= 5TeV.
1414 double a = (end_cut - start_cut) / 4000.0;
1415 double b = end_cut - a * 5000.0;
1416 double coefficient = (pt_CB > 1000.) ? (a * pt_CB + b) : start_cut;
1417 if (std::abs(qOverPerr_CB / qOverP_CB) < coefficient * sigma) { passErrorCutCB = true; }
1418 }
1419 // ::
1420 if (m_use2stationMuonsHighPt && m_doBadMuonVetoMimic && summary.nprecisionLayers == 2) {
1422
1423 if (eventInfo->eventType(xAOD::EventInfo::IS_SIMULATION)) {
1424 ATH_MSG_DEBUG("The current event is a MC event. Use bad muon veto mimic.");
1425 return passErrorCutCB && passedBMVmimicCut(mu);
1426 }
1427 }
1428
1429 // ::
1430 return passErrorCutCB;
1431 }
1432
1434 TF1* cutFunction;
1435 double p1, p2;
1436 if (std::abs(mu.eta()) < 1.05) {
1437 cutFunction = m_BMVcutFunction_barrel.get();
1438 p1 = 0.066265;
1439 p2 = 0.000210047;
1440 } else {
1441 cutFunction = m_BMVcutFunction_endcap.get();
1442 p1 = 0.0629747;
1443 p2 = 0.000196466;
1444 }
1445
1446 double qOpRelResolution = std::hypot(p1, p2 * mu.primaryTrackParticle()->pt() * MeVtoGeV);
1447
1448 double qOverPabs_unsmeared = std::abs(mu.primaryTrackParticle()->definingParameters()[4]);
1449 double qOverPabs_smeared = 1.0 / (mu.pt() * std::cosh(mu.eta()));
1450
1451 if ((qOverPabs_smeared - qOverPabs_unsmeared) / (qOpRelResolution * qOverPabs_unsmeared) <
1452 cutFunction->Eval(mu.primaryTrackParticle()->pt() * MeVtoGeV))
1453 return false;
1454 else
1455 return true;
1456 }
1457
1459 // ::
1460 if (mu.muonType() == xAOD::Muon::Combined) { return mu.author() != xAOD::Muon::STACO; }
1461 // ::
1462 if (mu.muonType() == xAOD::Muon::CaloTagged && std::abs(mu.eta()) < 0.105)
1463 return passedCaloTagQuality(mu);
1464 // ::
1465 if (mu.muonType() == xAOD::Muon::SegmentTagged && (std::abs(mu.eta()) < 0.105 || m_useSegmentTaggedLowPt)) return true;
1466 // ::
1467 if (mu.author() == xAOD::Muon::MuidSA && std::abs(mu.eta()) > 2.4) return true;
1468 // ::
1469 if (mu.muonType() == xAOD::Muon::SiliconAssociatedForwardMuon) {
1470 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1471 return (cbtrack && std::abs(cbtrack->eta()) > 2.4);
1472 }
1473 // ::
1474 return false;
1475 }
1476
1478 uint8_t value1{0}, value2{0};
1479
1482 " !! Tool configured with some of the ID hits requirements changed... FOR DEVELOPMENT ONLY: muon efficiency SF won't be "
1483 "valid !! ");
1484
1487 if ((value1 + value2 == 0) && !m_PixCutOff) return false;
1488
1491 if ((value1 + value2 <= 4) && !m_SctCutOff) return false;
1492
1495 if ((value1 + value2 >= 3) && !m_SiHolesCutOff) return false;
1496
1497 if (!m_TrtCutOff) {
1498 const float abseta = std::abs(track.eta());
1501 const uint8_t totTRThits = value1 + value2;
1502 if (!((0.1 < abseta && abseta <= 1.9 && totTRThits > 5 && value2 < (0.9 * totTRThits)) || (abseta <= 0.1 || abseta > 1.9)))
1503 return false;
1504 }
1505 // Reached end - all ID hit cuts are passed.
1506 return true;
1507 } // passedIDCuts
1508
1510 // Use CaloScore variable based on Neural Network if enabled
1511 // The neural network is only trained until eta = 1
1512 // cf. https://cds.cern.ch/record/2802605/files/CERN-THESIS-2021-290.pdf
1513 constexpr float eta_range = 1.;
1514 if (std::abs(mu.eta()) < eta_range && m_useCaloScore) return passedCaloScore(mu);
1515
1516 // Otherwise we use CaloMuonIDTag
1517 int CaloMuonIDTag = -20;
1518
1519 // Extract CaloMuonIDTag variable
1520 bool readID = mu.parameter(CaloMuonIDTag, xAOD::Muon::CaloMuonIDTag);
1521 if (!readID) {
1522 ATH_MSG_WARNING("Unable to read CaloMuonIDTag Quality information! Rejecting the CALO muon!");
1523 return false;
1524 }
1525
1526 // Cut on CaloMuonIDTag variable
1527 return (CaloMuonIDTag > 10);
1528 }
1529
1531 // We use a working point with a pT-dependent cut on the NN discriminant, designed to achieve a constant
1532 // fakes rejection as function of pT in Z->mumu MC
1533
1534 // Extract the relevant score variable (NN discriminant)
1535
1536 float CaloMuonScore{-999.0};
1537 retrieveParam(mu, CaloMuonScore, xAOD::Muon::CaloMuonScore);
1538
1539 if(m_caloScoreWP==1) return (CaloMuonScore >= 0.92);
1540 if(m_caloScoreWP==2) return (CaloMuonScore >= 0.56);
1541 else if(m_caloScoreWP==3 || m_caloScoreWP==4)
1542 {
1543 // Cut on the score variable
1544 float pT = mu.pt() * MeVtoGeV; // GeV
1545
1546 if (pT > 20.0) // constant cut above 20 GeV
1547 return (CaloMuonScore >= 0.77);
1548 else {
1549 // pT-dependent cut below 20 GeV
1550 // The pT-dependent cut is based on a fit of a third-degree polynomial, with coefficients as given below
1551
1552 if(m_caloScoreWP==3) return (CaloMuonScore >= (-1.98e-4 * std::pow(pT, 3) +6.04e-3 * std::pow(pT, 2) -6.13e-2 * pT + 1.16));
1553 if(m_caloScoreWP==4) return (CaloMuonScore >= (-1.80e-4 * std::pow(pT, 3) +5.02e-3 * std::pow(pT, 2) -4.62e-2 * pT + 1.12));
1554 }
1555 }
1556
1557 return false;
1558 }
1559
1560 bool MuonSelectionTool::passTight(const xAOD::Muon& mu, float rho, float oneOverPSig) const {
1561
1563 ATH_MSG_VERBOSE("for run3, Tight WP is only supported when ExcludeNSWFromPrecisionLayers=False and RecalcPrecisionLayerswNSW=True");
1564 return false;
1565 }
1566 float symmetric_eta = std::abs(mu.eta());
1567 float pt = mu.pt() * MeVtoGeV; // GeV
1568
1569 // Impose pT and eta cuts; the bounds of the cut maps
1570 if (pt < 4.0 || symmetric_eta >= 2.5) return false;
1571 ATH_MSG_VERBOSE("Muon is passing tight WP kinematic cuts with pT,eta " << mu.pt() << " , " << mu.eta());
1572
1573 // ** Low pT specific cuts ** //
1574 if (pt < 20.0) {
1575 double rhoCut = m_tightWP_lowPt_rhoCuts->Interpolate(pt, symmetric_eta);
1576 double qOverPCut = m_tightWP_lowPt_qOverPCuts->Interpolate(pt, symmetric_eta);
1577
1578 ATH_MSG_VERBOSE("Applying tight WP cuts to a low pt muon with (pt,eta) ( " << pt << " , " << mu.eta() << " ) ");
1579 ATH_MSG_VERBOSE("Rho value " << rho << ", required to be less than " << rhoCut);
1580 ATH_MSG_VERBOSE("Momentum significance value " << oneOverPSig << ", required to be less than " << qOverPCut);
1581
1582 if (rho > rhoCut) return false;
1583 ATH_MSG_VERBOSE("Muon passed tight WP, low pT rho cut!");
1584
1585 if (oneOverPSig > qOverPCut) return false;
1586 ATH_MSG_VERBOSE("Muon passed tight WP, low pT momentum significance cut");
1587
1588 // Tight muon!
1589 return true;
1590
1591 }
1592
1593 // ** Medium pT specific cuts ** //
1594 else if (pt < 100.0) {
1595 double rhoCut = m_tightWP_mediumPt_rhoCuts->Interpolate(pt, symmetric_eta);
1596 //
1597 ATH_MSG_VERBOSE("Applying tight WP cuts to a medium pt muon with (pt,eta) (" << pt << "," << mu.eta() << ")");
1598 ATH_MSG_VERBOSE("Rho value " << rho << " required to be less than " << rhoCut);
1599
1600 // Apply cut
1601 if (rho > rhoCut) return false;
1602 ATH_MSG_VERBOSE("Muon passed tight WP, medium pT rho cut!");
1603
1604 // Tight muon!
1605 return true;
1606 }
1607
1608 // ** High pT specific cuts
1609 else if (pt < 500.0) {
1610 //
1611 ATH_MSG_VERBOSE("Applying tight WP cuts to a high pt muon with (pt,eta) (" << pt << "," << mu.eta() << ")");
1612 // No interpolation, since bins with -1 mean we should cut really loose
1613 double rhoCut = m_tightWP_highPt_rhoCuts->GetBinContent(m_tightWP_highPt_rhoCuts->FindFixBin(pt, symmetric_eta));
1614 ATH_MSG_VERBOSE("Rho value " << rho << ", required to be less than " << rhoCut << " unless -1, in which no cut is applied");
1615 //
1616 if (rhoCut < 0.0) return true;
1617 if (rho > rhoCut) return false;
1618 ATH_MSG_VERBOSE("Muon passed tight WP, high pT rho cut!");
1619
1620 return true;
1621 }
1622 // For muons with pT > 500 GeV, no extra cuts
1623 else {
1624 ATH_MSG_VERBOSE("Not applying any tight WP cuts to a very high pt muon with (pt,eta) (" << pt << "," << mu.eta() << ")");
1625 return true;
1626 }
1627
1628 // you should never reach this point
1629 return false;
1630 }
1631
1632 //============================================================================
1634
1635 checkSanity();
1636
1650
1651 if (!isRun3()) {
1652 // ignore missing of cscUnspoiledEtaHits in case we are running in expert developer mode
1653 // e.g. for when we want to apply Run2 WPs in Run3
1655
1656 if (std::abs(muon.eta()) > 2.0) {
1657 ATH_MSG_VERBOSE("Recalculating number of precision layers for combined muon");
1658 summary.nprecisionLayers = (summary.innerSmallHits > 1 || summary.innerLargeHits > 1)
1659 + (summary.middleSmallHits > 2 || summary.middleLargeHits > 2)
1660 + (summary.outerSmallHits > 2 || summary.outerLargeHits > 2);
1661 }
1662
1663 } else if (std::abs(muon.eta()) > 1.3 && (m_excludeNSWFromPrecisionLayers || m_recalcPrecisionLayerswNSW)) {
1664 summary.nprecisionLayers = (summary.middleSmallHits > 2 || summary.middleLargeHits > 2)
1665 + (summary.outerSmallHits > 2 || summary.outerLargeHits > 2)
1666 + (summary.extendedSmallHits > 2 || summary.extendedLargeHits > 2);
1667
1669
1670 if (!eta1stgchits_acc.isAvailable(muon) || !eta2stgchits_acc.isAvailable(muon) || !mmhits_acc.isAvailable(muon)) {
1671 ATH_MSG_FATAL(__FILE__ << ":" << __LINE__ << " Failed to retrieve NSW hits!"
1672 << " (Please use DxAODs with p-tags >= p5834 OR set ExcludeNSWFromPrecisionLayers to True (tests only)");
1673 throw std::runtime_error("Failed to retrieve NSW hits");
1674 }
1675
1679 summary.nprecisionLayers += ((summary.etaLayer1STGCHits + summary.etaLayer2STGCHits) > 3 || summary.MMHits > 3);
1680 }
1681 }
1682 }
1683
1684
1685 void MuonSelectionTool::retrieveParam(const xAOD::Muon& muon, float& value, const xAOD::Muon::ParamDef param) const {
1686 if (!muon.parameter(value, param)) {
1687 ATH_MSG_FATAL(__FILE__ << ":" << __LINE__ << " Failed to retrieve parameter " << param
1688 << " for muon with pT:" << muon.pt() * MeVtoGeV << ", eta:" << muon.eta() << ", phi: " << muon.phi()
1689 << ", q:" << muon.charge() << ", author: " << muon.author());
1690 throw std::runtime_error("Failed to retrieve Parameter");
1691 }
1692 }
1693
1694 // Returns an integer corresponding to categorization of muons with different resolutions
1696 // Resolutions have only been evaluated for medium combined muons
1697 if (mu.muonType() != xAOD::Muon::Combined || getQuality(mu) > xAOD::Muon::Medium) return ResolutionCategory::unclassified;
1698
1699 // :: Access MS hits information
1700 hitSummary summary{};
1701 fillSummary(mu, summary);
1702
1703 // For muons passing the high-pT working point, distinguish between 2-station tracks and the rest
1704 if (passedHighPtCuts(mu)) {
1705 if (summary.nprecisionLayers == 2)
1707 else
1709 }
1710
1711 const xAOD::TrackParticle* CB_track = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1712 const xAOD::TrackParticle* MS_track = mu.trackParticle(xAOD::Muon::MuonSpectrometerTrackParticle);
1713 if (!MS_track) {
1714 ATH_MSG_VERBOSE("getResolutionCategory - No MS track available for muon. Using combined track.");
1715 MS_track = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1716 }
1717
1718 if (!MS_track || !CB_track) return ResolutionCategory::unclassified;
1719 const float etaMS = MS_track->eta();
1720 const float etaCB = CB_track->eta();
1721 const float phiMS = MS_track->phi();
1722
1723 int category = ResolutionCategory::unclassified;
1724
1725 if ((summary.isSmallGoodSectors && summary.innerSmallHits < 3) || (!summary.isSmallGoodSectors && summary.innerLargeHits < 3))
1726 category = ResolutionCategory::missingInner; // missing-inner
1727
1728 if ((summary.isSmallGoodSectors && summary.middleSmallHits < 3) || (!summary.isSmallGoodSectors && summary.middleLargeHits < 3))
1729 category = ResolutionCategory::missingMiddle; // missing-middle
1730
1731 if ((summary.isSmallGoodSectors && summary.outerSmallHits < 3 && summary.extendedSmallHits < 3) ||
1732 (!summary.isSmallGoodSectors && summary.outerLargeHits < 3 && summary.extendedLargeHits < 3))
1733 category = ResolutionCategory::missingOuter; // missing-outer
1734
1735 if (!isRun3() && (std::abs(etaMS) > 2.0 || std::abs(etaCB) > 2.0) && summary.cscUnspoiledEtaHits == 0)
1736 category = ResolutionCategory::spoiledCSC; // spoiled CSC
1737
1738 if ((1.01 < std::abs(etaMS) && std::abs(etaMS) < 1.1) || (1.01 < std::abs(etaCB) && std::abs(etaCB) < 1.1))
1739 category = ResolutionCategory::BEoverlap; // barrel-end-cap overlap
1740
1741 if (isBIS78(etaMS, phiMS)) category = ResolutionCategory::BIS78; // BIS7/8
1742
1743 //::: BEE
1744 if (isBEE(etaMS, phiMS) || (std::abs(etaCB) > 1.4 && (summary.extendedSmallHits > 0 || summary.extendedSmallHoles > 0))) {
1745 if (summary.extendedSmallHits < 3 && summary.middleSmallHits >= 3 && summary.outerSmallHits >= 3)
1746 category = ResolutionCategory::missingBEE; // missing-BEE
1747
1748 if (summary.extendedSmallHits >= 3 && summary.outerSmallHits < 3) category = ResolutionCategory::missingOuter; // missing-outer
1749
1750 if (!summary.isSmallGoodSectors)
1751 category = ResolutionCategory::unclassified; // ambiguity due to eta/phi differences between MS and CB track
1752 }
1753
1754 if (summary.nprecisionLayers == 1) category = ResolutionCategory::oneStation; // one-station track
1755
1756 return category;
1757 }
1758
1759 //============================================================================
1760 // need run number (or random run number) to apply period-dependent selections
1761 unsigned int MuonSelectionTool::getRunNumber(bool needOnlyCorrectYear /*=false*/) const {
1762
1763 static const SG::AuxElement::ConstAccessor<unsigned int> acc_rnd("RandomRunNumber");
1764
1766 //overwrite run number
1767 unsigned int runNumber = 0;
1768 if(m_expertMode_RunNumber.value()!=0) runNumber=m_expertMode_RunNumber.value();
1769 else runNumber = eventInfo->runNumber();
1770
1771 // Case of data
1772 if (!eventInfo->eventType(xAOD::EventInfo::IS_SIMULATION)) {
1773 ATH_MSG_DEBUG("The current event is a data event. Return runNumber.");
1774 return runNumber;
1775 }
1776
1777 // Case of MC
1778 // attempt to get the run number assigned by the PRW tool
1779 static std::atomic<bool> issuedWarningPRW{false};
1780 if (acc_rnd.isAvailable(*eventInfo)) {
1781 unsigned int rn = acc_rnd(*eventInfo);
1782 if (rn != 0) return acc_rnd(*eventInfo);
1783
1784 if (!issuedWarningPRW) {
1785 ATH_MSG_WARNING("Pile up tool has assigned runNumber = 0");
1786 issuedWarningPRW = true;
1787 }
1788 }
1789
1790 // otherwise return a dummy run number
1791 if (needOnlyCorrectYear) {
1792 if (runNumber < 300000) { // mc16a (2016): 284500
1793 ATH_MSG_DEBUG("Random run number not available and this is mc16a or mc20a, returning dummy 2016 run number.");
1794 return 311071;
1795
1796 } else if (runNumber < 310000) { // mc16d (2017): 300000
1797 ATH_MSG_DEBUG("Random run number not available and this is mc16d or mc20d, returning dummy 2017 run number.");
1798 return 340072;
1799
1800 } else if (runNumber < 320000) { // mc16e (2018): 310000
1801 ATH_MSG_DEBUG("Random run number not available and this is mc16e or mc20e, returning dummy 2018 run number.");
1802 return 351359;
1803
1804 } else if (runNumber < 600000) { //mc21: 330000, mc23a: 410000, mc23c: 450000
1805 ATH_MSG_DEBUG("Random run number not available and this is mc21/mc23, for the time being we're returing a dummy run number.");
1806 return 399999;
1807 } else {
1808 ATH_MSG_DEBUG("Detected some run 4 / phase II runnumber "<<runNumber<<". ");
1809 return 666666;
1810 }
1811
1812 ATH_MSG_FATAL("Random run number not available, fallback option of using runNumber failed since "<<runNumber<<" cannot be recognised");
1813 throw std::runtime_error("MuonSelectionTool() - need RandomRunNumber decoration by the PileupReweightingTool");
1814 }
1815
1816 ATH_MSG_FATAL("Failed to find the RandomRunNumber decoration by the PileupReweightingTool");
1817 throw std::runtime_error("MuonSelectionTool() - need RandomRunNumber decoration from PileupReweightingTool");
1818 }
1819
1820
1821 // Check if eta/phi coordinates correspond to BIS7/8 chambers
1822 bool MuonSelectionTool::isBIS78(const float eta, const float phi) const {
1823 static constexpr std::array<float, 2> BIS78_eta{1.05, 1.3};
1824 static constexpr std::array<float, 8> BIS78_phi{0.21, 0.57, 1.00, 1.33, 1.78, 2.14, 2.57, 2.93};
1825
1826 float abs_eta = std::abs(eta);
1827 float abs_phi = std::abs(phi);
1828
1829 if (abs_eta >= BIS78_eta[0] && abs_eta <= BIS78_eta[1]) {
1830 if ((abs_phi >= BIS78_phi[0] && abs_phi <= BIS78_phi[1]) || (abs_phi >= BIS78_phi[2] && abs_phi <= BIS78_phi[3]) ||
1831 (abs_phi >= BIS78_phi[4] && abs_phi <= BIS78_phi[5]) || (abs_phi >= BIS78_phi[6] && abs_phi <= BIS78_phi[7])) {
1832 return true;
1833 }
1834 }
1835
1836 return false;
1837 }
1838
1839 // Check if eta/phi coordinates correspond to BEE chambers
1840 bool MuonSelectionTool::isBEE(const float eta, const float phi) const {
1841 static constexpr std::array<float, 2> BEE_eta{1.440, 1.692};
1842 static constexpr std::array<float, 8> BEE_phi{0.301, 0.478, 1.086, 1.263, 1.872, 2.049, 2.657, 2.834};
1843
1844 float abs_eta = std::abs(eta);
1845 float abs_phi = std::abs(phi);
1846
1847 if (abs_eta >= BEE_eta[0] && abs_eta <= BEE_eta[1]) {
1848 if ((abs_phi >= BEE_phi[0] && abs_phi <= BEE_phi[1]) || (abs_phi >= BEE_phi[2] && abs_phi <= BEE_phi[3]) ||
1849 (abs_phi >= BEE_phi[4] && abs_phi <= BEE_phi[5]) || (abs_phi >= BEE_phi[6] && abs_phi <= BEE_phi[7])) {
1850 return true;
1851 }
1852 }
1853
1854 return false;
1855 }
1856
1857 // Check if eta/phi coordinates correspond to BMG chambers
1858 bool MuonSelectionTool::isBMG(const float eta, const float phi) const {
1859 static constexpr std::array<float, 6> BMG_eta{0.35, 0.47, 0.68, 0.80, 0.925, 1.04};
1860 static constexpr std::array<float, 4> BMG_phi{-1.93, -1.765, -1.38, -1.21};
1861
1862 float abs_eta = std::abs(eta);
1863
1864 if ((abs_eta >= BMG_eta[0] && abs_eta <= BMG_eta[1]) || (abs_eta >= BMG_eta[2] && abs_eta <= BMG_eta[3]) ||
1865 (abs_eta >= BMG_eta[4] && abs_eta <= BMG_eta[5])) {
1866 if ((phi >= BMG_phi[0] && phi <= BMG_phi[1]) || (phi >= BMG_phi[2] && phi <= BMG_phi[3])) { return true; }
1867 }
1868
1869 return false;
1870 }
1871
1874 {
1875 ATH_MSG_ERROR("TightNNScore calculation is disabled. Please set the property CalculateTightNNScore to true.");
1876 throw std::runtime_error("cannot calculate TightNNScore");
1877 }
1878 //this score currently only can be calculated for combined muons
1879 if (mu.muonType() != xAOD::Muon::Combined) return -999;
1880 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
1881 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
1882 if(!idtrack || !metrack) return -999;
1883 //the score is only calculated for muons which pass the Medium WP
1884 if (getQuality(mu) > xAOD::Muon::Medium) return -999;
1885 //only muons with pt > 4 GeV and |eta|<2.5 are considered
1886 if (std::abs(mu.eta())>2.5) return -999;
1887 if(mu.pt()<4000.) return -999;
1888
1889 std::vector<float> input_features;
1890 // 1. Fill input features
1891 int mu_author=mu.author();
1892 float mu_rhoPrime=rhoPrime(mu);
1893 float mu_scatteringCurvatureSignificance=0.;
1894 retrieveParam(mu, mu_scatteringCurvatureSignificance, xAOD::Muon::scatteringCurvatureSignificance);
1895 float mu_scatteringNeighbourSignificance=0.;
1896 retrieveParam(mu, mu_scatteringNeighbourSignificance, xAOD::Muon::scatteringNeighbourSignificance);
1897 float mu_momentumBalanceSignificance=0.;
1898 retrieveParam(mu, mu_momentumBalanceSignificance, xAOD::Muon::momentumBalanceSignificance);
1899 float mu_qOverPSignificance=qOverPsignificance(mu);
1900 float mu_reducedChi2=mu.primaryTrackParticle()->chiSquared() / mu.primaryTrackParticle()->numberDoF();
1901 float mu_reducedChi2_ID=idtrack->chiSquared() / idtrack->numberDoF();
1902 float mu_reducedChi2_ME=metrack->chiSquared() / metrack->numberDoF();
1903 float mu_spectrometerFieldIntegral=0.;
1904 retrieveParam(mu, mu_spectrometerFieldIntegral, xAOD::Muon::spectrometerFieldIntegral);
1905 float mu_segmentDeltaEta=0;
1906 retrieveParam(mu, mu_segmentDeltaEta, xAOD::Muon::segmentDeltaEta);
1907 uint8_t mu_numberOfPixelHits=0;
1909 uint8_t mu_numberOfPixelDeadSensors=0;
1910 retrieveSummaryValue(mu, mu_numberOfPixelDeadSensors, xAOD::SummaryType::numberOfPixelDeadSensors);
1911 uint8_t mu_innerLargeHits=0;
1913 uint8_t mu_innerSmallHits=0;
1915 uint8_t mu_middleLargeHits=0;
1917 uint8_t mu_middleSmallHits=0;
1919 uint8_t mu_outerLargeHits=0;
1921 uint8_t mu_outerSmallHits=0;
1923
1924 if(!isRun3())
1925 {
1926 input_features = {(float)mu_author,
1927 mu_rhoPrime,
1928 mu_scatteringCurvatureSignificance,
1929 mu_scatteringNeighbourSignificance,
1930 mu_momentumBalanceSignificance,
1931 mu_qOverPSignificance,
1932 mu_reducedChi2,
1933 mu_reducedChi2_ID,
1934 mu_reducedChi2_ME,
1935 mu_spectrometerFieldIntegral,
1936 mu_segmentDeltaEta,
1937 (float)mu_numberOfPixelHits,
1938 (float)mu_numberOfPixelDeadSensors,
1939 (float)mu_innerLargeHits,
1940 (float)mu_innerSmallHits,
1941 (float)mu_middleLargeHits,
1942 (float)mu_middleSmallHits,
1943 (float)mu_outerLargeHits,
1944 (float)mu_outerSmallHits};
1945 }
1946 else
1947 {
1948 uint8_t mu_phiLayer1STGCHits=0;
1950 uint8_t mu_phiLayer2STGCHits=0;
1952 uint8_t mu_etaLayer1STGCHits=0;
1954 uint8_t mu_etaLayer2STGCHits=0;
1956 uint8_t mu_MMHits=0;
1958 input_features = {(float)mu_author,
1959 mu_rhoPrime,
1960 mu_scatteringCurvatureSignificance,
1961 mu_scatteringNeighbourSignificance,
1962 mu_momentumBalanceSignificance,
1963 mu_qOverPSignificance,
1964 mu_reducedChi2,
1965 mu_reducedChi2_ID,
1966 mu_reducedChi2_ME,
1967 mu_spectrometerFieldIntegral,
1968 mu_segmentDeltaEta,
1969 (float)mu_numberOfPixelHits,
1970 (float)mu_numberOfPixelDeadSensors,
1971 (float)mu_innerLargeHits,
1972 (float)mu_innerSmallHits,
1973 (float)mu_middleLargeHits,
1974 (float)mu_middleSmallHits,
1975 (float)mu_outerLargeHits,
1976 (float)mu_outerSmallHits,
1977 (float)mu_phiLayer1STGCHits,
1978 (float)mu_phiLayer2STGCHits,
1979 (float)mu_etaLayer1STGCHits,
1980 (float)mu_etaLayer2STGCHits,
1981 (float)mu_MMHits};
1982 }
1983
1984 float score=-999.;
1985 std::vector<int64_t> inputShape = {1, static_cast<int64_t>(input_features.size())};
1986
1987 AthInfer::InputDataMap inputData;
1988 inputData["flatten_input"] = std::make_pair(
1989 inputShape, std::move(input_features)
1990 );
1991
1992 AthInfer::OutputDataMap outputData;
1993 outputData["sequential"] = std::make_pair(
1994 std::vector<int64_t>{1, 1}, std::vector<float>{}
1995 );
1996
1997 if (!m_onnxTool->inference(inputData, outputData).isSuccess()) {
1998 ATH_MSG_WARNING("ONNX inference failed!");
1999 return -999.;
2000 }
2001 const auto& variant = outputData["sequential"].second;
2002 if (std::holds_alternative<std::vector<float>>(variant)) {
2003 const auto& vec = std::get<std::vector<float>>(variant);
2004 if (!vec.empty()) score = vec[0];
2005 else {
2006 ATH_MSG_WARNING("ONNX output vector is empty!");
2007 return -999.;
2008 }
2009 } else {
2010 ATH_MSG_WARNING("ONNX output is not a float vector!");
2011 return -999.;
2012 }
2013
2014 ATH_MSG_DEBUG("TightNNScore for muon with pT " << mu.pt() << " GeV, eta " << mu.eta() << " is " << score);
2015
2016 return score;
2017 }
2018
2019} // namespace CP
Scalar eta() const
pseudorapidity method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Handle class for reading from StoreGate.
std::vector< size_t > vec
unsigned int uint
static Double_t a
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
#define y
#define x
#define z
Gaudi::Property< bool > m_developMode
float rhoPrime(const xAOD::Muon &muon) const
Returns rhoPrime of the muon (see definition in https://cds.cern.ch/record/2665711 )
virtual bool passedHighPtCuts(const xAOD::Muon &) const override
Returns true if the muon passes the standard MCP High Pt cuts.
float qOverPsignificance(const xAOD::Muon &muon) const
Returns q/p significance of the muon (see definition in https://cds.cern.ch/record/2665711 )
std::unique_ptr< TH1 > m_tightWP_mediumPt_rhoCuts
Gaudi::Property< bool > m_SctCutOff
Gaudi::Property< bool > m_PixCutOff
virtual xAOD::Muon::Quality getQuality(const xAOD::Muon &mu) const override
Returns the quality of the muon. To set the value on the muon, instead call setQuality(xAOD::Muon&) c...
std::unique_ptr< MVAUtils::BDT > m_MuidCO
virtual void setQuality(xAOD::Muon &mu) const override
set the passes low pT cuts variable of the muon
std::vector< double > m_lowPtMuidCO_scaler
std::unique_ptr< TMVA::Reader > m_readerE_MUID
std::unique_ptr< TMVA::Reader > m_reader_MUTAGIMO_etaBin2
Gaudi::Property< bool > m_isRun3
std::unique_ptr< TMVA::Reader > m_readerO_MUGIRL
std::unique_ptr< TH1 > m_tightWP_highPt_rhoCuts
std::unique_ptr< TH1 > m_tightWP_lowPt_qOverPCuts
Gaudi::Property< double > m_maxEta
bool passedBMVmimicCut(const xAOD::Muon &) const
Returns true if the muon passes a cut which mimics the effect of the combined error cut This is neces...
virtual bool isBadMuon(const xAOD::Muon &) const override
Returns true if a CB muon fails some loose quaility requirements designed to remove pathological trac...
virtual ~MuonSelectionTool()
virtual bool passedCaloTagQuality(const xAOD::Muon &mu) const override
Returns true if the muon passed additional calo-tag quality cuts.
std::vector< double > m_lowPtMuidCO_means
Gaudi::Property< bool > m_SiHolesCutOff
bool isRun3(bool forceOnTheFly=false) const
Gaudi::Property< bool > m_toroidOff
Gaudi::Property< bool > m_geoOnTheFly
void fillSummary(const xAOD::Muon &muon, hitSummary &summary) const
functions that fills a hitSummary for a muon
Gaudi::Property< bool > m_doBadMuonVetoMimic
Gaudi::Property< bool > m_useLRT
Gaudi::Property< std::string > m_MVAreaderFile_ODD_MuidCB
Gaudi::Property< bool > m_excludeNSWFromPrecisionLayers
Gaudi::Property< std::string > m_MVAreaderFile_MuTagIMO_etaBin2
virtual int getResolutionCategory(const xAOD::Muon &) const override
Returns an integer corresponding to categorization of muons with different resolutions.
std::vector< double > m_lowPtMuGirl_scaler
bool passedLowPtEfficiencyMVACutRun3(const xAOD::Muon &) const
Gaudi::Property< bool > m_useBEEBISInHighPtRun3
virtual bool passedLowPtEfficiencyCuts(const xAOD::Muon &) const override
Returns true if the muon passes the standard MCP low pt cuts.
Gaudi::Property< std::string > m_calibration_version
Gaudi::Property< bool > m_useCaloScore
Gaudi::Property< bool > m_recalcPrecisionLayerswNSW
Gaudi::Property< bool > m_turnOffMomCorr
void retrieveParam(const xAOD::Muon &muon, float &value, const xAOD::Muon::ParamDef param) const
std::unique_ptr< TF1 > m_BMVcutFunction_endcap
std::unique_ptr< TH1 > m_tightWP_lowPt_rhoCuts
Gaudi::Property< bool > m_calculateTightNNScore
Gaudi::Property< int > m_caloScoreWP
Gaudi::Property< std::string > m_MVAreaderFile_ODD_MuGirl
Gaudi::Property< std::string > m_MVAreaderFile_MuTagIMO_etaBin3
virtual bool passedCaloScore(const xAOD::Muon &mu) const override
Returns true if the muon passed the CaloScore calo-tag working point.
asg::AcceptInfo m_acceptInfo
Store selection information.
virtual StatusCode initialize() override
Function initialising the tool.
virtual void setPassesIDCuts(xAOD::Muon &) const override
set the passes ID cuts variable of the muon
Gaudi::Property< unsigned long long > m_expertMode_EvtNumber
std::vector< const xAOD::MuonSegment * > getSegmentsSorted(const xAOD::Muon &mu) const
Returns a vector of the muon's segments, sorted according to chamber index.
Gaudi::Property< int > m_quality
Gaudi::Property< std::string > m_BMVcutFile
file for bad muon veto mimic cut functions
virtual float getTightNNScore(const xAOD::Muon &muon) const override
Returns the TightNNscore of the muon, an experimental ML-based score for the identification of muons ...
Gaudi::Property< bool > m_use2stationMuonsHighPt
virtual bool passedIDCuts(const xAOD::Muon &) const override
Returns true if the muon passes the standard MCP ID cuts.
Gaudi::Property< bool > m_disablePtCuts
unsigned int getRunNumber(bool needOnlyCorrectYear=false) const
bool isBMG(const float eta, const float phi) const
Check if muon eta/phi falls in BMG chambers.
virtual asg::AcceptData accept(const xAOD::IParticle *p) const override
Get the decision using a generic IParticle pointer.
virtual const asg::AcceptInfo & getAcceptInfo() const override
Get an object describing the "selection steps" of the tool.
Gaudi::Property< std::string > m_MVAreaderFile_EVEN_MuidCB
std::unique_ptr< TF1 > m_BMVcutFunction_barrel
std::unique_ptr< TMVA::Reader > m_readerE_MUGIRL
void IdMsPt(const xAOD::Muon &muon, float &idPt, float &msPt) const
bool passTight(const xAOD::Muon &mu, float rho, float oneOverPSig) const
Returns true if the muon passed the tight working point cuts.
std::unique_ptr< TMVA::Reader > m_readerO_MUID
Gaudi::Property< std::string > m_MVAreaderFile_EVEN_MuGirl
Gaudi::Property< bool > m_TrtCutOff
ToolHandle< AthInfer::IAthInferenceTool > m_onnxTool
Gaudi::Property< bool > m_useSegmentTaggedLowPt
virtual bool passedErrorCutCB(const xAOD::Muon &) const override
Returns true if a CB muon passes a pt- and eta-dependent cut on the relative CB q/p error.
virtual bool passedMuonCuts(const xAOD::Muon &) const override
Returns true if the muon passes a standardized loose preselection.
StatusCode getHist(TFile *file, const std::string &histName, std::unique_ptr< TH1 > &hist) const
Checks for each histogram.
std::unique_ptr< TMVA::Reader > m_reader_MUTAGIMO_etaBin1
Gaudi::Property< bool > m_useAllAuthors
std::vector< double > m_lowPtMuGirl_means
Gaudi::Property< int > m_expertMode_RunNumber
std::unique_ptr< TMVA::Reader > m_reader_MUTAGIMO_etaBin3
bool isBIS78(const float eta, const float phi) const
Check if muon eta/phi falls in BIS7/8 chambers.
bool passedLowPtEfficiencyMVACut(const xAOD::Muon &) const
void retrieveSummaryValue(const P &muon, T &value, const S type, bool ignoreMissing=false) const
helper function to retrieve a hitSummary value
SG::ReadHandleKey< xAOD::EventInfo > m_eventInfo
MuonSelectionTool(const std::string &name="MuonSelection")
Create a proper constructor for Athena.
std::unique_ptr< MVAUtils::BDT > m_MuGirl
Gaudi::Property< bool > m_useMVALowPt
Gaudi::Property< std::string > m_custom_dir
Gaudi::Property< std::string > m_MVAreaderFile_MuTagIMO_etaBin1
Gaudi::Property< bool > m_allowComm
bool isBEE(const float eta, const float phi) const
Check if muon eta/phi falls in BEE chambers.
SG::ConstAccessor< T, ALLOC > ConstAccessor
Definition AuxElement.h:570
SG::Accessor< T, ALLOC > Accessor
Definition AuxElement.h:573
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
void setCutResult(const std::string &cutName, bool cutResult)
Set the result of a cut, based on the cut name (safer)
Definition AcceptData.h:134
AsgTool(const std::string &name)
Constructor specifying the tool instance's name.
Definition AsgTool.cxx:58
@ IS_SIMULATION
true: simulation, false: data
Class providing the definition of the 4-vector interface.
float theta() const
Returns the parameter, which has range 0 to .
float numberDoF() const
Returns the number of degrees of freedom of the overall track or vertex fit as float.
virtual double phi() const override final
The azimuthal angle ( ) of the particle (has range to .)
const ParametersCovMatrix_t definingParametersCovMatrix() const
Returns the 5x5 symmetric matrix containing the defining parameters covariance matrix.
float qOverP() const
Returns the parameter.
virtual double pt() const override final
The transverse momentum ( ) of the particle.
float chiSquared() const
Returns the of the overall track fit.
virtual double eta() const override final
The pseudorapidity ( ) of the particle.
float charge() const
Returns the charge.
std::map< std::string, InferenceData > OutputDataMap
std::map< std::string, InferenceData > InputDataMap
Select isolated Photons, Electrons and Muons.
constexpr float MeVtoGeV
bool first
Definition DeMoScan.py:534
Muon::MuonStationIndex::ChIndex ChIdx
constexpr int toInt(const EnumType enumVal)
ChIndex
enum to classify the different chamber layers in the muon spectrometer
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
@ Muon
The object is a muon.
Definition ObjectType.h:48
setRcore setEtHad setFside pt
setSAddress etaMS
TrackParticle_v1 TrackParticle
Reference the current persistent version:
Muon_v1 Muon
Reference the current persistent version:
MuonSegment_v1 MuonSegment
Reference the current persistent version:
@ numberOfPixelHoles
number of pixel layers on track with absence of hits [unit8_t].
@ numberOfPrecisionLayers
layers with at least 3 hits [unit8_t].
@ numberOfTRTHits
number of TRT hits [unit8_t].
@ numberOfSCTDeadSensors
number of dead SCT sensors crossed [unit8_t].
@ numberOfPrecisionHoleLayers
layers with holes AND no hits [unit8_t].
@ numberOfSCTHits
number of hits in SCT [unit8_t].
@ numberOfGoodPrecisionLayers
layers with at least 3 hits that are not deweighted [uint8_t]
@ numberOfPixelHits
these are the pixel hits, including the b-layer [unit8_t].
@ numberOfTRTOutliers
number of TRT outliers [unit8_t].
@ numberOfPixelDeadSensors
number of dead pixel sensors crossed [unit8_t].
@ numberOfSCTHoles
number of SCT holes [unit8_t].
@ SiSpacePointsSeedMaker_LargeD0
@ middleSmallHoles
number of precision holes in the middle small layer
@ outerSmallHits
number of precision hits in the outer small layer
@ middleSmallHits
number of precision hits in the middle small layer
@ middleClosePrecisionHits
number of close precision hits in the middle layer
@ outerLargeHits
number of precision hits in the outer large layer
@ middleLargeHits
number of precision hits in the middle large layer
@ outerClosePrecisionHits
number of close precision hits in the outer layer
@ isSmallGoodSectors
if non-deweighted track chambers are small
@ extendedSmallHits
number of precision hits in the extended small layer
@ extendedLargeHits
number of precision hits in the extended large layer
@ phiLayer1STGCHits
number of phi hits in the first STGC trigger layer (STGC1)
@ outerLargeHoles
number of precision holes in the outer large layer
@ extendedSmallHoles
number of precision holes in the extended small layer
@ innerLargeHits
number of precision hits in the inner large layer
@ middleLargeHoles
number of precision holes in the middle large layer
@ etaLayer2STGCHits
number of eta hits in the second STGC trigger layer (STGC2)
@ etaLayer1STGCHits
number of eta hits in the first STGC trigger layer (STGC1)
@ phiLayer2STGCHits
number of phi hits in the second STGC trigger layer (STGC2)
@ combinedTrackOutBoundsPrecisionHits
total out-of-bounds hits on the combined track
@ cscUnspoiledEtaHits
number of unspoiled CSC eta clusters on track
@ outerSmallHoles
number of precision holes in the outer small layer
@ innerSmallHits
number of precision hits in the inner small layer
struct to handle easily number of hits in different parts of the MS
TFile * file
Tell the compiler to optimize assuming that FP may trap.
#define CXXUTILS_TRAPPING_FP
Definition trapping_fp.h:24