ATLAS Offline Software
Loading...
Searching...
No Matches
TrigMultiTrkComboHypo.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5/**************************************************************************
6 **
7 ** File: Trigger/TrigHypothesis/TrigBPhysHypo/TrigMultiTrkComboHypo.cxx
8 **
9 ** Description: multi-track hypothesis algorithm
10 **
11 ** Author: Heather Russell
12 **
13 **************************************************************************/
14
15#include <algorithm>
16#include <numeric>
17
18#include "Constants.h"
20
21#include "xAODMuon/Muon.h"
22#include "xAODEgamma/Electron.h"
30
34
35#include "AthViews/View.h"
36#include "AthViews/ViewHelper.h"
38
39#include "Math/GenVector/VectorUtil.h"
40#include "Math/Vector2D.h"
41
42
47using ROOT::Math::XYVector;
48
49typedef struct PDG20 PDG;
50
51
52namespace {
53
54
55// Break this out into a separate function to avoid some bogus
56// warnings from gcc15.
58bool prev_perm (std::vector<char>& v)
59{
60 return std::ranges::prev_permutation(v).found;;
61}
62
63
64} // anonymous namespace
65
66
67TrigMultiTrkComboHypo::TrigMultiTrkComboHypo(const std::string& name, ISvcLocator* pSvcLocator)
68 : ::ComboHypo(name, pSvcLocator) {}
69
70
72 ATH_MSG_DEBUG( "TrigMultiTrkHypo::initialize()" );
73
75
76 // check consistency of the properties
77 ATH_CHECK( !m_nTrk.empty() );
78
79 if (m_nTrkCharge.empty()) {
80 ATH_MSG_INFO( "totalCharge value is not specified, no charge selection for track combinations will be used" );
81 m_nTrkCharge = std::vector<int>(m_nTrk.size(), -1);
82 }
83
84 if (m_trkMass.empty()) {
85 ATH_MSG_INFO( "trackMasses value is not specified, muon/electron mass will be used" );
86 for (const auto& n : m_nTrk) {
87 m_trkMass.value().emplace_back(std::vector<double>(n, (m_doElectrons ? PDG::mElectron : PDG::mMuon)));
88 }
89 }
90 if (m_trkPt.empty()) {
91 ATH_MSG_INFO( "trackPtThresholds value is not specified" );
92 for (const auto& n : m_nTrk) {
93 m_trkPt.value().emplace_back(std::vector<double>(n, -100.));
94 }
95 }
96 m_trkPtMin = m_trkPt[0].back();
97 for (size_t i = 0; i < m_trkPt.size(); ++i) {
98 m_trkPtMin = std::min(m_trkPtMin, m_trkPt[i].back());
99 }
100
101 ATH_CHECK( m_trkMass.size() == m_nTrk.size() );
102 ATH_CHECK( m_trkPt.size() == m_nTrk.size() );
103
104 for (size_t i = 0; i < m_nTrk.size(); ++i) {
105 ATH_CHECK( m_trkMass[i].size() == m_nTrk[i] );
106 ATH_CHECK( m_trkPt[i].size() == m_nTrk[i] );
107 ATH_CHECK( std::is_sorted(m_trkPt[i].begin(), m_trkPt[i].end(), std::greater<>()) );
108 }
109
110 for (const auto& range : m_massRange.value()) {
111 ATH_CHECK( range.first < range.second );
112 }
113
114 // dump numerical values
115 if (msgLvl(MSG::DEBUG)) {
116 for (size_t i = 0; i < m_nTrk.size(); ++i) {
117 ATH_MSG_DEBUG( "vertex topology: nTrk = " << m_nTrk[i] );
118 for (size_t j = 0; j < m_nTrk[i]; ++j) {
119 ATH_MSG_DEBUG( " " << j + 1 << " trk: mass = " << m_trkMass[i][j] << ", Pt > " << m_trkPt[i][j] );
120 }
121 }
122 msg() << MSG::DEBUG << " mass range: {";
123 for (const auto& range : m_massRange.value()) {
124 msg() << MSG::DEBUG << " { " << range.first << ", " << range.second << " }";
125 }
126 msg() << MSG::DEBUG << " }" << std::endl;
127 }
128
129 if (m_isStreamer) {
130 ATH_MSG_DEBUG( "Configured to run in a streamer mode: no trigger objects will be created" );
131 }
132 if (!m_isStreamer && m_trigLevel != "EF") {
133 ATH_MSG_ERROR( "Could not create trigger objects from tracks or L2 CB muons, use the streamer mode for L2 step" );
134 return StatusCode::FAILURE;
135 }
136
137 ATH_CHECK( !((m_trigLevel == "L2IO" || m_trigLevel == "L2MT") && m_doElectrons) );
138
139 if (m_trigLevel == "L2" || (m_trigLevel == "EF" && m_isMuTrkMode)) {
142 }
143 else if (m_trigLevel == "L2IO" || m_trigLevel == "L2MT" || m_trigLevel == "EF") {
144 ATH_CHECK( m_trackParticleContainerKey.initialize(false) );
145 }
146 else {
147 ATH_MSG_ERROR( "trigLevel should be L2, L2IO, L2MT or EF, but " << m_trigLevel << " provided" );
148 return StatusCode::FAILURE;
149 }
150
151 ATH_CHECK( m_trigBphysContainerKey.initialize(!m_isStreamer.value()) );
152 ATH_CHECK( m_beamSpotKey.initialize(!m_isStreamer.value()) ); // need beamSpot only to create xAOD::TrigBphys object
153
154 ATH_CHECK( m_vertexFitter.retrieve() );
155 ATH_CHECK( m_vertexPointEstimator.retrieve() );
156 ATH_CHECK( m_v0Tools.retrieve() );
157
158 // allowed IDs to filter out incoming decisions at L2 level
159 for (const auto& item : triggerMultiplicityMap()) {
160 const HLT::Identifier id = HLT::Identifier(item.first);
161 m_allowedIDs.insert(id.numeric());
162 if (item.second.size() > 1) {
163 for (size_t i = 0; i < item.second.size(); i++) {
164 m_allowedIDs.insert(TrigCompositeUtils::createLegName(id, i).numeric());
165 }
166 }
167 }
168 if (msgLvl(MSG::DEBUG)) {
169 ATH_MSG_DEBUG( "Allowed decisions:" );
170 for (const DecisionID& id : m_allowedIDs) {
171 ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) );
172 }
173 }
174 if (m_doElectrons) {
175 for (const DecisionID& id : m_allowedIDs) {
176 std::string name = HLT::Identifier(id).name();
177 bool isMergedElectronChain = false;
178 for (size_t i = 0; i < m_mergedElectronChains.size(); ++i) {
179 if (name.find(m_mergedElectronChains.value().at(i)) != std::string::npos) {
180 isMergedElectronChain = true;
181 break;
182 }
183 }
184 (isMergedElectronChain ? m_mergedElectronIDs.insert(id) : m_resolvedElectronIDs.insert(id));
185 }
186 if (msgLvl(MSG::DEBUG)) {
187 ATH_MSG_DEBUG( "Well-separated electron decisions:" );
188 for (const DecisionID& id : m_resolvedElectronIDs) {
189 ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) );
190 }
191 ATH_MSG_DEBUG( "Overlapping electron decisions:" );
192 for (const DecisionID& id : m_mergedElectronIDs) {
193 ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) );
194 }
195 }
196 }
197
198 if (!m_monTool.empty()) {
199 ATH_CHECK( m_monTool.retrieve() );
200 ATH_MSG_DEBUG( "GenericMonitoringTool name:" << m_monTool );
201 }
202 else {
203 ATH_MSG_DEBUG( "No GenericMonitoringTool configured: no monitoring histograms will be available" );
204 }
205
206 return StatusCode::SUCCESS;
207}
208
209
210StatusCode TrigMultiTrkComboHypo::execute(const EventContext& context) const {
211
212 ATH_MSG_DEBUG( "TrigMultiTrkHypo::execute() starts" );
213
214 ATH_MSG_DEBUG( "decision input key: " << decisionsInput().at(0).key() );
215 auto previousDecisionsHandle = SG::makeHandle(decisionsInput().at(0), context);
216 CHECK( previousDecisionsHandle.isValid() );
217 ATH_MSG_DEBUG( "Running with "<< previousDecisionsHandle->size() << " previous decisions" );
218
220
221 const InDet::BeamSpotData* beamSpotData = nullptr;
222 if (!m_isStreamer) {
224 ATH_CHECK( beamSpotHandle.isValid() );
225 beamSpotData = *beamSpotHandle;
226 }
227
228 std::unique_ptr<TrigMultiTrkState<xAOD::MuonContainer>> muonState;
229 std::unique_ptr<TrigMultiTrkState<xAOD::ElectronContainer>> electronState;
230 TrigMultiTrkStateBase* commonState = nullptr;
231 if (m_doElectrons) {
232 electronState = std::make_unique<TrigMultiTrkState<xAOD::ElectronContainer>>(context, *previousDecisionsHandle, *outputDecisionsHandle, nullptr, beamSpotData);
233 commonState = electronState.get();
234 }
235 else {
236 muonState = std::make_unique<TrigMultiTrkState<xAOD::MuonContainer>>(context, *previousDecisionsHandle, *outputDecisionsHandle, nullptr, beamSpotData);
237 commonState = muonState.get();
238 }
239
240 if (m_isStreamer) {
241 if (m_trigLevel == "L2") {
242 ATH_CHECK( mergeTracksFromViews(*commonState) );
243 }
244 else if (m_trigLevel == "L2IO" || m_trigLevel == "L2MT") {
246 }
247 else if (m_trigLevel == "EF") {
249 }
250 ATH_CHECK( filterTrackCombinations(*commonState) );
251 ATH_CHECK( copyDecisionObjects(*commonState) );
252 }
253 else {
254 auto trigBphysHandle = SG::makeHandle(m_trigBphysContainerKey, context);
255 ATH_CHECK( trigBphysHandle.record(std::make_unique<xAOD::TrigBphysContainer>(),
256 std::make_unique<xAOD::TrigBphysAuxContainer>()) );
257 commonState->setTrigBphysCollection(trigBphysHandle.ptr());
258
259 if (m_doElectrons) {
260 ATH_CHECK( mergeLeptonsFromDecisions(*electronState) );
261 ATH_CHECK( findMultiLeptonCandidates(*electronState) );
262 ATH_CHECK( processMergedElectrons(*electronState) );
263 }
264 else if (m_isMuTrkMode) {
265 ATH_CHECK( findMuTrkCandidates(*muonState) );
266 }
267 else {
270 }
271 ATH_CHECK( createDecisionObjects(*commonState) );
272 }
273
274 ATH_MSG_DEBUG( "TrigMultiTrkHypo::execute() terminates with StatusCode::SUCCESS" );
275 return StatusCode::SUCCESS;
276}
277
278
279template<typename T>
281
282 auto& leptons = state.leptons();
283 leptons.clear();
284
285 std::vector<const Decision*> previousDecisions(state.previousDecisions().begin(), state.previousDecisions().end());
286 std::map<const Decision*, int> decisionToInputCollectionIndexMap;
287 for (const auto& decision : previousDecisions) decisionToInputCollectionIndexMap.emplace(decision, 0);
289 for (size_t k = 1; k < decisionsInput().size(); ++k) {
290 auto previousDecisionsHandle = SG::makeHandle(decisionsInput().at(k), state.context());
291 CHECK( previousDecisionsHandle.isValid() );
292 ATH_MSG_DEBUG( "Adding " << previousDecisionsHandle->size() << " decisions from " << decisionsInput().at(k).key() );
293 for (const Decision* decision : *previousDecisionsHandle) {
294 previousDecisions.push_back(decision);
295 decisionToInputCollectionIndexMap.emplace(decision, static_cast<int>(k));
296 }
297 }
298 }
299
300 // all muons/electrons from views are already connected with previous decisions by TrigMuonEFHypoAlg
301 for (const Decision* decision : previousDecisions) {
303
304 ElementLink<T> leptonEL;
305 if (decision->hasObjectLink(TrigCompositeUtils::featureString(), ClassID_traits<T>::ID())) {
306 leptonEL = decision->objectLink<T>(TrigCompositeUtils::featureString());
307 }
308 else {
309 auto leptonLinkInfo = TrigCompositeUtils::findLink<T>(decision, TrigCompositeUtils::featureString(), true);
310 ATH_CHECK( leptonLinkInfo.isValid() );
311 leptonEL = leptonLinkInfo.link;
312 }
313
314 const auto lepton = *leptonEL;
315 if constexpr(std::is_same<T, xAOD::MuonContainer>::value) {
316 if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) continue;
317 if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) continue;
318 }
319 else if constexpr(std::is_same<T, xAOD::ElectronContainer>::value) {
320 if (!lepton->trackParticle()) continue;
321 }
322 else {
323 ATH_MSG_ERROR( "mergeLeptonsFromDecisions(): no scenario for the provided CONTAINER is specified" );
324 return StatusCode::FAILURE;
325 }
326 auto decisionEL = TrigCompositeUtils::decisionToElementLink(decision, state.context());
327
328 auto itr = leptons.end();
330 itr = std::find_if(leptons.begin(), leptons.end(),
331 [this, lepton = lepton](const auto& x){ return this->isIdenticalTracks(lepton, *x.link); });
332 }
333 if (itr == leptons.end()) {
334 leptons.push_back({leptonEL, std::vector<ElementLink<DecisionContainer>>(1, decisionEL), DecisionIDContainer()});
335 }
336 else {
337 (*itr).decisionLinks.push_back(decisionEL);
338 }
339 }
340
341 // muon->pt() is equal to muon->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)->pt()
342 // and the later is used by TrigMuonEFHypoTool for classification of muEFCB candidates
343 std::sort(leptons.begin(), leptons.end(), [](const auto& lhs, const auto& rhs){ return ((*lhs.link)->pt() > (*rhs.link)->pt()); });
344
345 // for each muon we extract DecisionIDs stored in the associated Decision objects and copy them at muon.decisionIDs
346 const auto& legToInputCollectionIndexMap = legToInputCollectionMap();
347 for (auto& item : leptons) {
348 for (const ElementLink<xAOD::TrigCompositeContainer>& decisionEL : item.decisionLinks) {
350 auto decisionIndex = decisionToInputCollectionIndexMap[*decisionEL];
351 for (const auto& id : TrigCompositeUtils::decisionIDs(*decisionEL)) {
353 auto legIndex = static_cast<size_t>(TrigCompositeUtils::getIndexFromLeg(id));
354 std::string chain = TrigCompositeUtils::getIDFromLeg(id).name();
355 if (legToInputCollectionIndexMap.at(chain).at(legIndex) == decisionIndex) item.decisionIDs.insert(id);
356 }
357 }
358 else {
359 TrigCompositeUtils::decisionIDs(*decisionEL, item.decisionIDs);
360 }
361 }
362 }
363
364 if (msgLvl(MSG::DEBUG)) {
365 ATH_MSG_DEBUG( "Dump found leptons before vertex fit: " << leptons.size() << " candidates" );
366 for (const auto& item : leptons) {
367 const auto lepton = *item.link;
368 const xAOD::TrackParticle* track;
369 if constexpr(std::is_same<T, xAOD::MuonContainer>::value) {
370 track = lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle);
371 }
372 else {
373 track = lepton->trackParticle();
374 }
375 ATH_MSG_DEBUG( " -- lepton InDetTrackParticle pt/eta/phi/q: " << track->pt() << " / " << track->eta() << " / " << track->phi() << " / " << track->charge() );
376 ATH_MSG_DEBUG( " lepton pt (muon: CombinedTrackParticle): " << lepton->pt() << " / " << lepton->eta() << " / " << lepton->phi() << " / " << lepton->charge() );
377 ATH_MSG_DEBUG( " allowed decisions:" );
378 for (const DecisionID& id : item.decisionIDs) {
379 ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) );
380 }
381 }
382 }
383
384 return StatusCode::SUCCESS;
385}
386
387
389
390 auto& tracks = state.tracks();
391 tracks.clear();
392
393 std::set<const SG::View*> views;
394 for (const Decision* decision : state.previousDecisions()) {
395 if (!TrigCompositeUtils::isAnyIDPassing(decision, m_allowedIDs)) continue;
396
398 ATH_CHECK( viewLinkInfo.isValid() );
399 const SG::View* view = *viewLinkInfo.link;
400 if (views.find(view) != views.end()) continue; // tracks from this view have already been fetched
401
402 auto tracksHandle = ViewHelper::makeHandle(view, m_trackParticleContainerKey, state.context());
403 ATH_CHECK( tracksHandle.isValid() );
404 ATH_MSG_DEBUG( "tracks handle " << m_trackParticleContainerKey << " size: " << tracksHandle->size() );
405
406 std::vector<ElementLink<xAOD::TrackParticleContainer>> tracksFromView;
407 tracksFromView.reserve(tracksHandle->size());
408 for (size_t idx = 0; idx < tracksHandle->size(); ++idx) {
409 tracksFromView.emplace_back(ViewHelper::makeLink<xAOD::TrackParticleContainer>(view, tracksHandle, idx));
410 }
411
412 for (const auto& trackEL : tracksFromView) {
413 const xAOD::TrackParticle* track = *trackEL;
414 if (track->definingParametersCovMatrixVec().empty() || track->pt() < m_trkPtMin) continue;
415
416 if (views.empty() || !m_applyOverlapRemoval ||
417 std::find_if(tracks.begin(), tracks.end(),
418 [this, track](const auto& x){ return isIdenticalTracks(track, *x); }) == tracks.end()) {
419 tracks.emplace_back(trackEL);
420 }
421 }
422 views.insert(view);
423 }
424 std::sort(tracks.begin(), tracks.end(), [](const auto& lhs, const auto& rhs){ return ((*lhs)->pt() > (*rhs)->pt()); });
425
426 if (msgLvl(MSG::DEBUG)) {
427 ATH_MSG_DEBUG( "Dump found tracks before vertex fit: " << tracks.size() << " candidates" );
428 for (const auto& trackEL : tracks) {
429 const xAOD::TrackParticle* track = *trackEL;
430 ATH_MSG_DEBUG( " -- track pt/eta/phi/q: " << track->pt() << " / " << track->eta() << " / " << track->phi() << " / " << track->charge() );
431 }
432 }
433
434 return StatusCode::SUCCESS;
435}
436
437
438template<typename CONTAINER>
440
441 auto& tracks = state.tracks();
442 tracks.clear();
443
444 // all muons/electrons from views are already connected with previous decisions by TrigMuonEFHypoAlg or by TrigEgammaPrecisionElectronHypoAlg
445 for (const Decision* decision : state.previousDecisions()) {
446 if (!TrigCompositeUtils::isAnyIDPassing(decision, m_allowedIDs)) continue;
447
449 auto leptonEL = decision->objectLink<CONTAINER>(TrigCompositeUtils::featureString());
450 const auto lepton = *leptonEL;
451
453 if constexpr(std::is_same<CONTAINER, xAOD::MuonContainer>::value) {
454 if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) continue;
455 if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) continue;
456 trackEL = lepton->inDetTrackParticleLink();
457 }
458 else if constexpr(std::is_same<CONTAINER, xAOD::L2CombinedMuonContainer>::value) {
459 if (!lepton->idTrack()) continue;
460 trackEL = lepton->idTrackLink();
461 }
462 else if constexpr(std::is_same<CONTAINER, xAOD::ElectronContainer>::value) {
463 if (!lepton->trackParticle()) continue;
464 trackEL = lepton->trackParticleLink();
465 }
466 else {
467 ATH_MSG_ERROR( "mergeTracksFromDecisions(): no scenario for the provided CONTAINER is specified" );
468 return StatusCode::FAILURE;
469 }
470
471 if (!trackEL.isValid()) continue;
473 std::find_if(tracks.begin(), tracks.end(),
474 [this, track = *trackEL](const auto& x){ return this->isIdenticalTracks(track, *x); }) == tracks.end()) {
475 tracks.emplace_back(trackEL);
476 }
477 }
478 std::sort(tracks.begin(), tracks.end(), [](const auto& lhs, const auto& rhs){ return ((*lhs)->pt() > (*rhs)->pt()); });
479
480 if (msgLvl(MSG::DEBUG)) {
481 ATH_MSG_DEBUG( "Dump found tracks before vertex fit: " << tracks.size() << " candidates" );
482 for (const auto& trackEL : tracks) {
483 const xAOD::TrackParticle* track = *trackEL;
484 ATH_MSG_DEBUG( " -- track pt/eta/phi/q: " << track->pt() << " / " << track->eta() << " / " << track->phi() << " / " << track->charge() );
485 }
486 }
487
488 return StatusCode::SUCCESS;
489}
490
491
493
494 const auto& tracks = state.tracks();
495 state.setEventAccepted(false);
496
497 // monitored variables
498 auto mon_nAcceptedTrk = Monitored::Scalar<int>("nAcceptedTrk", tracks.size());
499 auto mon_nVertexFitterCalls = Monitored::Scalar<int>("nVertexFitterCalls", 0);
500 auto mon_isEventAccepted = Monitored::Scalar<int>("acceptance", 0);
501 auto mon_timer = Monitored::Timer( "TIME_all" );
502
503 auto group = Monitored::Group(m_monTool,
504 mon_nAcceptedTrk, mon_nVertexFitterCalls, mon_isEventAccepted,
505 mon_timer);
506
507 for (size_t iTrk = 0; iTrk < m_nTrk.size(); ++iTrk) {
508 if (state.isEventAccepted()) break;
509 size_t nTrk = m_nTrk[iTrk];
510
511 if (tracks.size() < nTrk) {
512 ATH_MSG_DEBUG( "Could not build a subset of " << nTrk << " tracks from collection which contains only " << tracks.size() << " objects" );
513 continue;
514 }
515 ATH_MSG_DEBUG( "Consider combinations of " << nTrk << " tracks from collection which contains " << tracks.size() << " objects until find a good one" );
516
517 std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(nTrk);
518 std::vector<xAOD::TrackParticle::GenVecFourMom_t> p(nTrk);
519
520 // tracks from the current combination will have non-zero value against their position in the 'idx' vector
521 // consider first nTrk tracks as an initial combination, then get the next one with std::prev_permutation()
522 std::vector<char> idx(tracks.size(), 0);
523 std::fill(idx.begin(), idx.begin() + nTrk, 1);
524 do {
525 // fill tracklist and momenta of tracks, also check that the track pT passes the threshold value
526 bool isValidCombination = true;
527 int totalCharge = 0;
528 size_t j = 0;
529 for (size_t i = 0; i < idx.size(); ++i) {
530 if (!idx[i]) continue;
531 const auto& trackEL = tracks[i];
532 tracklist[j] = trackEL;
533 const auto track = *trackEL;
534 p[j] = track->genvecP4();
535 p[j].SetM(m_trkMass[iTrk][j]);
536 totalCharge += static_cast<int>(track->charge());
537 if (p[j].Pt() < m_trkPt[iTrk][j]) {
538 isValidCombination = false;
539 break;
540 }
541 ++j;
542 }
543 if (!isValidCombination || (m_nTrkCharge[iTrk] >= 0 && totalCharge != m_nTrkCharge[iTrk]) || !passedDeltaRcut(p)) continue;
544
545 if (msgLvl(MSG::DEBUG)) {
546 ATH_MSG_DEBUG( "Dump found tracks before vertex fit: pT / eta / phi / charge" );
547 for (size_t i = 0; i < tracklist.size(); ++i) {
548 const auto track = *tracklist[i];
549 ATH_MSG_DEBUG( "track " << i + 1 << ": " << p[i].Pt() << " / " << p[i].Eta() << " / " << p[i].Phi() << " / " << track->charge() );
550 }
551 }
552
553 auto mass = (std::accumulate(p.begin(), p.end(), xAOD::TrackParticle::GenVecFourMom_t())).M();
554 ATH_MSG_DEBUG( "invariant mass: " << mass );
555
556 if (!isInMassRange(mass, iTrk)) continue;
557
558 auto fitterState = m_vertexFitter->makeState(state.context());
559 auto vertex = fit(tracklist, m_trkMass[iTrk], *fitterState);
560 ++mon_nVertexFitterCalls;
561 if (!vertex) continue;
562
563 ATH_MSG_DEBUG( "Filter found a subset of tracks which passed the rough selection: stop looking for other combinations" );
564 state.setEventAccepted(true);
565 break;
566
567 } while (prev_perm(idx));
568 }
569
570 if (!state.isEventAccepted()) {
571 ATH_MSG_DEBUG( "Filter could not find a good subset of tracks" );
572 }
573
574 mon_isEventAccepted = state.isEventAccepted();
575
576 return StatusCode::SUCCESS;
577}
578
579
580template<typename T>
582
583 state.trigBphysLegIndices().clear();
584 const auto& leptons = state.leptons();
585
586 // monitored variables
587 auto mon_nAcceptedTrk = Monitored::Scalar<int>("nAcceptedTrk", leptons.size());
588 auto mon_nCombination = Monitored::Scalar<int>("nCombination", 0);
589 auto mon_nCombinationBeforeFit = Monitored::Scalar<int>("nCombinationBeforeFit", 0);
590 auto mon_nBPhysObject = Monitored::Scalar<int>("nBPhysObject", 0);
591
592 std::vector<float> trkMassBeforeFit;
593 std::vector<float> bphysMass;
594 std::vector<float> d0track1, d0track2;
595 std::vector<float> pttrack1, pttrack2;
596 std::vector<float> etatrack1, etatrack2;
597 std::vector<int> bphysCharge;
598 auto mon_trkMassBeforeFit = Monitored::Collection("trkMassBeforeFit", trkMassBeforeFit);
599 auto mon_bphysChi2 = Monitored::Collection("bphysChi2", state.trigBphysCollection(), &xAOD::TrigBphys::fitchi2);
600 auto mon_bphysLxy = Monitored::Collection("bphysLxy", state.trigBphysCollection(), &xAOD::TrigBphys::lxy);
601 auto mon_bphysFitMass = Monitored::Collection("bphysFitMass", state.trigBphysCollection(), [](const xAOD::TrigBphys* x){ return x->fitmass()*0.001; });
602 auto mon_bphysMass = Monitored::Collection("bphysMass", bphysMass);
603 auto mon_d0track1 = Monitored::Collection("bphysd0_trk1", d0track1);
604 auto mon_d0track2 = Monitored::Collection("bphysd0_trk2", d0track2);
605 auto mon_pttrack1 = Monitored::Collection("bphysPt_trk1", pttrack1);
606 auto mon_pttrack2 = Monitored::Collection("bphysPt_trk2", pttrack2);
607 auto mon_etatrack1 = Monitored::Collection("bphysEtatrack1", etatrack1);
608 auto mon_etatrack2 = Monitored::Collection("bphysEtatrack2", etatrack2);
609 auto mon_bphysCharge = Monitored::Collection("bphysCharge", bphysCharge);
610
611 auto mon_timer = Monitored::Timer( "TIME_all" );
612
613 auto group = Monitored::Group(m_monTool,
614 mon_nAcceptedTrk, mon_nCombination, mon_nCombinationBeforeFit, mon_nBPhysObject,
615 mon_trkMassBeforeFit, mon_bphysChi2, mon_bphysLxy, mon_bphysFitMass, mon_bphysMass, mon_bphysCharge, mon_d0track1, mon_d0track2,
616 mon_timer, mon_pttrack1, mon_pttrack2, mon_etatrack1, mon_etatrack2);
617
618 for (size_t iTrk = 0; iTrk < m_nTrk.size(); ++iTrk) {
619 size_t nTrk = m_nTrk[iTrk];
620
621 if (leptons.size() < nTrk) {
622 ATH_MSG_DEBUG( "Could not build a subset of " << nTrk << " legs from collection which contains only " << leptons.size() << " objects" );
623 continue;
624 }
625 ATH_MSG_DEBUG( "Consider all combinations of " << nTrk << " legs from collection which contains " << leptons.size() << " objects" );
626
627 std::vector<size_t> leptonIndices(nTrk);
628 std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(nTrk);
629 std::vector<xAOD::TrackParticle::GenVecFourMom_t> p(nTrk);
630
631 std::vector<char> combination(leptons.size(), 0);
632 std::fill(combination.begin(), combination.begin() + nTrk, 1);
633 do {
634 // fill tracklist and momenta of muons in the current subset
635 bool isValidCombination = true;
636 int charge = 0;
637 size_t j = 0;
638 for (size_t i = 0; i < combination.size(); ++i) {
639 if (!combination[i]) continue;
640 leptonIndices[j] = i;
641 auto leg = *leptons[i].link;
642 charge += static_cast<int>(lround(leg->charge()));
644 if constexpr(std::is_same<T, xAOD::MuonContainer>::value) {
645 trackEL = leg->inDetTrackParticleLink();
646 }
647 else {
648 trackEL = leg->trackParticleLink();
649 }
650 tracklist[j] = trackEL;
652 p[j] = leg->genvecP4();
653 p[j].SetM(0.); // to keep consistency with TrigComboHypoTool::compute()
654 }
655 else {
656 p[j] = (*trackEL)->genvecP4();
657 p[j].SetM(m_trkMass[iTrk][j]);
658 }
659 if (p[j].Pt() < m_trkPt[iTrk][j]) {
660 isValidCombination = false;
661 break;
662 }
663 ++j;
664 }
665 if (!isValidCombination || (m_nTrkCharge[iTrk] >= 0 && charge != m_nTrkCharge[iTrk]) || !passedDeltaRcut(p)) continue;
666
667 if (msgLvl(MSG::DEBUG)) {
668 ATH_MSG_DEBUG( "Dump found leptons before vertex fit: pT / eta / phi / charge" );
669 for (size_t i = 0; i < tracklist.size(); ++i) {
670 const auto track = *tracklist[i];
671 ATH_MSG_DEBUG( "legs " << i + 1 << ": " << p[i].Pt() << " / " << p[i].Eta() << " / " << p[i].Phi() << " / " << track->charge() );
672 }
673 }
674
675 auto mass = (std::accumulate(p.begin(), p.end(), xAOD::TrackParticle::GenVecFourMom_t())).M();
676 ATH_MSG_DEBUG( "invariant mass: " << mass );
677
678 mon_nCombination++;
679 trkMassBeforeFit.push_back(mass * 0.001);
680 if (!isInMassRange(mass, iTrk)) continue;
681
682 mon_nCombinationBeforeFit++;
683 auto fitterState = m_vertexFitter->makeState(state.context());
684 auto vertex = fit(tracklist, m_trkMass[iTrk], *fitterState);
685 if (!vertex) continue;
686 xAOD::TrigBphys* trigBphys = makeTrigBPhys(*vertex, m_trkMass[iTrk], state.beamSpot(), *fitterState);
687 if (m_useLeptonMomentum) trigBphys->setMass(mass);
688 state.addTrigBphysObject(trigBphys, leptonIndices);
689
690 mon_nBPhysObject++;
691 bphysMass.push_back(mass * 0.001);
692 bphysCharge.push_back(charge);
693 d0track1.push_back((*tracklist[0])->d0());
694 d0track2.push_back((*tracklist[1])->d0());
695 pttrack1.push_back((*tracklist[0])->pt() * 0.001);
696 pttrack2.push_back((*tracklist[1])->pt() * 0.001);
697 etatrack1.push_back((*tracklist[0])->eta());
698 etatrack2.push_back((*tracklist[1])->eta());
699
700 } while (prev_perm(combination));
701 }
702 return StatusCode::SUCCESS;
703}
704
705
707
708 ATH_MSG_DEBUG( "Try to find electrons originating from the same EM cluster" );
709
710 // some electrons can be already attached to the list, add electrons from BPH-0DR3-EM7J15 clusters to the end
711 auto& leptons = state.leptons();
712
713 if (m_mergedElectronIDs.empty()) {
714 ATH_MSG_DEBUG( "no chains similar to BPH-0DR3-EM7J15 have been requested, should not look for close-by electrons" );
715 return StatusCode::SUCCESS;
716 }
717
718 const std::vector<double> particleMasses(2, PDG::mElectron);
719
720 for (const Decision* decision : state.previousDecisions()) {
722
723 auto decisionEL = TrigCompositeUtils::decisionToElementLink(decision, state.context());
725 auto electronEL = decision->objectLink<xAOD::ElectronContainer>(TrigCompositeUtils::featureString());
726 const auto electron = *electronEL;
727 if (!electron->trackParticle()) continue;
728
729 // get all electrons from the same SG::View, i.e. from the same initialRoI
730 const auto electronContainer = electronEL.getStorableObjectPointer();
731 if (electronContainer->size() <= 1) continue;
732
733 // add electron from decision to state.leptons
736 leptons.push_back({electronEL, std::vector<ElementLink<DecisionContainer>>(1, decisionEL), decisionIDs});
737
738 // get initialRoI this electron originating from
740 ATH_CHECK( roiInfo.isValid() );
741 auto initialRoI = *roiInfo.link;
742
743 // try to build di-electron pairs: first electron is always from decision, second from the same SG::View
744 std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(2);
745 tracklist[0] = electron->trackParticleLink();
746 for (size_t i = 0; i < electronContainer->size(); ++i) {
747 const auto electronFromView = electronContainer->at(i);
748 if (electronFromView == electron) continue;
749 if (!electronFromView->trackParticle()) continue;
750 if (!electronFromView->caloCluster() || electronFromView->caloCluster()->et() < m_caloClusterEtThreshold) continue;
751 tracklist[1] = electronFromView->trackParticleLink();
752
753 auto fitterState = m_vertexFitter->makeState(state.context());
754 auto vertex = fit(tracklist, particleMasses, *fitterState);
755 if (!vertex) continue;
756 xAOD::TrigBphys* trigBphys = makeTrigBPhys(*vertex, particleMasses, state.beamSpot(), *fitterState);
757 trigBphys->setRoiId(initialRoI->roiWord());
758 state.addTrigBphysObject(trigBphys, std::vector<size_t>(1, leptons.size() - 1));
759 }
760 }
761
762 return StatusCode::SUCCESS;
763}
764
765
767
768 ATH_MSG_DEBUG( "Try to find muon + track combinations from the same SG::View" );
769
770 auto& muons = state.leptons();
771 muons.clear();
772
773 const std::vector<double> particleMasses(2, PDG::mMuon);
774
775 for (const Decision* decision : state.previousDecisions()) {
776 if (!TrigCompositeUtils::isAnyIDPassing(decision, m_allowedIDs)) continue;
777
778 auto decisionEL = TrigCompositeUtils::decisionToElementLink(decision, state.context());
780 auto muonEL = decision->objectLink<xAOD::MuonContainer>(TrigCompositeUtils::featureString());
781 const auto muon = *muonEL;
782 if (!muon->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) continue;
783 if (!muon->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) continue;
784 const auto muonInDetTrack = muon->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle);
785 auto muonMomentum = muonInDetTrack->genvecP4();
786 muonMomentum.SetM(PDG::mMuon);
787
788 // add muon from decision to state.leptons
791 muons.push_back({muonEL, std::vector<ElementLink<DecisionContainer>>(1, decisionEL), decisionIDs});
792
793 ATH_MSG_DEBUG( "Found muon (CombinedTrackParticle): " << muon->pt() << " / " << muon->eta() << " / " << muon->phi() << " / " << muon->charge() );
794
796 ATH_CHECK( viewLinkInfo.isValid() );
797 auto view = *viewLinkInfo.link;
798
799 auto tracksHandle = ViewHelper::makeHandle(view, m_trackParticleContainerKey, state.context());
800 ATH_CHECK( tracksHandle.isValid() );
801 ATH_MSG_DEBUG( "Tracks container " << m_trackParticleContainerKey << " size: " << tracksHandle->size() );
802
803 // try to fit muon and track into common vertex: first track is always muon, second tracks comes from the same SG::View
804 std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(2);
805 tracklist[0] = muon->inDetTrackParticleLink();
806 for (size_t idx = 0; idx < tracksHandle->size(); ++idx) {
807 const xAOD::TrackParticle* track = tracksHandle->at(idx);
808
809 if (track->pt() < m_trkPt[0][1] || isIdenticalTracks(track, muonInDetTrack)) continue;
810 auto trackMomentum = track->genvecP4();
811 trackMomentum.SetM(PDG::mMuon);
812 if (!isInMassRange((muonMomentum + trackMomentum).M(), 0)) continue;
813 if (m_nTrkCharge[0] >= 0 && muonInDetTrack->charge() * track->charge() > 0.) continue;
814
815 tracklist[1] = ViewHelper::makeLink<xAOD::TrackParticleContainer>(view, tracksHandle, idx);
816
817 ATH_MSG_DEBUG( "Dump found muon+track pair before vertex fit: pT / eta / phi / charge" << endmsg <<
818 " muon: " << muonMomentum.Pt() << " / " << muonMomentum.Eta() << " / " << muonMomentum.Phi() << " / " << muon->charge() << endmsg <<
819 " track: " << trackMomentum.Pt() << " / " << trackMomentum.Eta() << " / " << trackMomentum.Phi() << " / " << track->charge() );
820
821 auto fitterState = m_vertexFitter->makeState(state.context());
822 auto vertex = fit(tracklist, particleMasses, *fitterState);
823 if (!vertex) continue;
824 xAOD::TrigBphys* trigBphys = makeTrigBPhys(*vertex, particleMasses, state.beamSpot(), *fitterState);
825 // trigBphys->setRoiId(initialRoI->roiWord());
826 state.addTrigBphysObject(trigBphys, std::vector<size_t>(1, muons.size() - 1));
827 }
828 }
829
830 return StatusCode::SUCCESS;
831}
832
833
835
836 if (state.isEventAccepted()) {
837 ATH_MSG_DEBUG( "Copying decisions from " << decisionsInput().at(0).key() << " to " << decisionsOutput().at(0).key() );
838 for (const Decision* previousDecision : state.previousDecisions()) {
839 if (!TrigCompositeUtils::isAnyIDPassing(previousDecision, m_allowedIDs)) continue;
840
841 DecisionIDContainer previousDecisionIDs;
842 TrigCompositeUtils::decisionIDs(previousDecision, previousDecisionIDs);
844 std::set_intersection(previousDecisionIDs.begin(), previousDecisionIDs.end(), m_allowedIDs.begin(), m_allowedIDs.end(),
845 std::inserter(decisionIDs, decisionIDs.begin()));
846
848 TrigCompositeUtils::linkToPrevious(decision, previousDecision, state.context());
850 }
851
852 // copy additional decisions for combined chains, as 'HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_mu4_L1BPH-0M9-EM7-EM5_MU6'
854 }
855 return StatusCode::SUCCESS;
856}
857
858
860
861 size_t idx = 0;
862 for (const xAOD::TrigBphys* triggerObject : state.trigBphysCollection()) {
863 ATH_MSG_DEBUG( "Found xAOD::TrigBphys: mass / chi2 = " << triggerObject->mass() << " / " << triggerObject->fitchi2() );
864
865 auto triggerObjectEL = ElementLink<xAOD::TrigBphysContainer>(state.trigBphysCollection(), triggerObject->index());
866 ATH_CHECK( triggerObjectEL.isValid() );
867
868 // create a new output Decision object, backed by the 'decisions' container.
870
871 std::vector<const DecisionIDContainer*> previousDecisionIDs;
872 for (const size_t& i : state.getTrigBphysLegIndices(idx)) {
873
874 // attach all previous decisions: if the same previous decision is called twice, that's fine - internally takes care of that
875 // we already have an array of links to the previous decisions, so there is no need to use TrigCompositeUtils::linkToPrevious()
877 previousDecisionIDs.push_back(&state.getDecisionIDs(i));
878 }
879
880 // set mandatory feature ElementLink to xAOD::TrigBphys object
882 decision->setDetail<int32_t>("noCombo", 1);
883
884 for (const auto& tool : hypoTools()) {
885 ATH_MSG_DEBUG( "Go to " << tool );
886 if (!m_checkMultiplicity || state.checkMultiplicity(tool->legMultiplicity(), tool->legDecisionIds())) ATH_CHECK( tool->decideOnSingleObject(decision, previousDecisionIDs) );
887 }
888 ++idx;
889 }
890
891 // copy additional decisions from Empty muon step for Combined chains, as 'HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_mu4_L1BPH-0M9-EM7-EM5_MU6'
893
894 return StatusCode::SUCCESS;
895}
896
897
899
900 if (decisionsInput().size() > 1) {
901 ATH_MSG_DEBUG( "Found more than one decision input key, decisionsInput().size = " << decisionsInput().size() );
902 for (size_t i = 1; i < decisionsInput().size(); ++i) {
903 ATH_MSG_DEBUG( "Copying decisions from " << decisionsInput().at(i).key() << " to " << decisionsOutput().at(i).key() );
904 auto previousDecisionsHandle = SG::makeHandle(decisionsInput().at(i), state.context());
905 CHECK( previousDecisionsHandle.isValid() );
906 ATH_MSG_DEBUG( "Running with "<< previousDecisionsHandle->size() << " previous decisions" );
908 for (const Decision* previousDecision : *previousDecisionsHandle) {
909 if (!TrigCompositeUtils::isAnyIDPassing(previousDecision, m_allowedIDs)) continue;
910
911 DecisionIDContainer previousDecisionIDs;
912 TrigCompositeUtils::decisionIDs(previousDecision, previousDecisionIDs);
914 std::set_intersection(previousDecisionIDs.begin(), previousDecisionIDs.end(), m_allowedIDs.begin(), m_allowedIDs.end(),
915 std::inserter(decisionIDs, decisionIDs.begin()));
916
918 TrigCompositeUtils::linkToPrevious(decision, previousDecision, state.context());
920 }
921 }
922 }
923 return StatusCode::SUCCESS;
924}
925
926
927std::unique_ptr<xAOD::Vertex> TrigMultiTrkComboHypo::fit(
928 const std::vector<ElementLink<xAOD::TrackParticleContainer>>& trackParticleLinks,
929 const std::vector<double>& particleMasses,
930 Trk::IVKalState& fitterState) const {
931
932 ATH_MSG_DEBUG( "Perform vertex fit" );
933 std::vector<const xAOD::TrackParticle*> tracklist(trackParticleLinks.size(), nullptr);
934 std::transform(trackParticleLinks.begin(), trackParticleLinks.end(), tracklist.begin(),
935 [](const ElementLink<xAOD::TrackParticleContainer>& link){ return *link; });
936
937 const Trk::Perigee& perigee1 = tracklist[0]->perigeeParameters();
938 const Trk::Perigee& perigee2 = tracklist[1]->perigeeParameters();
939 int flag = 0;
940 int errorcode = 0;
941 Amg::Vector3D startingPoint = m_vertexPointEstimator->getCirclesIntersectionPoint(&perigee1, &perigee2, flag, errorcode);
942 if (errorcode != 0) startingPoint = Amg::Vector3D::Zero(3);
943 ATH_MSG_DEBUG( "Starting point: (" << startingPoint(0) << ", " << startingPoint(1) << ", " << startingPoint(2) << ")" );
944
945 m_vertexFitter->setMassInputParticles(particleMasses, fitterState);
946 std::unique_ptr<xAOD::Vertex> vertex(m_vertexFitter->fit(tracklist, startingPoint, fitterState));
947 if (!vertex) {
948 ATH_MSG_DEBUG( "Vertex fit fails" );
949 return vertex;
950 }
951 if (vertex->chiSquared() > m_chi2) {
952 ATH_MSG_DEBUG( "Fit is successful, but vertex chi2 is too high, we are not going to save it (chi2 = " << vertex->chiSquared() << " > " << m_chi2.value() << ")" );
953 vertex.reset();
954 return vertex;
955 }
956 ATH_MSG_DEBUG( "Fit is successful" );
957 vertex->clearTracks();
958 vertex->setTrackParticleLinks(trackParticleLinks);
959 return vertex;
960}
961
962
964 const xAOD::Vertex& vertex,
965 const std::vector<double>& particleMasses,
966 const xAOD::Vertex& beamSpot,
967 const Trk::IVKalState& fitterState) const {
968
969 double invariantMass = 0.;
970 double invariantMassError = 0.;
971 if (!m_vertexFitter->VKalGetMassError(invariantMass, invariantMassError, fitterState).isSuccess()) {
972 ATH_MSG_DEBUG( "Warning from TrkVKalVrtFitter: can not calculate uncertainties" );
973 invariantMass = -9999.;
974 }
975
977 for (size_t i = 0; i < vertex.nTrackParticles(); ++i) {
978 auto p = vertex.trackParticle(i)->genvecP4();
979 p.SetM(particleMasses[i]);
980 momentum += p;
981 }
982
983 xAOD::TrigBphys* result = new xAOD::TrigBphys();
984 result->makePrivateStore();
985 result->initialise(0, momentum.Eta(), momentum.Phi(), momentum.Pt(), xAOD::TrigBphys::MULTIMU, momentum.M(), xAOD::TrigBphys::EF);
986
987 if (m_doElectrons) result->setParticleType(xAOD::TrigBphys::JPSIEE);
988 result->setFitmass(invariantMass);
989 result->setFitchi2(vertex.chiSquared());
990 result->setFitndof(vertex.numberDoF());
991 result->setFitx(vertex.x());
992 result->setFity(vertex.y());
993 result->setFitz(vertex.z());
994 result->setTrackParticleLinks(vertex.trackParticleLinks());
995 result->setLxy(m_v0Tools->lxy(&vertex, &beamSpot));
996 result->setLxyError(m_v0Tools->lxyError(&vertex, &beamSpot));
997
999 "TrigBphys objects:\n\t " <<
1000 "roiId: " << result->roiId() << "\n\t " <<
1001 "particleType: " << result->particleType() << "\n\t " <<
1002 "level: " << result->level() << "\n\t " <<
1003 "eta: " << result->eta() << "\n\t " <<
1004 "phi: " << result->phi() << "\n\t " <<
1005 "mass: " << result->mass() << "\n\t " <<
1006 "fitmass: " << result->fitmass() << "\n\t " <<
1007 "chi2/NDF: " << result->fitchi2() << " / " << result->fitndof() << "\n\t " <<
1008 "vertex: (" << result->fitx() << ", " << result->fity() << ", " << result->fitz() << ")\n\t " <<
1009 "Lxy/LxyError: " << result->lxy() << " / " << result->lxyError() );
1010
1011 return result;
1012}
1013
1014
1016
1017 if (lhs->charge() * rhs->charge() < 0.) return false;
1018 return (ROOT::Math::VectorUtil::DeltaR(lhs->genvecP4(), rhs->genvecP4()) < m_deltaR);
1019}
1020
1021
1022bool TrigMultiTrkComboHypo::isIdenticalTracks(const xAOD::Muon* lhs, const xAOD::Muon* rhs) const {
1023
1025}
1026
1028
1029 return isIdenticalTracks(*lhs->trackParticleLink(), *rhs->trackParticleLink());
1030}
1031
1032
1033bool TrigMultiTrkComboHypo::isInMassRange(double mass, size_t idx) const {
1034
1035 const auto& range = m_massRange[idx];
1036 return (mass > range.first && mass < range.second);
1037}
1038
1039
1040bool TrigMultiTrkComboHypo::passedDeltaRcut(const std::vector<xAOD::TrackParticle::GenVecFourMom_t>& p) const {
1041
1042 if (m_deltaRMax == std::numeric_limits<float>::max() && m_deltaRMin == std::numeric_limits<float>::lowest()) {
1043 return true;
1044 }
1045 for (size_t i = 0; i < p.size(); ++i) {
1046 for (size_t j = i + 1; j < p.size(); ++j) {
1047 double deltaR = ROOT::Math::VectorUtil::DeltaR(p[i], p[j]);
1048 if (deltaR > m_deltaRMax || deltaR < m_deltaRMin) return false;
1049 }
1050 }
1051 return true;
1052}
Scalar eta() const
pseudorapidity method
Scalar deltaR(const MatrixBase< Derived > &vec) const
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_DEBUG(x)
double charge(const T &p)
Definition AtlasPID.h:997
#define CHECK(...)
Evaluate an expression and check for errors.
void decisionIDs(const Decision *d, DecisionIDContainer &id)
Extracts DecisionIDs stored in the Decision object.
@ Phi
Definition RPCdef.h:8
@ Eta
Definition RPCdef.h:8
struct PDG20 PDG
xAOD::ElectronContainer * electronContainer
#define x
std::enable_if_t< std::is_void_v< std::result_of_t< decltype(&T::renounce)(T)> > &&!std::is_base_of_v< SG::VarHandleKeyArray, T > &&std::is_base_of_v< Gaudi::DataHandle, T >, void > renounce(T &h)
bool msgLvl(const MSG::Level lvl) const
ComboHypo(const std::string &name, ISvcLocator *pSvcLocator)
Definition ComboHypo.cxx:13
const SG::WriteHandleKeyArray< TrigCompositeUtils::DecisionContainer > & decisionsOutput() const
Definition ComboHypo.h:42
const Combo::MultiplicityReqMap & triggerMultiplicityMap() const
Definition ComboHypo.h:43
ToolHandleArray< ComboHypoToolBase > & hypoTools()
Definition ComboHypo.h:45
const Combo::LegMap & legToInputCollectionMap() const
Definition ComboHypo.h:44
const SG::ReadHandleKeyArray< TrigCompositeUtils::DecisionContainer > & decisionsInput() const
Definition ComboHypo.h:41
virtual StatusCode initialize() override
Definition ComboHypo.cxx:22
const_iterator end() const noexcept
Return a const_iterator pointing past the end of the collection.
const_iterator begin() const noexcept
Return a const_iterator pointing at the beginning of the collection.
std::string name() const
reports human redable name
TrigCompositeUtils::DecisionContainer & decisions()
void setTrigBphysCollection(xAOD::TrigBphysContainer *trigBphysCollection)
const TrigCompositeUtils::DecisionContainer & previousDecisions() const
const xAOD::Vertex & beamSpot() const
xAOD::TrigBphysContainer & trigBphysCollection()
const EventContext & context() const
Group of local monitoring quantities and retain correlation when filling histograms
Declare a monitored scalar variable.
A monitored timer.
A "view" of the event store (IProxyDict).
Definition View.h:46
pointer_type ptr()
Dereference the pointer.
SG::ReadHandleKey< xAOD::TrackParticleContainer > m_trackParticleContainerKey
StatusCode mergeLeptonsFromDecisions(TrigMultiTrkState< CONTAINER > &) const
Go through state.previousDecisions(), fetch xAOD::Muons/xAODElectron objects attached to decisions an...
Gaudi::Property< bool > m_combineInputDecisionCollections
ToolHandle< GenericMonitoringTool > m_monTool
Gaudi::Property< bool > m_checkMultiplicity
SG::WriteHandleKey< xAOD::TrigBphysContainer > m_trigBphysContainerKey
virtual StatusCode initialize() override
TrigCompositeUtils::DecisionIDContainer m_mergedElectronIDs
Gaudi::Property< float > m_deltaR
xAOD::TrigBphys * makeTrigBPhys(const xAOD::Vertex &vertex, const std::vector< double > &particleMasses, const xAOD::Vertex &beamSpot, const Trk::IVKalState &fitterState) const
Construct the trigger object that may be stored for debugging or matching.
virtual StatusCode execute(const EventContext &context) const override
Gaudi::Property< float > m_chi2
StatusCode findMuTrkCandidates(TrigMultiTrkState< xAOD::MuonContainer > &) const
Build J/psi candidates from muon from SG::View and tracks from the same SG::View, to be used for Tag-...
Gaudi::Property< bool > m_useLeptonMomentum
std::unique_ptr< xAOD::Vertex > fit(const std::vector< ElementLink< xAOD::TrackParticleContainer > > &trackParticleLinks, const std::vector< double > &particleMasses, Trk::IVKalState &fitterState) const
Perform a vertex fit on selected tracks.
bool isInMassRange(double mass, size_t idx) const
Gaudi::Property< std::vector< std::vector< double > > > m_trkMass
Gaudi::Property< std::vector< int > > m_nTrkCharge
ToolHandle< Trk::V0Tools > m_v0Tools
StatusCode filterTrackCombinations(TrigMultiTrkStateBase &) const
Make all possible combinations from state.tracks(), fit tracks to the common vertex and set state....
ToolHandle< InDet::VertexPointEstimator > m_vertexPointEstimator
Gaudi::Property< std::vector< std::vector< double > > > m_trkPt
StatusCode copyAdditionalDecisionObjects(TrigMultiTrkStateBase &) const
For chains from CombinedSlice (similar to 'HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_mu4_L1BPH-0M9-EM7-EM...
Gaudi::Property< std::vector< std::string > > m_mergedElectronChains
TrigCompositeUtils::DecisionIDContainer m_allowedIDs
Gaudi::Property< std::vector< unsigned int > > m_nTrk
Gaudi::Property< bool > m_applyOverlapRemoval
SG::ReadCondHandleKey< InDet::BeamSpotData > m_beamSpotKey
bool isIdenticalTracks(const xAOD::TrackParticle *lhs, const xAOD::TrackParticle *rhs) const
Attempts to identify identical tracks by selection on DeltaR.
bool passedDeltaRcut(const std::vector< xAOD::TrackParticle::GenVecFourMom_t > &momenta) const
Gaudi::Property< bool > m_isStreamer
Gaudi::Property< float > m_deltaRMin
ToolHandle< Trk::TrkVKalVrtFitter > m_vertexFitter
Gaudi::Property< float > m_deltaRMax
Gaudi::Property< std::vector< std::pair< double, double > > > m_massRange
Gaudi::Property< bool > m_doElectrons
StatusCode findMultiLeptonCandidates(TrigMultiTrkState< CONTAINER > &) const
Make all possible combinations from state.leptons(), fit tracks to the common vertex,...
StatusCode copyDecisionObjects(TrigMultiTrkStateBase &) const
All appropriate decisions from state.previousDecisions() will be copied to state.decisions() if flag ...
StatusCode processMergedElectrons(TrigMultiTrkState< xAOD::ElectronContainer > &) const
Make all possible combinations from electrons originating from the same BPH-0DR3-EM7J15 cluster,...
Gaudi::Property< double > m_caloClusterEtThreshold
Gaudi::Property< std::string > m_trigLevel
Gaudi::Property< bool > m_isMuTrkMode
TrigMultiTrkComboHypo()=delete
StatusCode mergeTracksFromViews(TrigMultiTrkStateBase &) const
Go through state.previousDecisions() and fetch xAOD::TrackParticle objects associated with the neares...
StatusCode createDecisionObjects(TrigMultiTrkStateBase &) const
Create a decision for each xAOD::TrigBphys object from state.trigBphysCollection() and save it to sta...
TrigCompositeUtils::DecisionIDContainer m_resolvedElectronIDs
StatusCode mergeTracksFromDecisions(TrigMultiTrkStateBase &) const
Go through state.previousDecisions(), fetch xAOD::Muons/xAODElectron objects attached to decisions an...
virtual bool checkMultiplicity(const std::vector< int > &legMultiplicity, const std::vector< HLT::Identifier > &legDecisionIDs) const =0
std::vector< std::vector< size_t > > & trigBphysLegIndices()
std::vector< ElementLink< xAOD::TrackParticleContainer > > & tracks()
std::vector< size_t > & getTrigBphysLegIndices(size_t idx)
virtual TrigCompositeUtils::DecisionIDContainer & getDecisionIDs(size_t)=0
virtual std::vector< ElementLink< TrigCompositeUtils::DecisionContainer > > & getDecisionLinks(size_t)=0
void setEventAccepted(bool flag=true)
State class for TrigMultiTrkComboHypo algorithm.
std::vector< LEPTON > & leptons()
virtual void addTrigBphysObject(xAOD::TrigBphys *trigBphysObject, const std::vector< size_t > &legIndices) override final
const ElementLink< TrackParticleContainer > & trackParticleLink(size_t index=0) const
ElementLink to the xAOD::TrackParticle/s that match the electron candidate.
const ElementLink< TrackParticleContainer > & inDetTrackParticleLink() const
Returns an ElementLink to the InnerDetector TrackParticle used in identification of this muon.
ROOT::Math::LorentzVector< ROOT::Math::PxPyPzM4D< double > > GenVecFourMom_t
Base 4 Momentum type for TrackParticle.
GenVecFourMom_t genvecP4() const
The full 4-momentum of the particle : GenVector form.
float charge() const
Returns the charge.
void setRoiId(uint32_t id)
set method: roiId
float fitchi2() const
accessor method: chi2 from vertex fit
float lxy() const
accessor method: lxy
void setMass(float)
Set the mass of the object.
bool setObjectLink(const std::string &name, const ElementLink< CONTAINER > &link)
Set the link to an object.
bool setDetail(const std::string &name, const TYPE &value)
Set an TYPE detail on the object.
bool addObjectCollectionLinks(const std::string &collectionName, const std::vector< ElementLink< CONTAINER > > &links)
Add links to multiple objects within a collection. Performs de-duplication.
#define ATH_NOINLINE
Eigen::Matrix< double, 3, 1 > Vector3D
ValuesCollection< T > Collection(std::string name, const T &collection)
Declare a monitored (double-convertible) collection.
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
HLT::Identifier createLegName(const HLT::Identifier &chainIdentifier, size_t counter)
Generate the HLT::Identifier which corresponds to a specific leg of a given chain.
unsigned int DecisionID
const std::string & viewString()
void insertDecisionIDs(const Decision *src, Decision *dest)
Appends the decision IDs of src to the dest decision object.
Decision * newDecisionIn(DecisionContainer *dc, const std::string &name)
Helper method to create a Decision object, place it in the container and return a pointer to it.
const std::string & featureString()
int32_t getIndexFromLeg(const HLT::Identifier &legIdentifier)
Extract the numeric index of a leg identifier.
HLT::Identifier getIDFromLeg(const HLT::Identifier &legIdentifier)
Generate the HLT::Identifier which corresponds to the chain name from the leg name.
bool passed(DecisionID id, const DecisionIDContainer &idSet)
checks if required decision ID is in the set of IDs in the container
const std::string & comboHypoAlgNodeName()
std::set< DecisionID > DecisionIDContainer
SG::WriteHandle< DecisionContainer > createAndStore(const SG::WriteHandleKey< DecisionContainer > &key, const EventContext &ctx)
Creates and right away records the DecisionContainer with the key.
void linkToPrevious(Decision *d, const std::string &previousCollectionKey, size_t previousIndex)
Links to the previous object, location of previous 'seed' decision supplied by hand.
LinkInfo< T > findLink(const Decision *start, const std::string &linkName, const bool suppressMultipleLinksWarning=false)
Perform a recursive search for ElementLinks of type T and name 'linkName', starting from Decision obj...
const std::string & initialRoIString()
const std::string & seedString()
void decisionIDs(const Decision *d, DecisionIDContainer &destination)
Extracts DecisionIDs stored in the Decision object.
bool isAnyIDPassing(const Decision *d, const DecisionIDContainer &required)
Checks if any of the DecisionIDs passed in arg required is availble in Decision object.
ElementLink< DecisionContainer > decisionToElementLink(const Decision *d, const EventContext &ctx)
Takes a raw pointer to a Decision and returns an ElementLink to the Decision.
bool isLegId(const HLT::Identifier &legIdentifier)
Recognise whether the chain ID is a leg ID.
ParametersT< TrackParametersDim, Charged, PerigeeSurface > Perigee
ElementLink< T > makeLink(const SG::View *view, const SG::ReadHandle< T > &handle, size_t index)
Create EL to a collection in view.
Definition ViewHelper.h:309
auto makeHandle(const SG::View *view, const KEY &key, const EventContext &ctx)
Create a view handle from a handle key.
Definition ViewHelper.h:273
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
ElectronContainer_v1 ElectronContainer
Definition of the current "electron container version".
TrigBphys_v1 TrigBphys
Definition TrigBphys.h:18
TrackParticle_v1 TrackParticle
Reference the current persistent version:
Vertex_v1 Vertex
Define the latest version of the vertex class.
Muon_v1 Muon
Reference the current persistent version:
MuonContainer_v1 MuonContainer
Definition of the current "Muon container version".
TrigBphysContainer_v1 TrigBphysContainer
Electron_v1 Electron
Definition of the current "egamma version".