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 // Set up TMVA readers for MVA-based low-pT working point
175 // E and O refer to even and odd event numbers to avoid applying the MVA on events used for training
176 TString weightPath_EVEN_MuidCB = PathResolverFindCalibFile(m_MVAreaderFile_EVEN_MuidCB);
177 TString weightPath_ODD_MuidCB = PathResolverFindCalibFile(m_MVAreaderFile_ODD_MuidCB);
178 TString weightPath_EVEN_MuGirl = PathResolverFindCalibFile(m_MVAreaderFile_EVEN_MuGirl);
179 TString weightPath_ODD_MuGirl = PathResolverFindCalibFile(m_MVAreaderFile_ODD_MuGirl);
180
181 auto make_mva_reader = [](TString file_path) {
182 std::vector<std::string> mva_var_names{"momentumBalanceSignificance",
183 "scatteringCurvatureSignificance",
184 "scatteringNeighbourSignificance",
185 "EnergyLoss",
186 "middleLargeHoles+middleSmallHoles",
187 "muonSegmentDeltaEta",
188 "muonSeg1ChamberIdx",
189 "muonSeg2ChamberIdx"};
190 std::unique_ptr<TMVA::Reader> reader = std::make_unique<TMVA::Reader>(mva_var_names);
191 reader->BookMVA("BDTG", file_path);
192 return reader;
193 };
194 m_readerE_MUID = make_mva_reader(weightPath_EVEN_MuidCB);
195
196 m_readerO_MUID = make_mva_reader(weightPath_ODD_MuidCB);
197
198 m_readerE_MUGIRL = make_mva_reader(weightPath_EVEN_MuGirl);
199
200 m_readerO_MUGIRL = make_mva_reader(weightPath_ODD_MuGirl);
201
203 TString weightPath_MuTagIMO_etaBin1 = PathResolverFindCalibFile(m_MVAreaderFile_MuTagIMO_etaBin1);
204 TString weightPath_MuTagIMO_etaBin2 = PathResolverFindCalibFile(m_MVAreaderFile_MuTagIMO_etaBin2);
205 TString weightPath_MuTagIMO_etaBin3 = PathResolverFindCalibFile(m_MVAreaderFile_MuTagIMO_etaBin3);
206
207 auto make_mva_reader_MuTagIMO = [](TString file_path, bool useSeg2ChamberIndex) {
208 std::vector<std::string> mva_var_names;
209 if (useSeg2ChamberIndex) mva_var_names.push_back("muonSeg2ChamberIndex");
210 mva_var_names.push_back("muonSeg1ChamberIndex");
211 mva_var_names.push_back("muonSeg1NPrecisionHits");
212 mva_var_names.push_back("muonSegmentDeltaEta");
213 mva_var_names.push_back("muonSeg1GlobalR");
214 mva_var_names.push_back("muonSeg1Chi2OverDoF");
215 mva_var_names.push_back("muonSCS");
216
217 std::unique_ptr<TMVA::Reader> reader = std::make_unique<TMVA::Reader>(mva_var_names);
218 reader->BookMVA("BDT", file_path);
219 return reader;
220 };
221
222 m_reader_MUTAGIMO_etaBin1 = make_mva_reader_MuTagIMO(weightPath_MuTagIMO_etaBin1, false);
223 m_reader_MUTAGIMO_etaBin2 = make_mva_reader_MuTagIMO(weightPath_MuTagIMO_etaBin2, false);
224 m_reader_MUTAGIMO_etaBin3 = make_mva_reader_MuTagIMO(weightPath_MuTagIMO_etaBin3, true);
225 }
226 }
227
228 ATH_MSG_INFO("TightNNScore calculation is " << (m_calculateTightNNScore ? "enabled." : "disabled."));
229
231 if (m_onnxTool.empty()) {
232 ATH_MSG_ERROR("Cannot calculate TightNNScore: ONNX tool not configured! "
233 "Please set the ORTInferenceTool property to a valid AthOnnx::OnnxRuntimeInferenceTool instance.");
234 return StatusCode::FAILURE;
235 }
236
237 ATH_MSG_INFO("Retrieving ONNX tool: " << m_onnxTool.name());
238 ATH_CHECK(m_onnxTool.retrieve());
239 } else ATH_MSG_INFO("ONNX tool not configured — skipping retrieval.");
240
241 ATH_MSG_INFO("Finished ONNX tool setup");
242
243 ATH_CHECK(m_eventInfo.initialize());
244 // Return gracefully:
245 return StatusCode::SUCCESS;
246 }
247
248 StatusCode MuonSelectionTool::getHist(TFile* file, const std::string& histName, std::unique_ptr<TH1>& hist) const {
249 //
250 if (!file) {
251 ATH_MSG_ERROR(" getHist(...) TFile is nullptr! Check that the Tight cut map is loaded correctly");
252 return StatusCode::FAILURE;
253 }
254 TH1* h_ptr = nullptr;
255 file->GetObject(histName.c_str(), h_ptr);
256 //
257 //
258 if (!h_ptr) {
259 ATH_MSG_ERROR("Cannot retrieve histogram " << histName);
260 return StatusCode::FAILURE;
261 }
262 hist = std::unique_ptr<TH1>{h_ptr};
263 hist->SetDirectory(nullptr);
264 ATH_MSG_INFO("Successfully read tight working point histogram: " << hist->GetName());
265 //
266 return StatusCode::SUCCESS;
267 }
268
270
272 // Check if this is a muon:
273 if (p->type() != xAOD::Type::Muon) {
274 ATH_MSG_ERROR("accept(...) Function received a non-muon");
276 }
277
278 // Cast it to a muon:
279 const xAOD::Muon* mu = dynamic_cast<const xAOD::Muon*>(p);
280 if (!mu) {
281 ATH_MSG_FATAL("accept(...) Failed to cast particle to muon");
283 }
284
285 // Let the specific function do the work:
286 return accept(*mu);
287 }
288
289 //============================================================================
291
292 static std::atomic<bool> checkDone{false};
293
294 if(!checkDone) {
295 // Check that the user either set the correct geometry or enabled on-the-fly determination
296 // This can happen intentionally in developer mode. In any case don't throw an exception
297 // (we should either trust the user or ignore in the first place and force on-the-fly determination).
298 if (isRun3() != isRun3(true)) {
299 ATH_MSG_WARNING("MuonSelectionTool is configured with isRun3Geo="<<isRun3()
300 <<" while on-the fly check for runNumber "<<getRunNumber(true)<<" indicates isRun3Geo="<<isRun3(true));
301 }
302
303 // Check that the requested WP is currently supported by the MCP group.
304 if (isRun3()) {
305 if(m_quality!=0 && m_quality!=1 && m_quality!=2 && m_quality!=4 && m_quality!=5) {
306 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");
307 }
308
310 ATH_MSG_WARNING("For Run3, Tight WP is supported only when ExcludeNSWFromPrecisionLayers=False and RecalcPrecisionLayerswNSW=True");
311 }
312 }
313
314 checkDone = true;
315 }
316 }
317
318 //============================================================================
320 // Verbose information
321 ATH_MSG_VERBOSE("-----------------------------------");
322 ATH_MSG_VERBOSE("New muon passed to accept function:");
323 if (mu.muonType() == xAOD::Muon::Combined)
324 ATH_MSG_VERBOSE("Muon type: combined");
325 else if (mu.muonType() == xAOD::Muon::MuonStandAlone)
326 ATH_MSG_VERBOSE("Muon type: stand-alone");
327 else if (mu.muonType() == xAOD::Muon::SegmentTagged)
328 ATH_MSG_VERBOSE("Muon type: segment-tagged");
329 else if (mu.muonType() == xAOD::Muon::CaloTagged)
330 ATH_MSG_VERBOSE("Muon type: calorimeter-tagged");
331 else if (mu.muonType() == xAOD::Muon::SiliconAssociatedForwardMuon)
332 ATH_MSG_VERBOSE("Muon type: silicon-associated forward");
333 ATH_MSG_VERBOSE("Muon pT [GeV]: " << mu.pt() * MeVtoGeV);
334 ATH_MSG_VERBOSE("Muon eta: " << mu.eta());
335 ATH_MSG_VERBOSE("Muon phi: " << mu.phi());
336
337 checkSanity();
338
339 asg::AcceptData acceptData(&m_acceptInfo);
340
341 // Do the eta cut:
342 if (std::abs(mu.eta()) >= m_maxEta) {
343 ATH_MSG_VERBOSE("Failed eta cut");
344 return acceptData;
345 }
346 acceptData.setCutResult("Eta", true);
347
348 // Passes ID hit cuts
349 bool passIDCuts = passedIDCuts(mu);
350 ATH_MSG_VERBOSE("Passes ID Hit cuts " << passIDCuts);
351 acceptData.setCutResult("IDHits", passIDCuts);
352
353 // passes muon preselection
354 bool passMuonCuts = passedMuonCuts(mu);
355 ATH_MSG_VERBOSE("Passes preselection cuts " << passMuonCuts);
356 acceptData.setCutResult("Preselection", passMuonCuts);
357
358 if (!passIDCuts || !passMuonCuts) { return acceptData; }
359
360 // Passes quality requirements
361 xAOD::Muon::Quality thisMu_quality = getQuality(mu);
362 bool thisMu_highpt = false;
363 thisMu_highpt = passedHighPtCuts(mu);
364 bool thisMu_lowptE = false;
365 thisMu_lowptE = passedLowPtEfficiencyCuts(mu, thisMu_quality);
366 ATH_MSG_VERBOSE("Summary of quality information for this muon: ");
367 ATH_MSG_VERBOSE("Muon quality: " << thisMu_quality << " passes HighPt: " << thisMu_highpt
368 << " passes LowPtEfficiency: " << thisMu_lowptE);
369 if (m_quality < 4 && thisMu_quality > m_quality) { return acceptData; }
370 if (m_quality == 4 && !thisMu_highpt) { return acceptData; }
371 if (m_quality == 5 && !thisMu_lowptE) { return acceptData; }
372 acceptData.setCutResult("Quality", true);
373 // Return the result:
374 return acceptData;
375 }
376
378 mu.setQuality(getQuality(mu));
379 return;
380 }
381 void MuonSelectionTool::IdMsPt(const xAOD::Muon& mu, float& idPt, float& mePt) const {
382 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
383 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
384 if (!idtrack || !metrack) idPt = mePt = -1.;
385 else if (m_turnOffMomCorr) {
386 mePt = metrack->pt();
387 idPt = idtrack->pt();
388 } else {
389 if (!mePt_acc.isAvailable(mu) || !idPt_acc.isAvailable(mu)) {
390 ATH_MSG_FATAL("The muon with pT " << mu.pt() * MeVtoGeV << " eta: " << mu.eta() << ", phi:" << mu.phi()
391 << " q:" << mu.charge() << ", author:" << mu.author()
392 << " is not decorated with calibrated momenta. Please fix");
393 throw std::runtime_error("MuonSelectionTool() - qOverP significance calculation failed");
394 }
395 mePt = mePt_acc(mu);
396 idPt = idPt_acc(mu);
397 }
398 }
399
401 // Avoid spurious FPEs in the clang build.
403
404 if (m_disablePtCuts) {
405 ATH_MSG_VERBOSE(__FILE__ << ":"<<__LINE__
406 << " Momentum dependent cuts are disabled. Return 0.");
407 return 0.;
408 }
409 const xAOD::TrackParticle* idtrack = muon.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
410 const xAOD::TrackParticle* metrack = muon.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
411 if (!idtrack || !metrack) {
412 ATH_MSG_VERBOSE("No ID / MS track. Return dummy large value of 1 mio");
413 return 1.e6;
414 }
415 float mePt{-1.}, idPt{-1.};
416 IdMsPt(muon, idPt, mePt);
417
418 const float meP = mePt / std::sin(metrack->theta());
419 const float idP = idPt / std::sin(idtrack->theta());
420
421 float qOverPsigma = std::sqrt(idtrack->definingParametersCovMatrix()(4, 4) + metrack->definingParametersCovMatrix()(4, 4));
422 return std::abs((metrack->charge() / meP) - (idtrack->charge() / idP)) / qOverPsigma;
423 }
425 if (m_disablePtCuts) {
426 ATH_MSG_VERBOSE(__FILE__ << ":"<<__LINE__
427 << "Momentum dependent cuts are disabled. Return 0.");
428 return 0.;
429 }
430 float mePt{-1.}, idPt{-1.};
431 IdMsPt(muon, idPt, mePt);
432 return std::abs(idPt - mePt) / muon.pt();
433 }
434
435 xAOD::Muon::Quality MuonSelectionTool::getQuality(const xAOD::Muon& mu) const {
436 ATH_MSG_VERBOSE("Evaluating muon quality...");
437 if (isRun3() && mu.isAuthor(xAOD::Muon::Author::Commissioning) && !m_allowComm) {
438 ATH_MSG_VERBOSE("Reject authors from the commissioning chain");
439 return xAOD::Muon::VeryLoose;
440 }
441
442 // SegmentTagged muons
443 if (mu.muonType() == xAOD::Muon::SegmentTagged) {
444 ATH_MSG_VERBOSE("Muon is segment-tagged");
445
446 if (std::abs(mu.eta()) < 0.1) {
447 ATH_MSG_VERBOSE("Muon is loose");
448 return xAOD::Muon::Loose;
449 } else {
450 ATH_MSG_VERBOSE("Do not allow segment-tagged muon at |eta| > 0.1 - return VeryLoose");
451 return xAOD::Muon::VeryLoose;
452 }
453 }
454
455 // CaloTagged muons
456 if (mu.muonType() == xAOD::Muon::CaloTagged) {
457 ATH_MSG_VERBOSE("Muon is calorimeter-tagged");
458
459 if (std::abs(mu.eta()) < 0.1 && passedCaloTagQuality(mu)) {
460 ATH_MSG_VERBOSE("Muon is loose");
461 return xAOD::Muon::Loose;
462 }
463 }
464
465 // Combined muons
466 hitSummary summary{};
467 fillSummary(mu, summary);
468
469 if (mu.muonType() == xAOD::Muon::Combined) {
470 ATH_MSG_VERBOSE("Muon is combined");
471 if (mu.author() == xAOD::Muon::STACO) {
472 ATH_MSG_VERBOSE("Muon is STACO - return VeryLoose");
473 return xAOD::Muon::VeryLoose;
474 }
475
476 // rejection muons with out-of-bounds hits
479
481 ATH_MSG_VERBOSE("Muon has out-of-bounds precision hits - return VeryLoose");
482 return xAOD::Muon::VeryLoose;
483 }
484
485 // LOOSE / MEDIUM / TIGHT WP
486 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
487 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
488 if (idtrack && metrack && metrack->definingParametersCovMatrix()(4, 4) > 0) {
489 const float qOverPsignif = qOverPsignificance(mu);
490 const float rho = rhoPrime(mu);
491 const float reducedChi2 = mu.primaryTrackParticle()->chiSquared() / mu.primaryTrackParticle()->numberDoF();
492
493 ATH_MSG_VERBOSE("Relevant cut variables:");
494 ATH_MSG_VERBOSE("number of precision layers = " << (int)summary.nprecisionLayers);
495 ATH_MSG_VERBOSE("reduced Chi2 = " << reducedChi2);
496 ATH_MSG_VERBOSE("qOverP significance = " << qOverPsignif);
497
498 // NEW TIGHT WP
499 if (summary.nprecisionLayers > 1 && reducedChi2 < 8 && std::abs(qOverPsignif) < 7) {
500 if (passTight(mu, rho, qOverPsignif)) {
501 ATH_MSG_VERBOSE("Muon is tight");
502 return xAOD::Muon::Tight;
503 }
504 }
505
506 ATH_MSG_VERBOSE("Muon did not pass requirements for tight combined muon");
507
508 // MEDIUM WP
509 if ((std::abs(qOverPsignif) < 7 || m_toroidOff) &&
510 (summary.nprecisionLayers > 1 ||(summary.nprecisionLayers == 1 && summary.nprecisionHoleLayers < 2 && std::abs(mu.eta()) < 0.1))
511
512 ) {
513 ATH_MSG_VERBOSE("Muon is medium");
514 return xAOD::Muon::Medium;
515 }
516
517 ATH_MSG_VERBOSE("Muon did not pass requirements for medium combined muon");
518
519 } else {
520 ATH_MSG_VERBOSE("Muon is missing the ID and/or ME tracks...");
521
522 // CB muons with missing ID or ME track
523 if ((summary.nprecisionLayers > 1 ||
524 (summary.nprecisionLayers == 1 && summary.nprecisionHoleLayers < 2 && std::abs(mu.eta()) < 0.1))) {
525 // In toroid-off data ME/MS tracks often missing - need special treatment => flagging as "Medium"
526 // In toroid-on data ME/MS tracks missing only for <1% of CB muons, mostly MuGirl (to be fixed) => flagging as "Loose"
527 if (m_toroidOff) {
528 ATH_MSG_VERBOSE("...this is toroid-off data - returning medium");
529 return xAOD::Muon::Medium;
530 } else {
531 ATH_MSG_VERBOSE("...this is not toroid-off data - returning loose");
532 return xAOD::Muon::Loose;
533 }
534 }
535 }
536
537 // Improvement for Loose targeting low-pT muons (pt<7 GeV)
538 if ((m_disablePtCuts || mu.pt() * MeVtoGeV < 7.) && std::abs(mu.eta()) < 1.3 && summary.nprecisionLayers > 0 &&
539 (mu.author() == xAOD::Muon::MuGirl && mu.isAuthor(xAOD::Muon::MuTagIMO))) {
540 ATH_MSG_VERBOSE("Muon passed selection for loose working point at low pT");
541 return xAOD::Muon::Loose;
542 }
543
544 // didn't pass the set of requirements for a medium or tight combined muon
545 ATH_MSG_VERBOSE("Did not pass selections for combined muon - returning VeryLoose");
546 return xAOD::Muon::VeryLoose;
547 }
548
549 // SA muons
550 if (mu.author() == xAOD::Muon::MuidSA) {
551 ATH_MSG_VERBOSE("Muon is stand-alone");
552
553 if (std::abs(mu.eta()) > 2.5) {
554 ATH_MSG_VERBOSE("number of precision layers = " << (int)summary.nprecisionLayers);
555
556 // 3 station requirement for medium
557 if (summary.nprecisionLayers > 2 && !m_toroidOff) {
558 ATH_MSG_VERBOSE("Muon is medium");
559 return xAOD::Muon::Medium;
560 }
561 }
562
563 // didn't pass the set of requirements for a medium SA muon
564 ATH_MSG_VERBOSE("Muon did not pass selection for medium stand-alone muon - return VeryLoose");
565 return xAOD::Muon::VeryLoose;
566 }
567
568 // SiliconAssociatedForward (SAF) muons
569 if (mu.muonType() == xAOD::Muon::SiliconAssociatedForwardMuon) {
570 ATH_MSG_VERBOSE("Muon is silicon-associated forward muon");
571
572 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
573 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
574
575 if (cbtrack && metrack) {
576 if (std::abs(cbtrack->eta()) > 2.5) {
577 ATH_MSG_VERBOSE("number of precision layers = " << (int)summary.nprecisionLayers);
578
579 if (summary.nprecisionLayers > 2 && !m_toroidOff) {
580 if (mu.trackParticle(xAOD::Muon::Primary) == mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle) &&
581 !m_developMode) {
583 "SiliconForwardAssociated muon has ID track as primary track particle. "
584 << "This is a bug fixed starting with xAODMuon-00-17-07, which should be present in this release. "
585 << "Please report this to the Muon CP group!");
586 }
587 ATH_MSG_VERBOSE("Muon is medium");
588 return xAOD::Muon::Medium;
589 }
590 }
591 }
592
593 // didn't pass the set of requirements for a medium SAF muon
594 ATH_MSG_VERBOSE("Muon did not pass selection for medium silicon-associated forward muon - return VeryLoose");
595 return xAOD::Muon::VeryLoose;
596 }
597
598 ATH_MSG_VERBOSE("Muon did not pass selection for loose/medium/tight for any muon type - return VeryLoose");
599 return xAOD::Muon::VeryLoose;
600 }
601
602 void MuonSelectionTool::setPassesIDCuts(xAOD::Muon& mu) const { mu.setPassesIDCuts(passedIDCuts(mu)); }
603
605 if (m_useLRT) {
606 static const SG::AuxElement::Accessor<char> isLRTmuon("isLRT");
607 if (isLRTmuon.isAvailable(mu)) {
608 if (isLRTmuon(mu)) return true;
609 }
610 else {
611 static const SG::AuxElement::Accessor<uint64_t> patternAcc("patternRecoInfo");
612 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
613 if(idtrack) {
614 if(!patternAcc.isAvailable(*idtrack)) {
615 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.");
616 throw std::runtime_error("MuonSelectionTool() - isLRT decor and patternRecoInfo both unavailable for a muon.");
617 }
618 std::bitset<xAOD::NumberOfTrackRecoInfo> patternBitSet(patternAcc(*idtrack));
619 if (patternBitSet.test(xAOD::SiSpacePointsSeedMaker_LargeD0)) return true;
620 }
621 }
622 }
623 // do not apply the ID hit requirements for SA muons for |eta| > 2.5
624 if (mu.author() == xAOD::Muon::MuidSA && std::abs(mu.eta()) > 2.5) {
625 return true;
626 } else if (mu.muonType() == xAOD::Muon::SiliconAssociatedForwardMuon) {
627 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
628 if (cbtrack && std::abs(cbtrack->eta()) > 2.5) { return true; }
629 return false;
630 } else {
631 if (mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle))
632 return passedIDCuts(*mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle));
633 else if (mu.primaryTrackParticle())
634 return passedIDCuts(*mu.primaryTrackParticle());
635 }
636 return false;
637 }
638
640 if (mu.muonType() != xAOD::Muon::Combined) return false;
641 // ::
642 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
643 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
644 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
645 // ::
646 // Some spurious muons are found to have negative ME track fit covariance, and are typically poorly reconstructed
647 if (metrack && metrack->definingParametersCovMatrix()(4, 4) < 0.0) return true;
648 // ::
649 bool IsBadMuon = false;
650 if (idtrack && metrack && cbtrack) {
651 // ::
652 double qOverP_ID = idtrack->qOverP();
653 double qOverPerr_ID = std::sqrt(idtrack->definingParametersCovMatrix()(4, 4));
654 double qOverP_ME = metrack->qOverP();
655 double qOverPerr_ME = std::sqrt(metrack->definingParametersCovMatrix()(4, 4));
656 double qOverP_CB = cbtrack->qOverP();
657 double qOverPerr_CB = std::sqrt(cbtrack->definingParametersCovMatrix()(4, 4));
658 // ::
659 if (m_quality == 4) {
660 // recipe for high-pt selection
661 IsBadMuon = !passedErrorCutCB(mu);
662
663 hitSummary summary{};
664 fillSummary(mu, summary);
665
666 // temporarily apply same recipe as for other working points in addition to CB error
667 // cut for 2-station muons, pending better treatment of ID/MS misalignments
668 if (m_use2stationMuonsHighPt && summary.nprecisionLayers == 2) {
669 double IdCbRatio = std::abs((qOverPerr_ID / qOverP_ID) / (qOverPerr_CB / qOverP_CB));
670 double MeCbRatio = std::abs((qOverPerr_ME / qOverP_ME) / (qOverPerr_CB / qOverP_CB));
671 IsBadMuon = (IdCbRatio < 0.8 || MeCbRatio < 0.8 || IsBadMuon);
672 }
673 } else {
674 // recipe for other WP
675 double IdCbRatio = std::abs((qOverPerr_ID / qOverP_ID) / (qOverPerr_CB / qOverP_CB));
676 double MeCbRatio = std::abs((qOverPerr_ME / qOverP_ME) / (qOverPerr_CB / qOverP_CB));
677 IsBadMuon = (IdCbRatio < 0.8 || MeCbRatio < 0.8);
678 }
679 } else {
680 return true;
681 }
682 return IsBadMuon;
683 }
684
686 xAOD::Muon::Quality thisMu_quality = getQuality(mu);
687 return passedLowPtEfficiencyCuts(mu, thisMu_quality);
688 }
689
690 bool MuonSelectionTool::passedLowPtEfficiencyCuts(const xAOD::Muon& mu, xAOD::Muon::Quality thisMu_quality) const {
691 ATH_MSG_VERBOSE("Checking whether muon passes low-pT selection...");
692
693 if (!m_useAllAuthors) { // no allAuthors, always fail the WP
694 ATH_MSG_VERBOSE("Do not have allAuthors variable - fail low-pT");
695 return false;
696 }
697
698 // requiring combined muons, unless segment-tags are included
700 if (mu.muonType() != xAOD::Muon::Combined) {
701 ATH_MSG_VERBOSE("Muon is not combined - fail low-pT");
702 return false;
703 }
704 } else {
705 if (mu.muonType() != xAOD::Muon::Combined && mu.muonType() != xAOD::Muon::SegmentTagged) {
706 ATH_MSG_VERBOSE("Muon is not combined or segment-tagged - fail low-pT");
707 return false;
708 }
709 }
710
711 // author check
713 if (mu.author() != xAOD::Muon::MuGirl && mu.author() != xAOD::Muon::MuidCo) {
714 ATH_MSG_VERBOSE("Muon is neither MuGirl nor MuidCo - fail low-pT");
715 return false;
716 }
717 } else {
718 if (mu.author() != xAOD::Muon::MuGirl && mu.author() != xAOD::Muon::MuidCo && mu.author() != xAOD::Muon::MuTagIMO) {
719 ATH_MSG_VERBOSE("Muon is neither MuGirl / MuidCo / MuTagIMO - fail low-pT");
720 return false;
721 }
722 }
723
724 // applying Medium selection above pT = 18 GeV
725 if (mu.pt() * MeVtoGeV > 18.) {
726 ATH_MSG_VERBOSE("pT > 18 GeV - apply medium selection");
727 if (thisMu_quality <= xAOD::Muon::Medium) {
728 ATH_MSG_VERBOSE("Muon passed low-pT selection");
729 return true;
730 } else {
731 ATH_MSG_VERBOSE("Muon failed low-pT selection");
732 return false;
733 }
734 }
735
736 // requiring Medium in forward regions
737 if (!m_useMVALowPt && std::abs(mu.eta()) > 1.55 && thisMu_quality > xAOD::Muon::Medium) {
738 ATH_MSG_VERBOSE("Not using MVA selection, failing low-pT selection due to medium requirement in forward region");
739 return false;
740 }
741
742 // rejection of muons with out-of-bounds hits
746 ATH_MSG_VERBOSE("Muon has out-of-bounds precision hits - fail low-pT");
747 return false;
748 }
749
750 // requiring explicitely >=1 station (2 in the |eta|>1.3 region when Medium selection is not explicitely required)
751 if (mu.muonType() == xAOD::Muon::Combined) {
752 hitSummary summary{};
753 fillSummary(mu, summary);
754 uint nStationsCut = (std::abs(mu.eta()) > 1.3 && std::abs(mu.eta()) < 1.55) ? 2 : 1;
755 if (summary.nprecisionLayers < nStationsCut) {
756 ATH_MSG_VERBOSE("number of precision layers = " << (int)summary.nprecisionLayers << " is lower than cut value " << nStationsCut
757 << " - fail low-pT");
758 return false;
759 }
760 }
761
762 // reject MuGirl muon if not found also by MuTagIMO
763 if (m_useAllAuthors) {
764 if (mu.author() == xAOD::Muon::MuGirl && !mu.isAuthor(xAOD::Muon::MuTagIMO)) {
765 ATH_MSG_VERBOSE("MuGirl muon is not confirmed by MuTagIMO - fail low-pT");
766 return false;
767 }
768 } else
769 return false;
770
771 if (m_useMVALowPt) {
772 ATH_MSG_VERBOSE("Applying MVA-based selection");
774 }
775
776 ATH_MSG_VERBOSE("Applying cut-based selection");
777
778 // apply some loose quality requirements
779 float momentumBalanceSignificance{0.}, scatteringCurvatureSignificance{0.}, scatteringNeighbourSignificance{0.};
780
781 retrieveParam(mu, momentumBalanceSignificance, xAOD::Muon::momentumBalanceSignificance);
782 retrieveParam(mu, scatteringCurvatureSignificance, xAOD::Muon::scatteringCurvatureSignificance);
783 retrieveParam(mu, scatteringNeighbourSignificance, xAOD::Muon::scatteringNeighbourSignificance);
784
785 ATH_MSG_VERBOSE("momentum balance significance: " << momentumBalanceSignificance);
786 ATH_MSG_VERBOSE("scattering curvature significance: " << scatteringCurvatureSignificance);
787 ATH_MSG_VERBOSE("scattering neighbour significance: " << scatteringNeighbourSignificance);
788
789 if (std::abs(momentumBalanceSignificance) > 3. || std::abs(scatteringCurvatureSignificance) > 3. ||
790 std::abs(scatteringNeighbourSignificance) > 3.) {
791 ATH_MSG_VERBOSE("Muon failed cut-based low-pT selection");
792 return false;
793 }
794
795 // passed low pt selection
796 ATH_MSG_VERBOSE("Muon passed cut-based low-pT selection");
797 return true;
798 }
799
800 std::vector<const xAOD::MuonSegment*> MuonSelectionTool::getSegmentsSorted(const xAOD::Muon& mu) const {
801 std::vector<const xAOD::MuonSegment*> segments_sorted;
802 segments_sorted.reserve(mu.nMuonSegments());
803
804 for (unsigned int i = 0; i < mu.nMuonSegments(); i++) {
805 if (!mu.muonSegment(i))
806 ATH_MSG_WARNING("The muon reports more segments than are available. Please report this to the muon software community!");
807 else
808 segments_sorted.push_back(mu.muonSegment(i));
809 }
810
811 std::sort(segments_sorted.begin(), segments_sorted.end(), chamberIndexCompare);
812
813 return segments_sorted;
814 }
815
817 //LowPt Not supported in run3 for the time being
818 if(isRun3() && !m_developMode){
819 ATH_MSG_VERBOSE("LowPt WP currently not supported for run3 if not in expert mode");
820 return false;
821 }
822 if (!m_useMVALowPt) {
823 ATH_MSG_DEBUG("Low pt MVA disabled. Return... ");
824 return false;
825 }
826 std::lock_guard<std::mutex> guard(m_low_pt_mva_mutex);
827 // set values for all BDT input variables from the muon in question
828 float momentumBalanceSig{-1}, CurvatureSig{-1}, energyLoss{-1}, muonSegmentDeltaEta{-1}, scatteringNeigbour{-1};
829 retrieveParam(mu, momentumBalanceSig, xAOD::Muon::momentumBalanceSignificance);
830 retrieveParam(mu, CurvatureSig, xAOD::Muon::scatteringCurvatureSignificance);
831 retrieveParam(mu, scatteringNeigbour, xAOD::Muon::scatteringNeighbourSignificance);
832 retrieveParam(mu, energyLoss, xAOD::Muon::EnergyLoss);
833 retrieveParam(mu, muonSegmentDeltaEta, xAOD::Muon::segmentDeltaEta);
834
838
839 float seg1ChamberIdx{-1.}, seg2ChamberIdx{-1.}, middleHoles{-1.}, seg1NPrecisionHits{-1.}, seg1GlobalR{-1.}, seg1Chi2OverDoF{-1.};
840
841 std::vector<const xAOD::MuonSegment*> muonSegments = getSegmentsSorted(mu);
842
843 if (mu.author() == xAOD::Muon::MuTagIMO && muonSegments.size() == 0)
844 ATH_MSG_WARNING("passedLowPtEfficiencyMVACut - found segment-tagged muon with no segments!");
845
846 using namespace Muon::MuonStationIndex;
847 seg1ChamberIdx = (!muonSegments.empty()) ? toInt(muonSegments[0]->chamberIndex()) : -9;
848 seg2ChamberIdx = (muonSegments.size() > 1) ? toInt(muonSegments[1]->chamberIndex()) : -9;
849
850 // these variables are only used for MuTagIMO
851 if (mu.author() == xAOD::Muon::MuTagIMO) {
852 seg1NPrecisionHits = (!muonSegments.empty()) ? muonSegments[0]->nPrecisionHits() : -1;
853 seg1GlobalR = (!muonSegments.empty())
854 ? std::hypot(muonSegments[0]->x(), muonSegments[0]->y(), muonSegments[0]->z())
855 : 0;
856 seg1Chi2OverDoF = (!muonSegments.empty()) ? muonSegments[0]->chiSquared() / muonSegments[0]->numberDoF() : -1;
857 }
858
859 middleHoles = middleSmallHoles + middleLargeHoles;
860
861 // get event number from event info
863 //overwrite event number
864 unsigned long long eventNumber = 0;
866 else eventNumber = eventInfo->eventNumber();
867
868 // variables for the BDT
869 std::vector<float> var_vector;
870 if (mu.author() == xAOD::Muon::MuidCo || mu.author() == xAOD::Muon::MuGirl) {
871 var_vector = {momentumBalanceSig, CurvatureSig, scatteringNeigbour, energyLoss,
872 middleHoles, muonSegmentDeltaEta, seg1ChamberIdx, seg2ChamberIdx};
873 } else {
874 if (std::abs(mu.eta()) >= 1.3)
875 var_vector = {seg2ChamberIdx, seg1ChamberIdx, seg1NPrecisionHits, muonSegmentDeltaEta,
876 seg1GlobalR, seg1Chi2OverDoF, std::abs(CurvatureSig)};
877 else
878 var_vector = {seg1ChamberIdx, seg1NPrecisionHits, muonSegmentDeltaEta,
879 seg1GlobalR, seg1Chi2OverDoF, std::abs(CurvatureSig)};
880 }
881
882 // use different trainings for even/odd numbered events
883 TMVA::Reader *reader_MUID, *reader_MUGIRL;
884 if (eventNumber % 2 == 1) {
885 reader_MUID = m_readerE_MUID.get();
886 reader_MUGIRL = m_readerE_MUGIRL.get();
887 } else {
888 reader_MUID = m_readerO_MUID.get();
889 reader_MUGIRL = m_readerO_MUGIRL.get();
890 }
891
892 // BDT for MuTagIMO is binned in |eta|
893 TMVA::Reader* reader_MUTAGIMO;
894 if (std::abs(mu.eta()) < 0.7)
895 reader_MUTAGIMO = m_reader_MUTAGIMO_etaBin1.get();
896 else if (std::abs(mu.eta()) < 1.3)
897 reader_MUTAGIMO = m_reader_MUTAGIMO_etaBin2.get();
898 else
899 reader_MUTAGIMO = m_reader_MUTAGIMO_etaBin3.get();
900
901 // get the BDT discriminant response
902 float BDTdiscriminant;
903
904 if (mu.author() == xAOD::Muon::MuidCo)
905 BDTdiscriminant = reader_MUID->EvaluateMVA(var_vector, "BDTG");
906 else if (mu.author() == xAOD::Muon::MuGirl)
907 BDTdiscriminant = reader_MUGIRL->EvaluateMVA(var_vector, "BDTG");
908 else if (mu.author() == xAOD::Muon::MuTagIMO && m_useSegmentTaggedLowPt)
909 BDTdiscriminant = reader_MUTAGIMO->EvaluateMVA(var_vector, "BDT");
910 else {
911 ATH_MSG_WARNING("Invalid author for low-pT MVA, failing selection...");
912 return false;
913 }
914
915 // cut on dicriminant
916 float BDTcut = (mu.author() == xAOD::Muon::MuTagIMO) ? 0.12 : -0.6;
917
918 if (BDTdiscriminant > BDTcut) {
919 ATH_MSG_VERBOSE("Passed low-pT MVA cut");
920 return true;
921 } else {
922 ATH_MSG_VERBOSE("Failed low-pT MVA cut");
923 return false;
924 }
925 }
926
928 ATH_MSG_VERBOSE("Checking whether muon passes high-pT selection...");
929
930 // :: Request combined muons
931 if (mu.muonType() != xAOD::Muon::Combined) {
932 ATH_MSG_VERBOSE("Muon is not combined - fail high-pT");
933 return false;
934 }
935 if (mu.author() == xAOD::Muon::STACO) {
936 ATH_MSG_VERBOSE("Muon is STACO - fail high-pT");
937 return false;
938 }
939
940 // :: Reject muons with out-of-bounds hits
944 ATH_MSG_VERBOSE("Muon has out-of-bounds precision hits - fail high-pT");
945 return false;
946 }
947
948 // :: Access MS hits information
949 hitSummary summary{};
950 fillSummary(mu, summary);
951
952
953 ATH_MSG_VERBOSE("number of precision layers: " << (int)summary.nprecisionLayers);
954
955 //::: Apply MS Chamber Vetoes
956 // Given according to their eta-phi locations in the muon spectrometer
957 // FORM: CHAMBERNAME[ array of four values ] = { eta 1, eta 2, phi 1, phi 2}
958 // The vetoes are applied based on the MS track if available. If the MS track is not available,
959 // the vetoes are applied according to the combined track, and runtime warning is printed to
960 // the command line.
961 const xAOD::TrackParticle* CB_track = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
962 const xAOD::TrackParticle* MS_track = mu.trackParticle(xAOD::Muon::MuonSpectrometerTrackParticle);
963 if (!MS_track) {
964 ATH_MSG_VERBOSE("passedHighPtCuts - No MS track available for muon. Using combined track.");
965 MS_track = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
966 }
967
968 if (MS_track && CB_track) {
969 float etaMS = MS_track->eta();
970 float phiMS = MS_track->phi();
971 float etaCB = CB_track->eta();
972
973 //::: no unspoiled clusters in CSC
974 if (!isRun3() && (std::abs(etaMS) > 2.0 || std::abs(etaCB) > 2.0)) {
975 if (summary.cscUnspoiledEtaHits == 0) {
976 ATH_MSG_VERBOSE("Muon has only spoiled CSC clusters - fail high-pT");
977 return false;
978 }
979 }
980
981 // veto bad CSC giving troubles with scale factors
982 if (!isRun3() && mu.eta() < -1.899 && std::abs(mu.phi()) < 0.211) {
983 ATH_MSG_VERBOSE("Muon is in eta/phi region vetoed due to disabled chambers in MC - fail high-pT");
984 return false;
985 }
986
987 //::: Barrel/Endcap overlap region
988 if ((1.01 < std::abs(etaMS) && std::abs(etaMS) < 1.1) || (1.01 < std::abs(etaCB) && std::abs(etaCB) < 1.1)) {
989 ATH_MSG_VERBOSE("Muon is in barrel/endcap overlap region - fail high-pT");
990 return false;
991 }
992
993 //::: BIS78
994 if (isBIS78(etaMS, phiMS)) {
995 if (!isRun3() || !m_useBEEBISInHighPtRun3) {
996 ATH_MSG_VERBOSE("Muon is in BIS7/8 eta/phi region - fail high-pT");
997 return false;
998 }
999 }
1000
1003 //if (getRunNumber(true) >= 324320) {
1004 //if (isBMG(etaMS, phiMS)) {
1005 //ATH_MSG_VERBOSE("Muon is in BMG eta/phi region - fail high-pT");
1006 //return false;
1007 //}
1008 //}
1009
1010 //::: BEE
1011 if (isBEE(etaMS, phiMS)) {
1012 // in Run3, large mis-alignment on the BEE chamber was found. temporarily mask the BEE region
1013 if (isRun3() && !m_useBEEBISInHighPtRun3) {
1014 ATH_MSG_VERBOSE("Muon is in BEE eta/phi region - fail high-pT");
1015 return false;
1016 }
1017 // Muon falls in the BEE eta-phi region: asking for 4 good precision layers
1018 // if( nGoodPrecLayers < 4 ) return false; // postponed (further studies needed)
1019 if (summary.nprecisionLayers < 4) {
1020 ATH_MSG_VERBOSE("Muon is in BEE eta/phi region and does not have 4 precision layers - fail high-pT");
1021 return false;
1022 }
1023 }
1024 if (std::abs(etaCB) > 1.4) {
1025 // Veto residual 3-station muons in BEE region due to MS eta/phi resolution effects
1026 // if( nGoodPrecLayers<4 && (extendedSmallHits>0||extendedSmallHoles>0) ) return false; // postponed (further studies
1027 // needed)
1028 if (summary.nprecisionLayers < 4 && (summary.extendedSmallHits > 0 || summary.extendedSmallHoles > 0)) {
1029 ATH_MSG_VERBOSE("Muon is in BEE eta/phi region and does not have 4 precision layers - fail high-pT");
1030 return false;
1031 }
1032 }
1033 } else {
1034 ATH_MSG_WARNING("passedHighPtCuts - MS or CB track missing in muon! Failing High-pT selection...");
1035 return false;
1036 }
1037
1038 //::: Apply 1/p significance cut
1039 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
1040 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
1041 if (idtrack && metrack && metrack->definingParametersCovMatrix()(4, 4) > 0) { const float qOverPsignif = qOverPsignificance(mu);
1042
1043 ATH_MSG_VERBOSE("qOverP significance: " << qOverPsignif);
1044
1045 if (std::abs(qOverPsignif) > 7) {
1046 ATH_MSG_VERBOSE("Muon failed qOverP significance cut");
1047 return false;
1048 }
1049 } else {
1050 ATH_MSG_VERBOSE("Muon missing ID or ME tracks - fail high-pT");
1051 return false;
1052 }
1053
1054 // Accept good 2-station muons if the user has opted to include these
1055 if (m_use2stationMuonsHighPt && summary.nprecisionLayers == 2) {
1056 // should not accept EM+EO muons due to ID/MS alignment issues
1057 if (std::abs(mu.eta()) > 1.2 && summary.extendedSmallHits < 3 && summary.extendedLargeHits < 3) {
1058 ATH_MSG_VERBOSE("2-station muon with EM+EO - fail high-pT");
1059 return false;
1060 }
1061
1062 // only select muons missing the inner precision layer
1063 // apply strict veto on overlap between small and large sectors
1064
1065 if (summary.innerLargeHits == 0 && summary.middleLargeHits == 0 && summary.outerLargeHits == 0 &&
1066 summary.extendedLargeHits == 0 && summary.middleSmallHits > 2 &&
1067 (summary.outerSmallHits > 2 || summary.extendedSmallHits > 2)) {
1068 ATH_MSG_VERBOSE("Accepted 2-station muon in small sector");
1069 return true;
1070 }
1071
1072 if (summary.innerSmallHits == 0 && summary.middleSmallHits == 0 && summary.outerSmallHits == 0 &&
1073 summary.extendedSmallHits == 0 && summary.middleLargeHits > 2 &&
1074 (summary.outerLargeHits > 2 || summary.extendedLargeHits > 2)) {
1075 ATH_MSG_VERBOSE("Accepted 2-station muon in large sector");
1076 return true;
1077 }
1078 }
1079
1080 //::: Require 3 (good) station muons
1081 if (summary.nprecisionLayers < 3) {
1082 ATH_MSG_VERBOSE("Muon has less than 3 precision layers - fail high-pT");
1083 return false;
1084 }
1085
1086 // Remove 3-station muons with small-large sectors overlap
1087 if (summary.isSmallGoodSectors) {
1088 if (!(summary.innerSmallHits > 2 && summary.middleSmallHits > 2 &&
1089 (summary.outerSmallHits > 2 || summary.extendedSmallHits > 2))) {
1090 ATH_MSG_VERBOSE("Muon has small/large sectors overlap - fail high-pT");
1091 return false;
1092 }
1093 } else {
1094 if (!(summary.innerLargeHits > 2 && summary.middleLargeHits > 2 &&
1095 (summary.outerLargeHits > 2 || summary.extendedLargeHits > 2))) {
1096 ATH_MSG_VERBOSE("Muon has small/large sectors overlap - fail high-pT");
1097 return false;
1098 }
1099 }
1100
1101 ATH_MSG_VERBOSE("Muon passed high-pT selection");
1102 return true;
1103 }
1104
1106 // ::
1107 if (mu.muonType() != xAOD::Muon::Combined) return false;
1108 // ::
1109 double start_cut = 3.0;
1110 double end_cut = 1.6;
1111 double abs_eta = std::abs(mu.eta());
1112
1113 // parametrization of expected q/p error as function of pT
1114 double p0(8.0), p1(0.), p2(0.);
1115 if(isRun3()) //MC21 optimization
1116 {
1117 if(abs_eta<=1.05){
1118 p1=0.046;
1119 p2=0.00005;
1120 }
1121 else if (abs_eta > 1.05 && abs_eta <= 1.3) {
1122 p1 = 0.052;
1123 p2 = 0.00008;
1124 } else if (abs_eta > 1.3 && abs_eta <= 1.7) {
1125 p1 = 0.068;
1126 p2 = 0.00006;
1127 } else if (abs_eta > 1.7 && abs_eta <= 2.0) {
1128 p1 = 0.048;
1129 p2 = 0.00006;
1130 } else if (abs_eta > 2.0) {
1131 p1 = 0.037;
1132 p2 = 0.00006;
1133 }
1134 }
1135 else
1136 {
1137 if(abs_eta<=1.05){
1138 p1=0.039;
1139 p2=0.00006;
1140 }
1141 else if (abs_eta > 1.05 && abs_eta <= 1.3) {
1142 p1 = 0.040;
1143 p2 = 0.00009;
1144 } else if (abs_eta > 1.3 && abs_eta <= 1.7) {
1145 p1 = 0.056;
1146 p2 = 0.00008;
1147 } else if (abs_eta > 1.7 && abs_eta <= 2.0) {
1148 p1 = 0.041;
1149 p2 = 0.00006;
1150 } else if (abs_eta > 2.0) {
1151 p1 = 0.031;
1152 p2 = 0.00006;
1153 }
1154 }
1155 // ::
1156 hitSummary summary{};
1157 fillSummary(mu, summary);
1158
1159 // independent parametrization for 2-station muons
1160 if (m_use2stationMuonsHighPt && summary.nprecisionLayers == 2) {
1161 start_cut = 1.1;
1162 end_cut=0.7;
1163 p1 = 0.0739568;
1164 p2 = 0.00012443;
1165 if (abs_eta > 1.05 && abs_eta < 1.3) {
1166 p1 = 0.0674484;
1167 p2 = 0.000119879;
1168 } else if (abs_eta >= 1.3 && abs_eta < 1.7) {
1169 p1 = 0.041669;
1170 p2 = 0.000178349;
1171 } else if (abs_eta >= 1.7 && abs_eta < 2.0) {
1172 p1 = 0.0488664;
1173 p2 = 0.000137648;
1174 } else if (abs_eta >= 2.0) {
1175 p1 = 0.028077;
1176 p2 = 0.000152707;
1177 }
1178 }
1179 // ::
1180 bool passErrorCutCB = false;
1181 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1182 if (cbtrack) {
1183 // ::
1184 double pt_CB = (cbtrack->pt() * MeVtoGeV < 5000.) ? cbtrack->pt() * MeVtoGeV : 5000.; // GeV
1185 double qOverP_CB = cbtrack->qOverP();
1186 double qOverPerr_CB = std::sqrt(cbtrack->definingParametersCovMatrix()(4, 4));
1187 // sigma represents the average expected error at the muon's pt/eta
1188 double sigma = std::sqrt(std::pow(p0 / pt_CB, 2) + std::pow(p1, 2) + std::pow(p2 * pt_CB, 2));
1189 // cutting at start_cut*sigma for pt <=1 TeV depending on eta region,
1190 // then linearly tightening until end_cut*sigma is reached at pt >= 5TeV.
1191 double a = (end_cut - start_cut) / 4000.0;
1192 double b = end_cut - a * 5000.0;
1193 double coefficient = (pt_CB > 1000.) ? (a * pt_CB + b) : start_cut;
1194 if (std::abs(qOverPerr_CB / qOverP_CB) < coefficient * sigma) { passErrorCutCB = true; }
1195 }
1196 // ::
1197 if (m_use2stationMuonsHighPt && m_doBadMuonVetoMimic && summary.nprecisionLayers == 2) {
1199
1200 if (eventInfo->eventType(xAOD::EventInfo::IS_SIMULATION)) {
1201 ATH_MSG_DEBUG("The current event is a MC event. Use bad muon veto mimic.");
1202 return passErrorCutCB && passedBMVmimicCut(mu);
1203 }
1204 }
1205
1206 // ::
1207 return passErrorCutCB;
1208 }
1209
1211 TF1* cutFunction;
1212 double p1, p2;
1213 if (std::abs(mu.eta()) < 1.05) {
1214 cutFunction = m_BMVcutFunction_barrel.get();
1215 p1 = 0.066265;
1216 p2 = 0.000210047;
1217 } else {
1218 cutFunction = m_BMVcutFunction_endcap.get();
1219 p1 = 0.0629747;
1220 p2 = 0.000196466;
1221 }
1222
1223 double qOpRelResolution = std::hypot(p1, p2 * mu.primaryTrackParticle()->pt() * MeVtoGeV);
1224
1225 double qOverPabs_unsmeared = std::abs(mu.primaryTrackParticle()->definingParameters()[4]);
1226 double qOverPabs_smeared = 1.0 / (mu.pt() * std::cosh(mu.eta()));
1227
1228 if ((qOverPabs_smeared - qOverPabs_unsmeared) / (qOpRelResolution * qOverPabs_unsmeared) <
1229 cutFunction->Eval(mu.primaryTrackParticle()->pt() * MeVtoGeV))
1230 return false;
1231 else
1232 return true;
1233 }
1234
1236 // ::
1237 if (mu.muonType() == xAOD::Muon::Combined) { return mu.author() != xAOD::Muon::STACO; }
1238 // ::
1239 if (mu.muonType() == xAOD::Muon::CaloTagged && std::abs(mu.eta()) < 0.105)
1240 return passedCaloTagQuality(mu);
1241 // ::
1242 if (mu.muonType() == xAOD::Muon::SegmentTagged && (std::abs(mu.eta()) < 0.105 || m_useSegmentTaggedLowPt)) return true;
1243 // ::
1244 if (mu.author() == xAOD::Muon::MuidSA && std::abs(mu.eta()) > 2.4) return true;
1245 // ::
1246 if (mu.muonType() == xAOD::Muon::SiliconAssociatedForwardMuon) {
1247 const xAOD::TrackParticle* cbtrack = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1248 return (cbtrack && std::abs(cbtrack->eta()) > 2.4);
1249 }
1250 // ::
1251 return false;
1252 }
1253
1255 uint8_t value1{0}, value2{0};
1256
1259 " !! Tool configured with some of the ID hits requirements changed... FOR DEVELOPMENT ONLY: muon efficiency SF won't be "
1260 "valid !! ");
1261
1264 if ((value1 + value2 == 0) && !m_PixCutOff) return false;
1265
1268 if ((value1 + value2 <= 4) && !m_SctCutOff) return false;
1269
1272 if ((value1 + value2 >= 3) && !m_SiHolesCutOff) return false;
1273
1274 if (!m_TrtCutOff) {
1275 const float abseta = std::abs(track.eta());
1278 const uint8_t totTRThits = value1 + value2;
1279 if (!((0.1 < abseta && abseta <= 1.9 && totTRThits > 5 && value2 < (0.9 * totTRThits)) || (abseta <= 0.1 || abseta > 1.9)))
1280 return false;
1281 }
1282 // Reached end - all ID hit cuts are passed.
1283 return true;
1284 } // passedIDCuts
1285
1287 // Use CaloScore variable based on Neural Network if enabled
1288 // The neural network is only trained until eta = 1
1289 // cf. https://cds.cern.ch/record/2802605/files/CERN-THESIS-2021-290.pdf
1290 constexpr float eta_range = 1.;
1291 if (std::abs(mu.eta()) < eta_range && m_useCaloScore) return passedCaloScore(mu);
1292
1293 // Otherwise we use CaloMuonIDTag
1294 int CaloMuonIDTag = -20;
1295
1296 // Extract CaloMuonIDTag variable
1297 bool readID = mu.parameter(CaloMuonIDTag, xAOD::Muon::CaloMuonIDTag);
1298 if (!readID) {
1299 ATH_MSG_WARNING("Unable to read CaloMuonIDTag Quality information! Rejecting the CALO muon!");
1300 return false;
1301 }
1302
1303 // Cut on CaloMuonIDTag variable
1304 return (CaloMuonIDTag > 10);
1305 }
1306
1308 // We use a working point with a pT-dependent cut on the NN discriminant, designed to achieve a constant
1309 // fakes rejection as function of pT in Z->mumu MC
1310
1311 // Extract the relevant score variable (NN discriminant)
1312
1313 float CaloMuonScore{-999.0};
1314 retrieveParam(mu, CaloMuonScore, xAOD::Muon::CaloMuonScore);
1315
1316 if(m_caloScoreWP==1) return (CaloMuonScore >= 0.92);
1317 if(m_caloScoreWP==2) return (CaloMuonScore >= 0.56);
1318 else if(m_caloScoreWP==3 || m_caloScoreWP==4)
1319 {
1320 // Cut on the score variable
1321 float pT = mu.pt() * MeVtoGeV; // GeV
1322
1323 if (pT > 20.0) // constant cut above 20 GeV
1324 return (CaloMuonScore >= 0.77);
1325 else {
1326 // pT-dependent cut below 20 GeV
1327 // The pT-dependent cut is based on a fit of a third-degree polynomial, with coefficients as given below
1328
1329 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));
1330 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));
1331 }
1332 }
1333
1334 return false;
1335 }
1336
1337 bool MuonSelectionTool::passTight(const xAOD::Muon& mu, float rho, float oneOverPSig) const {
1338
1340 ATH_MSG_VERBOSE("for run3, Tight WP is only supported when ExcludeNSWFromPrecisionLayers=False and RecalcPrecisionLayerswNSW=True");
1341 return false;
1342 }
1343 float symmetric_eta = std::abs(mu.eta());
1344 float pt = mu.pt() * MeVtoGeV; // GeV
1345
1346 // Impose pT and eta cuts; the bounds of the cut maps
1347 if (pt < 4.0 || symmetric_eta >= 2.5) return false;
1348 ATH_MSG_VERBOSE("Muon is passing tight WP kinematic cuts with pT,eta " << mu.pt() << " , " << mu.eta());
1349
1350 // ** Low pT specific cuts ** //
1351 if (pt < 20.0) {
1352 double rhoCut = m_tightWP_lowPt_rhoCuts->Interpolate(pt, symmetric_eta);
1353 double qOverPCut = m_tightWP_lowPt_qOverPCuts->Interpolate(pt, symmetric_eta);
1354
1355 ATH_MSG_VERBOSE("Applying tight WP cuts to a low pt muon with (pt,eta) ( " << pt << " , " << mu.eta() << " ) ");
1356 ATH_MSG_VERBOSE("Rho value " << rho << ", required to be less than " << rhoCut);
1357 ATH_MSG_VERBOSE("Momentum significance value " << oneOverPSig << ", required to be less than " << qOverPCut);
1358
1359 if (rho > rhoCut) return false;
1360 ATH_MSG_VERBOSE("Muon passed tight WP, low pT rho cut!");
1361
1362 if (oneOverPSig > qOverPCut) return false;
1363 ATH_MSG_VERBOSE("Muon passed tight WP, low pT momentum significance cut");
1364
1365 // Tight muon!
1366 return true;
1367
1368 }
1369
1370 // ** Medium pT specific cuts ** //
1371 else if (pt < 100.0) {
1372 double rhoCut = m_tightWP_mediumPt_rhoCuts->Interpolate(pt, symmetric_eta);
1373 //
1374 ATH_MSG_VERBOSE("Applying tight WP cuts to a medium pt muon with (pt,eta) (" << pt << "," << mu.eta() << ")");
1375 ATH_MSG_VERBOSE("Rho value " << rho << " required to be less than " << rhoCut);
1376
1377 // Apply cut
1378 if (rho > rhoCut) return false;
1379 ATH_MSG_VERBOSE("Muon passed tight WP, medium pT rho cut!");
1380
1381 // Tight muon!
1382 return true;
1383 }
1384
1385 // ** High pT specific cuts
1386 else if (pt < 500.0) {
1387 //
1388 ATH_MSG_VERBOSE("Applying tight WP cuts to a high pt muon with (pt,eta) (" << pt << "," << mu.eta() << ")");
1389 // No interpolation, since bins with -1 mean we should cut really loose
1390 double rhoCut = m_tightWP_highPt_rhoCuts->GetBinContent(m_tightWP_highPt_rhoCuts->FindFixBin(pt, symmetric_eta));
1391 ATH_MSG_VERBOSE("Rho value " << rho << ", required to be less than " << rhoCut << " unless -1, in which no cut is applied");
1392 //
1393 if (rhoCut < 0.0) return true;
1394 if (rho > rhoCut) return false;
1395 ATH_MSG_VERBOSE("Muon passed tight WP, high pT rho cut!");
1396
1397 return true;
1398 }
1399 // For muons with pT > 500 GeV, no extra cuts
1400 else {
1401 ATH_MSG_VERBOSE("Not applying any tight WP cuts to a very high pt muon with (pt,eta) (" << pt << "," << mu.eta() << ")");
1402 return true;
1403 }
1404
1405 // you should never reach this point
1406 return false;
1407 }
1408
1409 //============================================================================
1411
1412 checkSanity();
1413
1427
1428 if (!isRun3()) {
1429 // ignore missing of cscUnspoiledEtaHits in case we are running in expert developer mode
1430 // e.g. for when we want to apply Run2 WPs in Run3
1432
1433 if (std::abs(muon.eta()) > 2.0) {
1434 ATH_MSG_VERBOSE("Recalculating number of precision layers for combined muon");
1435 summary.nprecisionLayers = (summary.innerSmallHits > 1 || summary.innerLargeHits > 1)
1436 + (summary.middleSmallHits > 2 || summary.middleLargeHits > 2)
1437 + (summary.outerSmallHits > 2 || summary.outerLargeHits > 2);
1438 }
1439
1440 } else if (std::abs(muon.eta()) > 1.3 && (m_excludeNSWFromPrecisionLayers || m_recalcPrecisionLayerswNSW)) {
1441 summary.nprecisionLayers = (summary.middleSmallHits > 2 || summary.middleLargeHits > 2)
1442 + (summary.outerSmallHits > 2 || summary.outerLargeHits > 2)
1443 + (summary.extendedSmallHits > 2 || summary.extendedLargeHits > 2);
1444
1446
1447 if (!eta1stgchits_acc.isAvailable(muon) || !eta2stgchits_acc.isAvailable(muon) || !mmhits_acc.isAvailable(muon)) {
1448 ATH_MSG_FATAL(__FILE__ << ":" << __LINE__ << " Failed to retrieve NSW hits!"
1449 << " (Please use DxAODs with p-tags >= p5834 OR set ExcludeNSWFromPrecisionLayers to True (tests only)");
1450 throw std::runtime_error("Failed to retrieve NSW hits");
1451 }
1452
1456 summary.nprecisionLayers += ((summary.etaLayer1STGCHits + summary.etaLayer2STGCHits) > 3 || summary.MMHits > 3);
1457 }
1458 }
1459 }
1460
1461
1462 void MuonSelectionTool::retrieveParam(const xAOD::Muon& muon, float& value, const xAOD::Muon::ParamDef param) const {
1463 if (!muon.parameter(value, param)) {
1464 ATH_MSG_FATAL(__FILE__ << ":" << __LINE__ << " Failed to retrieve parameter " << param
1465 << " for muon with pT:" << muon.pt() * MeVtoGeV << ", eta:" << muon.eta() << ", phi: " << muon.phi()
1466 << ", q:" << muon.charge() << ", author: " << muon.author());
1467 throw std::runtime_error("Failed to retrieve Parameter");
1468 }
1469 }
1470
1471 // Returns an integer corresponding to categorization of muons with different resolutions
1473 // Resolutions have only been evaluated for medium combined muons
1474 if (mu.muonType() != xAOD::Muon::Combined || getQuality(mu) > xAOD::Muon::Medium) return ResolutionCategory::unclassified;
1475
1476 // :: Access MS hits information
1477 hitSummary summary{};
1478 fillSummary(mu, summary);
1479
1480 // For muons passing the high-pT working point, distinguish between 2-station tracks and the rest
1481 if (passedHighPtCuts(mu)) {
1482 if (summary.nprecisionLayers == 2)
1484 else
1486 }
1487
1488 const xAOD::TrackParticle* CB_track = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1489 const xAOD::TrackParticle* MS_track = mu.trackParticle(xAOD::Muon::MuonSpectrometerTrackParticle);
1490 if (!MS_track) {
1491 ATH_MSG_VERBOSE("getResolutionCategory - No MS track available for muon. Using combined track.");
1492 MS_track = mu.trackParticle(xAOD::Muon::CombinedTrackParticle);
1493 }
1494
1495 if (!MS_track || !CB_track) return ResolutionCategory::unclassified;
1496 const float etaMS = MS_track->eta();
1497 const float etaCB = CB_track->eta();
1498 const float phiMS = MS_track->phi();
1499
1500 int category = ResolutionCategory::unclassified;
1501
1502 if ((summary.isSmallGoodSectors && summary.innerSmallHits < 3) || (!summary.isSmallGoodSectors && summary.innerLargeHits < 3))
1503 category = ResolutionCategory::missingInner; // missing-inner
1504
1505 if ((summary.isSmallGoodSectors && summary.middleSmallHits < 3) || (!summary.isSmallGoodSectors && summary.middleLargeHits < 3))
1506 category = ResolutionCategory::missingMiddle; // missing-middle
1507
1508 if ((summary.isSmallGoodSectors && summary.outerSmallHits < 3 && summary.extendedSmallHits < 3) ||
1509 (!summary.isSmallGoodSectors && summary.outerLargeHits < 3 && summary.extendedLargeHits < 3))
1510 category = ResolutionCategory::missingOuter; // missing-outer
1511
1512 if (!isRun3() && (std::abs(etaMS) > 2.0 || std::abs(etaCB) > 2.0) && summary.cscUnspoiledEtaHits == 0)
1513 category = ResolutionCategory::spoiledCSC; // spoiled CSC
1514
1515 if ((1.01 < std::abs(etaMS) && std::abs(etaMS) < 1.1) || (1.01 < std::abs(etaCB) && std::abs(etaCB) < 1.1))
1516 category = ResolutionCategory::BEoverlap; // barrel-end-cap overlap
1517
1518 if (isBIS78(etaMS, phiMS)) category = ResolutionCategory::BIS78; // BIS7/8
1519
1520 //::: BEE
1521 if (isBEE(etaMS, phiMS) || (std::abs(etaCB) > 1.4 && (summary.extendedSmallHits > 0 || summary.extendedSmallHoles > 0))) {
1522 if (summary.extendedSmallHits < 3 && summary.middleSmallHits >= 3 && summary.outerSmallHits >= 3)
1523 category = ResolutionCategory::missingBEE; // missing-BEE
1524
1525 if (summary.extendedSmallHits >= 3 && summary.outerSmallHits < 3) category = ResolutionCategory::missingOuter; // missing-outer
1526
1527 if (!summary.isSmallGoodSectors)
1528 category = ResolutionCategory::unclassified; // ambiguity due to eta/phi differences between MS and CB track
1529 }
1530
1531 if (summary.nprecisionLayers == 1) category = ResolutionCategory::oneStation; // one-station track
1532
1533 return category;
1534 }
1535
1536 //============================================================================
1537 // need run number (or random run number) to apply period-dependent selections
1538 unsigned int MuonSelectionTool::getRunNumber(bool needOnlyCorrectYear /*=false*/) const {
1539
1540 static const SG::AuxElement::ConstAccessor<unsigned int> acc_rnd("RandomRunNumber");
1541
1543 //overwrite run number
1544 unsigned int runNumber = 0;
1545 if(m_expertMode_RunNumber.value()!=0) runNumber=m_expertMode_RunNumber.value();
1546 else runNumber = eventInfo->runNumber();
1547
1548 // Case of data
1549 if (!eventInfo->eventType(xAOD::EventInfo::IS_SIMULATION)) {
1550 ATH_MSG_DEBUG("The current event is a data event. Return runNumber.");
1551 return runNumber;
1552 }
1553
1554 // Case of MC
1555 // attempt to get the run number assigned by the PRW tool
1556 static std::atomic<bool> issuedWarningPRW{false};
1557 if (acc_rnd.isAvailable(*eventInfo)) {
1558 unsigned int rn = acc_rnd(*eventInfo);
1559 if (rn != 0) return acc_rnd(*eventInfo);
1560
1561 if (!issuedWarningPRW) {
1562 ATH_MSG_WARNING("Pile up tool has assigned runNumber = 0");
1563 issuedWarningPRW = true;
1564 }
1565 }
1566
1567 // otherwise return a dummy run number
1568 if (needOnlyCorrectYear) {
1569 if (runNumber < 300000) { // mc16a (2016): 284500
1570 ATH_MSG_DEBUG("Random run number not available and this is mc16a or mc20a, returning dummy 2016 run number.");
1571 return 311071;
1572
1573 } else if (runNumber < 310000) { // mc16d (2017): 300000
1574 ATH_MSG_DEBUG("Random run number not available and this is mc16d or mc20d, returning dummy 2017 run number.");
1575 return 340072;
1576
1577 } else if (runNumber < 320000) { // mc16e (2018): 310000
1578 ATH_MSG_DEBUG("Random run number not available and this is mc16e or mc20e, returning dummy 2018 run number.");
1579 return 351359;
1580
1581 } else if (runNumber < 600000) { //mc21: 330000, mc23a: 410000, mc23c: 450000
1582 ATH_MSG_DEBUG("Random run number not available and this is mc21/mc23, for the time being we're returing a dummy run number.");
1583 return 399999;
1584 } else {
1585 ATH_MSG_DEBUG("Detected some run 4 / phase II runnumber "<<runNumber<<". ");
1586 return 666666;
1587 }
1588
1589 ATH_MSG_FATAL("Random run number not available, fallback option of using runNumber failed since "<<runNumber<<" cannot be recognised");
1590 throw std::runtime_error("MuonSelectionTool() - need RandomRunNumber decoration by the PileupReweightingTool");
1591 }
1592
1593 ATH_MSG_FATAL("Failed to find the RandomRunNumber decoration by the PileupReweightingTool");
1594 throw std::runtime_error("MuonSelectionTool() - need RandomRunNumber decoration from PileupReweightingTool");
1595 }
1596
1597
1598 // Check if eta/phi coordinates correspond to BIS7/8 chambers
1599 bool MuonSelectionTool::isBIS78(const float eta, const float phi) const {
1600 static constexpr std::array<float, 2> BIS78_eta{1.05, 1.3};
1601 static constexpr std::array<float, 8> BIS78_phi{0.21, 0.57, 1.00, 1.33, 1.78, 2.14, 2.57, 2.93};
1602
1603 float abs_eta = std::abs(eta);
1604 float abs_phi = std::abs(phi);
1605
1606 if (abs_eta >= BIS78_eta[0] && abs_eta <= BIS78_eta[1]) {
1607 if ((abs_phi >= BIS78_phi[0] && abs_phi <= BIS78_phi[1]) || (abs_phi >= BIS78_phi[2] && abs_phi <= BIS78_phi[3]) ||
1608 (abs_phi >= BIS78_phi[4] && abs_phi <= BIS78_phi[5]) || (abs_phi >= BIS78_phi[6] && abs_phi <= BIS78_phi[7])) {
1609 return true;
1610 }
1611 }
1612
1613 return false;
1614 }
1615
1616 // Check if eta/phi coordinates correspond to BEE chambers
1617 bool MuonSelectionTool::isBEE(const float eta, const float phi) const {
1618 static constexpr std::array<float, 2> BEE_eta{1.440, 1.692};
1619 static constexpr std::array<float, 8> BEE_phi{0.301, 0.478, 1.086, 1.263, 1.872, 2.049, 2.657, 2.834};
1620
1621 float abs_eta = std::abs(eta);
1622 float abs_phi = std::abs(phi);
1623
1624 if (abs_eta >= BEE_eta[0] && abs_eta <= BEE_eta[1]) {
1625 if ((abs_phi >= BEE_phi[0] && abs_phi <= BEE_phi[1]) || (abs_phi >= BEE_phi[2] && abs_phi <= BEE_phi[3]) ||
1626 (abs_phi >= BEE_phi[4] && abs_phi <= BEE_phi[5]) || (abs_phi >= BEE_phi[6] && abs_phi <= BEE_phi[7])) {
1627 return true;
1628 }
1629 }
1630
1631 return false;
1632 }
1633
1634 // Check if eta/phi coordinates correspond to BMG chambers
1635 bool MuonSelectionTool::isBMG(const float eta, const float phi) const {
1636 static constexpr std::array<float, 6> BMG_eta{0.35, 0.47, 0.68, 0.80, 0.925, 1.04};
1637 static constexpr std::array<float, 4> BMG_phi{-1.93, -1.765, -1.38, -1.21};
1638
1639 float abs_eta = std::abs(eta);
1640
1641 if ((abs_eta >= BMG_eta[0] && abs_eta <= BMG_eta[1]) || (abs_eta >= BMG_eta[2] && abs_eta <= BMG_eta[3]) ||
1642 (abs_eta >= BMG_eta[4] && abs_eta <= BMG_eta[5])) {
1643 if ((phi >= BMG_phi[0] && phi <= BMG_phi[1]) || (phi >= BMG_phi[2] && phi <= BMG_phi[3])) { return true; }
1644 }
1645
1646 return false;
1647 }
1648
1651 {
1652 ATH_MSG_ERROR("TightNNScore calculation is disabled. Please set the property CalculateTightNNScore to true.");
1653 throw std::runtime_error("cannot calculate TightNNScore");
1654 }
1655 //this score currently only can be calculated for combined muons
1656 if (mu.muonType() != xAOD::Muon::Combined) return -999;
1657 const xAOD::TrackParticle* idtrack = mu.trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
1658 const xAOD::TrackParticle* metrack = mu.trackParticle(xAOD::Muon::ExtrapolatedMuonSpectrometerTrackParticle);
1659 if(!idtrack || !metrack) return -999;
1660 //the score is only calculated for muons which pass the Medium WP
1661 if (getQuality(mu) > xAOD::Muon::Medium) return -999;
1662 //only muons with pt > 4 GeV and |eta|<2.5 are considered
1663 if (std::abs(mu.eta())>2.5) return -999;
1664 if(mu.pt()<4000.) return -999;
1665
1666 std::vector<float> input_features;
1667 // 1. Fill input features
1668 int mu_author=mu.author();
1669 float mu_rhoPrime=rhoPrime(mu);
1670 float mu_scatteringCurvatureSignificance=0.;
1671 retrieveParam(mu, mu_scatteringCurvatureSignificance, xAOD::Muon::scatteringCurvatureSignificance);
1672 float mu_scatteringNeighbourSignificance=0.;
1673 retrieveParam(mu, mu_scatteringNeighbourSignificance, xAOD::Muon::scatteringNeighbourSignificance);
1674 float mu_momentumBalanceSignificance=0.;
1675 retrieveParam(mu, mu_momentumBalanceSignificance, xAOD::Muon::momentumBalanceSignificance);
1676 float mu_qOverPSignificance=qOverPsignificance(mu);
1677 float mu_reducedChi2=mu.primaryTrackParticle()->chiSquared() / mu.primaryTrackParticle()->numberDoF();
1678 float mu_reducedChi2_ID=idtrack->chiSquared() / idtrack->numberDoF();
1679 float mu_reducedChi2_ME=metrack->chiSquared() / metrack->numberDoF();
1680 float mu_spectrometerFieldIntegral=0.;
1681 retrieveParam(mu, mu_spectrometerFieldIntegral, xAOD::Muon::spectrometerFieldIntegral);
1682 float mu_segmentDeltaEta=0;
1683 retrieveParam(mu, mu_segmentDeltaEta, xAOD::Muon::segmentDeltaEta);
1684 uint8_t mu_numberOfPixelHits=0;
1686 uint8_t mu_numberOfPixelDeadSensors=0;
1687 retrieveSummaryValue(mu, mu_numberOfPixelDeadSensors, xAOD::SummaryType::numberOfPixelDeadSensors);
1688 uint8_t mu_innerLargeHits=0;
1690 uint8_t mu_innerSmallHits=0;
1692 uint8_t mu_middleLargeHits=0;
1694 uint8_t mu_middleSmallHits=0;
1696 uint8_t mu_outerLargeHits=0;
1698 uint8_t mu_outerSmallHits=0;
1700
1701 if(!isRun3())
1702 {
1703 input_features = {(float)mu_author,
1704 mu_rhoPrime,
1705 mu_scatteringCurvatureSignificance,
1706 mu_scatteringNeighbourSignificance,
1707 mu_momentumBalanceSignificance,
1708 mu_qOverPSignificance,
1709 mu_reducedChi2,
1710 mu_reducedChi2_ID,
1711 mu_reducedChi2_ME,
1712 mu_spectrometerFieldIntegral,
1713 mu_segmentDeltaEta,
1714 (float)mu_numberOfPixelHits,
1715 (float)mu_numberOfPixelDeadSensors,
1716 (float)mu_innerLargeHits,
1717 (float)mu_innerSmallHits,
1718 (float)mu_middleLargeHits,
1719 (float)mu_middleSmallHits,
1720 (float)mu_outerLargeHits,
1721 (float)mu_outerSmallHits};
1722 }
1723 else
1724 {
1725 uint8_t mu_phiLayer1STGCHits=0;
1727 uint8_t mu_phiLayer2STGCHits=0;
1729 uint8_t mu_etaLayer1STGCHits=0;
1731 uint8_t mu_etaLayer2STGCHits=0;
1733 uint8_t mu_MMHits=0;
1735 input_features = {(float)mu_author,
1736 mu_rhoPrime,
1737 mu_scatteringCurvatureSignificance,
1738 mu_scatteringNeighbourSignificance,
1739 mu_momentumBalanceSignificance,
1740 mu_qOverPSignificance,
1741 mu_reducedChi2,
1742 mu_reducedChi2_ID,
1743 mu_reducedChi2_ME,
1744 mu_spectrometerFieldIntegral,
1745 mu_segmentDeltaEta,
1746 (float)mu_numberOfPixelHits,
1747 (float)mu_numberOfPixelDeadSensors,
1748 (float)mu_innerLargeHits,
1749 (float)mu_innerSmallHits,
1750 (float)mu_middleLargeHits,
1751 (float)mu_middleSmallHits,
1752 (float)mu_outerLargeHits,
1753 (float)mu_outerSmallHits,
1754 (float)mu_phiLayer1STGCHits,
1755 (float)mu_phiLayer2STGCHits,
1756 (float)mu_etaLayer1STGCHits,
1757 (float)mu_etaLayer2STGCHits,
1758 (float)mu_MMHits};
1759 }
1760
1761 float score=-999.;
1762 std::vector<int64_t> inputShape = {1, static_cast<int64_t>(input_features.size())};
1763
1764 AthInfer::InputDataMap inputData;
1765 inputData["flatten_input"] = std::make_pair(
1766 inputShape, std::move(input_features)
1767 );
1768
1769 AthInfer::OutputDataMap outputData;
1770 outputData["sequential"] = std::make_pair(
1771 std::vector<int64_t>{1, 1}, std::vector<float>{}
1772 );
1773
1774 if (!m_onnxTool->inference(inputData, outputData).isSuccess()) {
1775 ATH_MSG_WARNING("ONNX inference failed!");
1776 return -999.;
1777 }
1778 const auto& variant = outputData["sequential"].second;
1779 if (std::holds_alternative<std::vector<float>>(variant)) {
1780 const auto& vec = std::get<std::vector<float>>(variant);
1781 if (!vec.empty()) score = vec[0];
1782 else {
1783 ATH_MSG_WARNING("ONNX output vector is empty!");
1784 return -999.;
1785 }
1786 } else {
1787 ATH_MSG_WARNING("ONNX output is not a float vector!");
1788 return -999.;
1789 }
1790
1791 ATH_MSG_DEBUG("TightNNScore for muon with pT " << mu.pt() << " GeV, eta " << mu.eta() << " is " << score);
1792
1793 return score;
1794 }
1795
1796} // 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...
virtual void setQuality(xAOD::Muon &mu) const override
set the passes low pT cuts variable of the muon
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.
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.
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
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.
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:569
SG::Accessor< T, ALLOC > Accessor
Definition AuxElement.h:572
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
@ outerLargeHits
number of precision hits in the outer large layer
@ middleLargeHits
number of precision hits in the middle large 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)
@ 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
@ 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