38 #include "Math/GenVector/VectorUtil.h" 
   39 #include "Math/Vector2D.h" 
   46 using ROOT::Math::XYVector;
 
   57 bool prev_perm (std::vector<char>& 
v)
 
   59   return std::ranges::prev_permutation(
v).found;;
 
   79     ATH_MSG_INFO( 
"totalCharge value is not specified, no charge selection for track combinations will be used" );
 
   84     ATH_MSG_INFO( 
"trackMasses value is not specified, muon/electron mass will be used" );
 
   90     ATH_MSG_INFO( 
"trackPtThresholds value is not specified" );
 
   92       m_trkPt.value().emplace_back(std::vector<double>(
n, -100.));
 
  103   for (
size_t i = 0; 
i < 
m_nTrk.size(); ++
i) {
 
  115     for (
size_t i = 0; 
i < 
m_nTrk.size(); ++
i) {
 
  117       for (
size_t j = 0; j < 
m_nTrk[
i]; ++j) {
 
  129     ATH_MSG_DEBUG( 
"Configured to run in a streamer mode: no trigger objects will be created" );
 
  132     ATH_MSG_ERROR( 
"Could not create trigger objects from tracks or L2 CB muons, use the streamer mode for L2 step" );
 
  133     return StatusCode::FAILURE;
 
  147     return StatusCode::FAILURE;
 
  161     if (
item.second.size() > 1) {
 
  162       for (
size_t i = 0; 
i < 
item.second.size(); 
i++) {
 
  176       bool isMergedElectronChain = 
false;
 
  179           isMergedElectronChain = 
true;
 
  202     ATH_MSG_DEBUG( 
"No GenericMonitoringTool configured: no monitoring histograms will be available" );
 
  205   return StatusCode::SUCCESS;
 
  215   CHECK( previousDecisionsHandle.isValid() );
 
  216   ATH_MSG_DEBUG( 
"Running with "<< previousDecisionsHandle->size() << 
" previous decisions" );
 
  224     beamSpotData = *beamSpotHandle;
 
  227   std::unique_ptr<TrigMultiTrkState<xAOD::MuonContainer>> muonState;
 
  228   std::unique_ptr<TrigMultiTrkState<xAOD::ElectronContainer>> electronState;
 
  231     electronState = std::make_unique<TrigMultiTrkState<xAOD::ElectronContainer>>(context, *previousDecisionsHandle, *outputDecisionsHandle, 
nullptr, beamSpotData);
 
  232     commonState = electronState.get();
 
  235     muonState = std::make_unique<TrigMultiTrkState<xAOD::MuonContainer>>(context, *previousDecisionsHandle, *outputDecisionsHandle, 
nullptr, beamSpotData);
 
  236     commonState = muonState.get();
 
  244       ATH_CHECK( mergeTracksFromDecisions<xAOD::L2CombinedMuonContainer>(*commonState) );
 
  247       ATH_CHECK( (
m_doElectrons ? mergeTracksFromDecisions<xAOD::ElectronContainer>(*commonState) : mergeTracksFromDecisions<xAOD::MuonContainer>(*commonState)) );
 
  254     ATH_CHECK( trigBphysHandle.record(std::make_unique<xAOD::TrigBphysContainer>(),
 
  255                                       std::make_unique<xAOD::TrigBphysAuxContainer>()) );
 
  273   ATH_MSG_DEBUG( 
"TrigMultiTrkHypo::execute() terminates with StatusCode::SUCCESS" );
 
  274   return StatusCode::SUCCESS;
 
  281   auto& leptons = state.
leptons();
 
  285   std::map<const Decision*, int> decisionToInputCollectionIndexMap;
 
  286   for (
const auto& decision : previousDecisions) decisionToInputCollectionIndexMap.emplace(decision, 0);
 
  290       CHECK( previousDecisionsHandle.isValid() );
 
  292       for (
const Decision* decision : *previousDecisionsHandle) {
 
  293         previousDecisions.push_back(decision);
 
  294         decisionToInputCollectionIndexMap.emplace(decision, 
static_cast<int>(
k));
 
  300   for (
const Decision* decision : previousDecisions) {
 
  310       leptonEL = leptonLinkInfo.link;
 
  313     const auto lepton = *leptonEL;
 
  315       if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) 
continue;
 
  316       if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) 
continue;
 
  319       if (!lepton->trackParticle()) 
continue;
 
  322       ATH_MSG_ERROR( 
"mergeLeptonsFromDecisions(): no scenario for the provided CONTAINER is specified" );
 
  323       return StatusCode::FAILURE;
 
  327     auto itr = leptons.end();
 
  329       itr = std::find_if(leptons.begin(), leptons.end(),
 
  330                          [
this, lepton = lepton](
const auto& 
x){ return this->isIdenticalTracks(lepton, *x.link); });
 
  332     if (itr == leptons.end()) {
 
  333       leptons.push_back({leptonEL, std::vector<ElementLink<DecisionContainer>>(1, decisionEL), 
DecisionIDContainer()});
 
  336       (*itr).decisionLinks.push_back(decisionEL);
 
  342   std::sort(leptons.begin(), leptons.end(), [](
const auto& lhs, 
const auto& rhs){ return ((*lhs.link)->pt() > (*rhs.link)->pt()); });
 
  346   for (
auto& 
item : leptons) {
 
  349         auto decisionIndex = decisionToInputCollectionIndexMap[*decisionEL];
 
  354           if (legToInputCollectionIndexMap.at(
chain).at(legIndex) == decisionIndex) 
item.decisionIDs.insert(
id);
 
  364     ATH_MSG_DEBUG( 
"Dump found leptons before vertex fit: " << leptons.size() << 
" candidates" );
 
  365     for (
const auto& 
item : leptons) {
 
  366       const auto lepton = *
item.link;
 
  369         track = lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle);
 
  372         track = lepton->trackParticle();
 
  375       ATH_MSG_DEBUG( 
"    lepton pt (muon: CombinedTrackParticle): " << lepton->pt() << 
" / " << lepton->eta() << 
" / " << lepton->phi() << 
" / " << lepton->charge() );
 
  383   return StatusCode::SUCCESS;
 
  389   auto& tracks = state.
tracks();
 
  392   std::set<const SG::View*> views;
 
  399     if (views.find(
view) != views.end()) 
continue;  
 
  405     std::vector<ElementLink<xAOD::TrackParticleContainer>> tracksFromView;
 
  406     tracksFromView.reserve(tracksHandle->size());
 
  407     for (
size_t idx = 0; 
idx < tracksHandle->size(); ++
idx) {
 
  408       tracksFromView.emplace_back(ViewHelper::makeLink<xAOD::TrackParticleContainer>(
view, tracksHandle, 
idx));
 
  411     for (
const auto& trackEL : tracksFromView) {
 
  416           std::find_if(tracks.begin(), tracks.end(),
 
  417                        [
this, 
track](
const auto& 
x){ return isIdenticalTracks(track, *x); }) == tracks.end()) {
 
  418         tracks.emplace_back(trackEL);
 
  423   std::sort(tracks.begin(), tracks.end(), [](
const auto& lhs, 
const auto& rhs){ return ((*lhs)->pt() > (*rhs)->pt()); });
 
  426     ATH_MSG_DEBUG( 
"Dump found tracks before vertex fit: " << tracks.size() << 
" candidates" );
 
  427     for (
const auto& trackEL : tracks) {
 
  433   return StatusCode::SUCCESS;
 
  437 template<
typename CONTAINER>
 
  440   auto& tracks = state.
tracks();
 
  449     const auto lepton = *leptonEL;
 
  453       if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) 
continue;
 
  454       if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) 
continue;
 
  455       trackEL = lepton->inDetTrackParticleLink();
 
  458       if (!lepton->idTrack()) 
continue;
 
  459       trackEL = lepton->idTrackLink();
 
  462       if (!lepton->trackParticle()) 
continue;
 
  463       trackEL = lepton->trackParticleLink();
 
  466       ATH_MSG_ERROR( 
"mergeTracksFromDecisions(): no scenario for the provided CONTAINER is specified" );
 
  467       return StatusCode::FAILURE;
 
  470     if (!trackEL.
isValid()) 
continue;
 
  472         std::find_if(tracks.begin(), tracks.end(),
 
  473                      [
this, 
track = *trackEL](
const auto& 
x){ return this->isIdenticalTracks(track, *x); }) == tracks.end()) {
 
  474       tracks.emplace_back(trackEL);
 
  477   std::sort(tracks.begin(), tracks.end(), [](
const auto& lhs, 
const auto& rhs){ return ((*lhs)->pt() > (*rhs)->pt()); });
 
  480     ATH_MSG_DEBUG( 
"Dump found tracks before vertex fit: " << tracks.size() << 
" candidates" );
 
  481     for (
const auto& trackEL : tracks) {
 
  487   return StatusCode::SUCCESS;
 
  493   const auto& tracks = state.
tracks();
 
  503     mon_nAcceptedTrk, mon_nVertexFitterCalls, mon_isEventAccepted,
 
  506   for (
size_t iTrk = 0; iTrk < 
m_nTrk.size(); ++iTrk) {
 
  508     size_t nTrk = 
m_nTrk[iTrk];
 
  510     if (tracks.size() < nTrk) {
 
  511       ATH_MSG_DEBUG( 
"Could not build a subset of " << nTrk << 
" tracks from collection which contains only " << tracks.size() << 
" objects" );
 
  514     ATH_MSG_DEBUG( 
"Consider combinations of " << nTrk << 
" tracks from collection which contains " << tracks.size() << 
" objects until find a good one" );
 
  516     std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(nTrk);
 
  517     std::vector<xAOD::TrackParticle::GenVecFourMom_t> 
p(nTrk);
 
  521     std::vector<char> 
idx(tracks.size(), 0);
 
  525       bool isValidCombination = 
true;
 
  528       for (
size_t i = 0; 
i < 
idx.size(); ++
i) {
 
  529         if (!
idx[
i]) 
continue;
 
  530         const auto& trackEL = tracks[
i];
 
  531         tracklist[j] = trackEL;
 
  532         const auto track = *trackEL;
 
  535         totalCharge += 
static_cast<int>(
track->charge());
 
  537           isValidCombination = 
false;
 
  545         ATH_MSG_DEBUG( 
"Dump found tracks before vertex fit: pT / eta / phi / charge" );
 
  546         for (
size_t i = 0; 
i < tracklist.size(); ++
i) {
 
  547           const auto track = *tracklist[
i];
 
  559       ++mon_nVertexFitterCalls;
 
  562       ATH_MSG_DEBUG( 
"Filter found a subset of tracks which passed the rough selection: stop looking for other combinations" );
 
  566     } 
while (prev_perm(
idx));
 
  570     ATH_MSG_DEBUG( 
"Filter could not find a good subset of tracks" );
 
  575   return StatusCode::SUCCESS;
 
  583   const auto& leptons = state.
leptons();
 
  591   std::vector<float> trkMassBeforeFit;
 
  592   std::vector<float> bphysMass;
 
  593   std::vector<float> d0track1, d0track2;
 
  594   std::vector<float> pttrack1, pttrack2;
 
  595   std::vector<float> etatrack1, etatrack2;
 
  596   std::vector<int> bphysCharge;
 
  613     mon_nAcceptedTrk, mon_nCombination, mon_nCombinationBeforeFit, mon_nBPhysObject,
 
  614     mon_trkMassBeforeFit, mon_bphysChi2, mon_bphysLxy, mon_bphysFitMass, mon_bphysMass, mon_bphysCharge, mon_d0track1, mon_d0track2,
 
  615     mon_timer, mon_pttrack1, mon_pttrack2, mon_etatrack1, mon_etatrack2);
 
  617   for (
size_t iTrk = 0; iTrk < 
m_nTrk.size(); ++iTrk) {
 
  618     size_t nTrk = 
m_nTrk[iTrk];
 
  620     if (leptons.size() < nTrk) {
 
  621       ATH_MSG_DEBUG( 
"Could not build a subset of " << nTrk << 
" legs from collection which contains only " << leptons.size() << 
" objects" );
 
  624     ATH_MSG_DEBUG( 
"Consider all combinations of " << nTrk << 
" legs from collection which contains " << leptons.size() << 
" objects" );
 
  626     std::vector<size_t> leptonIndices(nTrk);
 
  627     std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(nTrk);
 
  628     std::vector<xAOD::TrackParticle::GenVecFourMom_t> 
p(nTrk);
 
  630     std::vector<char> combination(leptons.size(), 0);
 
  631     std::fill(combination.begin(), combination.begin() + nTrk, 1);
 
  634       bool isValidCombination = 
true;
 
  637       for (
size_t i = 0; 
i < combination.size(); ++
i) {
 
  638         if (!combination[
i]) 
continue;
 
  639         leptonIndices[j] = 
i;
 
  640         auto leg = *leptons[
i].link;
 
  641         charge += 
static_cast<int>(lround(
leg->charge()));
 
  644           trackEL = 
leg->inDetTrackParticleLink();
 
  647           trackEL = 
leg->trackParticleLink();
 
  649         tracklist[j] = trackEL;
 
  651           p[j] = 
leg->genvecP4();
 
  655           p[j] = (*trackEL)->genvecP4();
 
  659           isValidCombination = 
false;
 
  667         ATH_MSG_DEBUG( 
"Dump found leptons before vertex fit: pT / eta / phi / charge" );
 
  668         for (
size_t i = 0; 
i < tracklist.size(); ++
i) {
 
  669           const auto track = *tracklist[
i];
 
  678       trkMassBeforeFit.push_back(
mass * 0.001);
 
  681       mon_nCombinationBeforeFit++;
 
  690       bphysMass.push_back(
mass * 0.001);
 
  691       bphysCharge.push_back(
charge);
 
  692       d0track1.push_back((*tracklist[0])->
d0());
 
  693       d0track2.push_back((*tracklist[1])->
d0());
 
  694       pttrack1.push_back((*tracklist[0])->
pt() * 0.001);
 
  695       pttrack2.push_back((*tracklist[1])->
pt() * 0.001);
 
  696       etatrack1.push_back((*tracklist[0])->
eta());
 
  697       etatrack2.push_back((*tracklist[1])->
eta());
 
  699     } 
while (prev_perm(combination));
 
  701   return StatusCode::SUCCESS;
 
  707   ATH_MSG_DEBUG( 
"Try to find electrons originating from the same EM cluster" );
 
  710   auto& leptons = state.
leptons();
 
  713     ATH_MSG_DEBUG( 
"no chains similar to BPH-0DR3-EM7J15 have been requested, should not look for close-by electrons" );
 
  714     return StatusCode::SUCCESS;
 
  726     if (!
electron->trackParticle()) 
continue;
 
  735     leptons.push_back({electronEL, std::vector<ElementLink<DecisionContainer>>(1, decisionEL), 
decisionIDs});
 
  740     auto initialRoI = *roiInfo.link;
 
  743     std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(2);
 
  744     tracklist[0] = 
electron->trackParticleLink();
 
  747       if (electronFromView == 
electron) 
continue;
 
  748       if (!electronFromView->trackParticle()) 
continue;
 
  750       tracklist[1] = electronFromView->trackParticleLink();
 
  753       auto vertex = 
fit(tracklist, particleMasses, *fitterState);
 
  756       trigBphys->
setRoiId(initialRoI->roiWord());
 
  761   return StatusCode::SUCCESS;
 
  767   ATH_MSG_DEBUG( 
"Try to find muon + track combinations from the same SG::View" );
 
  772   const std::vector<double> particleMasses(2, 
PDG::mMuon);
 
  780     const auto muon = *muonEL;
 
  781     if (!
muon->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) 
continue;
 
  782     if (!
muon->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) 
continue;
 
  783     const auto muonInDetTrack = 
muon->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle);
 
  784     auto muonMomentum = muonInDetTrack->genvecP4();
 
  790     muons.push_back({muonEL, std::vector<ElementLink<DecisionContainer>>(1, decisionEL), 
decisionIDs});
 
  792     ATH_MSG_DEBUG( 
"Found muon (CombinedTrackParticle): " << 
muon->pt() << 
" / " << 
muon->eta() << 
" / " << 
muon->phi() << 
" / " << 
muon->charge() );
 
  796     auto view = *viewLinkInfo.link;
 
  803     std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(2);
 
  804     tracklist[0] = 
muon->inDetTrackParticleLink();
 
  805     for (
size_t idx = 0; 
idx < tracksHandle->size(); ++
idx) {
 
  812       if (
m_nTrkCharge[0] >= 0 && muonInDetTrack->charge() * 
track->charge() > 0.) 
continue;
 
  814       tracklist[1] = ViewHelper::makeLink<xAOD::TrackParticleContainer>(
view, tracksHandle, 
idx);
 
  816       ATH_MSG_DEBUG( 
"Dump found muon+track pair before vertex fit: pT / eta / phi / charge" << 
endmsg <<
 
  817                      "   muon:  " << muonMomentum.Pt() << 
" / " << muonMomentum.Eta() << 
" / " << muonMomentum.Phi() << 
" / " << 
muon->charge() << 
endmsg <<
 
  821       auto vertex = 
fit(tracklist, particleMasses, *fitterState);
 
  829   return StatusCode::SUCCESS;
 
  854   return StatusCode::SUCCESS;
 
  862     ATH_MSG_DEBUG( 
"Found xAOD::TrigBphys: mass / chi2 = " << triggerObject->mass() << 
" / " << triggerObject->fitchi2() );
 
  870     std::vector<const DecisionIDContainer*> previousDecisionIDs;
 
  881     decision->setDetail<int32_t>(
"noCombo", 1);
 
  893   return StatusCode::SUCCESS;
 
  904       CHECK( previousDecisionsHandle.isValid() );
 
  905       ATH_MSG_DEBUG( 
"Running with "<< previousDecisionsHandle->size() << 
" previous decisions" );
 
  907       for (
const Decision* previousDecision : *previousDecisionsHandle) {
 
  922   return StatusCode::SUCCESS;
 
  928     const std::vector<double>& particleMasses,
 
  932   std::vector<const xAOD::TrackParticle*> tracklist(
trackParticleLinks.size(), 
nullptr);
 
  936   const Trk::Perigee& perigee1 = tracklist[0]->perigeeParameters();
 
  937   const Trk::Perigee& perigee2 = tracklist[1]->perigeeParameters();
 
  942   ATH_MSG_DEBUG( 
"Starting point: (" << startingPoint(0) << 
", " << startingPoint(1) << 
", " << startingPoint(2) << 
")" );
 
  944   m_vertexFitter->setMassInputParticles(particleMasses, fitterState);
 
  945   std::unique_ptr<xAOD::Vertex> 
vertex(
m_vertexFitter->fit(tracklist, startingPoint, fitterState));
 
  951     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() << 
")" );
 
  964     const std::vector<double>& particleMasses,
 
  968   double invariantMass = 0.;
 
  969   double invariantMassError = 0.;
 
  970   if (!
m_vertexFitter->VKalGetMassError(invariantMass, invariantMassError, fitterState).isSuccess()) {
 
  971     ATH_MSG_DEBUG( 
"Warning from TrkVKalVrtFitter: can not calculate uncertainties" );
 
  972     invariantMass = -9999.;
 
  976   for (
size_t i = 0; 
i < 
vertex.nTrackParticles(); ++
i) {
 
  977     auto p = 
vertex.trackParticle(
i)->genvecP4();
 
  978     p.SetM(particleMasses[
i]);
 
  983   result->makePrivateStore();
 
  987   result->setFitmass(invariantMass);
 
  993   result->setTrackParticleLinks(
vertex.trackParticleLinks());
 
  998     "TrigBphys objects:\n\t  " <<
 
  999     "roiId:         " << 
result->roiId()  << 
"\n\t  " <<
 
 1000     "particleType:  " << 
result->particleType() << 
"\n\t  " <<
 
 1001     "level:         " << 
result->level() << 
"\n\t  " <<
 
 1002     "eta:           " << 
result->eta() << 
"\n\t  " <<
 
 1003     "phi:           " << 
result->phi() << 
"\n\t  " <<
 
 1004     "mass:          " << 
result->mass() << 
"\n\t  " <<
 
 1005     "fitmass:       " << 
result->fitmass() << 
"\n\t  " <<
 
 1006     "chi2/NDF:      " << 
result->fitchi2() << 
" / " << 
result->fitndof() << 
"\n\t  " <<
 
 1007     "vertex:        (" << 
result->fitx() << 
", " << 
result->fity() << 
", " << 
result->fitz() << 
")\n\t  " <<
 
 1008     "Lxy/LxyError:  " << 
result->lxy() << 
" / " << 
result->lxyError() );
 
 1044   for (
size_t i = 0; 
i < 
p.size(); ++
i) {
 
 1045     for (
size_t j = 
i + 1; j < 
p.size(); ++j) {