diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/CMakeLists.txt b/Trigger/TrigHypothesis/TrigBphysHypo/CMakeLists.txt index b9fea33fcd54c677433db89ddece17b96c6f8510..7277228c79334fe456bf0e253ef1ae1e514812ca 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/CMakeLists.txt +++ b/Trigger/TrigHypothesis/TrigBphysHypo/CMakeLists.txt @@ -12,7 +12,7 @@ atlas_add_component( TrigBphysHypo src/*.cxx src/components/*.cxx INCLUDE_DIRS ${CLHEP_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} - LINK_LIBRARIES ${CLHEP_LIBRARIES} ${ROOT_LIBRARIES} AthLinks AthViews AthenaBaseComps AthenaKernel AthenaMonitoringKernelLib BeamSpotConditionsData DecisionHandlingLib ElectronPhotonSelectorToolsLib FourMomUtils InDetConversionFinderToolsLib LumiBlockCompsLib StoreGateLib TrigBphysicsEvent TrigCompositeUtilsLib TrigConfHLTData TrigInDetEvent TrigInDetToolInterfacesLib TrigInterfacesLib TrigNavigationLib TrigParticle TrigSteeringEvent TrigTimeAlgsLib TrkVKalVrtFitterLib xAODBase xAODEgamma xAODEventInfo xAODMuon xAODTracking xAODTrigBphys xAODTrigMuon xAODTrigger ) + LINK_LIBRARIES ${CLHEP_LIBRARIES} ${ROOT_LIBRARIES} AthLinks AthViews AthenaBaseComps AthenaKernel AthenaMonitoringKernelLib BeamSpotConditionsData DecisionHandlingLib ElectronPhotonSelectorToolsLib FourMomUtils InDetConversionFinderToolsLib ITrackToVertex LumiBlockCompsLib StoreGateLib TrigBphysicsEvent TrigCompositeUtilsLib TrigConfHLTData TrigInDetEvent TrigInDetToolInterfacesLib TrigInterfacesLib TrigNavigationLib TrigParticle TrigSteeringEvent TrigTimeAlgsLib TrkVKalVrtFitterLib xAODBase xAODEgamma xAODEventInfo xAODMuon xAODTracking xAODTrigBphys xAODTrigMuon xAODTrigger ) # Install files from the package: atlas_install_python_modules( python/*.py POST_BUILD_CMD ${ATLAS_FLAKE8} --extend-extensions=ATL900,ATL901 ) diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoConfig.py b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoConfig.py new file mode 100644 index 0000000000000000000000000000000000000000..80f62a8821b1ff3f4abf6a1e813aafb2fda8444e --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoConfig.py @@ -0,0 +1,118 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +from TrigBphysHypo.TrigBphysHypoConf import TrigBmumuxComboHypo, TrigBmumuxComboHypoTool +from TrigBphysHypo.TrigBmumuxComboHypoMonitoringConfig import TrigBmumuxComboHypoMonitoring, TrigBmumuxComboHypoToolMonitoring + +from AthenaCommon.Logging import logging +log = logging.getLogger('TrigBmumuxComboHypoConfig') +log.setLevel(logging.DEBUG) + +def BmumuxComboHypoCfg(name): + log.debug('BmumuxComboHypoCfg.name = %s ', name) + suffix = 'Bmumux' + + from TrkExTools.AtlasExtrapolator import AtlasExtrapolator + from TrkVKalVrtFitter.TrkVKalVrtFitterConf import Trk__TrkVKalVrtFitter + vertexFitter = Trk__TrkVKalVrtFitter( + name = 'TrigBphysFitter_'+suffix, + FirstMeasuredPoint = False, + MakeExtendedVertex = False, + Extrapolator = AtlasExtrapolator()) + + from InDetConversionFinderTools.InDetConversionFinderToolsConf import InDet__VertexPointEstimator + vertexPointEstimator = InDet__VertexPointEstimator( + name = 'VertexPointEstimator_'+suffix, + MinDeltaR = [-10000., -10000., -10000.], + MaxDeltaR = [ 10000., 10000., 10000.], + MaxPhi = [ 10000., 10000., 10000.], + MaxChi2OfVtxEstimation = 2000.) + + from TrackToVertex.TrackToVertexConf import Reco__TrackToVertex + trackToVertexTool = Reco__TrackToVertex( + name = 'TrackToVertexTool_'+suffix, + Extrapolator = AtlasExtrapolator()) + + hypo = TrigBmumuxComboHypo( + name = 'BmumuxComboHypo', + VertexFitter = vertexFitter, + VertexPointEstimator = vertexPointEstimator, + TrackToVertexTool = trackToVertexTool, + CheckMultiplicityMap = False, + TrigBphysCollectionKey = 'HLT_Bmumux', + MuonCollectionKey = 'HLT_Muons_Bmumux', + TrackCollectionKey = 'HLT_IDTrack_Bmumux_IDTrig', + DeltaR = 0.01, + TrkZ0 = 50., + MaxFitAttempts_DimuTrk1 = 200, + MaxFitAttempts_DimuTrk1Trk2 = 2000, + # dimuon properties + Dimuon_rejectSameChargeTracks = True, + Dimuon_massRange = (100., 5500.), + Dimuon_chi2 = 20., + # B+ -> mu+ mu- K+ + BplusToMuMuKaon = True, + BplusToMuMuKaon_minKaonPt = 100., + BplusToMuMuKaon_massRange = (4500., 5900.), + BplusToMuMuKaon_chi2 = 50., + # B_c+ -> J/psi(-> mu+ mu-) pi+ + BcToMuMuPion = True, + BcToMuMuPion_minPionPt = 2000., + BcToMuMuPion_dimuonMassRange = (2500., 4300.), + BcToMuMuPion_massRange = (5500., 7300.), + BcToMuMuPion_chi2 = 50., + # B_s0 -> mu+ mu- phi(-> K+ K-) + BsToMuMuPhi1020 = True, + BsToMuMuPhi1020_rejectSameChargeTracks = True, + BsToMuMuPhi1020_minKaonPt = 100., + BsToMuMuPhi1020_massRange = (4800., 5800.), + BsToMuMuPhi1020_phiMassRange = (940., 1100.), + BsToMuMuPhi1020_chi2 = 60., + # B0 -> mu+ mu- K*0(-> K+ pi-) + BdToMuMuKstar0 = True, + BdToMuMuKstar0_rejectSameChargeTracks = True, + BdToMuMuKstar0_minKaonPt = 100., + BdToMuMuKstar0_minPionPt = 100., + BdToMuMuKstar0_massRange = (4600., 5900.), + BdToMuMuKstar0_KstarMassRange = (700., 1100.), + BdToMuMuKstar0_chi2 = 60., + # Lambda_b0 -> J/psi(-> mu+ mu-) p K- + LambdaBToMuMuProtonKaon = True, + LambdaBToMuMuProtonKaon_rejectSameChargeTracks = False, + LambdaBToMuMuProtonKaon_minProtonPt = 1000., + LambdaBToMuMuProtonKaon_minKaonPt = 1000., + LambdaBToMuMuProtonKaon_minKstarMass = 1300., + LambdaBToMuMuProtonKaon_dimuonMassRange = (2500., 4300.), + LambdaBToMuMuProtonKaon_massRange = (4800., 6400.), + LambdaBToMuMuProtonKaon_chi2 = 60., + # + MonTool = TrigBmumuxComboHypoMonitoring('TrigBmumuxComboHypoMonitoring')) + + return hypo + + +def TrigBmumuxComboHypoToolFromDict(chainDict): + config = TrigBmumuxComboHypoConfig() + tool = config.ConfigurationComboHypoTool(chainDict) + return tool + + +class TrigBmumuxComboHypoConfig(object): + def ConfigurationComboHypoTool(self, chainDict): + topoAlgs = chainDict['chainName'] + log.debug("Set for algorithm %s", topoAlgs) + + tool = TrigBmumuxComboHypoTool(topoAlgs) + decay = chainDict['topo'][-1] + trigDecayDict = { # xAOD::TrigBphys::pType + 'BpmumuKp': 7, # BKMUMU + 'BcmumuPi': 21, # BCPIMUMU + 'BsmumuPhi': 9, # BSPHIMUMU + 'BdmumuKst': 8, # BDKSTMUMU + 'LbPqKm': 22, # LBPQMUMU + } + + tool.Decay = trigDecayDict[decay] + + tool.MonTool = TrigBmumuxComboHypoToolMonitoring('MonTool') + + return tool diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoMonitoringConfig.py b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoMonitoringConfig.py new file mode 100644 index 0000000000000000000000000000000000000000..74eb8c8d62ca2e88164e9043f5d51d5d94261c03 --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoMonitoringConfig.py @@ -0,0 +1,23 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +from AthenaMonitoringKernel.GenericMonitoringTool import GenericMonitoringTool, defineHistogram + +class TrigBmumuxComboHypoMonitoring(GenericMonitoringTool): + def __init__ (self, name): + super(TrigBmumuxComboHypoMonitoring, self).__init__(name) + self.Histograms = [ + defineHistogram('nDimuon', type='TH1F', path='EXPERT', title="number of fitted dimuon vertices", xbins=10, xmin=0, xmax=10), + defineHistogram('nTrk', type='TH1F', path='EXPERT', title="number of merged tracks in extended RoIs", xbins=200, xmin=0, xmax=200), + defineHistogram('nSelectedTrk', type='TH1F', path='EXPERT', title="number of tracks in vicinity of dimuon vertex", xbins=200, xmin=0, xmax=200), + defineHistogram('nBPhysObject', type='TH1F', path='EXPERT', title="number of fitted BPhysObjects", xbins=100, xmin=0, xmax=100), + ] + +class TrigBmumuxComboHypoToolMonitoring(GenericMonitoringTool): + def __init__ (self, name): + super(TrigBmumuxComboHypoToolMonitoring, self).__init__(name) + self.Histograms = [ + defineHistogram('Chi2', type='TH1F', path='EXPERT', title="chi2 of the fitted vertex", xbins=100, xmin=0, xmax=100), + defineHistogram('Fitmass', type='TH1F', path='EXPERT', title="mass of BPhys object", xbins=100, xmin=4000, xmax=8000), + defineHistogram('Mass', type='TH1F', path='EXPERT', title="mass(BPhys object) - mass(dimuon) + PDG::mJpsi", xbins=100, xmin=4000, xmax=8000), + defineHistogram('Pt', type='TH1F', path='EXPERT', title="p_{T} of BPhys object", xbins=100, xmin=0, xmax=40000), + ] diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBphysStreamerHypoConfig.py b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBphysStreamerHypoConfig.py new file mode 100644 index 0000000000000000000000000000000000000000..fe26b09e085bbf1cea90885123fccc6c4fa599bb --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBphysStreamerHypoConfig.py @@ -0,0 +1,7 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +from TrigBphysHypo.TrigBphysHypoConf import TrigBphysStreamerHypoTool + +def TrigBphysStreamerHypoToolFromDict(chainDict): + tool = TrigBphysStreamerHypoTool(chainDict['chainName']) + return tool diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/Constants.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/Constants.h index 475f4edba579422b55cdadefac156d2bdfb78b40..1cab1e5542e6e03384c602700d3aa5bbed614990 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/Constants.h +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/Constants.h @@ -24,12 +24,33 @@ constexpr double LAMBDABMASS = 5620.2; //MeV constexpr double BCMASS = 6277.0; //MeV /// list of decay constexprants BMuMuX -constexpr int di_to_muons = 0; +constexpr int di_to_muons = 0; constexpr int b_to_K = 1; // to recognize type of decay B+/- ->K+/- mu+ mu- constexpr int bD_to_Kstar = 2; // to recognize type of decay Bd ->K*(K+Pi-) mu+ mu- constexpr int bS_to_Phi = 3; // to recognize type of decay Bs ->Phi(K+K-) mu+ mu- constexpr int lB_to_L = 4; // to recognize type of decay Lb ->L(PPi-) mu+ mu- constexpr int bC_to_PiPi = 5; // to recognize type of decay Bc ->D+(K-pi+pi+) mu+ mu- -constexpr int di_to_electrons = 6; +constexpr int di_to_electrons = 6; + +// PDG'2020 +struct PDG20 { + static constexpr double + mElectron = 0.5109989461, + mMuon = 105.6583745, + mPion = 139.57039, + mPion0 = 134.9768, + mKaon = 493.677, + mK_S0 = 497.611, + mPhi1020 = 1019.461, + mProton = 938.2720813, + mLambda0 = 1115.683, + mJpsi = 3096.900, + mPsi2S = 3686.097, + mB = 5279.32, + mB0 = 5279.63, + mB_s0 = 5366.89, + mB_c = 6274.9, + mLambda_b0 = 5619.60; +}; #endif diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/ITrigBphysState.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/ITrigBphysState.h new file mode 100644 index 0000000000000000000000000000000000000000..803de129bd8a12a91bd21f25fc6076deb62d7485 --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/ITrigBphysState.h @@ -0,0 +1,19 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIG_ITrigBphysState_H +#define TRIG_ITrigBphysState_H + +/** + * @class ITrigBphysState + * @brief Base class for TrigBphys state objects + */ + +class ITrigBphysState +{ + public: + virtual ~ITrigBphysState() = default; +}; + +#endif // TRIG_ITrigBphysState_H diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2ad51468e677782f1e0b852cea3c3499b4c902f2 --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.cxx @@ -0,0 +1,725 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#include <algorithm> +#include <numeric> +#include <iterator> + +#include "TrigBmumuxComboHypo.h" + +#include "xAODMuon/Muon.h" +#include "xAODTracking/TrackParticle.h" +#include "xAODTracking/Vertex.h" +#include "xAODTracking/VertexContainer.h" +#include "xAODTracking/VertexAuxContainer.h" +#include "xAODTrigger/TrigComposite.h" +#include "xAODTrigBphys/TrigBphys.h" +#include "xAODTrigBphys/TrigBphysContainer.h" +#include "xAODTrigBphys/TrigBphysAuxContainer.h" + +#include "TrigCompositeUtils/TrigCompositeUtils.h" +#include "TrigCompositeUtils/HLTIdentifier.h" +#include "TrigConfHLTData/HLTUtils.h" + +#include "AthViews/View.h" +#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 GenVecFourMom_t = ROOT::Math::LorentzVector<ROOT::Math::PxPyPzM4D<double>>; + + +namespace { +static const SG::AuxElement::Accessor<std::vector<size_t>> muonIndices("MuonIndices"); +static const SG::AuxElement::ConstAccessor<std::vector<size_t>> getMuonIndices("MuonIndices"); +} // namespace + + +TrigBmumuxComboHypo::TrigBmumuxComboHypo(const std::string& name, ISvcLocator* pSvcLocator) + : ::ComboHypo(name, pSvcLocator) {} + + +StatusCode TrigBmumuxComboHypo::initialize() { + ATH_MSG_DEBUG( "TrigBmumuxComboHypo::initialize()" ); + + ATH_CHECK( ::ComboHypo::initialize() ); + + ATH_CHECK( m_muonContainerKey.initialize() ); + renounce(m_muonContainerKey); + ATH_CHECK( m_trackParticleContainerKey.initialize() ); + renounce(m_trackParticleContainerKey); + ATH_CHECK( m_trigBphysContainerKey.initialize() ); + + ATH_CHECK( m_vertexFitter.retrieve() ); + ATH_CHECK( m_vertexPointEstimator.retrieve() ); + ATH_CHECK( m_trackToVertexTool.retrieve() ); + + // allowed IDs to filter out incoming decisions at L2 level + for (const auto& item : triggerMultiplicityMap()) { + const HLT::Identifier id = HLT::Identifier::fromToolName(item.first); + m_allowedIDs.insert(id.numeric()); + if (item.second.size() > 1) { + for (size_t i = 0; i < item.second.size(); i++) { + m_allowedIDs.insert(TrigCompositeUtils::createLegName(id, i).numeric()); + } + } + } + if (msgLvl(MSG::DEBUG)) { + ATH_MSG_DEBUG( "Allowed decisions:" ); + for (const DecisionID& id : m_allowedIDs) { + ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) ); + } + } + + // add IDs to ComboHypoTools to check that each trigger leg fulfill TrigCompositeUtils::passed() requirement + for (auto& tool : hypoTools()) { + const HLT::Identifier id = tool->decisionId(); + const auto itr = triggerMultiplicityMap().find(id.name()); + if (itr == triggerMultiplicityMap().end()) { + ATH_MSG_ERROR( "No entry found for " << tool->name() << " in triggerMultiplicityMap" ); + } + const std::vector<int>& multiplicity = itr->second; + std::vector<HLT::Identifier> legDecisionIds; + if (multiplicity.size() == 1) { + std::fill_n(std::back_inserter(legDecisionIds), multiplicity[0], id); + } + else { + size_t n = static_cast<size_t>(std::accumulate(multiplicity.begin(), multiplicity.end(), 0)); + for (size_t i = 0; i < n; i++) { + legDecisionIds.push_back(TrigCompositeUtils::createLegName(id, i)); + } + } + tool->setLegDecisionIds(legDecisionIds); + if (msgLvl(MSG::DEBUG)) { + ATH_MSG_DEBUG( "Leg decisions for tool " << tool->name() ); + for (const auto& legId : legDecisionIds) { + ATH_MSG_DEBUG( " +++ " << legId ); + } + } + } + + if (!m_monTool.empty()) { + ATH_CHECK( m_monTool.retrieve() ); + ATH_MSG_DEBUG( "GenericMonitoringTool name:" << m_monTool ); + } + else { + ATH_MSG_DEBUG( "No GenericMonitoringTool configured: no monitoring histograms will be available" ); + } + + return StatusCode::SUCCESS; +} + + +StatusCode TrigBmumuxComboHypo::finalize() { + TrigConf::HLTUtils::hashes2file(); + return StatusCode::SUCCESS; +} + + +StatusCode TrigBmumuxComboHypo::execute(const EventContext& context) const { + + ATH_MSG_DEBUG( "TrigBmumuxComboHypo::execute() starts" ); + + ATH_MSG_DEBUG( "decision input key: " << decisionsInput().at(0).key() ); + auto previousDecisionsHandle = SG::makeHandle(decisionsInput().at(0), context); + ATH_CHECK( previousDecisionsHandle.isValid() ); + ATH_MSG_DEBUG( "Running with " << previousDecisionsHandle->size() << " previous decisions" ); + + SG::WriteHandle<DecisionContainer> outputDecisionsHandle = TrigCompositeUtils::createAndStore(decisionsOutput().at(0), context); + + auto trigBphysHandle = SG::makeHandle(m_trigBphysContainerKey, context); + 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) ); + ATH_CHECK( findDimuonCandidates(*state) ); + + if ( !state->dimuons.empty() ) { + ATH_CHECK( mergeTracksFromViews(*state) ); + ATH_CHECK( findBmumuxCandidates(*state) ); + ATH_CHECK( createDecisionObjects(*state) ); + } + + ATH_MSG_DEBUG( "TrigBmumuxComboHypo::execute() terminates with StatusCode::SUCCESS" ); + return StatusCode::SUCCESS; +} + + +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 { + + auto& muons = state.muons; + muons.clear(); + + // all muons from views are already connected with previous decisions by TrigMuonEFHypoAlg + 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 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()}); + } + else { + (*itr).decisionLinks.push_back(decisionEL); + } + } + + // muon->pt() is equal to muon->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)->pt() + // and the later is used by TrigMuonEFHypoTool for classification of muEFCB candidates + std::sort(muons.begin(), muons.end(), [](const auto& lhs, const auto& rhs){ return ((*lhs.link)->pt() > (*rhs.link)->pt()); }); + + // for each muon we extract DecisionIDs stored in the associated Decision objects and copy them at muon.decisionIDs + for (auto& item : muons) { + for (const auto& decisionEL : item.decisionLinks) { + TrigCompositeUtils::decisionIDs(*decisionEL, item.decisionIDs); + } + } + + if (msgLvl(MSG::DEBUG)) { + ATH_MSG_DEBUG( "Dump found muons before vertex fit: " << muons.size() << " candidates" ); + for (const auto& item : muons) { + const xAOD::Muon* muon = *item.link; + const xAOD::TrackParticle* track = *muon->inDetTrackParticleLink(); + ATH_MSG_DEBUG( " -- muon InDetTrackParticle pt/eta/phi/q: " << track->pt() << " / " << track->eta() << " / " << track->phi() << " / " << track->charge() ); + ATH_MSG_DEBUG( " muon CombinedTrackParticle pt: " << muon->pt() ); + ATH_MSG_DEBUG( " allowed decisions:" ); + for (const DecisionID& id : item.decisionIDs) { + ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) ); + } + } + } + return StatusCode::SUCCESS; +} + + +StatusCode TrigBmumuxComboHypo::mergeTracksFromViews(TrigBmumuxState& state) const { + + auto& tracks = state.tracks; + tracks.clear(); + + size_t viewCounter = 0; + 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); + 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)); + } + + for (const auto& trackEL : tracksFromView) { + const xAOD::TrackParticle* track = *trackEL; + if (track->definingParametersCovMatrixVec().empty()) continue; + + if (viewCounter == 0 || + std::find_if(tracks.begin(), tracks.end(), + [this,track = track](const auto& x){ return isIdenticalTracks(track, *x); }) == tracks.end()) { + tracks.emplace_back(trackEL); + } + } + viewCounter++; + } + std::sort(tracks.begin(), tracks.end(), [](const auto& lhs, const auto& rhs){ return ((*lhs)->pt() > (*rhs)->pt()); }); + + if (msgLvl(MSG::DEBUG)) { + ATH_MSG_DEBUG( "Dump found tracks before vertex fit: " << tracks.size() << " candidates" ); + for (const auto& trackEL : tracks) { + const xAOD::TrackParticle* track = *trackEL; + ATH_MSG_DEBUG( " -- track pt/eta/phi/q: " << track->pt() << " / " << track->eta() << " / " << track->phi() << " / " << track->charge() ); + } + } + return StatusCode::SUCCESS; +} + + +StatusCode TrigBmumuxComboHypo::findDimuonCandidates(TrigBmumuxState& state) const { + + auto mon_nDimuon = Monitored::Scalar<int>("nDimuon", 0); + auto group = Monitored::Group(m_monTool, mon_nDimuon); + + const auto& muons = state.muons; + std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks(2); + std::vector<const DecisionIDContainer*> previousDecisionIDs(2, nullptr); + for (size_t itrk1 = 0; itrk1 < muons.size(); ++itrk1) { + const xAOD::Muon* mu1 = *muons[itrk1].link; + trackParticleLinks[0] = mu1->inDetTrackParticleLink(); + previousDecisionIDs[0] = &muons[itrk1].decisionIDs; + const xAOD::TrackParticle* trk1 = *trackParticleLinks[0]; + auto p1 = trk1->genvecP4(); + p1.SetM(PDG::mMuon); + auto charge1 = trk1->charge(); + + for (size_t itrk2 = itrk1 + 1; itrk2 < muons.size(); ++itrk2) { + const xAOD::Muon* mu2 = *muons[itrk2].link; + trackParticleLinks[1] = mu2->inDetTrackParticleLink(); + previousDecisionIDs[1] = &muons[itrk2].decisionIDs; + const xAOD::TrackParticle* trk2 = *trackParticleLinks[1]; + auto p2 = trk2->genvecP4(); + p2.SetM(PDG::mMuon); + auto charge2 = trk2->charge(); + + double mass = (p1 + p2).M(); + + ATH_MSG_DEBUG( "muon 1: " << p1.Pt()<< " / " << p1.Eta() << " / " << p1.Phi() << " / " << trk1->charge() ); + ATH_MSG_DEBUG( "muon 2: " << p2.Pt()<< " / " << p2.Eta() << " / " << p2.Phi() << " / " << trk2->charge() ); + ATH_MSG_DEBUG( "track pair mass: " << mass ); + + if (m_dimuon_rejectSameChargeTracks && charge1 * charge2 > 0.) { + ATH_MSG_DEBUG( "muon pair is rejected by opposite charge check" ); + continue; + } + + if (!passDimuonTrigger(previousDecisionIDs)) { + ATH_MSG_DEBUG( "muon pair did not pass passDimuonTrigger() check" ); + continue; + } + + if (!isInMassRange(mass, m_dimuon_massRange)) { + ATH_MSG_DEBUG( "muon pair is out of the requested mass range" ); + continue; + } + + // 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(trackParticleLinks); + if (!vertex) continue; + muonIndices(*vertex) = std::vector<size_t>{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); + 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); + } + } + mon_nDimuon = state.dimuons.size(); + ATH_MSG_DEBUG( "Found " << state.dimuons.size() << " dimuon candidates" ); + + return StatusCode::SUCCESS; +} + + +StatusCode TrigBmumuxComboHypo::findBmumuxCandidates(TrigBmumuxState& state) const { + + std::vector<int> nSelectedTrk; + auto mon_nTrk = Monitored::Scalar<int>("nTrk", 0); + auto mon_nSelectedTrk = Monitored::Collection("nSelectedTrk", nSelectedTrk); + auto mon_nBPhysObject = Monitored::Scalar<int>("nBPhysObject", 0); + auto group = Monitored::Group(m_monTool, mon_nTrk, mon_nSelectedTrk, mon_nBPhysObject); + + const auto& tracks = state.tracks; + mon_nTrk = tracks.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); + ATH_CHECK( dimuonTriggerObjectEL.isValid() ); + + // vtx1 = {mu1, mu2, trk1} + std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_vtx1(dimuon->trackParticleLinks()); + trackParticleLinks_vtx1.emplace_back(); + + // vtx2 = {mu1, mu2, trk1, trk2} + std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_vtx2(trackParticleLinks_vtx1); + trackParticleLinks_vtx2.emplace_back(); + + const xAOD::TrackParticle* mu1 = *trackParticleLinks_vtx1[0]; + const xAOD::TrackParticle* mu2 = *trackParticleLinks_vtx1[1]; + auto p_dimuon = mu1->genvecP4().SetM(PDG::mMuon) + mu2->genvecP4().SetM(PDG::mMuon); + + // check impact parameter of the track with respect to the fitted dimuon vertex + // we can safely omit tracks with large z0 + std::vector<ElementLink<xAOD::TrackParticleContainer>> selectedTracks; + selectedTracks.reserve(tracks.size()); + if (m_trkZ0 > 0.) { + for (const auto& trackEL : tracks) { + const Trk::Perigee* perigee = m_trackToVertexTool->perigeeAtVertex(**trackEL, dimuon->position()); + if (fabs(perigee->parameters()[Trk::z0]) < m_trkZ0) { + selectedTracks.push_back(trackEL); + } + delete perigee; + } + ATH_MSG_DEBUG( "Found " << selectedTracks.size() << " tracks consistent with dimuon vertex " << idx ); + } + else { + std::copy(tracks.begin(), tracks.end(), std::back_inserter(selectedTracks)); + } + nSelectedTrk.push_back(selectedTracks.size()); + + // dimuon + 1 track + for (size_t itrk1 = 0; itrk1 < selectedTracks.size(); ++itrk1) { + const xAOD::TrackParticle* trk1 = *selectedTracks[itrk1]; + if (isIdenticalTracks(mu1, trk1) || isIdenticalTracks(mu2, trk1)) continue; + + trackParticleLinks_vtx1[2] = selectedTracks[itrk1]; + auto p_trk1 = trk1->genvecP4(); + auto charge1 = trk1->charge(); + + xAOD::Vertex* vtx1 = nullptr; + 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(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); + ATH_CHECK( state.addTriggerObject(trigBphys) ); + } + } + + // B_c+ -> J/psi(-> mu+ mu-) pi+ + if (m_BcToMuMuPion && + p_trk1.Pt() > m_BcToMuMuPion_minPionPt && + 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(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); + ATH_CHECK( state.addTriggerObject(trigBphys) ); + } + } + + delete vtx1; + vtx1 = nullptr; + + // dimuon + 2 tracks + for (size_t itrk2 = itrk1 + 1; itrk2 < selectedTracks.size(); ++itrk2) { + const xAOD::TrackParticle* trk2 = *selectedTracks[itrk2]; + if (isIdenticalTracks(mu1, trk2) || isIdenticalTracks(mu2, trk2)) continue; + + trackParticleLinks_vtx2[2] = selectedTracks[itrk1]; + trackParticleLinks_vtx2[3] = selectedTracks[itrk2]; + auto p_trk2 = trk2->genvecP4(); + auto charge2 = trk2->charge(); + + xAOD::Vertex* vtx2 = nullptr; + bool makeFit_vtx2 = true; + + // B_s0 -> mu+ mu- phi(-> K+ K-) + if (m_BsToMuMuPhi1020 && + (!m_BsToMuMuPhi1020_rejectSameChargeTracks || charge1 * charge2 < 0.) && + p_trk1.Pt() > m_BsToMuMuPhi1020_minKaonPt && + 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(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); + ATH_CHECK( state.addTriggerObject(trigBphys) ); + } + } + + // B0 -> mu+ mu- K*0(-> K+ pi-) + if (m_BdToMuMuKstar0 && + (!m_BdToMuMuKstar0_rejectSameChargeTracks || charge1 * charge2 < 0.) && + p_trk1.Pt() > m_BdToMuMuKstar0_minKaonPt && + p_trk2.Pt() > m_BdToMuMuKstar0_minPionPt && + 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(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); + ATH_CHECK( state.addTriggerObject(trigBphys) ); + } + } + // anti-B0 -> mu+ mu- anti-K*0(-> K- pi+) + if (m_BdToMuMuKstar0 && + (!m_BdToMuMuKstar0_rejectSameChargeTracks || charge1 * charge2 < 0.) && + p_trk1.Pt() > m_BdToMuMuKstar0_minPionPt && + p_trk2.Pt() > m_BdToMuMuKstar0_minKaonPt && + 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(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); + ATH_CHECK( state.addTriggerObject(trigBphys) ); + } + } + + // Lambda_b0 -> J/psi(-> mu+ mu-) p K- + if (m_LambdaBToMuMuProtonKaon && + p_trk1.Pt() > m_LambdaBToMuMuProtonKaon_minProtonPt && + p_trk2.Pt() > m_LambdaBToMuMuProtonKaon_minKaonPt && + (p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mPion)).M() > m_LambdaBToMuMuProtonKaon_minKstarMass && + (p_trk1.SetM(PDG::mPion) + p_trk2.SetM(PDG::mKaon)).M() > m_LambdaBToMuMuProtonKaon_minKstarMass && + 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(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); + ATH_CHECK( state.addTriggerObject(trigBphys) ); + } + } + // anti-Lambda_b0 -> J/psi(-> mu+ mu-) anti-p K+ + if (m_LambdaBToMuMuProtonKaon && + p_trk1.Pt() > m_LambdaBToMuMuProtonKaon_minKaonPt && + p_trk2.Pt() > m_LambdaBToMuMuProtonKaon_minProtonPt && + (p_trk1.SetM(PDG::mKaon) + p_trk2.SetM(PDG::mPion)).M() > m_LambdaBToMuMuProtonKaon_minKstarMass && + (p_trk1.SetM(PDG::mPion) + p_trk2.SetM(PDG::mKaon)).M() > m_LambdaBToMuMuProtonKaon_minKstarMass && + 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(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); + ATH_CHECK( state.addTriggerObject(trigBphys) ); + } + } + + delete vtx2; + vtx2 = nullptr; + + } + } + } + mon_nBPhysObject = state.trigBphysCollection->size() - mon_nBPhysObject; + + return StatusCode::SUCCESS; +} + + +StatusCode TrigBmumuxComboHypo::createDecisionObjects(TrigBmumuxState& state) const { + + 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()); + ATH_CHECK( triggerObjectEL.isValid() ); + + const xAOD::TrigBphys* dimuonTriggerObject = triggerObject->lowerChain(); + if (!dimuonTriggerObject) { + ATH_MSG_ERROR( "Failed to found a valid link for preceding dimuon trigger object" ); + return StatusCode::FAILURE; + } + + // need to get the references to the original muon objects used to build the dimuon vertex + // the position of this vertex in state.dimuons container is the same as for dimuonTriggerObject in trigBphysCollection + // dimuon vertex has already been decorated with muon indices + const xAOD::Vertex* dimuon = state.dimuons.get(dimuonTriggerObject->index()); + if ( !dimuon || !getMuonIndices.isAvailable(*dimuon) ) { + ATH_MSG_ERROR( "Failed to find original muons the dimuon vertex had been built from" ); + return StatusCode::FAILURE; + } + + // create a new output Decision object, backed by the 'decisions' container. + Decision* decision = TrigCompositeUtils::newDecisionIn(state.decisions); + + std::vector<const DecisionIDContainer*> previousDecisionIDs; + for (const size_t& i : getMuonIndices(*dimuon)) { + const auto& muon = state.muons.at(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(), muon.decisionLinks); + previousDecisionIDs.push_back(&muon.decisionIDs); + } + + // set mandatory link to the trigger object + decision->setObjectLink<xAOD::TrigBphysContainer>(TrigCompositeUtils::featureString(), triggerObjectEL); + + for (const auto& tool : hypoTools()) { + ATH_MSG_DEBUG( "Go to " << tool ); + ATH_CHECK( tool->decideOnSingleObject(decision, previousDecisionIDs) ); + } + } + + return StatusCode::SUCCESS; +} + + +xAOD::Vertex* TrigBmumuxComboHypo::fit(const std::vector<ElementLink<xAOD::TrackParticleContainer>>& trackParticleLinks, + Decay decay, + const xAOD::TrigBphys* dimuon) const { + ATH_MSG_DEBUG( "Perform vertex fit" ); + + if (trackParticleLinks.size() < 2) { + ATH_MSG_WARNING( "At least two tracks should be given to the vertex fitter" ); + return nullptr; + } + + std::vector<const xAOD::TrackParticle*> tracklist(trackParticleLinks.size(), nullptr); + std::transform(trackParticleLinks.begin(), trackParticleLinks.end(), tracklist.begin(), + [](const ElementLink<xAOD::TrackParticleContainer>& link){ return *link; }); + + Amg::Vector3D startingPoint = Amg::Vector3D::Zero(3); + if (dimuon) { + startingPoint = Amg::Vector3D(dimuon->fitx(), dimuon->fity(), dimuon->fitz()); + } + else { + if (decay != Decay::kPsi_2mu) { + ATH_MSG_WARNING( "Already fitted dimuon vertex should be provided for B -> mu1 mu2 trk1 .. trkN decay as a starting point for fitter" ); + } + int flag = 0; + int errorcode = 0; + const Trk::Perigee& perigee1 = tracklist[0]->perigeeParameters(); + const Trk::Perigee& perigee2 = tracklist[1]->perigeeParameters(); + startingPoint = m_vertexPointEstimator->getCirclesIntersectionPoint(&perigee1, &perigee2, flag, errorcode); + if (errorcode != 0) startingPoint = Amg::Vector3D::Zero(3); + } + ATH_MSG_DEBUG( "Starting point: (" << startingPoint(0) << ", " << startingPoint(1) << ", " << startingPoint(2) << ")" ); + + auto fitterState = m_vertexFitter->makeState(); + m_vertexFitter->setMassInputParticles(m_trkMass[static_cast<size_t>(decay)], *fitterState); + xAOD::Vertex* vertex = m_vertexFitter->fit(tracklist, startingPoint, *fitterState); + if (!vertex) { + ATH_MSG_DEBUG( "Vertex fit fails" ); + return nullptr; + } + 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; + } + ATH_MSG_DEBUG( "Fit is successful" ); + + // update trackParticleLinks() + vertex->clearTracks(); + vertex->setTrackParticleLinks(trackParticleLinks); + + return vertex; +} + + +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(); + if (!perigee) return nullptr; + const Amg::Vector3D& p = perigee->momentum(); + momenta.emplace_back(p[Trk::px], p[Trk::py], p[Trk::pz], trkMass[i]); + momentum += momenta.back(); + } + + auto result = new xAOD::TrigBphys(); + result->makePrivateStore(); + + float mass = (type == xAOD::TrigBphys::MULTIMU ? momentum.M() : momentum.M() - (momenta[0] + momenta[1]).M() + PDG::mJpsi); + 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()); + + // set all the particles associated with the decay + result->setTrackParticleLinks(vertex->trackParticleLinks()); + + // use lowerChainLink() as a link to the preceding dimuon trigger object + if (type != xAOD::TrigBphys::MULTIMU) { + result->setLowerChainLink(dimuonLink); + } + + ATH_MSG_DEBUG( + "TrigBphys object:\n\t " << + "roiId: " << result->roiId() << "\n\t " << + "particleType: " << result->particleType() << "\n\t " << + "level: " << result->level() << "\n\t " << + "eta: " << result->eta() << "\n\t " << + "phi: " << result->phi() << "\n\t " << + "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() << ")" ); + + return result; +} + + +bool TrigBmumuxComboHypo::isIdenticalTracks(const xAOD::TrackParticle* lhs, const xAOD::TrackParticle* rhs) const { + + if (lhs->charge() * rhs->charge() < 0.) return false; + return (ROOT::Math::VectorUtil::DeltaR(lhs->genvecP4(), rhs->genvecP4()) < m_deltaR); +} + + +bool TrigBmumuxComboHypo::isIdenticalTracks(const xAOD::Muon* lhs, const xAOD::Muon* rhs) const { + + return isIdenticalTracks(*lhs->inDetTrackParticleLink(), *rhs->inDetTrackParticleLink()); +} + + +bool TrigBmumuxComboHypo::passDimuonTrigger(const std::vector<const DecisionIDContainer*>& previousDecisionIDs) const { + + if (previousDecisionIDs.size() != 2) { + return false; + } + + for (const auto& tool : hypoTools()) { + const std::vector<HLT::Identifier>& legDecisionIDs = tool->legDecisionIds(); + if (legDecisionIDs.size() != 2) continue; + + bool direct = true; + bool inverse = true; + for (size_t i = 0; i < 2; ++i) { + if (direct && !TrigCompositeUtils::passed(legDecisionIDs[i].numeric(), *previousDecisionIDs[i])) direct = false; + if (inverse && !TrigCompositeUtils::passed(legDecisionIDs[i].numeric(), *previousDecisionIDs[1-i])) inverse = false; + } + if (direct || inverse) return true; + } + return false; +} diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.h new file mode 100644 index 0000000000000000000000000000000000000000..2a26d34de3153f0d8112433991ed712f6ca79f68 --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.h @@ -0,0 +1,229 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIG_TrigBmumuxComboHypo_H +#define TRIG_TrigBmumuxComboHypo_H + +#include <string> +#include <vector> +#include <utility> + +#include "Gaudi/Property.h" +#include "xAODTracking/TrackParticleContainer.h" +#include "xAODTracking/VertexContainer.h" +#include "xAODTracking/VertexAuxContainer.h" +#include "xAODMuon/MuonContainer.h" +#include "xAODTrigger/TrigComposite.h" +#include "xAODTrigBphys/TrigBphys.h" +#include "xAODTrigBphys/TrigBphysContainer.h" + +#include "StoreGate/ReadHandleKey.h" +#include "StoreGate/WriteHandleKey.h" + +#include "TrigCompositeUtils/TrigCompositeUtils.h" +#include "DecisionHandling/ComboHypo.h" + +#include "TrkVKalVrtFitter/TrkVKalVrtFitter.h" +#include "InDetConversionFinderTools/VertexPointEstimator.h" +#include "ITrackToVertex/ITrackToVertex.h" + +#include "AthenaMonitoringKernel/Monitored.h" +#include "AthenaMonitoringKernel/GenericMonitoringTool.h" + +#include "ITrigBphysState.h" +#include "TrigBmumuxComboHypoTool.h" + +#include "Constants.h" +typedef struct PDG20 PDG; + + +/** + * @class TrigBmumuxState + * @brief State class for TrigBmumuxComboHypo algorithm + */ +class TrigBmumuxState: public ::ITrigBphysState { + public: + const EventContext* context; + const TrigCompositeUtils::DecisionContainer* previousDecisions; + TrigCompositeUtils::DecisionContainer* decisions; + xAOD::TrigBphysContainer* trigBphysCollection; + struct Muon { + ElementLink<xAOD::MuonContainer> link; + ElementLinkVector<TrigCompositeUtils::DecisionContainer> decisionLinks; + TrigCompositeUtils::DecisionIDContainer decisionIDs; + }; + std::vector<Muon> muons; + std::vector<ElementLink<xAOD::TrackParticleContainer>> tracks; + xAOD::VertexContainer dimuons; + xAOD::VertexAuxContainer dimuonsStore; + + StatusCode addTriggerObject(xAOD::TrigBphys* triggerObject) { + if (!triggerObject) { + return StatusCode::FAILURE; + } + trigBphysCollection->push_back(triggerObject); + return StatusCode::SUCCESS; + } +}; + + +/** + * @class TrigBmumuxComboHypo + * @brief EF hypothesis algorithm for B -> mu+ mu- X decays: + * B+ -> mu+ mu- K+ + * B_s0 -> mu+ mu- phi1020(-> K+, K-) + */ +class TrigBmumuxComboHypo: public ::ComboHypo { + public: + TrigBmumuxComboHypo(const std::string& name, ISvcLocator* pSvcLocator); + TrigBmumuxComboHypo() = delete; + + virtual StatusCode initialize() override; + virtual StatusCode execute(const EventContext& context) const override; + virtual StatusCode finalize() override; + + enum Decay : size_t { + kPsi_2mu, // psi -> mu+ mu- + kB_2mu1trk, // B -> mu+ mu- trk1 + kB_2mu2trk // B -> mu+ mu- trk1 trk2 + }; + + 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 mergeTracksFromViews(TrigBmumuxState&) const; + StatusCode findDimuonCandidates(TrigBmumuxState&) const; + StatusCode findBmumuxCandidates(TrigBmumuxState&) const; + StatusCode createDecisionObjects(TrigBmumuxState&) const; + xAOD::Vertex* fit(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; + + bool isIdenticalTracks(const xAOD::TrackParticle* lhs, const xAOD::TrackParticle* rhs) const; + bool isIdenticalTracks(const xAOD::Muon* lhs, const xAOD::Muon* rhs) const; + bool passDimuonTrigger(const std::vector<const DecisionIDContainer*>& previousDecisionIDs) const; + bool isInMassRange(double mass, const std::pair<double, double>& range) const { return (mass > range.first && mass < range.second); } + + SG::ReadHandleKey<xAOD::TrackParticleContainer> m_trackParticleContainerKey {this, + "TrackCollectionKey", "InDetTrackParticles", "input TrackParticle container name"}; + SG::ReadHandleKey<xAOD::MuonContainer> m_muonContainerKey {this, + "MuonCollectionKey", "Muons", "input EF Muon container name"}; + SG::WriteHandleKey<xAOD::TrigBphysContainer> m_trigBphysContainerKey {this, + "TrigBphysCollectionKey", "TrigBphysContainer", "output TrigBphysContainer name"}; + + // general properties + Gaudi::Property<double> m_deltaR {this, + "DeltaR", 0.01, "minimum deltaR between same-sign tracks (overlap removal)"}; + Gaudi::Property<double> m_trkZ0 {this, + "TrkZ0", 50., "maximum z0 impact parameter of the track wrt the fitted dimuon vertex; no preselection if negative"}; + Gaudi::Property<unsigned int> m_maxFitAttempts_DimuTrk1 {this, + "MaxFitAttempts_DimuTrk1", 200, "maximum vertex fitter calls for dimu+trk1 decays (time-out protect)"}; + Gaudi::Property<unsigned int> m_maxFitAttempts_DimuTrk1Trk2 {this, + "MaxFitAttempts_DimuTrk1Trk2", 2000, "maximum vertex fitter calls for dimu+trk1+trk2 decays (time-out protect)"}; + + // dimuon properties + Gaudi::Property<bool> m_dimuon_rejectSameChargeTracks {this, + "Dimuon_rejectSameChargeTracks", true, "if true, the only (mu+, mu-) pairs will be kept (no wrong-charge combinations)"}; + Gaudi::Property<std::pair<double, double>> m_dimuon_massRange {this, + "Dimuon_massRange", {100., 5500.}, "dimuon mass range"}; + Gaudi::Property<double> m_dimuon_chi2 {this, + "Dimuon_chi2", 20., "maximum chi2 of the dimuon vertex"}; + + // B+ -> mu+ mu- K+ + Gaudi::Property<bool> m_BplusToMuMuKaon {this, + "BplusToMuMuKaon", true, "switch on/off B+ -> mu+ mu- K+ decay"}; + Gaudi::Property<double> m_BplusToMuMuKaon_minKaonPt {this, + "BplusToMuMuKaon_minKaonPt", 100., "minimum pT of kaon track"}; + Gaudi::Property<std::pair<double, double>> m_BplusToMuMuKaon_massRange {this, + "BplusToMuMuKaon_massRange", {4500., 5900.}, "B+ mass range"}; + Gaudi::Property<float> m_BplusToMuMuKaon_chi2 {this, + "BplusToMuMuKaon_chi2", 50., "maximum chi2 of the fitted B+ vertex"}; + + // B_c+ -> J/psi(-> mu+ mu-) pi+ + Gaudi::Property<bool> m_BcToMuMuPion {this, + "BcToMuMuPion", true, "switch on/off B_c+ -> J/psi(-> mu+ mu-) pi+ decay"}; + Gaudi::Property<double> m_BcToMuMuPion_minPionPt {this, + "BcToMuMuPion_minPionPt", 2000., "minimum pT of pion track"}; + Gaudi::Property<std::pair<double, double>> m_BcToMuMuPion_dimuonMassRange {this, + "BcToMuMuPion_dimuonMassRange", {2500., 4300.}, "dimuon mass range for B_c+ decay"}; + Gaudi::Property<std::pair<double, double>> m_BcToMuMuPion_massRange {this, + "BcToMuMuPion_massRange", {5500., 7300.}, "B_c+ mass range"}; + Gaudi::Property<float> m_BcToMuMuPion_chi2 {this, + "BcToMuMuPion_chi2", 50., "maximum chi2 of the fitted B_c+ vertex"}; + + // B_s0 -> mu+ mu- phi(-> K+ K-) + Gaudi::Property<bool> m_BsToMuMuPhi1020 {this, + "BsToMuMuPhi1020", true, "switch on/off B_s0 -> mu+ mu- phi(-> K+ K-) decay"}; + Gaudi::Property<bool> m_BsToMuMuPhi1020_rejectSameChargeTracks {this, + "BsToMuMuPhi1020_rejectSameChargeTracks", true, "if true, the only (K+, K-) pairs will be kept (no wrong-charge combinations)"}; + Gaudi::Property<double> m_BsToMuMuPhi1020_minKaonPt {this, + "BsToMuMuPhi1020_minKaonPt", 100., "minimum pT of kaon tracks"}; + Gaudi::Property<std::pair<double, double>> m_BsToMuMuPhi1020_massRange {this, + "BsToMuMuPhi1020_massRange", {4800., 5800.}, "B_s0 mass range"}; + Gaudi::Property<std::pair<double, double>> m_BsToMuMuPhi1020_phiMassRange {this, + "BsToMuMuPhi1020_phiMassRange", {940., 1100.}, "phi1020 mass range"}; + Gaudi::Property<float> m_BsToMuMuPhi1020_chi2 {this, + "BsToMuMuPhi1020_chi2", 60., "maximum chi2 of the fitted B+ vertex"}; + + // B0 -> mu+ mu- K*0(-> K+ pi-) + Gaudi::Property<bool> m_BdToMuMuKstar0 {this, + "BdToMuMuKstar0", true, "switch on/off B0 -> mu+ mu- K*0(-> K+ pi-) decay"}; + Gaudi::Property<bool> m_BdToMuMuKstar0_rejectSameChargeTracks {this, + "BdToMuMuKstar0_rejectSameChargeTracks", true, "if true, the only (K+, pi-) and (K-, pi+) pairs will be kept (no wrong-charge combinations)"}; + Gaudi::Property<double> m_BdToMuMuKstar0_minKaonPt {this, + "BdToMuMuKstar0_minKaonPt", 100., "minimum pT of kaon track"}; + Gaudi::Property<double> m_BdToMuMuKstar0_minPionPt {this, + "BdToMuMuKstar0_minPionPt", 100., "minimum pT of pion track"}; + Gaudi::Property<std::pair<double, double>> m_BdToMuMuKstar0_massRange {this, + "BdToMuMuKstar0_massRange", {4600., 5900.}, "B0 mass range"}; + Gaudi::Property<std::pair<double, double>> m_BdToMuMuKstar0_KstarMassRange {this, + "BdToMuMuKstar0_KstarMassRange", {700., 1100.}, "K*0 mass range"}; + Gaudi::Property<float> m_BdToMuMuKstar0_chi2 {this, + "BdToMuMuKstar0_chi2", 60., "maximum chi2 of the fitted B0 vertex"}; + + // Lambda_b0 -> J/psi(-> mu+ mu-) p K- + Gaudi::Property<bool> m_LambdaBToMuMuProtonKaon {this, + "LambdaBToMuMuProtonKaon", true, "switch on/off Lambda_b0 -> J/psi(-> mu+ mu-) p K- decay"}; + Gaudi::Property<bool> m_LambdaBToMuMuProtonKaon_rejectSameChargeTracks {this, + "LambdaBToMuMuProtonKaon_rejectSameChargeTracks", false, "if true, the only (p, K-) and (anti-p, K+) pairs will be kept (no wrong-charge combinations)"}; + Gaudi::Property<double> m_LambdaBToMuMuProtonKaon_minProtonPt {this, + "LambdaBToMuMuProtonKaon_minProtonPt", 1000., "minimum pT of proton track"}; + Gaudi::Property<double> m_LambdaBToMuMuProtonKaon_minKaonPt {this, + "LambdaBToMuMuProtonKaon_minKaonPt", 1000., "minimum pT of kaon track"}; + Gaudi::Property<double> m_LambdaBToMuMuProtonKaon_minKstarMass {this, + "LambdaBToMuMuProtonKaon_minKstarMass", 1300., "min value for both mass(trk1=kaon, trk2=pion) and mass(trk1=pion, trk2=kaon)"}; + Gaudi::Property<std::pair<double, double>> m_LambdaBToMuMuProtonKaon_dimuonMassRange {this, + "LambdaBToMuMuProtonKaon_dimuonMassRange", {2500., 4300.}, "dimuon mass range for Lambda_b0 decay"}; + Gaudi::Property<std::pair<double, double>> m_LambdaBToMuMuProtonKaon_massRange {this, + "LambdaBToMuMuProtonKaon_massRange", {4800., 6400.}, "Lambda_b0 mass range"}; + Gaudi::Property<float> m_LambdaBToMuMuProtonKaon_chi2 {this, + "LambdaBToMuMuProtonKaon_chi2", 60., "maximum chi2 of the fitted Lambda_b0 vertex"}; + + // external tools + 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"}; + ToolHandle<Reco::ITrackToVertex> m_trackToVertexTool {this, + "TrackToVertexTool", "", "tool to extrapolate track to vertex or beamspot"}; + ToolHandle<GenericMonitoringTool> m_monTool {this, + "MonTool", "", "monitoring tool"}; + + TrigCompositeUtils::DecisionIDContainer m_allowedIDs; + + const std::vector<std::vector<double>> m_trkMass{ + {PDG::mMuon, PDG::mMuon}, + {PDG::mMuon, PDG::mMuon, PDG::mKaon}, + {PDG::mMuon, PDG::mMuon, PDG::mKaon, PDG::mKaon} + }; + +}; + +#endif // TRIG_TrigBmumuxComboHypo_H + diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypoTool.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypoTool.cxx new file mode 100644 index 0000000000000000000000000000000000000000..23326121de7b54c0f6851ca7b7b98ecd3729a3b6 --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypoTool.cxx @@ -0,0 +1,83 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#include "TrigBmumuxComboHypoTool.h" + + +TrigBmumuxComboHypoTool::TrigBmumuxComboHypoTool(const std::string& type, const std::string& name, const IInterface* parent) + : ComboHypoToolBase(type, name, parent) {} + + +StatusCode TrigBmumuxComboHypoTool::initialize() +{ + if (!m_monTool.empty()) { + ATH_CHECK( m_monTool.retrieve() ); + ATH_MSG_DEBUG( "GenericMonitoringTool name:" << m_monTool ); + } + else { + ATH_MSG_DEBUG( "No GenericMonitoringTool configured: no monitoring histograms will be available" ); + } + + ATH_MSG_DEBUG( "Initialization completed successfully" ); + + return StatusCode::SUCCESS; +} + + +bool TrigBmumuxComboHypoTool::passed(const xAOD::TrigBphys* trigBphys) const { + + using namespace Monitored; + + auto mon_chi2 = Monitored::Scalar<float>("Chi2", -1.); + auto mon_fitmass = Monitored::Scalar<float>("Fitmass", -1.); + auto mon_mass = Monitored::Scalar<float>("Mass", -1.); + auto mon_pt = Monitored::Scalar<float>("Pt", -1.); + auto group = Monitored::Group(m_monTool, mon_chi2, mon_fitmass, mon_mass, mon_pt); + + ATH_MSG_DEBUG( "in TrigBmumuxComboHypoTool::decideOnSingleObject(), looking at TrigBphys object"); + + bool result = false; + + if (static_cast<int>(trigBphys->particleType()) == m_decay) { + result = true; + ATH_MSG_DEBUG("accepting event"); + } + + if (result) { + mon_chi2 = trigBphys->fitchi2(); + mon_fitmass = trigBphys->fitmass(); + mon_mass = trigBphys->mass(); + mon_pt = trigBphys->pt(); + } + + return result; +} + + +StatusCode TrigBmumuxComboHypoTool::decideOnSingleObject(Decision* decision, const std::vector<const DecisionIDContainer*>& previousDecisionIDs) const { + + ATH_CHECK( decision->hasObjectLink(TrigCompositeUtils::featureString()) ); + + auto trigBphysEL = decision->objectLink<xAOD::TrigBphysContainer>(TrigCompositeUtils::featureString()); + ATH_CHECK( trigBphysEL.isValid() ); + + ATH_CHECK( previousDecisionIDs.size() == 2 ); + ATH_CHECK( previousDecisionIDs.size() == legDecisionIds().size() ); + bool direct = true; + bool inverse = true; + for (size_t i = 0; i < 2; ++i) { + if (direct && !TrigCompositeUtils::passed(legDecisionId(i).numeric(), *previousDecisionIDs[i])) direct = false; + if (inverse && !TrigCompositeUtils::passed(legDecisionId(i).numeric(), *previousDecisionIDs[1-i])) inverse = false; + } + if (!direct && !inverse) { + ATH_MSG_DEBUG( "Trigger legs matched to the previous decisions neither direct nor inverse way" ); + return StatusCode::SUCCESS; + } + + if (passed(*trigBphysEL)) { + TrigCompositeUtils::addDecisionID(decisionId(), decision); + } + + return StatusCode::SUCCESS; +} diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypoTool.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypoTool.h new file mode 100644 index 0000000000000000000000000000000000000000..f3b7078121ff4f0c1c4c0cce53866a89ca122489 --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypoTool.h @@ -0,0 +1,44 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIG_TrigBmumuxComboHypoTool_H +#define TRIG_TrigBmumuxComboHypoTool_H + +#include <string> +#include <vector> + +#include "DecisionHandling/ComboHypoToolBase.h" + +#include "xAODTrigBphys/TrigBphys.h" +#include "xAODTrigBphys/TrigBphysContainer.h" + +#include "TrigCompositeUtils/HLTIdentifier.h" +#include "TrigCompositeUtils/TrigCompositeUtils.h" + +#include "AthenaMonitoringKernel/Monitored.h" +#include "AthenaMonitoringKernel/GenericMonitoringTool.h" + +using TrigCompositeUtils::Decision; +using TrigCompositeUtils::DecisionIDContainer; + +/** + * @class TrigBmumuxComboHypoTool + * @brief ComboHypoTool for B -> mu+, mu- X decays + */ +class TrigBmumuxComboHypoTool: public ComboHypoToolBase { + public: + TrigBmumuxComboHypoTool(const std::string& type, const std::string& name, const IInterface* parent); + virtual StatusCode initialize() override; + virtual StatusCode decideOnSingleObject(Decision*, const std::vector<const DecisionIDContainer*>&) const override; + + private: + bool passed(const xAOD::TrigBphys*) const; + virtual bool executeAlg(std::vector<LegDecision>&) const override { return true; } + + Gaudi::Property<int> m_decay {this, "Decay", 9999, "decay as enumerated in xAOD::TrigBphys::pType"}; + + ToolHandle<GenericMonitoringTool> m_monTool {this, "MonTool", "", "Monitoring tool"}; +}; + +#endif // TRIG_TrigBmumuxComboHypoTool_H diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypo.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypo.cxx new file mode 100644 index 0000000000000000000000000000000000000000..579228b6404bb1b87a5ebe6b089751221c0cc407 --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypo.cxx @@ -0,0 +1,71 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#include "TrigBphysStreamerHypo.h" + +#include "xAODMuon/Muon.h" +#include "xAODMuon/MuonContainer.h" + +#include "TrigCompositeUtils/HLTIdentifier.h" +#include "TrigCompositeUtils/TrigCompositeUtils.h" + +using TrigCompositeUtils::Decision; +using TrigCompositeUtils::DecisionContainer; +using TrigCompositeUtils::DecisionIDContainer; + + +TrigBphysStreamerHypo::TrigBphysStreamerHypo(const std::string& name, ISvcLocator* pSvcLocator) + : ::HypoBase(name, pSvcLocator) {} + + +StatusCode TrigBphysStreamerHypo::initialize() { + ATH_MSG_DEBUG( "TrigBphysStreamerHypo::initialize()" ); + + ATH_CHECK( m_hypoTools.retrieve() ); + return StatusCode::SUCCESS; +} + + +StatusCode TrigBphysStreamerHypo::execute( const EventContext& context ) const { + + ATH_MSG_DEBUG( "TrigMultiTrkHypo::execute() starts" ); + + ATH_MSG_DEBUG( "decision input key: " << decisionInput().key() ); + auto previousDecisionsHandle = SG::makeHandle(decisionInput(), context); + 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> outputHandle = TrigCompositeUtils::createAndStore(decisionOutput(), context); + DecisionContainer* decisions = outputHandle.ptr(); + + for (const Decision* previousDecision : *previousDecisionsHandle) { + Decision* decision = TrigCompositeUtils::newDecisionIn(decisions, previousDecision, "", context); + + auto muonLinkInfo = TrigCompositeUtils::findLink<xAOD::MuonContainer>(previousDecision, TrigCompositeUtils::featureString(), true); + ATH_CHECK( muonLinkInfo.isValid() ); + decision->setObjectLink<xAOD::MuonContainer>(TrigCompositeUtils::featureString(), muonLinkInfo.link); + TrigCompositeUtils::insertDecisionIDs(previousDecision, decision); + + if (msgLvl(MSG::DEBUG)) { + const xAOD::Muon* muon = *(muonLinkInfo.link); + if (muon->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle)) { + const ElementLink<xAOD::TrackParticleContainer> trackEL = muon->inDetTrackParticleLink(); + ATH_CHECK( trackEL.isValid() ); + const xAOD::TrackParticle* track = *trackEL; + ATH_MSG_DEBUG( " -- muon pt/eta/phi/q: " << track->pt() << " / " << track->eta() << " / " << track->phi() << " / " << track->charge() ); + ATH_MSG_DEBUG( " Allowed decisions:" ); + DecisionIDContainer IDs; + TrigCompositeUtils::decisionIDs(decision, IDs); + for (const TrigCompositeUtils::DecisionID& id : IDs) { + ATH_MSG_DEBUG( " +++ " << HLT::Identifier(id) ); + } + } + } + } + + ATH_CHECK( hypoBaseOutputProcessing(outputHandle) ); + + return StatusCode::SUCCESS; +} diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypo.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypo.h new file mode 100644 index 0000000000000000000000000000000000000000..581708c471d9bc9015df9c189806c57615bcba9d --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypo.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIG_TrigBphysStreamerHypo_H +#define TRIG_TrigBphysStreamerHypo_H + +#include <string> + +#include "DecisionHandling/HypoBase.h" + +#include "TrigBphysStreamerHypoTool.h" + +/** + * @class TrigBphysStreamerHypo + * @brief Implements a streamer (no selection) for the HLT framework + */ +class TrigBphysStreamerHypo : public ::HypoBase { + public: + TrigBphysStreamerHypo(const std::string& name, ISvcLocator* pSvcLocator); + TrigBphysStreamerHypo() = delete; + + virtual StatusCode initialize() override; + virtual StatusCode execute(const EventContext&) const override; + + private: + ToolHandleArray<TrigBphysStreamerHypoTool> m_hypoTools {this, "HypoTools", {}, "Tools to perform selection"}; + +}; + +#endif // TRIG_TrigBphysStreamerHypo_H diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypoTool.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypoTool.cxx new file mode 100644 index 0000000000000000000000000000000000000000..fb16e7edc6b2b612984e7edcecb9c6080dc2425c --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypoTool.cxx @@ -0,0 +1,10 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#include "TrigBphysStreamerHypoTool.h" + + +TrigBphysStreamerHypoTool::TrigBphysStreamerHypoTool(const std::string& type, const std::string& name, const IInterface* parent) + : AthAlgTool(type, name, parent), + m_decisionId(HLT::Identifier::fromToolName(name)) {} diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypoTool.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypoTool.h new file mode 100644 index 0000000000000000000000000000000000000000..e5cb0494578105554c3dfbe7f8b30360578a2bb0 --- /dev/null +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypoTool.h @@ -0,0 +1,23 @@ +/* + Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIG_TrigBphysStreamerHypoTool_H +#define TRIG_TrigBphysStreamerHypoTool_H + +#include <string> + +#include "AthenaBaseComps/AthAlgTool.h" +#include "TrigCompositeUtils/HLTIdentifier.h" + +class TrigBphysStreamerHypoTool : public ::AthAlgTool { + public: + TrigBphysStreamerHypoTool(const std::string& type, const std::string& name, const IInterface* parent); + HLT::Identifier decisionId() const { return m_decisionId; } + + private: + HLT::Identifier m_decisionId; + +}; + +#endif // TRIG_TrigBphysStreamerHypoTool_H diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.cxx index 87237ada82ea6805d35f63e241cca1a3d6ece6d1..5103d00cf2826c51f16f1983a88cb88b0572ae43 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.cxx +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypo.cxx @@ -445,7 +445,7 @@ StatusCode TrigMultiTrkComboHypo::executeEF(const EventContext& context) const { // create a new output Decision object, backed by the 'decisions' container. Decision* decision = TrigCompositeUtils::newDecisionIn(decisions); - std::vector<DecisionIDContainer*> previousDecisionIDs; + std::vector<const DecisionIDContainer*> previousDecisionIDs; for (size_t itrk : trigBphysTrackIdx[idx]) { // attach all previous decisions: if the same previous decision is called twice, that's fine - internally takes care of that for (const ElementLink<DecisionContainer>& previousDecisionEL : tracks[itrk].second) { diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.cxx index 47bf5d63ce8f27743168590edcc7befe76d0fbb6..d73bdaa2731bf843ce5bcb1e1fad525e9b9bba16 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.cxx +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.cxx @@ -158,7 +158,7 @@ bool TrigMultiTrkComboHypoTool::passed(const xAOD::TrigBphys* trigBphys) const { } -StatusCode TrigMultiTrkComboHypoTool::decideOnSingleObject(Decision* decision, const std::vector<DecisionIDContainer*>& previousDecisionIDs) const { +StatusCode TrigMultiTrkComboHypoTool::decideOnSingleObject(Decision* decision, const std::vector<const DecisionIDContainer*>& previousDecisionIDs) const { ATH_CHECK( decision->hasObjectLink(TrigCompositeUtils::featureString()) ); diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.h b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.h index 50895ba0228b4a2729397f9b0b6e637fd9798c88..5c87a3d19b88e35ca3cb6e7a21ea3f1d9a0200bf 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.h +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/TrigMultiTrkComboHypoTool.h @@ -34,7 +34,7 @@ class TrigMultiTrkComboHypoTool: public ComboHypoToolBase { public: TrigMultiTrkComboHypoTool(const std::string& type, const std::string& name, const IInterface* parent); virtual StatusCode initialize() override; - virtual StatusCode decideOnSingleObject(TrigCompositeUtils::Decision*, const std::vector<TrigCompositeUtils::DecisionIDContainer*>&) const override; + virtual StatusCode decideOnSingleObject(TrigCompositeUtils::Decision*, const std::vector<const TrigCompositeUtils::DecisionIDContainer*>&) const override; private: bool passed(const xAOD::TrigBphys*) const; diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/src/components/TrigBphysHypo_entries.cxx b/Trigger/TrigHypothesis/TrigBphysHypo/src/components/TrigBphysHypo_entries.cxx index 547a29ede789c37fe153162490da1221eac3edaa..de541a5cd9d2e76f66b169e146eeab6deb518f0c 100644 --- a/Trigger/TrigHypothesis/TrigBphysHypo/src/components/TrigBphysHypo_entries.cxx +++ b/Trigger/TrigHypothesis/TrigBphysHypo/src/components/TrigBphysHypo_entries.cxx @@ -29,10 +29,12 @@ #include "../TrigEFMultiMuFex.h" #include "../TrigEFTrkMassFex.h" +#include "src/TrigBphysStreamerHypo.h" +#include "src/TrigBphysStreamerHypoTool.h" #include "src/TrigMultiTrkComboHypo.h" #include "src/TrigMultiTrkComboHypoTool.h" - -//#include "../TrigBphysL1DiMuComboFex.h" +#include "src/TrigBmumuxComboHypo.h" +#include "src/TrigBmumuxComboHypoTool.h" //DECLARE_COMPONENT( TrigL2DiMuXHypo ) @@ -65,7 +67,10 @@ DECLARE_COMPONENT( TrigEFTrkMassFex ) DECLARE_COMPONENT( TrigBphysMuonCounter ) DECLARE_COMPONENT( TrigBphysTrackRoiMaker ) DECLARE_COMPONENT( TrigBphysElectronCounter ) -//DECLARE_COMPONENT( TrigBphysL1DiMuComboFex ) + +DECLARE_COMPONENT( TrigBphysStreamerHypo ) +DECLARE_COMPONENT( TrigBphysStreamerHypoTool ) DECLARE_COMPONENT( TrigMultiTrkComboHypo ) DECLARE_COMPONENT( TrigMultiTrkComboHypoTool ) - +DECLARE_COMPONENT( TrigBmumuxComboHypo ) +DECLARE_COMPONENT( TrigBmumuxComboHypoTool ) diff --git a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/ComboHypoToolBase.h b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/ComboHypoToolBase.h index a9937ad8ba42b1320f61126aa6ce0baca603338d..896c55006512b3a5c14a2ef497dda6f5d8a9e989 100644 --- a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/ComboHypoToolBase.h +++ b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/ComboHypoToolBase.h @@ -35,7 +35,7 @@ public: **/ virtual StatusCode decide(LegDecisionsMap & passingLegs, const EventContext& /* ctx */ ) const override; - virtual StatusCode decideOnSingleObject(TrigCompositeUtils::Decision*, const std::vector<TrigCompositeUtils::DecisionIDContainer*>&) const { return StatusCode::SUCCESS; } + virtual StatusCode decideOnSingleObject(TrigCompositeUtils::Decision*, const std::vector<const TrigCompositeUtils::DecisionIDContainer*>&) const { return StatusCode::SUCCESS; } /** * @brief retrieves this decision Id diff --git a/Trigger/TrigTools/TrigInDetConfig/python/ConfigSettings.py b/Trigger/TrigTools/TrigInDetConfig/python/ConfigSettings.py index 2b7f52f1bdf98ca2ab0cada0e0a1813c1b1dd913..82181e34607ea83dfe699bebb32307f3da814b01 100644 --- a/Trigger/TrigTools/TrigInDetConfig/python/ConfigSettings.py +++ b/Trigger/TrigTools/TrigInDetConfig/python/ConfigSettings.py @@ -904,6 +904,15 @@ class _Settings_bphysicsHighPt( _GlobalSettings ): self._configPT = _PrecisionTracking( signatureType = 'bphysHighPt', nameSuffix = 'Bjet' ) self._doRecord = False +class _Settings_bmumux( _GlobalSettings ): + def __init__( self ): + _GlobalSettings.__init__(self) + self._name = "bmumux" #To be appended to alg names + self._roi = "HLT_Roi_Bmumux" + self._configFT = _FastTracking( signatureType = 'bphysics', nameSuffix = 'Bmumux' ) + self._configPT = _PrecisionTracking( signatureType = 'bphysics', nameSuffix = 'Bmumux' ) + self._doRecord = True #Allow recording of track collections + class _Settings_beamgas( _GlobalSettings ): def __init__( self ): _GlobalSettings.__init__(self) @@ -961,6 +970,7 @@ _ConfigSettings = { "minBias0" : _Settings_minBias(), "bphysics" : _Settings_bphysics(), "bphysHighPt" : _Settings_bphysicsHighPt(), + "bmumux" : _Settings_bmumux(), "beamgas" : _Settings_beamgas(), "hadCalib" : _Settings_hadCalib() diff --git a/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref b/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref index dcad92db4df30fd22449ff990e03bbd680b6f19b..4e0d514b6667974c2db374233efd055ccdbe0b31 100644 --- a/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref +++ b/Trigger/TrigValidation/TrigAnalysisTest/share/ref_RDOtoRDOTrig_v1Dev_build.ref @@ -224,6 +224,76 @@ HLT_2mu4_L12MU4: 1: 12 2: 8 3: 8 +HLT_2mu4_bBmumux_BcmumuPi_L12MU4: + eventCount: 0 + stepCounts: + 0: 3 + 1: 3 + 2: 1 + 3: 1 + 4: 1 + stepFeatures: + 0: 12 + 1: 10 + 2: 4 + 3: 4 + 4: 2 +HLT_2mu4_bBmumux_BdmumuKst_L12MU4: + eventCount: 0 + stepCounts: + 0: 3 + 1: 3 + 2: 1 + 3: 1 + 4: 1 + stepFeatures: + 0: 12 + 1: 10 + 2: 4 + 3: 4 + 4: 2 +HLT_2mu4_bBmumux_BpmumuKp_L12MU4: + eventCount: 0 + stepCounts: + 0: 3 + 1: 3 + 2: 1 + 3: 1 + 4: 1 + stepFeatures: + 0: 12 + 1: 10 + 2: 4 + 3: 4 + 4: 2 +HLT_2mu4_bBmumux_BsmumuPhi_L12MU4: + eventCount: 0 + stepCounts: + 0: 3 + 1: 3 + 2: 1 + 3: 1 + 4: 1 + stepFeatures: + 0: 12 + 1: 10 + 2: 4 + 3: 4 + 4: 2 +HLT_2mu4_bBmumux_LbPqKm_L12MU4: + eventCount: 0 + stepCounts: + 0: 3 + 1: 3 + 2: 1 + 3: 1 + 4: 1 + stepFeatures: + 0: 12 + 1: 10 + 2: 4 + 3: 4 + 4: 2 HLT_2mu4_bDimu_L12MU4: eventCount: 0 stepCounts: diff --git a/Trigger/TrigValidation/TriggerTest/share/ref_data_v1Dev_build.ref b/Trigger/TrigValidation/TriggerTest/share/ref_data_v1Dev_build.ref index aa2dfc6345414c0cdb6b91cc8aa217cf8f51cb7c..03ac875bbe985b6db1250a562f6c58edbd7054c7 100644 --- a/Trigger/TrigValidation/TriggerTest/share/ref_data_v1Dev_build.ref +++ b/Trigger/TrigValidation/TriggerTest/share/ref_data_v1Dev_build.ref @@ -94,6 +94,46 @@ HLT_2mu4_L12MU4: 0: 1 stepFeatures: 0: 4 +HLT_2mu4_bBmumux_BcmumuPi_L12MU4: + eventCount: 0 + stepCounts: + 0: 1 + 1: 1 + stepFeatures: + 0: 4 + 1: 2 +HLT_2mu4_bBmumux_BdmumuKst_L12MU4: + eventCount: 0 + stepCounts: + 0: 1 + 1: 1 + stepFeatures: + 0: 4 + 1: 2 +HLT_2mu4_bBmumux_BpmumuKp_L12MU4: + eventCount: 0 + stepCounts: + 0: 1 + 1: 1 + stepFeatures: + 0: 4 + 1: 2 +HLT_2mu4_bBmumux_BsmumuPhi_L12MU4: + eventCount: 0 + stepCounts: + 0: 1 + 1: 1 + stepFeatures: + 0: 4 + 1: 2 +HLT_2mu4_bBmumux_LbPqKm_L12MU4: + eventCount: 0 + stepCounts: + 0: 1 + 1: 1 + stepFeatures: + 0: 4 + 1: 2 HLT_2mu4_bDimu_L12MU4: eventCount: 0 stepCounts: diff --git a/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py b/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py index 9b8c0dcc12d0395f9085132fd7dac23244aba278..76901fa90e34f87e55b67b593d2bb32e92607590 100644 --- a/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py +++ b/Trigger/TriggerCommon/TrigEDMConfig/python/TriggerEDMRun3.py @@ -212,9 +212,17 @@ TriggerHLTListRun3 = [ ('xAOD::TrackParticleAuxContainer#HLT_IDTrack_MuonLate_IDTrigAux.', 'BS ESD AODFULL', 'Muon'), - # bphys + # Bphysics Dimuon chains ('xAOD::TrigBphysContainer#HLT_DimuEF', 'BS ESD AODFULL AODSLIM AODVERYSLIM AODBLSSLIM', 'Bphys'), ('xAOD::TrigBphysAuxContainer#HLT_DimuEFAux.', 'BS ESD AODFULL AODSLIM AODVERYSLIM AODBLSSLIM', 'Bphys'), + # Bphysics Bmumux chains + ('xAOD::TrigBphysContainer#HLT_Bmumux', 'BS ESD AODFULL AODSLIM AODVERYSLIM AODBLSSLIM', 'Bphys'), + ('xAOD::TrigBphysAuxContainer#HLT_BmumuxAux.', 'BS ESD AODFULL AODSLIM AODVERYSLIM AODBLSSLIM', 'Bphys'), + ('xAOD::TrackParticleContainer#HLT_IDTrack_Bmumux_FTF', 'BS ESD AODFULL', 'Bphys', 'inViews:BmumuxViews'), + ('xAOD::TrackParticleAuxContainer#HLT_IDTrack_Bmumux_FTFAux.', 'BS ESD AODFULL', 'Bphys'), + ('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'), # xAOD muons (msonly (x2: roi+FS), combined (x3: FS+RoI (outside-in, inside-out+outside-in)) ('xAOD::MuonContainer#HLT_Muons_RoI', 'BS ESD AODFULL', 'Muon', 'inViews:MUEFSAViewRoIs'), diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsDef.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsDef.py index 984e278bab956db16a04353528d7edba0da9f5db..5db188cebf89a006a8a8fffdb1dfc58e1fb72086 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsDef.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsDef.py @@ -13,13 +13,18 @@ from TriggerMenuMT.HLTMenuConfig.Menu.ChainConfigurationBase import ChainConfigu from TriggerMenuMT.HLTMenuConfig.Muon.MuonDef import MuonChainConfiguration as MuonChainConfiguration from TriggerMenuMT.HLTMenuConfig.Muon.MuonDef import muCombSequenceCfg, muEFCBSequenceCfg +from TriggerMenuMT.HLTMenuConfig.Bphysics.BphysicsSequenceSetup import bmumuxSequence + from TrigBphysHypo.TrigMultiTrkComboHypoConfig import DimuL2ComboHypoCfg, DimuEFComboHypoCfg, TrigMultiTrkComboHypoToolFromDict +from TrigBphysHypo.TrigBmumuxComboHypoConfig import BmumuxComboHypoCfg, TrigBmumuxComboHypoToolFromDict #-------------------------------------------------------- # fragments generating config will be functions in new JO # I have no idea what the above sentence means - copy/paste from muons... #-------------------------------------------------------- +def bmumuxSequenceCfg(flags): + return bmumuxSequence() ############################################# ### Class/function to configure muon chains @@ -53,16 +58,14 @@ class BphysicsChainConfiguration(MuonChainConfiguration): def getBphysStepDictionary(self): stepDictionary = { - 'dimu' : [['getmuFast', 'getDimuComb'], ['getmuEFSA', 'getDimuEFCB']], - 'bl2io': [['getmuFast', 'getmuCombIO'], ['getmuEFSA', 'getDimuEFCB']], + 'dimu' : [['getmuFast', 'getDimuComb'], ['getmuEFSA', 'getDimuEFCB']], + 'bl2io' : [['getmuFast', 'getmuCombIO'], ['getmuEFSA', 'getDimuEFCB']], + 'bmumux' : [['getmuFast', 'getDimuComb'], ['getmuEFSA', 'getmuEFCB', 'getBmumux']], } return stepDictionary def getBphysKey(self): - if len(self.dict['topo']) > 1: - log.warning("BphysicsChainConfiguration.getBphysKey is not setup for > 1 topo! will use the first one.") - the_topo = self.dict['topo'][0] topo_dict = { @@ -73,7 +76,8 @@ class BphysicsChainConfiguration(MuonChainConfiguration): 'bDimu2700' : 'dimu', 'bPhi' : 'dimu', 'bTau' : 'dimu', - 'bJpsimumul2io' : 'bl2io' + 'bJpsimumul2io' : 'bl2io', + 'bBmumux' : 'bmumux' } return topo_dict[the_topo] @@ -83,3 +87,6 @@ class BphysicsChainConfiguration(MuonChainConfiguration): def getDimuEFCB(self): return self.getStep(4, 'dimuEFCB', [muEFCBSequenceCfg], comboHypoCfg=DimuEFComboHypoCfg, comboTools=[TrigMultiTrkComboHypoToolFromDict]) + + def getBmumux(self): + return self.getStep(5, 'bmumux', [bmumuxSequenceCfg], comboHypoCfg=BmumuxComboHypoCfg, comboTools=[TrigBmumuxComboHypoToolFromDict]) diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsSequenceSetup.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsSequenceSetup.py index f2f0fc1b7b487784e8781d2a41e43908019e0a7d..0646d2c83d60d4af131e5d999e7930d48f7f6bcd 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsSequenceSetup.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsSequenceSetup.py @@ -1 +1,52 @@ # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import MenuSequence, RecoFragmentsPool +from AthenaConfiguration.AllConfigFlags import ConfigFlags +from AthenaCommon.CFElements import seqAND +from TrigEDMConfig.TriggerEDMRun3 import recordable +from AthenaCommon.Logging import logging +log = logging.getLogger('BphysicsSequenceSetup') + + +def bmumuxAlgSequence(ConfigFlags): + from ViewAlgs.ViewAlgsConf import EventViewCreatorAlgorithm + from DecisionHandling.DecisionHandlingConf import ViewCreatorCentredOnIParticleROITool + + viewCreatorROITool = ViewCreatorCentredOnIParticleROITool( + RoIEtaWidth = 0.75, + RoIPhiWidth = 0.75, + RoisWriteHandleKey = recordable('HLT_Roi_Bmumux')) + + viewMaker = EventViewCreatorAlgorithm( + name = 'IMbmumux', + mergeUsingFeature = True, + RoITool = viewCreatorROITool, + Views = 'BmumuxViews', + InViewRoIs = 'BmumuxViewRoIs', + ViewFallThrough = True, + PlaceMuonInView = True, + InViewMuonCandidates = 'BmumuxMuonCandidates', + InViewMuons = 'HLT_Muons_Bmumux') + + from TriggerMenuMT.HLTMenuConfig.Bphysics.BphysicsSetup import bmumuxRecoSequence + recoSequence = bmumuxRecoSequence(viewMaker.InViewRoIs, viewMaker.InViewMuons) + + viewMaker.ViewNodeName = recoSequence.name() + + sequence = seqAND('bmumuxSequence', [viewMaker, recoSequence]) + + return (sequence, viewMaker) + + +def bmumuxSequence(): + from TrigBphysHypo.TrigBphysHypoConf import TrigBphysStreamerHypo + from TrigBphysHypo.TrigBphysStreamerHypoConfig import TrigBphysStreamerHypoToolFromDict + + sequence, viewMaker = RecoFragmentsPool.retrieve(bmumuxAlgSequence, ConfigFlags) + hypo = TrigBphysStreamerHypo('BmumuxStreamerHypoAlg') + + return MenuSequence( + Sequence = sequence, + Maker = viewMaker, + Hypo = hypo, + HypoToolGen = TrigBphysStreamerHypoToolFromDict) diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsSetup.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsSetup.py new file mode 100644 index 0000000000000000000000000000000000000000..4defe524929fb4cbb59e0dc7713028ad3124c014 --- /dev/null +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsSetup.py @@ -0,0 +1,40 @@ +# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration + +from AthenaCommon.GlobalFlags import globalflags +from AthenaCommon.CFElements import seqAND, parOR +from AthenaCommon.Logging import logging +log = logging.getLogger('BphysicsSetup') + + +def bmumuxRecoSequence(rois, muons): + + # ATR-20453, until such time as FS and RoI collections do not interfere, a hacky fix + #recoSequence = parOR('bmumuxViewNode') + recoSequence = seqAND('bmumuxViewNode') + + from TrigInDetConfig.ConfigSettings import getInDetTrigConfig + config = getInDetTrigConfig('bmumux') + + from TrigInDetConfig.InDetSetup import makeInDetAlgs + viewAlgs, viewDataVerifier = makeInDetAlgs(config, rois) + viewDataVerifier.DataObjects += [('TrigRoiDescriptorCollection', 'StoreGateSvc+%s' % rois), + ('xAOD::MuonContainer', 'StoreGateSvc+%s' % muons)] + + # Make sure required objects are still available at whole-event level + if not globalflags.InputFormat.is_bytestream(): + from AthenaCommon.AlgSequence import AlgSequence + topSequence = AlgSequence() + viewDataVerifier.DataObjects += [('TRT_RDO_Container', 'StoreGateSvc+TRT_RDOs')] + topSequence.SGInputLoader.Load += [('TRT_RDO_Container', 'StoreGateSvc+TRT_RDOs')] + + for viewAlg in viewAlgs: + recoSequence += viewAlg + + # Precision Tracking is requested in the same view as FTF, so viewDataVerifier must not be provided + from TrigInDetConfig.InDetPT import makeInDetPrecisionTracking + ptTracks, ptTrackParticles, ptAlgs = makeInDetPrecisionTracking(config, None, rois) + + precisionTrackingSequence = parOR('precisionTrackingInBmumux', ptAlgs) + recoSequence += precisionTrackingSequence + + return recoSequence diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py index 06d632fc3ec573f6346eec920d72aa78effff126..5e099641c19390025dc26bcc08415c0ccdf496c0 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1.py @@ -385,6 +385,12 @@ def setupMenu(): ChainProp(name='HLT_2mu4_bUpsimumu_L12MU4', groups=BphysicsGroup), #ATR-20839 ChainProp(name='HLT_2mu4_bDimu_L12MU4', groups=BphysicsGroup), + #ATR-21639 + ChainProp(name='HLT_2mu4_bBmumux_BpmumuKp_L12MU4', groups=BphysicsGroup), + ChainProp(name='HLT_2mu4_bBmumux_BcmumuPi_L12MU4', groups=BphysicsGroup), + ChainProp(name='HLT_2mu4_bBmumux_BsmumuPhi_L12MU4', groups=BphysicsGroup), + ChainProp(name='HLT_2mu4_bBmumux_BdmumuKst_L12MU4', groups=BphysicsGroup), + ChainProp(name='HLT_2mu4_bBmumux_LbPqKm_L12MU4', groups=BphysicsGroup), #ATR-21003 ChainProp(name='HLT_2mu6_bJpsimumul2io_L12MU6', groups=BphysicsGroup), ChainProp(name='HLT_2mu6_bJpsimumu_L12MU6', groups=BphysicsGroup), diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py index 473d1ac38e655358e0d4295d7a65bdd986775432..e6d9f2096f915e8e8a8dc96a13013b59e60e3b11 100644 --- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py +++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/SignatureDicts.py @@ -255,7 +255,10 @@ MuonChainParts_Default = { #========================================================== # Bphysics #========================================================== -AllowedTopos_Bphysics = ['bJpsimumu','bUpsimumu','bBmumu','bDimu','bDimu2700','bPhi','bTau','bJpsimumul2io'] +AllowedTopos_Bphysics = [ + 'bJpsimumu','bUpsimumu','bBmumu','bDimu','bDimu2700','bPhi','bTau','bJpsimumul2io', + 'bBmumux','BpmumuKp','BcmumuPi','BsmumuPhi','BdmumuKst','LbPqKm' +] # ---- Bphysics Dictionary of all allowed Values ---- BphysicsChainParts = deepcopy(MuonChainParts)