diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoConfig.py b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoConfig.py index 3746904608f277e6c24ca50ae3d7acdd49c74904..2b1874949c0ad8f2721925f4851942896de29769 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoConfig.py +++ b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoConfig.py @@ -80,17 +80,23 @@ def BmumuxComboHypoCfg(name): LambdaBToMuMuProtonKaon_dimuonMassRange = (2500., 4300.), LambdaBToMuMuProtonKaon_massRange = (4800., 6400.), LambdaBToMuMuProtonKaon_chi2 = 60., - # Bc -> J/psi D+ (-> phi pi) - BcToDsMuMuPhiPi = True, - Bc_DsMuMuKaon_minKaonPt = 1000, - Bc_rangePhiDs_MassCut = (980, 1080), - Bc_rangeDs_MassCut = (1600, 2400), - Bc_DsMuMu_chi2 = 60, - # Bc -> J/psi D+ (-> K pi pi) - BcToDpMuMuPhiPi = True, - Bc_DpMuMuKaon_minKaonPt = 1000, - Bc_rangeDp_MassCut = (1500, 2300), - Bc_DpMuMu_chi2 = 60, + # B_c+ -> J/psi(-> mu+ mu-) D_s+(->phi(-> K+ K-) pi+) + BcToDsMuMu = True, + BcToDsMuMu_minKaonPt = 1000., + BcToDsMuMu_minPionPt = 1000., + BcToDsMuMu_massRange = (5500., 7300.), + BcToDsMuMu_dimuonMassRange = (2500., 4300.), + BcToDsMuMu_phiMassRange = (940., 1100.), + BcToDsMuMu_DsMassRange = (1850., 2100.), + BcToDsMuMu_chi2 = 60., + # B_c+ -> J/psi(-> mu+ mu-) D+(-> K- pi+ pi+) + BcToDplusMuMu = True, + BcToDplusMuMu_minKaonPt = 1000., + BcToDplusMuMu_minPionPt = 1000., + BcToDplusMuMu_massRange = (5500., 7300.), + BcToDplusMuMu_dimuonMassRange = (2500., 4300.), + BcToDplusMuMu_DplusMassRange = (1750., 2000.), + BcToDplusMuMu_chi2 = 60., MonTool = TrigBmumuxComboHypoMonitoring('TrigBmumuxComboHypoMonitoring')) return hypo diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigMultiTrkComboHypoConfig.py b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigMultiTrkComboHypoConfig.py index 75ae77d5b07abe6c17b8b262f988bfec40cdb091..23cf2612c8b1bb95c67d861f365301a15d8ff425 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigMultiTrkComboHypoConfig.py +++ b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigMultiTrkComboHypoConfig.py @@ -63,7 +63,7 @@ def StreamerDimuEFComboHypoCfg(name): trigSequenceName = 'StreamerDimu', trigLevel = 'EF') hypo.chi2 = 20. - hypo.massRanges = [ (100., 6000.) ] + hypo.massRange = [ (100., 6000.) ] return hypo def StreamerDiElecFastComboHypoCfg(name): @@ -78,6 +78,18 @@ def StreamerDiElecFastComboHypoCfg(name): trackCollection='HLT_IDTrack_Electron_FTF') return hypo +def StreamerNoMuonDiElecFastComboHypoCfg(name): + log.debug('StreamerNoMuonDiElecFastComboHypoCfg.name = %s ', name) + + config = TrigMultiTrkComboHypoConfig() + hypo = config.ConfigurationComboHypo( + isStreamer = True, + trigSequenceName = 'NoMuonDiElecFast', + trigLevel = 'L2', + doElectrons = True, + trackCollection='HLT_IDTrack_Electron_FTF') + return hypo + def DiElecPrecisionComboHypoCfg(name): log.debug('DiElecPrecisionComboHypoCfg.name = %s ', name) @@ -90,6 +102,17 @@ def DiElecPrecisionComboHypoCfg(name): outputTrigBphysCollection = 'HLT_DiElecPrecision') return hypo +def NoMuonDiElecPrecisionComboHypoCfg(name): + log.debug('NoMuonDiElecPrecisionComboHypoCfg.name = %s ', name) + + config = TrigMultiTrkComboHypoConfig() + hypo = config.ConfigurationComboHypo( + isStreamer = False, + trigSequenceName = 'NoMuonDiElecPrecision', + trigLevel = 'EF', + doElectrons = True, + outputTrigBphysCollection = 'HLT_NoMuonDiElecPrecision') + return hypo def TrigMultiTrkComboHypoToolFromDict(chainDict): config = TrigMultiTrkComboHypoConfig() @@ -125,15 +148,14 @@ class TrigMultiTrkComboHypoConfig(object): MaxChi2OfVtxEstimation = 2000.) if doElectrons: - trackMasses = [0.511,0.511] tool = CompFactory.TrigMultiTrkComboHypo( name = baseName+'ComboHypo', isStreamer = isStreamer, doElectrons = True, trigLevel = trigLevel, - nTracks = 2, - trackMasses = trackMasses, - massRanges = [ (100., 20000.) ], + nTracks = [ 2 ], + massRange = [ (100., 20000.) ], + trackPtThresholds = [ [ 4000., 4000. ] ], TrackCollectionKey = trackCollection, TrigBphysCollectionKey = outputTrigBphysCollection, VertexFitter = VertexFitter, @@ -146,8 +168,8 @@ class TrigMultiTrkComboHypoConfig(object): name = baseName+'ComboHypo', isStreamer = isStreamer, trigLevel = trigLevel, - nTracks = 2, - massRanges = [ (100., 20000.) ], + nTracks = [ 2 ], + massRange = [ (100., 20000.) ], TrackCollectionKey = trackCollection, TrigBphysCollectionKey = outputTrigBphysCollection, VertexFitter = VertexFitter, @@ -179,5 +201,12 @@ class TrigMultiTrkComboHypoConfig(object): if 'Lxy0' in chainDict['topo']: tool.LxyCut = 0.0 + if 'BPH-0DR3-EM7J15' in chainDict['L1item']: + tool.isMergedElectronChain = True + + signatures = chainDict['signatures'] + tool.isCombinedChain = (signatures.count(signatures[0]) != len(signatures)) + tool.legMultiplicities = chainDict['chainMultiplicities'] + tool.MonTool = TrigMultiTrkComboHypoToolMonitoring('MonTool') return tool diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/ITrigBphysState.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/ITrigBphysState.h index 803de129bd8a12a91bd21f25fc6076deb62d7485..dba23410f673ae498ccf9f72b292c8825211a06a 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/ITrigBphysState.h +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/ITrigBphysState.h @@ -1,19 +1,51 @@ /* - Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration */ #ifndef TRIG_ITrigBphysState_H #define TRIG_ITrigBphysState_H +#include "xAODTrigger/TrigCompositeContainer.h" +#include "xAODTrigBphys/TrigBphysContainer.h" +#include "BeamSpotConditionsData/BeamSpotData.h" + + +class EventContext; + + /** * @class ITrigBphysState * @brief Base class for TrigBphys state objects */ -class ITrigBphysState -{ +class ITrigBphysState { public: + ITrigBphysState() = delete; + ITrigBphysState(const EventContext& context, + const TrigCompositeUtils::DecisionContainer& previousDecisions, + TrigCompositeUtils::DecisionContainer& decisions, + xAOD::TrigBphysContainer* trigBphysCollection = nullptr, + const InDet::BeamSpotData* beamSpotData = nullptr) + : m_context(&context), + m_previousDecisions(&previousDecisions), + m_decisions(&decisions), + m_trigBphysCollection(trigBphysCollection), + m_beamSpotData(beamSpotData) {} virtual ~ITrigBphysState() = default; + + inline const EventContext& context() const { return *m_context; } + inline const TrigCompositeUtils::DecisionContainer& previousDecisions() const { return *m_previousDecisions; } + inline TrigCompositeUtils::DecisionContainer& decisions() { return *m_decisions; } + inline xAOD::TrigBphysContainer& trigBphysCollection() { return *m_trigBphysCollection; } + void setTrigBphysCollection(xAOD::TrigBphysContainer* trigBphysCollection) { m_trigBphysCollection = trigBphysCollection; } + Amg::Vector3D beamSpotPosition() const { return (m_beamSpotData ? m_beamSpotData->beamPos() : Amg::Vector3D::Zero(3)); } + + private: + const EventContext* m_context; + const TrigCompositeUtils::DecisionContainer* m_previousDecisions; + TrigCompositeUtils::DecisionContainer* m_decisions; + xAOD::TrigBphysContainer* m_trigBphysCollection; + const InDet::BeamSpotData* m_beamSpotData; }; #endif // TRIG_ITrigBphysState_H diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.cxx index faf37b5d48f819dcf573e90664f6f0827d8418c9..c8273b0753f356895ffdc916ee7a7a616a2b441d 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.cxx +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.cxx @@ -25,16 +25,14 @@ #include "AthViews/ViewHelper.h" #include "AthContainers/AuxElement.h" -#include "Math/Vector4D.h" #include "Math/GenVector/VectorUtil.h" + using TrigCompositeUtils::Decision; using TrigCompositeUtils::DecisionContainer; using TrigCompositeUtils::DecisionID; using TrigCompositeUtils::DecisionIDContainer; -using TrigCompositeUtils::comboHypoAlgNodeName; -using GenVecFourMom_t = ROOT::Math::LorentzVector<ROOT::Math::PxPyPzM4D<double>>; const std::vector<std::vector<double>> TrigBmumuxComboHypo::s_trkMass{ {PDG::mMuon, PDG::mMuon}, @@ -107,11 +105,12 @@ StatusCode TrigBmumuxComboHypo::execute(const EventContext& context) const { ATH_CHECK( trigBphysHandle.record(std::make_unique<xAOD::TrigBphysContainer>(), std::make_unique<xAOD::TrigBphysAuxContainer>()) ); - auto state = makeState(&context, previousDecisionsHandle.cptr(), outputDecisionsHandle.ptr(), trigBphysHandle.ptr()); - ATH_CHECK( mergeMuonsFromViews(*state) ); + auto state = std::make_unique<TrigBmumuxState>(context, *previousDecisionsHandle, *outputDecisionsHandle, trigBphysHandle.ptr()); + + ATH_CHECK( mergeMuonsFromDecisions(*state) ); ATH_CHECK( findDimuonCandidates(*state) ); - if ( !state->dimuons.empty() ) { + if (!state->dimuons.empty()) { ATH_CHECK( mergeTracksFromViews(*state) ); ATH_CHECK( findBmumuxCandidates(*state) ); ATH_CHECK( createDecisionObjects(*state) ); @@ -122,33 +121,19 @@ StatusCode TrigBmumuxComboHypo::execute(const EventContext& context) const { } -std::unique_ptr<TrigBmumuxState> TrigBmumuxComboHypo::makeState(const EventContext* context, - const DecisionContainer* previousDecisions, - DecisionContainer* decisions, - xAOD::TrigBphysContainer* trigBphysCollection) const { - auto state = std::make_unique<TrigBmumuxState>(); - state->context = context; - state->previousDecisions = previousDecisions; - state->decisions = decisions; - state->trigBphysCollection = trigBphysCollection; - state->dimuons.setStore(&state->dimuonsStore); - return state; -} - - -StatusCode TrigBmumuxComboHypo::mergeMuonsFromViews(TrigBmumuxState& state) const { +StatusCode TrigBmumuxComboHypo::mergeMuonsFromDecisions(TrigBmumuxState& state) const { auto& muons = state.muons; muons.clear(); // all muons from views are already connected with previous decisions by TrigMuonEFHypoAlg - for (const Decision* decision : *state.previousDecisions) { + for (const Decision* decision : state.previousDecisions()) { ATH_CHECK( decision->hasObjectLink(TrigCompositeUtils::featureString(), ClassID_traits<xAOD::MuonContainer>::ID()) ); auto muonEL = decision->objectLink<xAOD::MuonContainer>(TrigCompositeUtils::featureString()); const xAOD::Muon* muon = *muonEL; if (!muon->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) continue; - auto decisionEL = TrigCompositeUtils::decisionToElementLink(decision, *state.context); + auto decisionEL = TrigCompositeUtils::decisionToElementLink(decision, state.context()); auto itr = std::find_if(muons.begin(), muons.end(), [this, muon = muon](const auto& x){ return isIdenticalTracks(muon, *x.link); }); if (itr == muons.end()) { muons.push_back({muonEL, ElementLinkVector<DecisionContainer>(1, decisionEL), DecisionIDContainer()}); @@ -182,6 +167,7 @@ StatusCode TrigBmumuxComboHypo::mergeMuonsFromViews(TrigBmumuxState& state) cons } } } + return StatusCode::SUCCESS; } @@ -192,12 +178,12 @@ StatusCode TrigBmumuxComboHypo::mergeTracksFromViews(TrigBmumuxState& state) con tracks.clear(); size_t viewCounter = 0; - for (const Decision* decision : *state.previousDecisions) { + for (const Decision* decision : state.previousDecisions()) { auto viewLinkInfo = TrigCompositeUtils::findLink<ViewContainer>(decision, TrigCompositeUtils::viewString(), true); ATH_CHECK( viewLinkInfo.isValid() ); auto viewEL = viewLinkInfo.link; - auto tracksHandle = ViewHelper::makeHandle(*viewEL, m_trackParticleContainerKey, *state.context); + auto tracksHandle = ViewHelper::makeHandle(*viewEL, m_trackParticleContainerKey, state.context()); ATH_CHECK( tracksHandle.isValid() ); ATH_MSG_DEBUG( "tracks handle " << m_trackParticleContainerKey << " size: " << tracksHandle->size() ); @@ -281,18 +267,19 @@ StatusCode TrigBmumuxComboHypo::findDimuonCandidates(TrigBmumuxState& state) con } // fit muons to the common vertex and pass this vertex to the dimuons collection which also takes the ownership of the created object - xAOD::Vertex* vertex = fit(state.context, trackParticleLinks); + auto vertex = fit(state.context(), trackParticleLinks); if (!vertex) continue; - state.trigBphysMuonIndices.emplace_back(std::array<size_t, 2>{itrk1, itrk2}); - state.dimuons.push_back(vertex); // convert vertex to trigger object and add it to the output xAOD::TrigBphysContainer - xAOD::TrigBphys* trigBphys = makeTriggerObject(vertex); + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vertex); if (!trigBphys) { ATH_MSG_ERROR( "xAOD::Vertex could not be converted to xAOD::TrigBphys object: please enable MakeExtendedVertex option in vertex fitter " << m_vertexFitter->name() ); return StatusCode::FAILURE; } - state.trigBphysCollection->push_back(trigBphys); + + state.dimuons.push_back(vertex.release()); + state.trigBphysMuonIndices.emplace_back(std::array<size_t, 2>{itrk1, itrk2}); + state.trigBphysCollection().push_back(trigBphys); } } mon_nDimuon = state.dimuons.size(); @@ -312,12 +299,12 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con const auto& tracks = state.tracks; mon_nTrk = tracks.size(); - mon_nBPhysObject = state.trigBphysCollection->size(); + mon_nBPhysObject = state.trigBphysCollection().size(); for (size_t idx = 0; idx < state.dimuons.size(); ++idx) { const xAOD::Vertex* dimuon = state.dimuons.get(idx); - auto dimuonTriggerObjectEL = ElementLink<xAOD::TrigBphysContainer>(*state.trigBphysCollection, idx); + auto dimuonTriggerObjectEL = ElementLink<xAOD::TrigBphysContainer>(state.trigBphysCollection(), idx); ATH_CHECK( dimuonTriggerObjectEL.isValid() ); // vtx1 = {mu1, mu2, trk1} @@ -328,6 +315,7 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_vtx2(trackParticleLinks_vtx1); trackParticleLinks_vtx2.emplace_back(); + // vtx3 = {mu1, mu2, trk1, trk2, trk3} std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_vtx3(trackParticleLinks_vtx2); trackParticleLinks_vtx3.emplace_back(); @@ -363,17 +351,17 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con auto p_trk1 = trk1->genvecP4(); auto charge1 = trk1->charge(); - xAOD::Vertex* vtx1 = nullptr; + std::unique_ptr<xAOD::Vertex> vtx1; bool makeFit_vtx1 = true; // B+ -> mu+ mu- K+ if (m_BplusToMuMuKaon && p_trk1.Pt() > m_BplusToMuMuKaon_minKaonPt && isInMassRange((p_dimuon + p_trk1.SetM(PDG::mKaon)).M(), m_BplusToMuMuKaon_massRange)) { - vtx1 = fit(state.context, trackParticleLinks_vtx1, kB_2mu1trk, *dimuonTriggerObjectEL); + vtx1 = fit(state.context(), trackParticleLinks_vtx1, kB_2mu1trk, *dimuonTriggerObjectEL); makeFit_vtx1 = false; if (vtx1 && vtx1->chiSquared() < m_BplusToMuMuKaon_chi2) { - xAOD::TrigBphys* trigBphys = makeTriggerObject(vtx1, xAOD::TrigBphys::BKMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon}, dimuonTriggerObjectEL); + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vtx1, xAOD::TrigBphys::BKMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon}, dimuonTriggerObjectEL); ATH_CHECK( state.addTriggerObject(trigBphys) ); } } @@ -384,17 +372,15 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con isInMassRange(p_dimuon.M(), m_BcToMuMuPion_dimuonMassRange) && isInMassRange((p_dimuon + p_trk1.SetM(PDG::mPion)).M() - p_dimuon.M() + PDG::mJpsi, m_BcToMuMuPion_massRange)) { if (!vtx1 && makeFit_vtx1) { - vtx1 = fit(state.context, trackParticleLinks_vtx1, kB_2mu1trk, *dimuonTriggerObjectEL); + vtx1 = fit(state.context(), trackParticleLinks_vtx1, kB_2mu1trk, *dimuonTriggerObjectEL); makeFit_vtx1 = false; } if (vtx1 && vtx1->chiSquared() < m_BcToMuMuPion_chi2) { - xAOD::TrigBphys* trigBphys = makeTriggerObject(vtx1, xAOD::TrigBphys::BCPIMUMU, {PDG::mMuon, PDG::mMuon, PDG::mPion}, dimuonTriggerObjectEL); + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vtx1, xAOD::TrigBphys::BCPIMUMU, {PDG::mMuon, PDG::mMuon, PDG::mPion}, dimuonTriggerObjectEL); ATH_CHECK( state.addTriggerObject(trigBphys) ); } } - - delete vtx1; - vtx1 = nullptr; + vtx1.reset(); // dimuon + 2 tracks for (size_t itrk2 = itrk1 + 1; itrk2 < selectedTracks.size(); ++itrk2) { @@ -406,7 +392,7 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con auto p_trk2 = trk2->genvecP4(); auto charge2 = trk2->charge(); - xAOD::Vertex* vtx2 = nullptr; + std::unique_ptr<xAOD::Vertex> vtx2; bool makeFit_vtx2 = true; // B_s0 -> mu+ mu- phi(-> K+ K-) @@ -416,10 +402,10 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con p_trk2.Pt() > m_BsToMuMuPhi1020_minKaonPt && isInMassRange((p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mKaon)).M(), m_BsToMuMuPhi1020_phiMassRange) && isInMassRange((p_dimuon + p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mKaon)).M(), m_BsToMuMuPhi1020_massRange)) { - vtx2 = fit(state.context, trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); + vtx2 = fit(state.context(), trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); makeFit_vtx2 = false; if (vtx2 && vtx2->chiSquared() < m_BsToMuMuPhi1020_chi2) { - xAOD::TrigBphys* trigBphys = makeTriggerObject(vtx2, xAOD::TrigBphys::BSPHIMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mKaon}, dimuonTriggerObjectEL); + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vtx2, xAOD::TrigBphys::BSPHIMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mKaon}, dimuonTriggerObjectEL); ATH_CHECK( state.addTriggerObject(trigBphys) ); } } @@ -432,11 +418,11 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con isInMassRange((p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mPion)).M(), m_BdToMuMuKstar0_KstarMassRange) && isInMassRange((p_dimuon + p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mPion)).M(), m_BdToMuMuKstar0_massRange)) { if (!vtx2 && makeFit_vtx2) { - vtx2 = fit(state.context, trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); + vtx2 = fit(state.context(), trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); makeFit_vtx2 = false; } if (vtx2 && vtx2->chiSquared() < m_BdToMuMuKstar0_chi2) { - xAOD::TrigBphys* trigBphys = makeTriggerObject(vtx2, xAOD::TrigBphys::BDKSTMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mPion}, dimuonTriggerObjectEL); + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vtx2, xAOD::TrigBphys::BDKSTMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mPion}, dimuonTriggerObjectEL); ATH_CHECK( state.addTriggerObject(trigBphys) ); } } @@ -448,11 +434,11 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con isInMassRange((p_trk1.SetM(PDG::mPion) + p_trk2.SetM(PDG::mKaon)).M(), m_BdToMuMuKstar0_KstarMassRange) && isInMassRange((p_dimuon + p_trk1.SetM(PDG::mPion) + p_trk2.SetM(PDG::mKaon)).M(), m_BdToMuMuKstar0_massRange)) { if (!vtx2 && makeFit_vtx2) { - vtx2 = fit(state.context, trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); + vtx2 = fit(state.context(), trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); makeFit_vtx2 = false; } if (vtx2 && vtx2->chiSquared() < m_BdToMuMuKstar0_chi2) { - xAOD::TrigBphys* trigBphys = makeTriggerObject(vtx2, xAOD::TrigBphys::BDKSTMUMU, {PDG::mMuon, PDG::mMuon, PDG::mPion, PDG::mKaon}, dimuonTriggerObjectEL); + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vtx2, xAOD::TrigBphys::BDKSTMUMU, {PDG::mMuon, PDG::mMuon, PDG::mPion, PDG::mKaon}, dimuonTriggerObjectEL); ATH_CHECK( state.addTriggerObject(trigBphys) ); } } @@ -466,11 +452,11 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con isInMassRange(p_dimuon.M(), m_LambdaBToMuMuProtonKaon_dimuonMassRange) && isInMassRange((p_dimuon + p_trk1.SetM(PDG::mProton) + p_trk2.SetM(PDG::mKaon)).M() - p_dimuon.M() + PDG::mJpsi, m_LambdaBToMuMuProtonKaon_massRange)) { if (!vtx2 && makeFit_vtx2) { - vtx2 = fit(state.context, trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); + vtx2 = fit(state.context(), trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); makeFit_vtx2 = false; } if (vtx2 && vtx2->chiSquared() < m_LambdaBToMuMuProtonKaon_chi2) { - xAOD::TrigBphys* trigBphys = makeTriggerObject(vtx2, xAOD::TrigBphys::LBPQMUMU, {PDG::mMuon, PDG::mMuon, PDG::mProton, PDG::mKaon}, dimuonTriggerObjectEL); + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vtx2, xAOD::TrigBphys::LBPQMUMU, {PDG::mMuon, PDG::mMuon, PDG::mProton, PDG::mKaon}, dimuonTriggerObjectEL); ATH_CHECK( state.addTriggerObject(trigBphys) ); } } @@ -483,66 +469,80 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con isInMassRange(p_dimuon.M(), m_LambdaBToMuMuProtonKaon_dimuonMassRange) && isInMassRange((p_dimuon + p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mProton)).M() - p_dimuon.M() + PDG::mJpsi, m_LambdaBToMuMuProtonKaon_massRange)) { if (!vtx2 && makeFit_vtx2) { - vtx2 = fit(state.context, trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); + vtx2 = fit(state.context(), trackParticleLinks_vtx2, kB_2mu2trk, *dimuonTriggerObjectEL); makeFit_vtx2 = false; } if (vtx2 && vtx2->chiSquared() < m_LambdaBToMuMuProtonKaon_chi2) { - xAOD::TrigBphys* trigBphys = makeTriggerObject(vtx2, xAOD::TrigBphys::LBPQMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mProton}, dimuonTriggerObjectEL); + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vtx2, xAOD::TrigBphys::LBPQMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mProton}, dimuonTriggerObjectEL); ATH_CHECK( state.addTriggerObject(trigBphys) ); } } + vtx2.reset(); + + for (size_t itrk3 = 0; itrk3 < selectedTracks.size(); ++itrk3) { + const xAOD::TrackParticle* trk3 = *selectedTracks[itrk3]; + if (itrk3 == itrk1 || itrk3 == itrk2 || isIdenticalTracks(mu1, trk3) || isIdenticalTracks(mu2, trk3)) continue; + + trackParticleLinks_vtx3[2] = selectedTracks[itrk1]; + trackParticleLinks_vtx3[3] = selectedTracks[itrk2]; + trackParticleLinks_vtx3[4] = selectedTracks[itrk3]; + auto p_trk3 = trk3->genvecP4(); + auto charge3 = trk3->charge(); + + std::unique_ptr<xAOD::Vertex> vtx3; + bool makeFit_vtx3 = true; + + // B_c+ -> J/psi(-> mu+ mu-) D_s+(->phi(-> K+ K-) pi+) + p_trk1.SetM(PDG::mKaon); // D_s+.phi.K+ + p_trk2.SetM(PDG::mKaon); // D_s+.phi.K- + p_trk3.SetM(PDG::mPion); // D_s+.pi+ + if (m_BcToDsMuMu && + charge1 * charge2 < 0. && + p_trk1.Pt() > m_BcToDsMuMu_minKaonPt && + p_trk2.Pt() > m_BcToDsMuMu_minKaonPt && + p_trk3.Pt() > m_BcToDsMuMu_minPionPt && + isInMassRange(p_dimuon.M(), m_BcToDsMuMu_dimuonMassRange) && + isInMassRange((p_trk1 + p_trk2).M(), m_BcToDsMuMu_phiMassRange) && + isInMassRange((p_trk1 + p_trk2 + p_trk3).M(), m_BcToDsMuMu_DsMassRange) && + isInMassRange((p_dimuon + p_trk1 + p_trk2 + p_trk3).M() - p_dimuon.M() + PDG::mJpsi, m_BcToDsMuMu_massRange)) { + if (!vtx3 && makeFit_vtx3) { + vtx3 = fit(state.context(), trackParticleLinks_vtx3, kB_2mu3trk, *dimuonTriggerObjectEL); + makeFit_vtx3 = false; + } + if (vtx3 && vtx3->chiSquared() < m_BcToDsMuMu_chi2) { + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vtx3, xAOD::TrigBphys::BCDSMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mKaon, PDG::mPion}, dimuonTriggerObjectEL); + ATH_CHECK( state.addTriggerObject(trigBphys) ); + } + } - delete vtx2; - vtx2 = nullptr; - - - for (size_t itrk3 = itrk2 + 1; itrk3 < selectedTracks.size(); ++itrk3) { - const xAOD::TrackParticle* trk3 = *selectedTracks[itrk3]; - if (isIdenticalTracks(mu1, trk3) || isIdenticalTracks(mu2, trk3)) continue; - bool makeFit_vtx3 = true; - xAOD::Vertex* vtx3 = nullptr; - trackParticleLinks_vtx3[2] = selectedTracks[itrk1]; - trackParticleLinks_vtx3[3] = selectedTracks[itrk2]; - trackParticleLinks_vtx3[4] = selectedTracks[itrk3]; - auto p_trk3 = trk3->genvecP4(); - //auto charge3 = trk3->charge(); - - - if(m_Bc_DsMuMuDecay && - p_trk1.Pt() > m_LambdaBToMuMuProtonKaon_minKaonPt && - p_trk2.Pt() > m_LambdaBToMuMuProtonKaon_minKaonPt && - isInMassRange((p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mKaon)).M(), m_rangePhiDs_MassCut) && - isInMassRange((p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mKaon) + p_trk3.SetM(PDG::mPion)).M(), m_rangeDs_MassCut) ){ - if (!vtx3 && makeFit_vtx3){ - vtx3 = fit(state.context, trackParticleLinks_vtx3, kB_2mu3trk, *dimuonTriggerObjectEL); - makeFit_vtx3 = false; - } - if(vtx3 && vtx3->chiSquared() < m_Bc_DsMuMu_chi2){ - xAOD::TrigBphys* trigBphys = makeTriggerObject(vtx3, xAOD::TrigBphys::BCDSMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mKaon, PDG::mPion}, dimuonTriggerObjectEL); - ATH_CHECK( state.addTriggerObject(trigBphys) ); - } - } - - if(m_Bc_DpMuMuDecay && - p_trk1.Pt() > m_Bc_DpMuMuKaon_minKaonPt && - isInMassRange((p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mPion) + p_trk3.SetM(PDG::mPion)).M(), m_rangeDp_MassCut) ){ - if (!vtx3 && makeFit_vtx3){ - vtx3 = fit(state.context, trackParticleLinks_vtx3, kB_2mu3trk, *dimuonTriggerObjectEL); - makeFit_vtx3 = false; - } - if(vtx3 && vtx3->chiSquared() < m_Bc_DpMuMu_chi2){ - xAOD::TrigBphys* trigBphys = makeTriggerObject(vtx3, xAOD::TrigBphys::BCDPMUMU, {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mPion, PDG::mPion}, dimuonTriggerObjectEL); - ATH_CHECK( state.addTriggerObject(trigBphys) ); - } - } - delete vtx3; - vtx3 = nullptr; - } + // B_c+ -> J/psi(-> mu+ mu-) D+(-> K- pi+ pi+) + p_trk1.SetM(PDG::mPion); // D+.pi+ + p_trk2.SetM(PDG::mPion); // D+.pi+ + p_trk3.SetM(PDG::mKaon); // D+.K- + if (m_BcToDplusMuMu && + charge1 * charge2 > 0. && charge1 * charge3 < 0. && + p_trk1.Pt() > m_BcToDplusMuMu_minPionPt && + p_trk2.Pt() > m_BcToDplusMuMu_minPionPt && + p_trk3.Pt() > m_BcToDplusMuMu_minKaonPt && + isInMassRange(p_dimuon.M(), m_BcToDplusMuMu_dimuonMassRange) && + isInMassRange((p_trk1 + p_trk2 + p_trk3).M(), m_BcToDplusMuMu_DplusMassRange) && + isInMassRange((p_dimuon + p_trk1 + p_trk2 + p_trk3).M() - p_dimuon.M() + PDG::mJpsi, m_BcToDplusMuMu_massRange)) { + if (!vtx3 && makeFit_vtx3) { + vtx3 = fit(state.context(), trackParticleLinks_vtx3, kB_2mu3trk, *dimuonTriggerObjectEL); + makeFit_vtx3 = false; + } + if (vtx3 && vtx3->chiSquared() < m_BcToDplusMuMu_chi2) { + xAOD::TrigBphys* trigBphys = makeTriggerObject(*vtx3, xAOD::TrigBphys::BCDPMUMU, {PDG::mMuon, PDG::mMuon, PDG::mPion, PDG::mPion, PDG::mKaon}, dimuonTriggerObjectEL); + ATH_CHECK( state.addTriggerObject(trigBphys) ); + } + } + vtx3.reset(); + } } } } - mon_nBPhysObject = state.trigBphysCollection->size() - mon_nBPhysObject; + mon_nBPhysObject = state.trigBphysCollection().size() - mon_nBPhysObject; return StatusCode::SUCCESS; } @@ -550,13 +550,13 @@ StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) con StatusCode TrigBmumuxComboHypo::createDecisionObjects(TrigBmumuxState& state) const { - for (const xAOD::TrigBphys* triggerObject : *state.trigBphysCollection) { + for (const xAOD::TrigBphys* triggerObject : state.trigBphysCollection()) { // skip all dimuon trigger objects, they are already linked to the Bmumux trigger objects via lowerChainLink() if (triggerObject->particleType() == xAOD::TrigBphys::MULTIMU) continue; ATH_MSG_DEBUG( "Found xAOD::TrigBphys object: mass = " << triggerObject->mass() ); - auto triggerObjectEL = ElementLink<xAOD::TrigBphysContainer>(*state.trigBphysCollection, triggerObject->index()); + auto triggerObjectEL = ElementLink<xAOD::TrigBphysContainer>(state.trigBphysCollection(), triggerObject->index()); ATH_CHECK( triggerObjectEL.isValid() ); const xAOD::TrigBphys* dimuonTriggerObject = triggerObject->lowerChain(); @@ -576,7 +576,7 @@ StatusCode TrigBmumuxComboHypo::createDecisionObjects(TrigBmumuxState& state) co } // create a new output Decision object, backed by the 'decisions' container. - Decision* decision = TrigCompositeUtils::newDecisionIn(state.decisions, comboHypoAlgNodeName()); + Decision* decision = TrigCompositeUtils::newDecisionIn(&state.decisions(), TrigCompositeUtils::comboHypoAlgNodeName()); std::vector<const DecisionIDContainer*> previousDecisionIDs; for (const size_t& i : state.trigBphysMuonIndices.at(muonindex)) { @@ -600,10 +600,12 @@ StatusCode TrigBmumuxComboHypo::createDecisionObjects(TrigBmumuxState& state) co } -xAOD::Vertex* TrigBmumuxComboHypo::fit(const EventContext* context, - const std::vector<ElementLink<xAOD::TrackParticleContainer>>& trackParticleLinks, - Decay decay, - const xAOD::TrigBphys* dimuon) const { +std::unique_ptr<xAOD::Vertex> TrigBmumuxComboHypo::fit( + const EventContext& context, + const std::vector<ElementLink<xAOD::TrackParticleContainer>>& trackParticleLinks, + Decay decay, + const xAOD::TrigBphys* dimuon) const { + ATH_MSG_DEBUG( "Perform vertex fit" ); if (trackParticleLinks.size() < 2) { @@ -632,17 +634,17 @@ xAOD::Vertex* TrigBmumuxComboHypo::fit(const EventContext* context, } ATH_MSG_DEBUG( "Starting point: (" << startingPoint(0) << ", " << startingPoint(1) << ", " << startingPoint(2) << ")" ); - auto fitterState = m_vertexFitter->makeState(*context); + auto fitterState = m_vertexFitter->makeState(context); m_vertexFitter->setMassInputParticles(s_trkMass[static_cast<size_t>(decay)], *fitterState); - xAOD::Vertex* vertex = m_vertexFitter->fit(tracklist, startingPoint, *fitterState); + std::unique_ptr<xAOD::Vertex> vertex(m_vertexFitter->fit(tracklist, startingPoint, *fitterState)); if (!vertex) { ATH_MSG_DEBUG( "Vertex fit fails" ); - return nullptr; + return vertex; } if (vertex->chiSquared() > 150. || (decay == Decay::kPsi_2mu && vertex->chiSquared() > m_dimuon_chi2)) { ATH_MSG_DEBUG( "Fit is successful, but vertex chi2 is too high, we are not going to save it (chi2 = " << vertex->chiSquared() << ")" ); - delete vertex; - return nullptr; + vertex.reset(); + return vertex; } ATH_MSG_DEBUG( "Fit is successful" ); @@ -654,17 +656,18 @@ xAOD::Vertex* TrigBmumuxComboHypo::fit(const EventContext* context, } -xAOD::TrigBphys* TrigBmumuxComboHypo::makeTriggerObject(const xAOD::Vertex* vertex, - xAOD::TrigBphys::pType type, - const std::vector<double>& trkMass, - const ElementLink<xAOD::TrigBphysContainer>& dimuonLink) const { +xAOD::TrigBphys* TrigBmumuxComboHypo::makeTriggerObject( + const xAOD::Vertex& vertex, + xAOD::TrigBphys::pType type, + const std::vector<double>& trkMass, + const ElementLink<xAOD::TrigBphysContainer>& dimuonLink) const { // refitted track momentum as a 4-vector for mass hypothesis defined by the given decay value - GenVecFourMom_t momentum; - std::vector<GenVecFourMom_t> momenta; - if (!vertex->vxTrackAtVertexAvailable()) return nullptr; - for (size_t i = 0; i < vertex->vxTrackAtVertex().size(); ++i) { - const Trk::TrackParameters* perigee = vertex->vxTrackAtVertex()[i].perigeeAtVertex(); + xAOD::TrackParticle::GenVecFourMom_t momentum; + std::vector<xAOD::TrackParticle::GenVecFourMom_t> momenta; + if (!vertex.vxTrackAtVertexAvailable()) return nullptr; + for (size_t i = 0; i < vertex.vxTrackAtVertex().size(); ++i) { + const Trk::TrackParameters* perigee = vertex.vxTrackAtVertex()[i].perigeeAtVertex(); if (!perigee) return nullptr; const Amg::Vector3D& p = perigee->momentum(); momenta.emplace_back(p.x(), p.y(), p.z(), trkMass[i]); @@ -678,14 +681,14 @@ xAOD::TrigBphys* TrigBmumuxComboHypo::makeTriggerObject(const xAOD::Vertex* vert result->initialise(0, momentum.Eta(), momentum.Phi(), momentum.Pt(), type, mass, xAOD::TrigBphys::EF); result->setFitmass(momentum.M()); - result->setFitx(vertex->x()); - result->setFity(vertex->y()); - result->setFitz(vertex->z()); - result->setFitchi2(vertex->chiSquared()); - result->setFitndof(vertex->numberDoF()); + result->setFitx(vertex.x()); + result->setFity(vertex.y()); + result->setFitz(vertex.z()); + result->setFitchi2(vertex.chiSquared()); + result->setFitndof(vertex.numberDoF()); // set all the particles associated with the decay - result->setTrackParticleLinks(vertex->trackParticleLinks()); + result->setTrackParticleLinks(vertex.trackParticleLinks()); // use lowerChainLink() as a link to the preceding dimuon trigger object if (type != xAOD::TrigBphys::MULTIMU) { diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.h index 29f8f78ae63252ca3485e69368954094a706b797..710dcfd3c4035c06c914011b05c79a9f7422c99c 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.h +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.h @@ -44,10 +44,17 @@ typedef struct PDG20 PDG; */ class TrigBmumuxState: public ::ITrigBphysState { public: - const EventContext* context; - const TrigCompositeUtils::DecisionContainer* previousDecisions; - TrigCompositeUtils::DecisionContainer* decisions; - xAOD::TrigBphysContainer* trigBphysCollection; + TrigBmumuxState() = delete; + TrigBmumuxState(const EventContext& context, + const TrigCompositeUtils::DecisionContainer& previousDecisions, + TrigCompositeUtils::DecisionContainer& decisions, + xAOD::TrigBphysContainer* trigBphysCollection = nullptr, + const InDet::BeamSpotData* beamSpotData = nullptr) + : ITrigBphysState(context, previousDecisions, decisions, trigBphysCollection, beamSpotData) { + dimuons.setStore(&dimuonsStore); + } + virtual ~TrigBmumuxState() = default; + struct Muon { ElementLink<xAOD::MuonContainer> link; ElementLinkVector<TrigCompositeUtils::DecisionContainer> decisionLinks; @@ -62,7 +69,7 @@ class TrigBmumuxState: public ::ITrigBphysState { if (!triggerObject) { return StatusCode::FAILURE; } - trigBphysCollection->push_back(triggerObject); + trigBphysCollection().push_back(triggerObject); return StatusCode::SUCCESS; } }; @@ -85,26 +92,28 @@ class TrigBmumuxComboHypo: public ::ComboHypo { enum Decay : size_t { kPsi_2mu, // psi -> mu+ mu- kB_2mu1trk, // B -> mu+ mu- trk1 - kB_2mu2trk, // B -> mu+ mu- trk1 trk2 + kB_2mu2trk, // B -> mu+ mu- trk1 trk2 kB_2mu3trk // B -> mu+ mu- trk1 trk2 trk3 }; private: - std::unique_ptr<TrigBmumuxState> makeState(const EventContext* context, - const TrigCompositeUtils::DecisionContainer* previousDecisions, - TrigCompositeUtils::DecisionContainer* decisions, - xAOD::TrigBphysContainer* trigBphysCollection) const; - StatusCode mergeMuonsFromViews(TrigBmumuxState&) const; + StatusCode mergeMuonsFromDecisions(TrigBmumuxState&) const; StatusCode mergeTracksFromViews(TrigBmumuxState&) const; StatusCode findDimuonCandidates(TrigBmumuxState&) const; StatusCode findBmumuxCandidates(TrigBmumuxState&) const; StatusCode createDecisionObjects(TrigBmumuxState&) const; - xAOD::Vertex* fit(const EventContext* context, const std::vector<ElementLink<xAOD::TrackParticleContainer>>& tracklist, - Decay = kPsi_2mu, const xAOD::TrigBphys* dimuon = nullptr) const; - xAOD::TrigBphys* makeTriggerObject(const xAOD::Vertex*, - xAOD::TrigBphys::pType type = xAOD::TrigBphys::MULTIMU, - const std::vector<double>& trkMass = {PDG::mMuon, PDG::mMuon}, - const ElementLink<xAOD::TrigBphysContainer>& dimuonLink = ElementLink<xAOD::TrigBphysContainer>()) const; + + std::unique_ptr<xAOD::Vertex> fit( + const EventContext& context, + const std::vector<ElementLink<xAOD::TrackParticleContainer>>& trackParticleLinks, + Decay decay = kPsi_2mu, + const xAOD::TrigBphys* dimuon = nullptr) const; + + xAOD::TrigBphys* makeTriggerObject( + const xAOD::Vertex&, + xAOD::TrigBphys::pType type = xAOD::TrigBphys::MULTIMU, + const std::vector<double>& trkMass = {PDG::mMuon, PDG::mMuon}, + const ElementLink<xAOD::TrigBphysContainer>& dimuonLink = ElementLink<xAOD::TrigBphysContainer>()) const; bool isIdenticalTracks(const xAOD::TrackParticle* lhs, const xAOD::TrackParticle* rhs) const; bool isIdenticalTracks(const xAOD::Muon* lhs, const xAOD::Muon* rhs) const; @@ -206,25 +215,39 @@ class TrigBmumuxComboHypo: public ::ComboHypo { Gaudi::Property<float> m_LambdaBToMuMuProtonKaon_chi2 {this, "LambdaBToMuMuProtonKaon_chi2", 60., "maximum chi2 of the fitted Lambda_b0 vertex"}; - Gaudi::Property<bool> m_Bc_DsMuMuDecay {this, - "BcToDsMuMuPhiPi", true, "switch on/off B_c -> J/psi(-> mu+ mu-) phi Pi decay"}; - Gaudi::Property<double> m_Bc_DsMuMuKaon_minKaonPt {this, - "Bc_DsMuMuKaon_minKaonPt", 1000., "minimum pT of kaon track"}; - Gaudi::Property<std::pair<double, double>> m_rangePhiDs_MassCut {this, - "Bc_rangePhiDs_MassCut", {980., 1080.}, "range for mass of phi"}; - Gaudi::Property<std::pair<double, double>> m_rangeDs_MassCut {this, - "Bc_rangeDs_MassCut", { 1600., 2400.} , "range for mass of Ds"}; - Gaudi::Property<float> m_Bc_DsMuMu_chi2 {this, - "Bc_DsMuMu_chi2", 60., "maximum chi2 of the fitted Bc vertex"}; - - Gaudi::Property<bool> m_Bc_DpMuMuDecay {this, - "BcToDpMuMuPhiPi", true, "switch on/off B_c -> J/psi(-> mu+ mu-) phi Pi decay"}; - Gaudi::Property<double> m_Bc_DpMuMuKaon_minKaonPt {this, - "Bc_DpMuMuKaon_minKaonPt", 1000., "minimum pT of kaon track"}; - Gaudi::Property<std::pair<double, double>> m_rangeDp_MassCut {this, - "Bc_rangeDp_MassCut", { 1500., 2300.} , "range for mass of Ds"}; - Gaudi::Property<float> m_Bc_DpMuMu_chi2 {this, - "Bc_DpMuMu_chi2", 60., "maximum chi2 of the fitted Bc vertex"}; + // B_c+ -> J/psi(-> mu+ mu-) D_s+(->phi(-> K+ K-) pi+) + Gaudi::Property<bool> m_BcToDsMuMu {this, + "BcToDsMuMu", true, "switch on/off B_c+ -> J/psi(-> mu+ mu-) D_s+(->phi(-> K+ K-) pi+) decay"}; + Gaudi::Property<double> m_BcToDsMuMu_minKaonPt {this, + "BcToDsMuMu_minKaonPt", 1000., "minimum pT of kaon track from phi(1020)"}; + Gaudi::Property<double> m_BcToDsMuMu_minPionPt {this, + "BcToDsMuMu_minPionPt", 1000., "minimum pT of pion track from D_s+"}; + Gaudi::Property<std::pair<double, double>> m_BcToDsMuMu_massRange {this, + "BcToDsMuMu_massRange", {5500., 7300.}, "B_c+ mass range"}; + Gaudi::Property<std::pair<double, double>> m_BcToDsMuMu_dimuonMassRange {this, + "BcToDsMuMu_dimuonMassRange", {2500., 4300.}, "dimuon mass range for B_c+ -> J/psi D_s+ decay"}; + Gaudi::Property<std::pair<double, double>> m_BcToDsMuMu_phiMassRange {this, + "BcToDsMuMu_phiMassRange", {940., 1100.}, "phi(1020) mass range"}; + Gaudi::Property<std::pair<double, double>> m_BcToDsMuMu_DsMassRange {this, + "BcToDsMuMu_DsMassRange", {1850., 2100.}, "D_s+ mass range"}; + Gaudi::Property<float> m_BcToDsMuMu_chi2 {this, + "BcToDsMuMu_chi2", 60., "maximum chi2 of the fitted B_c+ vertex"}; + + // B_c+ -> J/psi(-> mu+ mu-) D+(-> K- pi+ pi+) + Gaudi::Property<bool> m_BcToDplusMuMu {this, + "BcToDplusMuMu", true, "switch on/off B_c+ -> J/psi(-> mu+ mu-) D+(-> K- pi+ pi+) decay"}; + Gaudi::Property<double> m_BcToDplusMuMu_minKaonPt {this, + "BcToDplusMuMu_minKaonPt", 1000., "minimum pT of kaon track from D+"}; + Gaudi::Property<double> m_BcToDplusMuMu_minPionPt {this, + "BcToDplusMuMu_minPionPt", 1000., "minimum pT of pion track from D+"}; + Gaudi::Property<std::pair<double, double>> m_BcToDplusMuMu_massRange {this, + "BcToDplusMuMu_massRange", {5500., 7300.}, "B_c+ mass range"}; + Gaudi::Property<std::pair<double, double>> m_BcToDplusMuMu_dimuonMassRange {this, + "BcToDplusMuMu_dimuonMassRange", {2500., 4300.}, "dimuon mass range for B_c+ -> J/psi D+ decay"}; + Gaudi::Property<std::pair<double, double>> m_BcToDplusMuMu_DplusMassRange {this, + "BcToDplusMuMu_DplusMassRange", {1750., 2000.}, "D+ mass range"}; + Gaudi::Property<float> m_BcToDplusMuMu_chi2 {this, + "BcToDplusMuMu_chi2", 60., "maximum chi2 of the fitted B_c+ vertex"}; // external tools @@ -240,7 +263,7 @@ class TrigBmumuxComboHypo: public ::ComboHypo { TrigCompositeUtils::DecisionIDContainer m_allowedIDs; const static std::vector<std::vector<double>> s_trkMass; - + }; #endif // TRIG_TrigBmumuxComboHypo_H diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.cxx index 1a4b08aac892e8bcb2111714974b1a1baaf42525..e46466eb247b29f3a5cad4537fb489be6ed09351 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.cxx +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.cxx @@ -15,6 +15,7 @@ #include <algorithm> #include <numeric> +#include "Constants.h" #include "TrigMultiTrkComboHypo.h" #include "xAODMuon/Muon.h" @@ -36,12 +37,16 @@ #include "Math/GenVector/VectorUtil.h" #include "Math/Vector2D.h" + using TrigCompositeUtils::Decision; using TrigCompositeUtils::DecisionContainer; using TrigCompositeUtils::DecisionID; using TrigCompositeUtils::DecisionIDContainer; using ROOT::Math::XYVector; +typedef struct PDG20 PDG; + + TrigMultiTrkComboHypo::TrigMultiTrkComboHypo(const std::string& name, ISvcLocator* pSvcLocator) : ::ComboHypo(name, pSvcLocator) {} @@ -52,27 +57,45 @@ StatusCode TrigMultiTrkComboHypo::initialize() { ATH_CHECK( ::ComboHypo::initialize() ); // check consistency of the properties - if (m_trkMass.size() != m_nTrk) { - ATH_MSG_ERROR( "Requested " << m_nTrk.value() << " tracks per vertex, but only provided " << m_trkMass.size() << " track masses"); - return StatusCode::FAILURE; + ATH_CHECK( !m_nTrk.empty() ); + + if (m_trkMass.empty()) { + ATH_MSG_INFO( "trackMasses value is not specified, muon/electron mass will be used" ); + for (const auto& n : m_nTrk) { + m_trkMass.value().emplace_back(std::vector<double>(n, (m_doElectrons ? PDG::mElectron : PDG::mMuon))); + } } - if (m_trkPt.size() != m_nTrk) { - ATH_MSG_ERROR( "Requested " << m_nTrk.value() << " tracks per vertex, but only provided " << m_trkPt.size() << " track pT thresholds"); - return StatusCode::FAILURE; + if (m_trkPt.empty()) { + ATH_MSG_INFO( "trackPtThresholds value is not specified" ); + for (const auto& n : m_nTrk) { + m_trkPt.value().emplace_back(std::vector<double>(n, -100.)); + } + } + m_trkPtMin = m_trkPt[0].back(); + for (size_t i = 0; i < m_trkPt.size(); ++i) { + m_trkPtMin = std::min(m_trkPtMin, m_trkPt[i].back()); + } + + ATH_CHECK( m_trkMass.size() == m_nTrk.size() ); + ATH_CHECK( m_trkPt.size() == m_nTrk.size() ); + + for (size_t i = 0; i < m_nTrk.size(); ++i) { + ATH_CHECK( m_trkMass[i].size() == m_nTrk[i] ); + ATH_CHECK( m_trkPt[i].size() == m_nTrk[i] ); + ATH_CHECK( std::is_sorted(m_trkPt[i].begin(), m_trkPt[i].end(), std::greater<>()) ); } for (const auto& range : m_massRange.value()) { - if (range.first > range.second) { - ATH_MSG_ERROR( "Mass range incorrect values: {Mi_min, Mi_max}" ); - return StatusCode::FAILURE; - } + ATH_CHECK( range.first < range.second ); } // dump numerical values if (msgLvl(MSG::DEBUG)) { - msg() << MSG::DEBUG << "vertex topology:" << std::endl; - for (size_t i = 0; i < m_nTrk; ++i) { - msg() << MSG::DEBUG << " " << i << " trk: mass = " << m_trkMass[i] << ", Pt > " << m_trkPt[i] << std::endl; + for (size_t i = 0; i < m_nTrk.size(); ++i) { + ATH_MSG_DEBUG( "vertex topology: nTrk = " << m_nTrk[i] ); + for (size_t j = 0; j < m_nTrk[i]; ++j) { + ATH_MSG_DEBUG( " " << j + 1 << " trk: mass = " << m_trkMass[i][j] << ", Pt > " << m_trkPt[i][j] ); + } } msg() << MSG::DEBUG << " mass range: {"; for (const auto& range : m_massRange.value()) { @@ -89,6 +112,8 @@ StatusCode TrigMultiTrkComboHypo::initialize() { return StatusCode::FAILURE; } + ATH_CHECK( !(m_trigLevel == "L2IO" && m_doElectrons) ); + if (m_trigLevel == "L2") { ATH_CHECK( m_trackParticleContainerKey.initialize() ); renounce(m_trackParticleContainerKey); @@ -123,6 +148,29 @@ StatusCode TrigMultiTrkComboHypo::initialize() { ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) ); } } + if (m_doElectrons) { + for (const DecisionID& id : m_allowedIDs) { + std::string name = HLT::Identifier(id).name(); + bool isMergedElectronChain = false; + for (size_t i = 0; i < m_mergedElectronChains.size(); ++i) { + if (name.find(m_mergedElectronChains.value().at(i)) != std::string::npos) { + isMergedElectronChain = true; + break; + } + } + (isMergedElectronChain ? m_mergedElectronIDs.insert(id) : m_resolvedElectronIDs.insert(id)); + } + if (msgLvl(MSG::DEBUG)) { + ATH_MSG_DEBUG( "Well-separated electron decisions:" ); + for (const DecisionID& id : m_resolvedElectronIDs) { + ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) ); + } + ATH_MSG_DEBUG( "Overlapping electron decisions:" ); + for (const DecisionID& id : m_mergedElectronIDs) { + ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) ); + } + } + } if (!m_monTool.empty()) { ATH_CHECK( m_monTool.retrieve() ); @@ -145,46 +193,56 @@ StatusCode TrigMultiTrkComboHypo::execute(const EventContext& context) const { CHECK( previousDecisionsHandle.isValid() ); ATH_MSG_DEBUG( "Running with "<< previousDecisionsHandle->size() << " previous decisions" ); - // create the mutable output DecisionContainer and register it to StoreGate SG::WriteHandle<DecisionContainer> outputDecisionsHandle = TrigCompositeUtils::createAndStore(decisionsOutput().at(0), context); - std::unique_ptr<TrigMultiTrkStateCand<xAOD::MuonContainer>> muonstate; - std::unique_ptr<TrigMultiTrkStateCand<xAOD::ElectronContainer>> electronstate; - TrigMultiTrkState* commonstate=nullptr; - if(m_doElectrons == true){ - electronstate = std::make_unique<TrigMultiTrkStateCand<xAOD::ElectronContainer>>(); - commonstate = electronstate.get(); - }else{ - muonstate = std::make_unique<TrigMultiTrkStateCand<xAOD::MuonContainer>>(); - commonstate = muonstate.get(); + const InDet::BeamSpotData* beamSpotData = nullptr; + if (!m_isStreamer) { + SG::ReadCondHandle<InDet::BeamSpotData> beamSpotHandle {m_beamSpotKey, context}; + ATH_CHECK( beamSpotHandle.isValid() ); + beamSpotData = *beamSpotHandle; + } + + std::unique_ptr<TrigMultiTrkState<xAOD::MuonContainer>> muonState; + std::unique_ptr<TrigMultiTrkState<xAOD::ElectronContainer>> electronState; + TrigMultiTrkStateBase* commonState = nullptr; + if (m_doElectrons) { + electronState = std::make_unique<TrigMultiTrkState<xAOD::ElectronContainer>>(context, *previousDecisionsHandle, *outputDecisionsHandle, nullptr, beamSpotData); + commonState = electronState.get(); + } + else { + muonState = std::make_unique<TrigMultiTrkState<xAOD::MuonContainer>>(context, *previousDecisionsHandle, *outputDecisionsHandle, nullptr, beamSpotData); + commonState = muonState.get(); } - FillState(commonstate, &context, previousDecisionsHandle.cptr(), outputDecisionsHandle.ptr(), nullptr); if (m_isStreamer) { if (m_trigLevel == "L2") { - ATH_CHECK( mergeTracksFromViews(*commonstate) ); + ATH_CHECK( mergeTracksFromViews(*commonState) ); } - else { - ATH_CHECK( mergeTracksFromDecisions(*commonstate) ); + else if (m_trigLevel == "L2IO") { + ATH_CHECK( mergeTracksFromDecisions<xAOD::L2CombinedMuonContainer>(*commonState) ); + } + else if (m_trigLevel == "EF") { + ATH_CHECK( (m_doElectrons ? mergeTracksFromDecisions<xAOD::ElectronContainer>(*commonState) : mergeTracksFromDecisions<xAOD::MuonContainer>(*commonState)) ); } - ATH_CHECK( filterTrackCombinations(*commonstate) ); - ATH_CHECK( copyDecisionObjects(*commonstate) ); + ATH_CHECK( filterTrackCombinations(*commonState) ); + ATH_CHECK( copyDecisionObjects(*commonState) ); } else { - auto trigBphysHandle = SG::makeHandle(m_trigBphysContainerKey, context); ATH_CHECK( trigBphysHandle.record(std::make_unique<xAOD::TrigBphysContainer>(), std::make_unique<xAOD::TrigBphysAuxContainer>()) ); - commonstate->trigBphysCollection = trigBphysHandle.ptr(); + commonState->setTrigBphysCollection(trigBphysHandle.ptr()); - if(m_doElectrons == true){ - ATH_CHECK( mergeFromDecisions(*electronstate) ); - ATH_CHECK( findDiTrackCandidates(*electronstate) ); - } else{ - ATH_CHECK( mergeFromDecisions(*muonstate) ); - ATH_CHECK( findDiTrackCandidates(*muonstate) ); + if (m_doElectrons) { + ATH_CHECK( mergeLeptonsFromDecisions(*electronState) ); + ATH_CHECK( findMultiLeptonCandidates(*electronState) ); + ATH_CHECK( processMergedElectrons(*electronState) ); } - ATH_CHECK( createDecisionObjects(*commonstate) ); + else { + ATH_CHECK( mergeLeptonsFromDecisions(*muonState) ); + ATH_CHECK( findMultiLeptonCandidates(*muonState) ); + } + ATH_CHECK( createDecisionObjects(*commonState) ); } ATH_MSG_DEBUG( "TrigMultiTrkHypo::execute() terminates with StatusCode::SUCCESS" ); @@ -192,38 +250,37 @@ StatusCode TrigMultiTrkComboHypo::execute(const EventContext& context) const { } -void TrigMultiTrkComboHypo::FillState(TrigMultiTrkState* state, - const EventContext* context, - const DecisionContainer* previousDecisions, - DecisionContainer* decisions, - xAOD::TrigBphysContainer* trigBphysCollection) const { - state->context = context; - state->previousDecisions = previousDecisions; - state->decisions = decisions; - state->trigBphysCollection = trigBphysCollection; -} - template<typename T> -StatusCode TrigMultiTrkComboHypo::mergeFromDecisions(TrigMultiTrkStateCand<T>& state) const { +StatusCode TrigMultiTrkComboHypo::mergeLeptonsFromDecisions(TrigMultiTrkState<T>& state) const { - auto& leptons = state.LegList; + auto& leptons = state.leptons(); leptons.clear(); - // all muons from views are already connected with previous decisions by TrigMuonEFHypoAlg - for (const Decision* decision : *state.previousDecisions) { - if (!TrigCompositeUtils::isAnyIDPassing(decision, m_allowedIDs)) continue; + // all muons/electrons from views are already connected with previous decisions by TrigMuonEFHypoAlg + for (const Decision* decision : state.previousDecisions()) { + if (!TrigCompositeUtils::isAnyIDPassing(decision, (m_doElectrons ? m_resolvedElectronIDs : m_allowedIDs))) continue; ATH_CHECK( decision->hasObjectLink(TrigCompositeUtils::featureString(), ClassID_traits<T>::ID()) ); auto leptonEL = decision->objectLink<T>(TrigCompositeUtils::featureString()); const auto lepton = *leptonEL; - if constexpr (std::is_same<T, xAOD::MuonContainer>::value){ - if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) continue; - if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) continue; - } else { - if (!lepton->trackParticle()) continue; - } - auto decisionEL = TrigCompositeUtils::decisionToElementLink(decision, *state.context); - auto itr = std::find_if(leptons.begin(), leptons.end(), [this, lepton = lepton](const auto& x){ return this->isIdenticalTracks(lepton, *x.link); }); + if constexpr(std::is_same<T, xAOD::MuonContainer>::value) { + if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) continue; + if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) continue; + } + else if constexpr(std::is_same<T, xAOD::ElectronContainer>::value) { + if (!lepton->trackParticle()) continue; + } + else { + ATH_MSG_ERROR( "mergeLeptonsFromDecisions(): no scenario for the provided CONTAINER is specified" ); + return StatusCode::FAILURE; + } + auto decisionEL = TrigCompositeUtils::decisionToElementLink(decision, state.context()); + + auto itr = leptons.end(); + if (m_applyOverlapRemoval) { + itr = std::find_if(leptons.begin(), leptons.end(), + [this, lepton = lepton](const auto& x){ return this->isIdenticalTracks(lepton, *x.link); }); + } if (itr == leptons.end()) { leptons.push_back({leptonEL, ElementLinkVector<DecisionContainer>(1, decisionEL), DecisionIDContainer()}); } @@ -248,59 +305,60 @@ StatusCode TrigMultiTrkComboHypo::mergeFromDecisions(TrigMultiTrkStateCand<T>& s for (const auto& item : leptons) { const auto lepton = *item.link; const xAOD::TrackParticle* track; - if constexpr (std::is_same<T, xAOD::MuonContainer>::value){ - track = *lepton->inDetTrackParticleLink(); - }else{ - track = *lepton->trackParticleLink(); + if constexpr(std::is_same<T, xAOD::MuonContainer>::value) { + track = lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle); + } + else { + track = lepton->trackParticle(); } ATH_MSG_DEBUG( " -- lepton InDetTrackParticle pt/eta/phi/q: " << track->pt() << " / " << track->eta() << " / " << track->phi() << " / " << track->charge() ); - ATH_MSG_DEBUG( " lepton pt (muon: CombinedTrackParticle): " << lepton->pt() ); + ATH_MSG_DEBUG( " lepton pt (muon: CombinedTrackParticle): " << lepton->pt() << " / " << lepton->eta() << " / " << lepton->phi() << " / " << lepton->charge() ); ATH_MSG_DEBUG( " allowed decisions:" ); for (const DecisionID& id : item.decisionIDs) { ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) ); } } } + return StatusCode::SUCCESS; } +StatusCode TrigMultiTrkComboHypo::mergeTracksFromViews(TrigMultiTrkStateBase& state) const { - -StatusCode TrigMultiTrkComboHypo::mergeTracksFromViews(TrigMultiTrkState& state) const { - - auto& tracks = state.tracks; + auto& tracks = state.tracks(); tracks.clear(); - size_t viewCounter = 0; - for (const Decision* decision : *state.previousDecisions) { + std::set<const SG::View*> views; + for (const Decision* decision : state.previousDecisions()) { if (!TrigCompositeUtils::isAnyIDPassing(decision, m_allowedIDs)) continue; auto viewLinkInfo = TrigCompositeUtils::findLink<ViewContainer>(decision, TrigCompositeUtils::viewString(), true); ATH_CHECK( viewLinkInfo.isValid() ); - auto viewEL = viewLinkInfo.link; + const SG::View* view = *viewLinkInfo.link; + if (views.find(view) != views.end()) continue; // tracks from this view have already been fetched - auto tracksHandle = ViewHelper::makeHandle(*viewEL, m_trackParticleContainerKey, *state.context); + auto tracksHandle = ViewHelper::makeHandle(view, m_trackParticleContainerKey, state.context()); ATH_CHECK( tracksHandle.isValid() ); ATH_MSG_DEBUG( "tracks handle " << m_trackParticleContainerKey << " size: " << tracksHandle->size() ); std::vector<ElementLink<xAOD::TrackParticleContainer>> tracksFromView; tracksFromView.reserve(tracksHandle->size()); for (size_t idx = 0; idx < tracksHandle->size(); ++idx) { - tracksFromView.emplace_back(ViewHelper::makeLink<xAOD::TrackParticleContainer>(*viewEL, tracksHandle, idx)); + tracksFromView.emplace_back(ViewHelper::makeLink<xAOD::TrackParticleContainer>(view, tracksHandle, idx)); } for (const auto& trackEL : tracksFromView) { const xAOD::TrackParticle* track = *trackEL; - if (track->definingParametersCovMatrixVec().empty() || track->pt() < m_trkPt.value().back()) continue; + if (track->definingParametersCovMatrixVec().empty() || track->pt() < m_trkPtMin) continue; - if (viewCounter == 0 || + if (views.empty() || !m_applyOverlapRemoval || std::find_if(tracks.begin(), tracks.end(), [this,track = track](const auto& x){ return isIdenticalTracks(track, *x); }) == tracks.end()) { tracks.emplace_back(trackEL); } } - viewCounter++; + views.insert(view); } std::sort(tracks.begin(), tracks.end(), [](const auto& lhs, const auto& rhs){ return ((*lhs)->pt() > (*rhs)->pt()); }); @@ -311,41 +369,47 @@ StatusCode TrigMultiTrkComboHypo::mergeTracksFromViews(TrigMultiTrkState& state) ATH_MSG_DEBUG( " -- track pt/eta/phi/q: " << track->pt() << " / " << track->eta() << " / " << track->phi() << " / " << track->charge() ); } } + return StatusCode::SUCCESS; } -StatusCode TrigMultiTrkComboHypo::mergeTracksFromDecisions(TrigMultiTrkState& state) const { +template<typename CONTAINER> +StatusCode TrigMultiTrkComboHypo::mergeTracksFromDecisions(TrigMultiTrkStateBase& state) const { - auto& tracks = state.tracks; + auto& tracks = state.tracks(); tracks.clear(); - // all muons from views are already connected with previous decisions by TrigMuonEFHypoAlg - for (const Decision* decision : *state.previousDecisions) { + // all muons/electrons from views are already connected with previous decisions by TrigMuonEFHypoAlg or by TrigEgammaPrecisionElectronHypoAlg + for (const Decision* decision : state.previousDecisions()) { if (!TrigCompositeUtils::isAnyIDPassing(decision, m_allowedIDs)) continue; + ATH_CHECK( decision->hasObjectLink(TrigCompositeUtils::featureString(), ClassID_traits<CONTAINER>::ID()) ); + auto leptonEL = decision->objectLink<CONTAINER>(TrigCompositeUtils::featureString()); + const auto lepton = *leptonEL; + ElementLink<xAOD::TrackParticleContainer> trackEL; - if (m_trigLevel == "EF") { - ATH_CHECK( decision->hasObjectLink(TrigCompositeUtils::featureString(), ClassID_traits<xAOD::MuonContainer>::ID()) ); - auto muonEL = decision->objectLink<xAOD::MuonContainer>(TrigCompositeUtils::featureString()); - const xAOD::Muon* muon = *muonEL; - if (!muon->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) continue; - if (!muon->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) continue; - trackEL = muon->inDetTrackParticleLink(); + if constexpr(std::is_same<CONTAINER, xAOD::MuonContainer>::value) { + if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) continue; + if (!lepton->trackParticle(xAOD::Muon::TrackParticleType::InnerDetectorTrackParticle)) continue; + trackEL = lepton->inDetTrackParticleLink(); } - else if (m_trigLevel == "L2IO") { - ATH_CHECK( decision->hasObjectLink(TrigCompositeUtils::featureString(), ClassID_traits<xAOD::L2CombinedMuonContainer>::ID()) ); - auto muonEL = decision->objectLink<xAOD::L2CombinedMuonContainer>(TrigCompositeUtils::featureString()); - const xAOD::L2CombinedMuon* muon = *muonEL; - trackEL = muon->idTrackLink(); + else if constexpr(std::is_same<CONTAINER, xAOD::L2CombinedMuonContainer>::value) { + if (!lepton->idTrack()) continue; + trackEL = lepton->idTrackLink(); + } + else if constexpr(std::is_same<CONTAINER, xAOD::ElectronContainer>::value) { + if (!lepton->trackParticle()) continue; + trackEL = lepton->trackParticleLink(); } else { - ATH_MSG_ERROR( "mergeTracksFromDecisions() expects trigLevel to be L2IO or EF, but " << m_trigLevel << " provided" ); + ATH_MSG_ERROR( "mergeTracksFromDecisions(): no scenario for the provided CONTAINER is specified" ); return StatusCode::FAILURE; } - if (!trackEL.isValid()) continue; - if (std::find_if(tracks.begin(), tracks.end(), + if (!trackEL.isValid()) continue; + if (!m_applyOverlapRemoval || + std::find_if(tracks.begin(), tracks.end(), [this, track = *trackEL](const auto& x){ return isIdenticalTracks(track, *x); }) == tracks.end()) { tracks.emplace_back(trackEL); } @@ -359,14 +423,15 @@ StatusCode TrigMultiTrkComboHypo::mergeTracksFromDecisions(TrigMultiTrkState& st ATH_MSG_DEBUG( " -- track pt/eta/phi/q: " << track->pt() << " / " << track->eta() << " / " << track->phi() << " / " << track->charge() ); } } + return StatusCode::SUCCESS; } -StatusCode TrigMultiTrkComboHypo::filterTrackCombinations(TrigMultiTrkState& state) const { +StatusCode TrigMultiTrkComboHypo::filterTrackCombinations(TrigMultiTrkStateBase& state) const { - state.isEventAccepted = 0; - auto& tracks = state.tracks; + const auto& tracks = state.tracks(); + state.setEventAccepted(false); // monitored variables auto mon_nAcceptedTrk = Monitored::Scalar<int>("nAcceptedTrk", tracks.size()); @@ -378,86 +443,84 @@ StatusCode TrigMultiTrkComboHypo::filterTrackCombinations(TrigMultiTrkState& sta mon_nAcceptedTrk, mon_nVertexFitterCalls, mon_isEventAccepted, mon_timer); - // combination is a selection of items from a collection, such that the order of selection does not matter - // tracks have already been sorted over pT, the algorithm will keep this order in created subsets of tracks, i.e. - // for m_nTrk = 2 combinations are [0, 1], [0, 2], ..., [1, 2], [1, 3], ... - // for m_nTrk = 3 combinations are [0, 1, 2], [0, 1, 3], ..., [0, 2, 3], [0, 2, 4], ..., [1, 2, 3], [1, 2, 4], ... + for (size_t iTrk = 0; iTrk < m_nTrk.size(); ++iTrk) { + if (state.isEventAccepted()) break; + size_t nTrk = m_nTrk[iTrk]; - if (tracks.size() < m_nTrk) { - ATH_MSG_DEBUG( "Could not build a subset of " << m_nTrk.value() << " tracks from collection which contains only " << tracks.size() << " objects" ); - return StatusCode::SUCCESS; - } - ATH_MSG_DEBUG( "Consider combinations of " << m_nTrk.value() << " tracks from collection which contains " << tracks.size() << " objects until find a good one" ); - - std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(m_nTrk); - std::vector<xAOD::TrackParticle::GenVecFourMom_t> p(m_nTrk); - - // tracks from the current combination will have non-zero value against their position in the 'idx' vector - // consider first m_nTrk tracks as an initial combination, then get the next one with std::prev_permutation() - std::vector<char> idx(tracks.size(), 0); - std::fill(idx.begin(), idx.begin() + m_nTrk, 1); - do { - // fill tracklist and momenta of tracks, also check that the track pT passes the threshold value - bool isValidCombination = true; - size_t j = 0; - for (size_t i = 0; i < idx.size(); ++i) { - if (!idx[i]) continue; - const auto& trackEL = tracks[i]; - tracklist[j] = trackEL; - p[j] = (*trackEL)->genvecP4(); - p[j].SetM(m_trkMass[j]); - if (p[j].Pt() < m_trkPt[j]) { - isValidCombination = false; - break; - } - ++j; + if (tracks.size() < nTrk) { + ATH_MSG_DEBUG( "Could not build a subset of " << nTrk << " tracks from collection which contains only " << tracks.size() << " objects" ); + continue; } - if (!isValidCombination) continue; - - if (msgLvl(MSG::DEBUG)) { - ATH_MSG_DEBUG( "Dump found tracks before vertex fit: pT / eta / phi / charge" ); - for (size_t i = 0; i < tracklist.size(); ++i) { - const auto track = *tracklist[i]; - ATH_MSG_DEBUG( "track " << i + 1 << ": " << p[i].Pt() << " / " << p[i].Eta() << " / " << p[i].Phi() << " / " << track->charge() ); + ATH_MSG_DEBUG( "Consider combinations of " << nTrk << " tracks from collection which contains " << tracks.size() << " objects until find a good one" ); + + std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(nTrk); + std::vector<xAOD::TrackParticle::GenVecFourMom_t> p(nTrk); + + // tracks from the current combination will have non-zero value against their position in the 'idx' vector + // consider first nTrk tracks as an initial combination, then get the next one with std::prev_permutation() + std::vector<char> idx(tracks.size(), 0); + std::fill(idx.begin(), idx.begin() + nTrk, 1); + do { + // fill tracklist and momenta of tracks, also check that the track pT passes the threshold value + bool isValidCombination = true; + size_t j = 0; + for (size_t i = 0; i < idx.size(); ++i) { + if (!idx[i]) continue; + const auto& trackEL = tracks[i]; + tracklist[j] = trackEL; + p[j] = (*trackEL)->genvecP4(); + p[j].SetM(m_trkMass[iTrk][j]); + if (p[j].Pt() < m_trkPt[iTrk][j]) { + isValidCombination = false; + break; + } + ++j; + } + if (!isValidCombination) continue; + + if (msgLvl(MSG::DEBUG)) { + ATH_MSG_DEBUG( "Dump found tracks before vertex fit: pT / eta / phi / charge" ); + for (size_t i = 0; i < tracklist.size(); ++i) { + const auto track = *tracklist[i]; + ATH_MSG_DEBUG( "track " << i + 1 << ": " << p[i].Pt() << " / " << p[i].Eta() << " / " << p[i].Phi() << " / " << track->charge() ); + } } - } - auto mass = (std::accumulate(p.begin(), p.end(), xAOD::TrackParticle::GenVecFourMom_t())).M(); - ATH_MSG_DEBUG( "invariant mass: " << mass ); + auto mass = (std::accumulate(p.begin(), p.end(), xAOD::TrackParticle::GenVecFourMom_t())).M(); + ATH_MSG_DEBUG( "invariant mass: " << mass ); - if (!isInMassRange(mass)) continue; + if (!isInMassRange(mass)) continue; - auto fitterState = m_vertexFitter->makeState(*state.context); - std::unique_ptr<xAOD::Vertex> vertex(fit(tracklist, fitterState.get())); - ++mon_nVertexFitterCalls; - if (!vertex) continue; + auto fitterState = m_vertexFitter->makeState(state.context()); + auto vertex = fit(tracklist, m_trkMass[iTrk], *fitterState); + ++mon_nVertexFitterCalls; + if (!vertex) continue; - ATH_MSG_DEBUG( "Filter found a subset of tracks which passed the rough selection: stop looking for other combinations" ); - state.isEventAccepted = 1; - break; + ATH_MSG_DEBUG( "Filter found a subset of tracks which passed the rough selection: stop looking for other combinations" ); + state.setEventAccepted(true); + break; - } while (std::prev_permutation(idx.begin(), idx.end())); + } while (std::prev_permutation(idx.begin(), idx.end())); + } - if (!state.isEventAccepted) { + if (!state.isEventAccepted()) { ATH_MSG_DEBUG( "Filter could not find a good subset of tracks" ); } - mon_isEventAccepted = state.isEventAccepted; + mon_isEventAccepted = state.isEventAccepted(); return StatusCode::SUCCESS; } -template<typename T> -StatusCode TrigMultiTrkComboHypo::findDiTrackCandidates(TrigMultiTrkStateCand<T>& state) const { - state.trigBphysIndices.clear(); - auto& legs = state.LegList; +template<typename T> +StatusCode TrigMultiTrkComboHypo::findMultiLeptonCandidates(TrigMultiTrkState<T>& state) const { - SG::ReadCondHandle<InDet::BeamSpotData> beamSpotHandle {m_beamSpotKey, *state.context}; - ATH_CHECK( beamSpotHandle.isValid() ); + state.trigBphysLegIndices().clear(); + const auto& leptons = state.leptons(); // monitored variables - auto mon_nAcceptedTrk = Monitored::Scalar<int>("nAcceptedTrk", legs.size()); + auto mon_nAcceptedTrk = Monitored::Scalar<int>("nAcceptedTrk", leptons.size()); auto mon_nCombination = Monitored::Scalar<int>("nCombination", 0); auto mon_nCombinationBeforeFit = Monitored::Scalar<int>("nCombinationBeforeFit", 0); auto mon_nBPhysObject = Monitored::Scalar<int>("nBPhysObject", 0); @@ -469,9 +532,9 @@ StatusCode TrigMultiTrkComboHypo::findDiTrackCandidates(TrigMultiTrkStateCand<T> std::vector<float> etatrack1, etatrack2; std::vector<int> bphysCharge; auto mon_trkMassBeforeFit = Monitored::Collection("trkMassBeforeFit", trkMassBeforeFit); - auto mon_bphysChi2 = Monitored::Collection("bphysChi2", *state.trigBphysCollection, &xAOD::TrigBphys::fitchi2); - auto mon_bphysLxy = Monitored::Collection("bphysLxy", *state.trigBphysCollection, &xAOD::TrigBphys::lxy); - auto mon_bphysFitMass = Monitored::Collection("bphysFitMass", *state.trigBphysCollection, [](const xAOD::TrigBphys* x){ return x->fitmass()*0.001; }); + auto mon_bphysChi2 = Monitored::Collection("bphysChi2", state.trigBphysCollection(), &xAOD::TrigBphys::fitchi2); + auto mon_bphysLxy = Monitored::Collection("bphysLxy", state.trigBphysCollection(), &xAOD::TrigBphys::lxy); + auto mon_bphysFitMass = Monitored::Collection("bphysFitMass", state.trigBphysCollection(), [](const xAOD::TrigBphys* x){ return x->fitmass()*0.001; }); auto mon_bphysMass = Monitored::Collection("bphysMass", bphysMass); auto mon_d0track1 = Monitored::Collection("bphysd0_trk1", d0track1); auto mon_d0track2 = Monitored::Collection("bphysd0_trk2", d0track2); @@ -488,90 +551,151 @@ StatusCode TrigMultiTrkComboHypo::findDiTrackCandidates(TrigMultiTrkStateCand<T> mon_trkMassBeforeFit, mon_bphysChi2, mon_bphysLxy, mon_bphysFitMass, mon_bphysMass, mon_bphysCharge, mon_d0track1, mon_d0track2, mon_timer, mon_pttrack1, mon_pttrack2, mon_etatrack1, mon_etatrack2); - if (legs.size() < m_nTrk) { - ATH_MSG_DEBUG( "Could not build a subset of " << m_nTrk.value() << " legs from collection which contains only " << legs.size() << " objects" ); - return StatusCode::SUCCESS; - } - ATH_MSG_DEBUG( "Consider all combinations of " << m_nTrk.value() << "legs from collection which contains " << legs.size() << " objects" ); - - std::vector<size_t> leptonIndices(m_nTrk); - std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(m_nTrk); - std::vector<xAOD::TrackParticle::GenVecFourMom_t> p(m_nTrk); - - std::vector<char> leptonTags(legs.size(), 0); - std::fill(leptonTags.begin(), leptonTags.begin() + m_nTrk, 1); - do { - // fill tracklist and momenta of muons in the current subset - bool isValidCombination = true; - int charge = 0; - size_t j = 0; - for (size_t i = 0; i < leptonTags.size(); ++i) { - if (!leptonTags[i]) continue; - leptonIndices[j] = i; - auto leg = *legs[i].link; - charge += static_cast<int>(lround(leg->charge())); - ElementLink<xAOD::TrackParticleContainer> trackEL; - if constexpr(std::is_same<T, xAOD::MuonContainer>::value){ - trackEL = leg->inDetTrackParticleLink(); - } else{ - trackEL = leg->trackParticleLink(); + for (size_t iTrk = 0; iTrk < m_nTrk.size(); ++iTrk) { + size_t nTrk = m_nTrk[iTrk]; + + if (leptons.size() < nTrk) { + ATH_MSG_DEBUG( "Could not build a subset of " << nTrk << " legs from collection which contains only " << leptons.size() << " objects" ); + continue; + } + ATH_MSG_DEBUG( "Consider all combinations of " << nTrk << " legs from collection which contains " << leptons.size() << " objects" ); + + std::vector<size_t> leptonIndices(nTrk); + std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(nTrk); + std::vector<xAOD::TrackParticle::GenVecFourMom_t> p(nTrk); + + std::vector<char> combination(leptons.size(), 0); + std::fill(combination.begin(), combination.begin() + nTrk, 1); + do { + // fill tracklist and momenta of muons in the current subset + bool isValidCombination = true; + int charge = 0; + size_t j = 0; + for (size_t i = 0; i < combination.size(); ++i) { + if (!combination[i]) continue; + leptonIndices[j] = i; + auto leg = *leptons[i].link; + charge += static_cast<int>(lround(leg->charge())); + ElementLink<xAOD::TrackParticleContainer> trackEL; + if constexpr(std::is_same<T, xAOD::MuonContainer>::value) { + trackEL = leg->inDetTrackParticleLink(); + } + else { + trackEL = leg->trackParticleLink(); + } + tracklist[j] = trackEL; + p[j] = (*trackEL)->genvecP4(); + p[j].SetM(m_trkMass[iTrk][j]); + if (p[j].Pt() < m_trkPt[iTrk][j]) { + isValidCombination = false; + break; + } + ++j; } - tracklist[j] = trackEL; - p[j] = (*trackEL)->genvecP4(); - p[j].SetM(m_trkMass[j]); - if (p[j].Pt() < m_trkPt[j]) { - isValidCombination = false; - break; + if (!isValidCombination) continue; + + if (msgLvl(MSG::DEBUG)) { + ATH_MSG_DEBUG( "Dump found leptons before vertex fit: pT / eta / phi / charge" ); + for (size_t i = 0; i < tracklist.size(); ++i) { + const auto track = *tracklist[i]; + ATH_MSG_DEBUG( "legs " << i + 1 << ": " << p[i].Pt() << " / " << p[i].Eta() << " / " << p[i].Phi() << " / " << track->charge() ); + } } - ++j; - } - if (!isValidCombination) continue; - if (msgLvl(MSG::DEBUG)) { - ATH_MSG_DEBUG( "Dump found leptons before vertex fit: pT / eta / phi / charge" ); - for (size_t i = 0; i < tracklist.size(); ++i) { - const auto track = *tracklist[i]; - ATH_MSG_DEBUG( "legs " << i + 1 << ": " << p[i].Pt() << " / " << p[i].Eta() << " / " << p[i].Phi() << " / " << track->charge() ); - } - } + auto mass = (std::accumulate(p.begin(), p.end(), xAOD::TrackParticle::GenVecFourMom_t())).M(); + ATH_MSG_DEBUG( "invariant mass: " << mass ); - auto mass = (std::accumulate(p.begin(), p.end(), xAOD::TrackParticle::GenVecFourMom_t())).M(); - ATH_MSG_DEBUG( "invariant mass: " << mass ); + mon_nCombination++; + trkMassBeforeFit.push_back(mass * 0.001); + if (!isInMassRange(mass)) continue; - mon_nCombination++; - trkMassBeforeFit.push_back(mass * 0.001); - if (!isInMassRange(mass)) continue; + mon_nCombinationBeforeFit++; + auto fitterState = m_vertexFitter->makeState(state.context()); + auto vertex = fit(tracklist, m_trkMass[iTrk], *fitterState); + if (!vertex) continue; + xAOD::TrigBphys* trigBphys = makeTrigBPhys(*vertex, m_trkMass[iTrk], state.beamSpotPosition(), *fitterState); + state.addTrigBphysObject(trigBphys, leptonIndices); - mon_nCombinationBeforeFit++; - auto fitterState = m_vertexFitter->makeState(*state.context); - std::unique_ptr<xAOD::Vertex> vertex(fit(tracklist, fitterState.get())); - if (!vertex) continue; - xAOD::TrigBphys* trigBphys = makeTrigBPhys(vertex.get(), fitterState.get(), beamSpotHandle->beamPos()); - state.trigBphysCollection->push_back(trigBphys); - state.trigBphysIndices.push_back(leptonIndices); + mon_nBPhysObject++; + bphysMass.push_back(mass * 0.001); + bphysCharge.push_back(charge); + d0track1.push_back((*tracklist[0])->d0()); + d0track2.push_back((*tracklist[1])->d0()); + pttrack1.push_back((*tracklist[0])->pt() * 0.001); + pttrack2.push_back((*tracklist[1])->pt() * 0.001); + etatrack1.push_back((*tracklist[0])->eta()); + etatrack2.push_back((*tracklist[1])->eta()); + + } while (std::prev_permutation(combination.begin(), combination.end())); + } + return StatusCode::SUCCESS; +} - mon_nBPhysObject++; - bphysMass.push_back(mass * 0.001); - bphysCharge.push_back(charge); - d0track1.push_back((*tracklist[0])->d0()); - d0track2.push_back((*tracklist[1])->d0()); - pttrack1.push_back((*tracklist[0])->pt() * 0.001); - pttrack2.push_back((*tracklist[1])->pt() * 0.001); - etatrack1.push_back((*tracklist[0])->eta()); - etatrack2.push_back((*tracklist[1])->eta()); - } while (std::prev_permutation(leptonTags.begin(), leptonTags.end())); +StatusCode TrigMultiTrkComboHypo::processMergedElectrons(TrigMultiTrkState<xAOD::ElectronContainer>& state) const { + + ATH_MSG_DEBUG( "Try to find electrons originating from the same EM cluster" ); + + // some electrons can be already attached to the list, add electrons from BPH-0DR3-EM7J15 clusters to the end + auto& leptons = state.leptons(); + + if (m_mergedElectronIDs.empty()) { + ATH_MSG_DEBUG( "no chains similar to BPH-0DR3-EM7J15 have been requested, should not look for close-by electrons" ); + return StatusCode::SUCCESS; + } + + const std::vector<double> particleMasses(2, PDG::mElectron); + + for (const Decision* decision : state.previousDecisions()) { + if (!TrigCompositeUtils::isAnyIDPassing(decision, m_mergedElectronIDs)) continue; + + auto decisionEL = TrigCompositeUtils::decisionToElementLink(decision, state.context()); + ATH_CHECK( decision->hasObjectLink(TrigCompositeUtils::featureString(), ClassID_traits<xAOD::ElectronContainer>::ID()) ); + auto electronEL = decision->objectLink<xAOD::ElectronContainer>(TrigCompositeUtils::featureString()); + const auto electron = *electronEL; + if (!electron->trackParticle()) continue; + + // get all electrons from the same SG::View, i.e. from the same initialRoI + const auto electronContainer = electronEL.getStorableObjectPointer(); + if (electronContainer->size() <= 1) continue; + + // add electron from decision to state.leptons + DecisionIDContainer decisionIDs; + TrigCompositeUtils::decisionIDs(decision, decisionIDs); + leptons.push_back({electronEL, ElementLinkVector<DecisionContainer>(1, decisionEL), decisionIDs}); + + // get initialRoI this electron originating from + auto roiInfo = TrigCompositeUtils::findLink<TrigRoiDescriptorCollection>(decision, TrigCompositeUtils::initialRoIString(), true); + ATH_CHECK( roiInfo.isValid() ); + auto initialRoI = *roiInfo.link; + + // try to build di-electron pairs: first electron is always from decision, second from the same SG::View + std::vector<ElementLink<xAOD::TrackParticleContainer>> tracklist(2); + tracklist[0] = electron->trackParticleLink(); + for (size_t i = 0; i < electronContainer->size(); ++i) { + const auto electronFromView = electronContainer->at(i); + if (electronFromView == electron) continue; + if (!electronFromView->trackParticle()) continue; + tracklist[1] = electronFromView->trackParticleLink(); + + auto fitterState = m_vertexFitter->makeState(state.context()); + auto vertex = fit(tracklist, particleMasses, *fitterState); + if (!vertex) continue; + xAOD::TrigBphys* trigBphys = makeTrigBPhys(*vertex, particleMasses, state.beamSpotPosition(), *fitterState); + trigBphys->setRoiId(initialRoI->roiWord()); + state.addTrigBphysObject(trigBphys, std::vector<size_t>(1, leptons.size() - 1)); + } + } return StatusCode::SUCCESS; } +StatusCode TrigMultiTrkComboHypo::copyDecisionObjects(TrigMultiTrkStateBase& state) const { -StatusCode TrigMultiTrkComboHypo::copyDecisionObjects(TrigMultiTrkState& state) const { - - if (state.isEventAccepted) { + if (state.isEventAccepted()) { ATH_MSG_DEBUG( "Copying decisions from " << decisionsInput().at(0).key() << " to " << decisionsOutput().at(0).key() ); - for (const Decision* previousDecision : *state.previousDecisions) { + for (const Decision* previousDecision : state.previousDecisions()) { if (!TrigCompositeUtils::isAnyIDPassing(previousDecision, m_allowedIDs)) continue; DecisionIDContainer previousDecisionIDs; @@ -580,39 +704,42 @@ StatusCode TrigMultiTrkComboHypo::copyDecisionObjects(TrigMultiTrkState& state) std::set_intersection(previousDecisionIDs.begin(), previousDecisionIDs.end(), m_allowedIDs.begin(), m_allowedIDs.end(), std::inserter(decisionIDs, decisionIDs.begin())); - Decision* decision = TrigCompositeUtils::newDecisionIn(state.decisions, TrigCompositeUtils::comboHypoAlgNodeName()); - TrigCompositeUtils::linkToPrevious(decision, previousDecision, *state.context); + Decision* decision = TrigCompositeUtils::newDecisionIn(&state.decisions(), TrigCompositeUtils::comboHypoAlgNodeName()); + TrigCompositeUtils::linkToPrevious(decision, previousDecision, state.context()); TrigCompositeUtils::insertDecisionIDs(decisionIDs, decision); } - } + // copy additional decisions for combined chains, as 'HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_mu4_L1BPH-0M9-EM7-EM5_MU6' + ATH_CHECK( copyAdditionalDecisionObjects(state) ); + } return StatusCode::SUCCESS; } -StatusCode TrigMultiTrkComboHypo::createDecisionObjects(TrigMultiTrkState& state) const { +StatusCode TrigMultiTrkComboHypo::createDecisionObjects(TrigMultiTrkStateBase& state) const { size_t idx = 0; - for (const xAOD::TrigBphys* triggerObject : *state.trigBphysCollection) { + for (const xAOD::TrigBphys* triggerObject : state.trigBphysCollection()) { ATH_MSG_DEBUG( "Found xAOD::TrigBphys: mass / chi2 = " << triggerObject->mass() << " / " << triggerObject->fitchi2() ); - auto triggerObjectEL = ElementLink<xAOD::TrigBphysContainer>(*state.trigBphysCollection, triggerObject->index()); + auto triggerObjectEL = ElementLink<xAOD::TrigBphysContainer>(state.trigBphysCollection(), triggerObject->index()); ATH_CHECK( triggerObjectEL.isValid() ); // create a new output Decision object, backed by the 'decisions' container. - Decision* decision = TrigCompositeUtils::newDecisionIn(state.decisions, TrigCompositeUtils::comboHypoAlgNodeName()); + Decision* decision = TrigCompositeUtils::newDecisionIn(&state.decisions(), TrigCompositeUtils::comboHypoAlgNodeName()); std::vector<const DecisionIDContainer*> previousDecisionIDs; - for (const size_t& i : state.trigBphysIndices[idx]) { + for (const size_t& i : state.getTrigBphysLegIndices(idx)) { - // attach all previous decisions: if the same previous decision is called twice, that's fine - internally takes care of that - // we already have an array of links to the previous decisions, so there is no need to use TrigCompositeUtils::linkToPrevious() - decision->addObjectCollectionLinks(TrigCompositeUtils::seedString(), state.getDecisionLinks(i)); - previousDecisionIDs.push_back(&state.getDecisionID(i)); + // attach all previous decisions: if the same previous decision is called twice, that's fine - internally takes care of that + // we already have an array of links to the previous decisions, so there is no need to use TrigCompositeUtils::linkToPrevious() + decision->addObjectCollectionLinks(TrigCompositeUtils::seedString(), state.getDecisionLinks(i)); + previousDecisionIDs.push_back(&state.getDecisionIDs(i)); } // set mandatory feature ElementLink to xAOD::TrigBphys object decision->setObjectLink<xAOD::TrigBphysContainer>(TrigCompositeUtils::featureString(), triggerObjectEL); + decision->setDetail<int32_t>("noCombo", 1); for (const auto& tool : hypoTools()) { ATH_MSG_DEBUG( "Go to " << tool ); @@ -621,12 +748,46 @@ StatusCode TrigMultiTrkComboHypo::createDecisionObjects(TrigMultiTrkState& state ++idx; } + // copy additional decisions from Empty muon step for Combined chains, as 'HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_mu4_L1BPH-0M9-EM7-EM5_MU6' + ATH_CHECK( copyAdditionalDecisionObjects(state) ); + + return StatusCode::SUCCESS; +} + + +StatusCode TrigMultiTrkComboHypo::copyAdditionalDecisionObjects(TrigMultiTrkStateBase& state) const { + + if (decisionsInput().size() > 1) { + ATH_MSG_DEBUG( "Found more than one decision input key, decisionsInput().size = " << decisionsInput().size() ); + for (size_t i = 1; i < decisionsInput().size(); ++i) { + ATH_MSG_DEBUG( "Copying decisions from " << decisionsInput().at(i).key() << " to " << decisionsOutput().at(i).key() ); + auto previousDecisionsHandle = SG::makeHandle(decisionsInput().at(i), state.context()); + CHECK( previousDecisionsHandle.isValid() ); + ATH_MSG_DEBUG( "Running with "<< previousDecisionsHandle->size() << " previous decisions" ); + SG::WriteHandle<DecisionContainer> outputDecisionsHandle = TrigCompositeUtils::createAndStore(decisionsOutput().at(i), state.context()); + for (const Decision* previousDecision : *previousDecisionsHandle) { + if (!TrigCompositeUtils::isAnyIDPassing(previousDecision, m_allowedIDs)) continue; + + DecisionIDContainer previousDecisionIDs; + TrigCompositeUtils::decisionIDs(previousDecision, previousDecisionIDs); + DecisionIDContainer decisionIDs; + std::set_intersection(previousDecisionIDs.begin(), previousDecisionIDs.end(), m_allowedIDs.begin(), m_allowedIDs.end(), + std::inserter(decisionIDs, decisionIDs.begin())); + + Decision* decision = TrigCompositeUtils::newDecisionIn(outputDecisionsHandle.ptr(), TrigCompositeUtils::comboHypoAlgNodeName()); + TrigCompositeUtils::linkToPrevious(decision, previousDecision, state.context()); + TrigCompositeUtils::insertDecisionIDs(decisionIDs, decision); + } + } + } return StatusCode::SUCCESS; } -xAOD::Vertex* TrigMultiTrkComboHypo::fit(const std::vector<ElementLink<xAOD::TrackParticleContainer>>& trackParticleLinks, - Trk::IVKalState* fitterState) const { +std::unique_ptr<xAOD::Vertex> TrigMultiTrkComboHypo::fit( + const std::vector<ElementLink<xAOD::TrackParticleContainer>>& trackParticleLinks, + const std::vector<double>& particleMasses, + Trk::IVKalState& fitterState) const { ATH_MSG_DEBUG( "Perform vertex fit" ); std::vector<const xAOD::TrackParticle*> tracklist(trackParticleLinks.size(), nullptr); @@ -641,16 +802,16 @@ xAOD::Vertex* TrigMultiTrkComboHypo::fit(const std::vector<ElementLink<xAOD::Tra if (errorcode != 0) startingPoint = Amg::Vector3D::Zero(3); ATH_MSG_DEBUG( "Starting point: (" << startingPoint(0) << ", " << startingPoint(1) << ", " << startingPoint(2) << ")" ); - m_vertexFitter->setMassInputParticles(m_trkMass, *fitterState); - xAOD::Vertex* vertex = m_vertexFitter->fit(tracklist, startingPoint, *fitterState); + m_vertexFitter->setMassInputParticles(particleMasses, fitterState); + std::unique_ptr<xAOD::Vertex> vertex(m_vertexFitter->fit(tracklist, startingPoint, fitterState)); if (!vertex) { ATH_MSG_DEBUG( "Vertex fit fails" ); - return nullptr; + return vertex; } if (vertex->chiSquared() > m_chi2) { 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() << ")" ); - delete vertex; - return nullptr; + vertex.reset(); + return vertex; } ATH_MSG_DEBUG( "Fit is successful" ); vertex->clearTracks(); @@ -659,18 +820,23 @@ xAOD::Vertex* TrigMultiTrkComboHypo::fit(const std::vector<ElementLink<xAOD::Tra } -xAOD::TrigBphys* TrigMultiTrkComboHypo::makeTrigBPhys(xAOD::Vertex* vertex, Trk::IVKalState* fitterState, const Amg::Vector3D& beamspot) const { +xAOD::TrigBphys* TrigMultiTrkComboHypo::makeTrigBPhys( + const xAOD::Vertex& vertex, + const std::vector<double>& particleMasses, + const Amg::Vector3D& beamSpot, + const Trk::IVKalState& fitterState) const { + double invariantMass = 0.; double invariantMassError = 0.; - if (!m_vertexFitter->VKalGetMassError(invariantMass, invariantMassError, *fitterState).isSuccess()) { + if (!m_vertexFitter->VKalGetMassError(invariantMass, invariantMassError, fitterState).isSuccess()) { ATH_MSG_DEBUG( "Warning from TrkVKalVrtFitter: can not calculate uncertainties" ); invariantMass = -9999.; } - xAOD::TrackParticle::GenVecFourMom_t momentum(0, 0, 0, 0); - for (size_t i = 0; i < vertex->nTrackParticles(); ++i) { - auto p = vertex->trackParticle(i)->genvecP4(); - p.SetM(m_trkMass[i]); + xAOD::TrackParticle::GenVecFourMom_t momentum; + for (size_t i = 0; i < vertex.nTrackParticles(); ++i) { + auto p = vertex.trackParticle(i)->genvecP4(); + p.SetM(particleMasses[i]); momentum += p; } @@ -678,14 +844,15 @@ xAOD::TrigBphys* TrigMultiTrkComboHypo::makeTrigBPhys(xAOD::Vertex* vertex, Trk: result->makePrivateStore(); result->initialise(0, momentum.Eta(), momentum.Phi(), momentum.Pt(), xAOD::TrigBphys::MULTIMU, momentum.M(), xAOD::TrigBphys::EF); + if (m_doElectrons) result->setParticleType(xAOD::TrigBphys::JPSIEE); result->setFitmass(invariantMass); - result->setFitchi2(vertex->chiSquared()); - result->setFitndof(vertex->numberDoF()); - result->setFitx(vertex->x()); - result->setFity(vertex->y()); - result->setFitz(vertex->z()); - result->setTrackParticleLinks(vertex->trackParticleLinks()); - result->setLxy(Lxy(result, beamspot)); + result->setFitchi2(vertex.chiSquared()); + result->setFitndof(vertex.numberDoF()); + result->setFitx(vertex.x()); + result->setFity(vertex.y()); + result->setFitz(vertex.z()); + result->setTrackParticleLinks(vertex.trackParticleLinks()); + result->setLxy(Lxy(*result, beamSpot)); ATH_MSG_DEBUG( "TrigBphys objects:\n\t " << @@ -697,8 +864,8 @@ xAOD::TrigBphys* TrigMultiTrkComboHypo::makeTrigBPhys(xAOD::Vertex* vertex, Trk: "mass: " << result->mass() << "\n\t " << "fitmass: " << result->fitmass() << "\n\t " << "chi2/NDF: " << result->fitchi2() << " / " << result->fitndof() << "\n\t " << - "vertex: (" << result->fitx() << ", " << result->fity() << ", " << result->fitz() << "\n\t " << - "Lxy " << result->lxy() << ")" ); + "vertex: (" << result->fitx() << ", " << result->fity() << ", " << result->fitz() << ")\n\t " << + "Lxy: " << result->lxy() ); return result; } @@ -722,10 +889,10 @@ bool TrigMultiTrkComboHypo::isIdenticalTracks(const xAOD::Electron* lhs, const x } -float TrigMultiTrkComboHypo::Lxy(const xAOD::TrigBphys* vertex, const Amg::Vector3D& beamSpot) const { +float TrigMultiTrkComboHypo::Lxy(const xAOD::TrigBphys& vertex, const Amg::Vector3D& beamSpot) const { - XYVector R(vertex->fitx() - beamSpot.x(), vertex->fity() - beamSpot.y()); - const auto& trackParticleLinks = vertex->trackParticleLinks(); + XYVector R(vertex.fitx() - beamSpot.x(), vertex.fity() - beamSpot.y()); + const auto& trackParticleLinks = vertex.trackParticleLinks(); auto pT = std::accumulate(trackParticleLinks.begin(), trackParticleLinks.end(), XYVector(), [](const auto& pT, const auto& trackEL){ const auto& p = (*trackEL)->genvecP4(); return pT + XYVector(p.x(), p.y()); }); return R.Dot(pT.unit()); @@ -743,5 +910,3 @@ bool TrigMultiTrkComboHypo::isInMassRange(double mass) const { } return result; } - -TrigMultiTrkState::~TrigMultiTrkState(){ } diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.h index 825222b9dc8558cf7cbd82f92c405532ecc2a36f..442527191337e7a466b6a87d88cb54bf8f5a314d 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.h +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.h @@ -43,6 +43,8 @@ #include "TrigMultiTrkComboHypoTool.h" #include "BeamSpotConditionsData/BeamSpotData.h" +#include "AthViews/View.h" + namespace Trk { class IVKalState; } @@ -53,34 +55,66 @@ class IVKalState; * @brief State class for TrigMultiTrkComboHypo algorithm */ -class TrigMultiTrkState: public ::ITrigBphysState { +class TrigMultiTrkStateBase: public ::ITrigBphysState { public: - virtual ~TrigMultiTrkState(); - const EventContext* context; - const TrigCompositeUtils::DecisionContainer* previousDecisions; - TrigCompositeUtils::DecisionContainer* decisions; - xAOD::TrigBphysContainer* trigBphysCollection; - std::vector<ElementLink<xAOD::TrackParticleContainer>> tracks; - int isEventAccepted; - std::vector<std::vector<size_t>> trigBphysIndices; - virtual ElementLinkVector<TrigCompositeUtils::DecisionContainer>& getDecisionLinks(size_t) =0 ; - virtual TrigCompositeUtils::DecisionIDContainer& getDecisionID(size_t) =0 ; + TrigMultiTrkStateBase() = delete; + TrigMultiTrkStateBase(const EventContext& context, + const TrigCompositeUtils::DecisionContainer& previousDecisions, + TrigCompositeUtils::DecisionContainer& decisions, + xAOD::TrigBphysContainer* trigBphysCollection = nullptr, + const InDet::BeamSpotData* beamSpotData = nullptr) + : ITrigBphysState(context, previousDecisions, decisions, trigBphysCollection, beamSpotData), + m_isEventAccepted(0) {} + virtual ~TrigMultiTrkStateBase() = default; + virtual ElementLinkVector<TrigCompositeUtils::DecisionContainer>& getDecisionLinks(size_t) = 0; + virtual TrigCompositeUtils::DecisionIDContainer& getDecisionIDs(size_t) = 0; + virtual void addTrigBphysObject(xAOD::TrigBphys*, const std::vector<size_t>& legIndices) = 0; + + void setEventAccepted(bool flag = true) { m_isEventAccepted = (flag ? 1 : 0); } + inline int isEventAccepted() const { return m_isEventAccepted; } + inline std::vector<std::vector<size_t>>& trigBphysLegIndices() { return m_trigBphysLegIndices; } + inline std::vector<size_t>& getTrigBphysLegIndices(size_t idx) { return m_trigBphysLegIndices[idx]; } + inline std::vector<ElementLink<xAOD::TrackParticleContainer>>& tracks() { return m_tracks; } + + private: + int m_isEventAccepted; + std::vector<std::vector<size_t>> m_trigBphysLegIndices; + std::vector<ElementLink<xAOD::TrackParticleContainer>> m_tracks; }; + template<typename CONTAINER> -class TrigMultiTrkStateCand : public TrigMultiTrkState{ +class TrigMultiTrkState : public TrigMultiTrkStateBase { public: - virtual ~TrigMultiTrkStateCand() = default; - struct LEG { + TrigMultiTrkState() = delete; + TrigMultiTrkState(const EventContext& context, + const TrigCompositeUtils::DecisionContainer& previousDecisions, + TrigCompositeUtils::DecisionContainer& decisions, + xAOD::TrigBphysContainer* trigBphysCollection = nullptr, + const InDet::BeamSpotData* beamSpotData = nullptr) + : TrigMultiTrkStateBase(context, previousDecisions, decisions, trigBphysCollection, beamSpotData) {} + virtual ~TrigMultiTrkState() = default; + + struct LEPTON { ElementLink<CONTAINER> link; ElementLinkVector<TrigCompositeUtils::DecisionContainer> decisionLinks; TrigCompositeUtils::DecisionIDContainer decisionIDs; }; - std::vector<LEG> LegList; - virtual ElementLinkVector<TrigCompositeUtils::DecisionContainer>& getDecisionLinks(size_t i) override - { return LegList.at(i).decisionLinks; } - virtual TrigCompositeUtils::DecisionIDContainer& getDecisionID(size_t i) override - { return LegList.at(i).decisionIDs; } + + std::vector<LEPTON>& leptons() { return m_leptons; } + virtual ElementLinkVector<TrigCompositeUtils::DecisionContainer>& getDecisionLinks(size_t i) override final { + return m_leptons.at(i).decisionLinks; + } + virtual TrigCompositeUtils::DecisionIDContainer& getDecisionIDs(size_t i) override final { + return m_leptons.at(i).decisionIDs; + } + virtual void addTrigBphysObject(xAOD::TrigBphys* trigBphysObject, const std::vector<size_t>& legIndices) override final { + trigBphysCollection().push_back(trigBphysObject); + trigBphysLegIndices().push_back(legIndices); + } + + private: + std::vector<LEPTON> m_leptons; }; @@ -95,32 +129,88 @@ class TrigMultiTrkComboHypo: public ::ComboHypo { private: - void FillState(TrigMultiTrkState* state, - const EventContext* context, - const TrigCompositeUtils::DecisionContainer* previousDecisions, - TrigCompositeUtils::DecisionContainer* decisions, - xAOD::TrigBphysContainer* trigBphysCollection) const; + /** + * @brief Go through state.previousDecisions() and fetch xAOD::TrackParticle objects associated with the nearest SG::View. + * Enable overlap removal to get collection of unique objects at state.tracks(). + */ + StatusCode mergeTracksFromViews(TrigMultiTrkStateBase&) const; + + /** + * @brief Go through state.previousDecisions(), fetch xAOD::Muons/xAOD::Electron objects attached to decisions + * and save links to the their xAOD::TrackParticle objects in state.tracks(). + */ + template<typename CONTAINER> + StatusCode mergeTracksFromDecisions(TrigMultiTrkStateBase&) const; + + /** + * @brief Go through state.previousDecisions(), fetch xAOD::Muons/xAOD::Electron objects attached to decisions + * and save links to them in state.leptons(). + */ + template<typename CONTAINER> + StatusCode mergeLeptonsFromDecisions(TrigMultiTrkState<CONTAINER>&) const; + + /** + * @brief Make all possible combinations from state.leptons(), fit tracks to the common vertex, + * create xAOD::TrigBphys objects and put them into state.trigBphysCollection() + * + * combination is a selection of items from a collection, such that the order of items does not matter + * leptons/tracks have already been sorted over pT, the algorithm will keep this order in created subsets of tracks, i.e. + * for nTrk = 2 combinations are [0, 1], [0, 2], ..., [1, 2], [1, 3], ... + * for nTrk = 3 combinations are [0, 1, 2], [0, 1, 3], ..., [0, 2, 3], [0, 2, 4], ..., [1, 2, 3], [1, 2, 4], ... + */ + template<typename CONTAINER> + StatusCode findMultiLeptonCandidates(TrigMultiTrkState<CONTAINER>&) const; + + /** + * @brief Make all possible combinations from state.tracks(), fit tracks to the common vertex + * and set state.isEventAccepted to true if at least one good combination is found; + * no xAOD::TrigBphys objects will be created. + */ + StatusCode filterTrackCombinations(TrigMultiTrkStateBase&) const; + + /** + * @brief Make all possible combinations from electrons originating from the same BPH-0DR3-EM7J15 cluster, + * only one track should pass e5_lhvloose requirements; + * initialRoI word will be saved into xAOD::TrigBphys objects. + */ + StatusCode processMergedElectrons(TrigMultiTrkState<xAOD::ElectronContainer>&) const; + + /** + * @brief All appropriate decisions from state.previousDecisions() will be copied to state.decisions() + * if flag state.isEventAccepted() is set by filterTrackCombinations() method; + * to be used only in Streamer mode. + */ + StatusCode copyDecisionObjects(TrigMultiTrkStateBase&) const; + + /** + * @brief For chains from CombinedSlice (similar to 'HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_mu4_L1BPH-0M9-EM7-EM5_MU6') + * we have decisionsInput().size() > 1, so that we should copy decisions created by EmptySteps from muon part. + */ + StatusCode copyAdditionalDecisionObjects(TrigMultiTrkStateBase&) const; + + /** + * @brief Create a decision for each xAOD::TrigBphys object from state.trigBphysCollection() and save it to state.decisions(); + * use hypoTools() to assign correct decisionIDs, not compatible with Streamer mode. + */ + StatusCode createDecisionObjects(TrigMultiTrkStateBase&) const; + + + std::unique_ptr<xAOD::Vertex> fit( + const std::vector<ElementLink<xAOD::TrackParticleContainer>>& trackParticleLinks, + const std::vector<double>& particleMasses, + Trk::IVKalState& fitterState) const; + + xAOD::TrigBphys* makeTrigBPhys( + const xAOD::Vertex& vertex, + const std::vector<double>& particleMasses, + const Amg::Vector3D& beamSpot, + const Trk::IVKalState& fitterState) const; - template<typename T> - StatusCode mergeFromDecisions(TrigMultiTrkStateCand<T>&) const; - - StatusCode mergeTracksFromViews(TrigMultiTrkState&) const; - StatusCode mergeTracksFromDecisions(TrigMultiTrkState&) const; - StatusCode filterTrackCombinations(TrigMultiTrkState&) const; - StatusCode copyDecisionObjects(TrigMultiTrkState&) const; - - template<typename T> - StatusCode findDiTrackCandidates(TrigMultiTrkStateCand<T>&) const; - - StatusCode createDecisionObjects(TrigMultiTrkState&) const; - - xAOD::Vertex* fit(const std::vector<ElementLink<xAOD::TrackParticleContainer>>& tracklist, Trk::IVKalState*) const; - xAOD::TrigBphys* makeTrigBPhys(xAOD::Vertex* vertex, Trk::IVKalState* fitterState, const Amg::Vector3D& beamspot) const; bool isIdenticalTracks(const xAOD::TrackParticle* lhs, const xAOD::TrackParticle* rhs) const; bool isIdenticalTracks(const xAOD::Muon* lhs, const xAOD::Muon* rhs) const; bool isIdenticalTracks(const xAOD::Electron* lhs, const xAOD::Electron* rhs) const; bool isInMassRange(double mass) const; - float Lxy(const xAOD::TrigBphys*, const Amg::Vector3D&) const; + float Lxy(const xAOD::TrigBphys& vertex, const Amg::Vector3D& beamSpot) const; SG::ReadHandleKey<xAOD::TrackParticleContainer> m_trackParticleContainerKey {this, "TrackCollectionKey", "Tracks", "input TrackParticle container name"}; @@ -131,16 +221,28 @@ class TrigMultiTrkComboHypo: public ::ComboHypo { SG::ReadCondHandleKey<InDet::BeamSpotData> m_beamSpotKey {this, "BeamSpotKey", "BeamSpotData", "SG key for beam spot"}; - Gaudi::Property<unsigned int> m_nTrk {this, "nTracks", 2, "number of tracks in the vertex"}; - Gaudi::Property<std::vector<double>> m_trkMass {this, "trackMasses", {105.6583745, 105.6583745}, "track masses for vertex reco (one per track)"}; - Gaudi::Property<std::vector<float>> m_trkPt {this, "trkPtThresholds", {3650., 3650.} ,"minimum track pTs (one per track, sorted descending)"}; - Gaudi::Property<std::vector<std::pair<double, double>>> m_massRange {this, "massRanges", { {0., 100000.} }, "mass ranges"}; - Gaudi::Property<float> m_deltaR {this, "deltaR", 0.01, "minimum deltaR between same-sign tracks (overlap removal)"}; - Gaudi::Property<float> m_chi2 {this, "chi2", 150., "chi2 cut for the fitted vertex"}; - - Gaudi::Property<bool> m_isStreamer {this, "isStreamer", false, "if true we do not create trigger objects, just copy all appropriate decisions to the next step or break the chain"}; - Gaudi::Property<bool> m_doElectrons {this, "doElectrons", false, "use electrons instead of muons"}; - Gaudi::Property<std::string> m_trigLevel {this, "trigLevel", "EF", "trigger Level to set for created TrigBphys objects"}; + Gaudi::Property<std::vector<unsigned int>> m_nTrk {this, "nTracks", {2}, + "number of tracks to be fitted into the common vertex"}; + Gaudi::Property<std::vector<std::vector<double>>> m_trkMass {this, "trackMasses", {}, + "track masses for vertex reco (one per track); muon mass is used by default"}; + Gaudi::Property<std::vector<std::vector<double>>> m_trkPt {this, "trackPtThresholds", { {3650., 3650.} }, + "minimum track transverse momenta (one per track, sorted descending)"}; + Gaudi::Property<std::vector<std::pair<double, double>>> m_massRange {this, "massRange", { {0., 100000.} }, + "range of the invariant mass of the track combinations"}; + Gaudi::Property<bool> m_applyOverlapRemoval {this, "applyOverlapRemoval", true, + "apply overlap removal for the close-by same-sign objects from different views"}; + Gaudi::Property<float> m_deltaR {this, "deltaR", 0.01, + "minimum deltaR between same-sign tracks (overlap removal)"}; + Gaudi::Property<float> m_chi2 {this, "chi2", 150., + "chi2 cut for the fitted vertex"}; + Gaudi::Property<bool> m_isStreamer {this, "isStreamer", false, + "if true we do not create trigger objects, just copy all appropriate decisions to the next step or break the chain"}; + Gaudi::Property<bool> m_doElectrons {this, "doElectrons", false, + "use electrons if true, otherwise use muons"}; + Gaudi::Property<std::string> m_trigLevel {this, "trigLevel", "EF", + "trigger Level to set for created TrigBphys objects: L2, L2IO or EF"}; + Gaudi::Property<std::vector<std::string>> m_mergedElectronChains {this, "mergedElectronChains", {"BPH-0DR3-EM7J15"}, + "patterns for BPH-0DR3-EM7J15 like chains"}; ToolHandle<InDet::VertexPointEstimator> m_vertexPointEstimator {this, "VertexPointEstimator", "", "tool to find starting point for the vertex fitter"}; ToolHandle<Trk::TrkVKalVrtFitter> m_vertexFitter {this, "VertexFitter", "", "VKalVrtFitter tool to fit tracks into the common vertex"}; @@ -148,6 +250,11 @@ class TrigMultiTrkComboHypo: public ::ComboHypo { ToolHandle<GenericMonitoringTool> m_monTool {this, "MonTool", "", "monitoring tool"}; TrigCompositeUtils::DecisionIDContainer m_allowedIDs; + TrigCompositeUtils::DecisionIDContainer m_resolvedElectronIDs; + TrigCompositeUtils::DecisionIDContainer m_mergedElectronIDs; + + double m_trkPtMin; + }; #endif // TRIG_TrigMultiTrkComboHypo_H diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.cxx index aeb01c226a41ece304479ed51338e7d7c021ac09..adf27b7b2c69c137635ab2307df9a2bd1a6da995 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.cxx +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.cxx @@ -30,12 +30,32 @@ TrigMultiTrkComboHypoTool::TrigMultiTrkComboHypoTool(const std::string& type, co StatusCode TrigMultiTrkComboHypoTool::initialize() { ATH_MSG_DEBUG( "configuration for '" << this->name() << "'" << endmsg << " AcceptAll = " << (m_acceptAll ? "True" : "False") << endmsg << + " isCombinedChain = " << (m_isCombinedChain ? "True" : "False") << endmsg << + " isMergedElectronChain = " << (m_isMergedElectronChain ? "True" : "False") << endmsg << " mass range: ( " << m_massRange.value().first << ", " << m_massRange.value().second << " )" << endmsg << " chi2 cut: " << m_chi2 << endmsg << - " " << (m_totalCharge < 0 ? "total charge cut is disabled" : "total charge cut: only right charge combinations")); - ATH_MSG_DEBUG(" LxyCut: > " << m_LxyCut.value()); + " " << (m_totalCharge < 0 ? "total charge cut is disabled" : "total charge cut: only right charge combinations") << endmsg << + " LxyCut: > " << m_LxyCut.value() ); ATH_CHECK( m_nTrk >= 2 ); + ATH_CHECK( !(m_isMergedElectronChain && m_legMultiplicities[0] != 1) ); + + ATH_CHECK( !m_legMultiplicities.empty() ); + if ( m_legMultiplicities.size() > 1 ) { // chain with asymmetric legs, as HLT_mu6_2mu4_bDimu_L1MU6_3MU4 + // here we consider each decision as an individual object, i.e. for HLT_mu6_2mu4_bDimu_L1MU6_3MU4 we use + // m_legDecisionIDs = {createLegName("HLT_mu6_2mu4_bDimu_L1MU6_3MU4", 0), + // createLegName("HLT_mu6_2mu4_bDimu_L1MU6_3MU4", 1), + // createLegName("HLT_mu6_2mu4_bDimu_L1MU6_3MU4", 1)}; + for (size_t legIndex = 0; legIndex < m_legMultiplicities.size(); ++legIndex) { + // can not use legDecisionId(legIndex) at initialize() step, use TrigCompositeUtils::createLegName() instead + auto legId = TrigCompositeUtils::createLegName(decisionId(), legIndex); + m_legDecisionIDs.insert(m_legDecisionIDs.end(), m_legMultiplicities[legIndex], legId.numeric()); + } + } + else { // chain with symmetric legs, as HLT_2mu4_bDimu_L12MU4 + m_legDecisionIDs.insert(m_legDecisionIDs.end(), m_legMultiplicities[0], decisionId().numeric()); + } + ATH_CHECK( m_nTrk <= m_legDecisionIDs.size() || m_isMergedElectronChain ); if (!m_monTool.empty()) { ATH_CHECK( m_monTool.retrieve() ); @@ -65,8 +85,11 @@ bool TrigMultiTrkComboHypoTool::passed(const xAOD::TrigBphys* trigBphys) const { auto mon = Monitored::Group( m_monTool, mon_totalCharge, mon_chi2, mon_mass, mon_pT_trk1, mon_pT_trk2, mon_Lxy, mon_D0_trk1, mon_D0_trk2, mon_Eta1, mon_Eta2); - if (m_acceptAll || (isInMassRange(trigBphys->mass()) && passedChi2Cut(trigBphys->fitchi2()) && passedChargeCut(totalCharge(trigBphys)) && - trigBphys->lxy() > m_LxyCut )) { + if (m_acceptAll || (trigBphys->nTrackParticles() == m_nTrk && + isInMassRange(trigBphys->mass()) && + passedChi2Cut(trigBphys->fitchi2()) && + passedChargeCut(totalCharge(trigBphys)) && + trigBphys->lxy() > m_LxyCut)) { mon_Lxy = trigBphys->lxy(); mon_totalCharge = totalCharge(trigBphys); mon_chi2 = trigBphys->fitchi2(); @@ -93,13 +116,21 @@ StatusCode TrigMultiTrkComboHypoTool::decideOnSingleObject(Decision* decision, c auto trigBphysEL = decision->objectLink<xAOD::TrigBphysContainer>(TrigCompositeUtils::featureString()); ATH_CHECK( trigBphysEL.isValid() ); - ATH_CHECK( previousDecisionIDs.size() == m_nTrk ); + if (previousDecisionIDs.size() != (m_isMergedElectronChain ? 1 : m_nTrk.value())) { + return StatusCode::SUCCESS; + } + if (!checkPreviousDecisionIDs(previousDecisionIDs)) { return StatusCode::SUCCESS; } - if (passed(*trigBphysEL)) { + if (m_acceptAll || passed(*trigBphysEL)) { TrigCompositeUtils::addDecisionID(decisionId(), decision); + if (m_isCombinedChain) { + for (size_t i = 0; i < m_nTrk; ++i) { + TrigCompositeUtils::addDecisionID(m_legDecisionIDs[i], decision); + } + } } return StatusCode::SUCCESS; @@ -113,9 +144,16 @@ bool TrigMultiTrkComboHypoTool::checkPreviousDecisionIDs(const std::vector<const // trigger with asymmetric legs (like HLT_mu6_2mu4_bDimu_L1MU6_3MU4) is treated in a specific way: // all 6 possible combinations should be checked: {leg0, leg1}, {leg0, leg2}, {leg1, leg0}, {leg1, leg2}, {leg2, leg0}, {leg2, leg1} - if (decisionId() == legDecisionId(0)) { // trigger with symmetric legs like HLT_3mu6_bDimu_L13MU6 + if (m_isMergedElectronChain) { + if (!TrigCompositeUtils::passed(m_legDecisionIDs.at(0), *previousDecisionIDs[0])) { + ATH_MSG_DEBUG( "Trigger for close-by electrons didn't pass previous decision" ); + return false; + } + return true; + } + else if (decisionId() == legDecisionId(0)) { // trigger with symmetric legs like HLT_3mu6_bDimu_L13MU6 for (size_t i = 0; i < m_nTrk; ++i) { - if (!TrigCompositeUtils::passed(decisionId().numeric(), *previousDecisionIDs[i])) { + if (!TrigCompositeUtils::passed(decisionId().numeric(), *previousDecisionIDs[i])) { ATH_MSG_DEBUG( "Trigger with symmetric legs didn't pass previous decision" ); return false; } @@ -125,29 +163,14 @@ bool TrigMultiTrkComboHypoTool::checkPreviousDecisionIDs(const std::vector<const } else { // trigger with asymmetric legs like HLT_mu6_2mu4_bDimu_L1MU6_3MU4 - std::vector<size_t> a; - std::vector<HLT::Identifier> legDecisionIdToCheck; - - size_t count = 0; - for (size_t legIndex = 0; legIndex < legMultiplicity().size(); ++legIndex) { - for (size_t objectIndex = 0; objectIndex < (size_t) legMultiplicity().at(legIndex); ++objectIndex) { - a.push_back(count++); - legDecisionIdToCheck.push_back(legDecisionId(legIndex)); - } - } - // For HLT_mu6_2mu4_bDimu_L1MU6_3MU4 - // a = [0,1,2] - // legDecisionIdToCheck = [ - // createLegName("HLT_mu6_2mu4_bDimu_L1MU6_3MU4", 0), - // createLegName("HLT_mu6_2mu4_bDimu_L1MU6_3MU4", 1), - // createLegName("HLT_mu6_2mu4_bDimu_L1MU6_3MU4", 1)] - + std::vector<size_t> a(m_legDecisionIDs.size()); + std::iota(a.begin(), a.end(), 0); // {0, 1, 2, .., m_legDecisionIDs().size() - 1} int i = 1; bool result = true; do { result = true; for (size_t k = 0; k < m_nTrk; ++k) { - result = result && TrigCompositeUtils::passed(legDecisionIdToCheck.at( a.at(k) ).numeric(), *previousDecisionIDs[k]); + result = result && TrigCompositeUtils::passed(m_legDecisionIDs.at(a[k]), *previousDecisionIDs[k]); } if (msgLvl(MSG::DEBUG)) { msg() << "combination #" << i++ << ": { "; @@ -156,7 +179,7 @@ bool TrigMultiTrkComboHypoTool::checkPreviousDecisionIDs(const std::vector<const } if (result) break; } while (std::next_permutation(a.begin(), a.end())); - ATH_MSG_DEBUG( "Trigger with asymmetric legs " << (result ? "passed" : "didn't pass" ) << " previous decision" ); + ATH_MSG_DEBUG( "Trigger with asymmetric legs " << (result ? "passed" : "didn't pass") << " previous decision" ); return result; } return true; @@ -182,7 +205,7 @@ int TrigMultiTrkComboHypoTool::totalCharge(const xAOD::TrigBphys* trigBphys) con } -bool TrigMultiTrkComboHypoTool::executeAlg(const std::vector<Combo::LegDecision>&) const { - ATH_MSG_ERROR("executeAlg not supported for TrigBmumuxComboHypoTool."); +bool TrigMultiTrkComboHypoTool::executeAlg(const std::vector<Combo::LegDecision>&) const { + ATH_MSG_ERROR( "executeAlg() is not supported by TrigMultiTrkComboHypoTool" ); return true; } diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.h index 1be2f0790babfc1e6189f4658e9528bac7fe0ea0..3d9b9b635de063dd607aa2b92d681817c38b0e3a 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.h +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.h @@ -50,9 +50,14 @@ class TrigMultiTrkComboHypoTool: public ComboHypoToolBase { Gaudi::Property<int> m_totalCharge {this, "totalCharge", 0, "magnitude of the total charge to accept, negative is none" }; Gaudi::Property<std::pair<double, double>> m_massRange {this, "massRange", {-99., -9.}, "range for the fitted mass, no selection applied if negative"}; Gaudi::Property<float> m_chi2 {this, "chi2", -99. , "Chi2 cut for vertex (0 < chi2 < cut), no selection applied if negative" }; - Gaudi::Property<bool> m_acceptAll {this, "AcceptAll", false, "if AcceptAll flag is set to true, no selection will be applied for xAOD::TrigBphys object" }; + Gaudi::Property<bool> m_acceptAll {this, "AcceptAll", false, "if AcceptAll flag is set to true, no selection will be applied for xAOD::TrigBphys object"}; Gaudi::Property<float> m_LxyCut {this, "LxyCut", -999., "Applies an Lxy Cut if set > -999"}; - ToolHandle<GenericMonitoringTool> m_monTool { this, "MonTool", "", "Monitoring tool" }; + Gaudi::Property<bool> m_isCombinedChain {this, "isCombinedChain", false, "true for chains with different signatures, e.g. HLT_e9_mu6"}; + Gaudi::Property<bool> m_isMergedElectronChain {this, "isMergedElectronChain", false, "true for close-by electrons, e.g. HLT_e5_lhvloose_L1BPH-0DR3-EM7J15"}; + Gaudi::Property<std::vector<unsigned int>> m_legMultiplicities {this, "legMultiplicities", {2}, "taken from dict[chainMultiplicities]"}; + ToolHandle<GenericMonitoringTool> m_monTool {this, "MonTool", "", "Monitoring tool" }; + + std::vector<TrigCompositeUtils::DecisionID> m_legDecisionIDs; }; #endif // TRIG_TrigMultiTrkComboHypoTool_H diff --git a/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref b/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref index a17147e56e334db7e8204c7aa90712db0e2d1cc5..ef4d01498a1372151ec4767da41939e221aed2d2 100644 --- a/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref +++ b/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref @@ -70,15 +70,27 @@ HLT_2e5_lhvloose_bBeeM6000_L12EM3: eventCount: 0 stepCounts: 0: 15 - 1: 8 - 2: 6 - 3: 5 + 1: 6 + 2: 4 + 3: 4 stepFeatures: 0: 50 1: 104 - 2: 20 - 3: 15 - 4: 5 + 2: 14 + 3: 10 + 4: 3 +HLT_2e5_lhvloose_bBeeM6000_L1BPH-0M9-EM7-EM5: + eventCount: 0 + stepCounts: + 0: 6 + 1: 2 + 2: 1 + 3: 1 + stepFeatures: + 0: 18 + 1: 50 + 2: 4 + 3: 2 HLT_2g10_loose_mu20_L1MU14FCH: eventCount: 0 stepCounts: @@ -455,6 +467,19 @@ HLT_2mu4_bBmumu_L1BPH-2M9-0DR15-2MU3V: eventCount: 0 HLT_2mu4_bBmumu_Lxy0_L1BPH-2M9-0DR15-2MU3V: eventCount: 0 +HLT_2mu4_bBmumux_BcmumuDploose_L12MU3V: + eventCount: 0 + stepCounts: + 0: 4 + 1: 3 + 2: 3 + 3: 1 + stepFeatures: + 0: 9 + 1: 9 + 2: 10 + 3: 8 + 4: 4 HLT_2mu4_bBmumux_BcmumuDsloose_L12MU3V: eventCount: 0 stepCounts: @@ -3421,6 +3446,19 @@ HLT_e5_lhtight_noringer_e9_etcut_Jpsiee_L1JPSI-1M5-EM7: eventCount: 0 HLT_e5_lhtight_noringer_e9_etcut_probe_1invmAB5_L1JPSI-1M5-EM7: eventCount: 0 +HLT_e5_lhvloose_bBeeM6000_L1BPH-0DR3-EM7J15: + eventCount: 0 + stepCounts: + 0: 11 + 1: 11 + 2: 11 + 3: 11 + stepFeatures: + 0: 16 + 1: 50 + 2: 18 + 3: 15 + 4: 9 HLT_e5_lhvloose_j70_0eta320_j50_0eta490_j0_DJMASS1000j50_xe50_tcpufit_L1MJJ-500-NFF: eventCount: 0 stepCounts: @@ -3617,6 +3655,27 @@ HLT_e9_lhtight_noringer_e4_etcut_Jpsiee_L1JPSI-1M5-EM7: eventCount: 0 HLT_e9_lhtight_noringer_e4_etcut_probe_1invmAB5_L1JPSI-1M5-EM7: eventCount: 0 +HLT_e9_lhvloose_bBeeM6000_L1BPH-0DR3-EM7J15: + eventCount: 0 + stepCounts: + 0: 11 + 1: 11 + 2: 8 + 3: 8 + stepFeatures: + 0: 16 + 1: 50 + 2: 11 + 3: 11 + 4: 8 +HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_L1BPH-0M9-EM7-EM5: + eventCount: 0 + stepCounts: + 0: 3 + stepFeatures: + 0: 21 + 1: 52 + 2: 3 HLT_e9_lhvloose_mu20_mu8noL1_L1MU14FCH: eventCount: 0 stepCounts: diff --git a/Trigger/TrigValidation/TrigP1Test/share/ref_v1Dev_decodeBS_build.ref b/Trigger/TrigValidation/TrigP1Test/share/ref_v1Dev_decodeBS_build.ref index a5b647448f10165b7c8f0fa1007fb62a40cc8a2d..0e0f85158dac209ae7f7a66c0eee8425e8bfdea9 100644 --- a/Trigger/TrigValidation/TrigP1Test/share/ref_v1Dev_decodeBS_build.ref +++ b/Trigger/TrigValidation/TrigP1Test/share/ref_v1Dev_decodeBS_build.ref @@ -34,6 +34,18 @@ HLT_2e5_lhvloose_bBeeM6000_L12EM3: 1: 49 2: 4 3: 4 +HLT_2e5_lhvloose_bBeeM6000_L1BPH-0M9-EM7-EM5: + eventCount: 0 + stepCounts: + 0: 1 + 1: 1 + 2: 1 + 3: 1 + stepFeatures: + 0: 5 + 1: 9 + 2: 2 + 3: 2 HLT_2g10_loose_mu20_L1MU14FCH: eventCount: 0 HLT_2g15_loose_25dphiAA_invmAA80_L1DPHI-M70-2eEM12M: @@ -153,6 +165,12 @@ HLT_2mu4_bBmumu_L1BPH-2M9-0DR15-2MU3V: eventCount: 0 HLT_2mu4_bBmumu_Lxy0_L1BPH-2M9-0DR15-2MU3V: eventCount: 0 +HLT_2mu4_bBmumux_BcmumuDploose_L12MU3V: + eventCount: 0 + stepCounts: + 0: 1 + stepFeatures: + 0: 2 HLT_2mu4_bBmumux_BcmumuDsloose_L12MU3V: eventCount: 0 stepCounts: @@ -1082,6 +1100,18 @@ HLT_e5_lhtight_noringer_e9_etcut_probe_1invmAB5_L1JPSI-1M5-EM7: 1: 8 2: 2 3: 2 +HLT_e5_lhvloose_bBeeM6000_L1BPH-0DR3-EM7J15: + eventCount: 0 + stepCounts: + 0: 7 + 1: 6 + 2: 5 + 3: 5 + stepFeatures: + 0: 9 + 1: 19 + 2: 6 + 3: 5 HLT_e5_lhvloose_j70_0eta320_j50_0eta490_j0_DJMASS1000j50_xe50_tcpufit_L1MJJ-500-NFF: eventCount: 0 HLT_e5_lhvloose_nopix_lrtloose_idperf_probe_g25_medium_L1EM20VH: @@ -1134,6 +1164,22 @@ HLT_e9_lhtight_noringer_e4_etcut_Jpsiee_L1JPSI-1M5-EM7: 0: 11 HLT_e9_lhtight_noringer_e4_etcut_probe_1invmAB5_L1JPSI-1M5-EM7: eventCount: 0 +HLT_e9_lhvloose_bBeeM6000_L1BPH-0DR3-EM7J15: + eventCount: 0 + stepCounts: + 0: 6 + 1: 5 + 2: 2 + 3: 2 + stepFeatures: + 0: 7 + 1: 16 + 2: 2 + 3: 2 +HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_L1BPH-0M9-EM7-EM5: + eventCount: 0 + stepFeatures: + 0: 6 HLT_e9_lhvloose_mu20_mu8noL1_L1MU14FCH: eventCount: 0 HLT_eb_low_L1RD2_FILLED: diff --git a/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py b/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py index f5a53a04448e8193ef6955a74b7ae0117a283f24..cf796e7a24bc85baf907dd7f0d852eaaeab6785f 100644 --- a/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py +++ b/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py @@ -401,6 +401,11 @@ TriggerHLTListRun3 = [ ('xAOD::TrackParticleContainer#HLT_IDTrack_Bmumux_IDTrig', 'BS ESD AODFULL', 'Bphys', 'inViews:BmumuxViews'), ('xAOD::TrackParticleAuxContainer#HLT_IDTrack_Bmumux_IDTrigAux.', 'BS ESD AODFULL', 'Bphys'), ('TrigRoiDescriptorCollection#HLT_Roi_Bmumux', 'BS ESD AODFULL', 'Bphys'), + # Bphysics Di-electron chains + ('xAOD::TrigBphysContainer#HLT_DiElecPrecision', 'BS ESD AODFULL AODSLIM AODVERYSLIM AODBLSSLIM', 'Bphys'), + ('xAOD::TrigBphysAuxContainer#HLT_DiElecPrecisionAux.', 'BS ESD AODFULL AODSLIM AODVERYSLIM AODBLSSLIM', 'Bphys'), + ('xAOD::TrigBphysContainer#HLT_NoMuonDiElecPrecision', 'BS ESD AODFULL AODSLIM AODVERYSLIM AODBLSSLIM', 'Bphys'), + ('xAOD::TrigBphysAuxContainer#HLT_NoMuonDiElecPrecisionAux.', 'BS ESD AODFULL AODSLIM AODVERYSLIM AODBLSSLIM', 'Bphys'), # xAOD muons (msonly (x2: roi+FS), combined (x2: FS+RoI) ('xAOD::MuonContainer#HLT_Muons_RoI', 'BS ESD AODFULL', 'Muon', 'inViews:MUEFSAViewRoIs'), diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Egamma/ElectronChainConfiguration.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Egamma/ElectronChainConfiguration.py index f655c8ba9d44c080901ed81b7939558b826d73db..1c5f16799e3c1de04b0125b2b7efe76dcea1835b 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Egamma/ElectronChainConfiguration.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Egamma/ElectronChainConfiguration.py @@ -21,7 +21,7 @@ from .PrecisionElectronMenuSequences_LRT import precisionElectronMenuSequence_LR from .PrecisionTrackingMenuSequences import precisionTrackingMenuSequence from .PrecisionTrackingMenuSequences_LRT import precisionTrackingMenuSequence_LRT -from TrigBphysHypo.TrigMultiTrkComboHypoConfig import StreamerDiElecFastComboHypoCfg, DiElecPrecisionComboHypoCfg, TrigMultiTrkComboHypoToolFromDict +from TrigBphysHypo.TrigMultiTrkComboHypoConfig import StreamerNoMuonDiElecFastComboHypoCfg, NoMuonDiElecPrecisionComboHypoCfg, StreamerDiElecFastComboHypoCfg, DiElecPrecisionComboHypoCfg, TrigMultiTrkComboHypoToolFromDict from AthenaMonitoringKernel.GenericMonitoringTool import GenericMonitoringTool, defineHistogram #---------------------------------------------------------------- @@ -201,9 +201,14 @@ class ElectronChainConfiguration(ChainConfigurationBase): return self.getStep(1,stepName,[ fastCaloCfg], is_probe_leg=is_probe_leg) def getFastElectron(self,is_probe_leg=False): - if "bBeeM6000" in self.chainDict['topo']: - stepName = "fast_electron_bBee" - return self.getStep(2,stepName,sequenceCfgArray=[fastElectronSequenceCfg], comboHypoCfg=StreamerDiElecFastComboHypoCfg) + if "bBeeM6000" in self.chainDict['topo'] and 'BPH-0DR3-EM7J15' not in self.chainDict['L1item']: + signatures = self.chainDict['signatures'] + if signatures.count(signatures[0]) == len(signatures): + stepName = "noMuon_fast_electron_bBee" + return self.getStep(2,stepName,sequenceCfgArray=[fastElectronSequenceCfg], comboHypoCfg=StreamerNoMuonDiElecFastComboHypoCfg) + else: + stepName = "fast_electron_bBee" + return self.getStep(2,stepName,sequenceCfgArray=[fastElectronSequenceCfg], comboHypoCfg=StreamerDiElecFastComboHypoCfg) elif 'idperf' in self.chainPart['idperfInfo']: stepName = "fast_electron_idperf" return self.getStep(2,stepName,[ fastElectronSequenceCfg_idperf], is_probe_leg=is_probe_leg) @@ -258,8 +263,13 @@ class ElectronChainConfiguration(ChainConfigurationBase): stepName = "precision_electron_Heg"+str(isocut) return self.getStep(5,stepName,sequenceCfgArray=[precisionElectronSequenceCfg], comboTools=[diEgammaHegMassComboHypoToolFromDict]) elif "bBeeM6000" in self.chainDict['topo']: - stepName = "precision_electron_bBee"+isocut - return self.getStep(5,stepName,sequenceCfgArray=[precisionElectronSequenceCfg], comboHypoCfg=DiElecPrecisionComboHypoCfg, comboTools=[TrigMultiTrkComboHypoToolFromDict]) + signatures = self.chainDict['signatures'] + if signatures.count(signatures[0]) == len(signatures): + stepName = "noMuon_precision_electron_bBee"+str(isocut) + return self.getStep(5,stepName,sequenceCfgArray=[precisionElectronSequenceCfg], comboHypoCfg=NoMuonDiElecPrecisionComboHypoCfg, comboTools=[TrigMultiTrkComboHypoToolFromDict]) + else: + stepName = "precision_electron_bBee"+str(isocut) + return self.getStep(5,stepName,sequenceCfgArray=[precisionElectronSequenceCfg], comboHypoCfg=DiElecPrecisionComboHypoCfg, comboTools=[TrigMultiTrkComboHypoToolFromDict]) elif self.chainPart['extra'] == 'ion': stepName = "precision_ion_electron" + str(isocut) return self.getStep(5,stepName,[precisionElectronSequenceCfg_ion], is_probe_leg=is_probe_leg) diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/ChainMerging.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/ChainMerging.py index 16111f4b91d6a5b6c0455f4c3240a8ce7b35c024..ba7b751fb448476b8eb3be8676599c8aa94f5c70 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/ChainMerging.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/ChainMerging.py @@ -478,7 +478,8 @@ def makeCombinedStep(parallel_steps, stepNumber, chainDefList, allSteps = [], cu if len(step.sequences) > 1: log.debug("[makeCombinedStep] combining in an already combined chain") - comboHypo = step.comboHypoCfg + if comboHypo is None or step.comboHypoCfg.__name__ != "ComboHypoCfg": + comboHypo = step.comboHypoCfg currentStepName = step.name #remove redundant instances of StepN_ and merged_ (happens when merging already merged chains) if re.search('^Step[0-9]_',currentStepName): diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py index cb9c60d559732eb9f6872bb2b3c0fb6454bb72d2..75641a8e53beac556f509482f93b4ea08b5ab5b6 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py @@ -184,6 +184,10 @@ def setupMenu(): #ATR-22749 ChainProp(name='HLT_2e5_lhvloose_bBeeM6000_L12EM3', l1SeedThresholds=['EM3'], groups=BphysElectronGroup), + ChainProp(name='HLT_2e5_lhvloose_bBeeM6000_L1BPH-0M9-EM7-EM5', l1SeedThresholds=['EM3'], groups=BphysElectronGroup), + ChainProp(name='HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_L1BPH-0M9-EM7-EM5', l1SeedThresholds=['EM7','EM3'], groups=BphysElectronGroup), + ChainProp(name='HLT_e5_lhvloose_bBeeM6000_L1BPH-0DR3-EM7J15', l1SeedThresholds=['EM7'], groups=BphysElectronGroup), + ChainProp(name='HLT_e9_lhvloose_bBeeM6000_L1BPH-0DR3-EM7J15', l1SeedThresholds=['EM7'], groups=BphysElectronGroup), #ART-23577 ChainProp(name='HLT_e20_lhloose_L1EM7_AFP_A_OR_C', l1SeedThresholds=['EM7'], groups=SingleElectronGroup+LowMuGroup), @@ -875,6 +879,7 @@ def setupMenu(): ChainProp(name='HLT_2mu4_bBmumux_BdmumuKst_L12MU3V', stream=["BphysDelayed"], groups=BphysicsGroup+EOFBPhysL1MuGroup), ChainProp(name='HLT_2mu4_bBmumux_LbPqKm_L12MU3V', stream=["BphysDelayed"], groups=BphysicsGroup+EOFBPhysL1MuGroup), ChainProp(name='HLT_2mu4_bBmumux_BcmumuDsloose_L12MU3V', stream=["BphysDelayed"], groups=BphysicsGroup+EOFBPhysL1MuGroup), + ChainProp(name='HLT_2mu4_bBmumux_BcmumuDploose_L12MU3V', stream=["BphysDelayed"], groups=BphysicsGroup+EOFBPhysL1MuGroup), #ATR-23576; dimuon + L1Topo; primary triggers; should be moved to Physics after validation ChainProp(name='HLT_2mu4_bBmumu_Lxy0_L1BPH-2M9-0DR15-2MU3V', l1SeedThresholds=['MU3V'], stream=["BphysDelayed"], groups=BphysicsGroup+EOFBPhysL1MuGroup), @@ -1063,9 +1068,11 @@ def setupMenu(): #Combined BPhys - #ATR-22749; chain configuration is broken, temporarily comment them out, see ATR-23839 - #ChainProp(name='HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_mu6_noL2Comb_L1BPH-0M9-EM7-EM5_MU5VF', l1SeedThresholds=['EM7','EM3','MU5VF'], groups=BphysElectronGroup), - #ChainProp(name='HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_2mu4_noL2Comb_L1BPH-0M9-EM7-EM5_2MU3V', l1SeedThresholds=['EM7','EM3','MU3V'], groups=BphysElectronGroup), + #ATR-22749; chain configuration is broken, temporarily comment them out, see ATR-23839 and ATR-23965 + #ChainProp(name='HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_mu6_l2io_L1BPH-0M9-EM7-EM5_MU5VF', l1SeedThresholds=['EM7','EM3','MU5VF'], groups=BphysElectronGroup), + #ChainProp(name='HLT_e9_lhvloose_e5_lhvloose_bBeeM6000_2mu4_l2io_L1BPH-0M9-EM7-EM5_2MU3V', l1SeedThresholds=['EM7','EM3','MU3V'], groups=BphysElectronGroup), + #ChainProp(name='HLT_e5_lhvloose_bBeeM6000_mu6_l2io_L1BPH-0DR3-EM7J15_MU5VF', l1SeedThresholds=['EM7','MU5VF'], groups=BphysElectronGroup), + #ChainProp(name='HLT_e5_lhvloose_bBeeM6000_2mu4_l2io_L1BPH-0DR3-EM7J15_2MU3V', l1SeedThresholds=['EM7','MU3V'], groups=BphysElectronGroup), # Tests of potential TLA chains for cost/rate # ATR-19317 - dijet+ISR diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py index af05a50e449931522c2d2aa88f4bc33ea20bcffa..3eb170474bddae9b9d64ab83081d6f28eef010b1 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py @@ -356,7 +356,7 @@ MuonChainParts_Default = { AllowedTopos_Bphysics = [ 'bJpsimumu','bJpsi','bUpsimumu','bUpsi','bBmumu','bDimu','bDimu2700','bDimu6000','bPhi','bTau','bJpsimumul2io', 'Lxy0', - 'bBmumux','BpmumuKp','BcmumuPi','BsmumuPhi','BdmumuKst','LbPqKm', 'BcmumuDsloose' + 'bBmumux','BpmumuKp','BcmumuPi','BsmumuPhi','BdmumuKst','LbPqKm', 'BcmumuDsloose', 'BcmumuDploose' ] # ---- Bphysics Dictionary of all allowed Values ----