From ed2554e59d2056d69b34b540d751a0116e8a9961 Mon Sep 17 00:00:00 2001 From: Vladimir Lyubushkin <vladimir.lyubushkin@cern.ch> Date: Tue, 8 Dec 2020 18:13:07 +0000 Subject: [PATCH] use Reco::ITrackToVertex tool to estimate d0/z0 impact parameters of track wrt already fitted dimuon vertex --- .../TrigBphysHypo/CMakeLists.txt | 2 +- .../python/TrigBmumuxComboHypoConfig.py | 118 +++ .../TrigBmumuxComboHypoMonitoringConfig.py | 23 + .../python/TrigBphysStreamerHypoConfig.py | 7 + .../TrigBphysHypo/src/Constants.h | 25 +- .../TrigBphysHypo/src/ITrigBphysState.h | 19 + .../TrigBphysHypo/src/TrigBmumuxComboHypo.cxx | 725 ++++++++++++++++++ .../TrigBphysHypo/src/TrigBmumuxComboHypo.h | 229 ++++++ .../src/TrigBmumuxComboHypoTool.cxx | 83 ++ .../src/TrigBmumuxComboHypoTool.h | 44 ++ .../src/TrigBphysStreamerHypo.cxx | 71 ++ .../TrigBphysHypo/src/TrigBphysStreamerHypo.h | 31 + .../src/TrigBphysStreamerHypoTool.cxx | 10 + .../src/TrigBphysStreamerHypoTool.h | 23 + .../src/TrigMultiTrkComboHypo.cxx | 2 +- .../src/TrigMultiTrkComboHypoTool.cxx | 2 +- .../src/TrigMultiTrkComboHypoTool.h | 2 +- .../src/components/TrigBphysHypo_entries.cxx | 13 +- .../DecisionHandling/ComboHypoToolBase.h | 2 +- .../TrigInDetConfig/python/ConfigSettings.py | 10 + .../share/ref_RDOtoRDOTrig_v1Dev_build.ref | 70 ++ .../share/ref_data_v1Dev_build.ref | 40 + .../TrigEDMConfig/python/TriggerEDMRun3.py | 10 +- .../HLTMenuConfig/Bphysics/BphysicsDef.py | 19 +- .../Bphysics/BphysicsSequenceSetup.py | 51 ++ .../HLTMenuConfig/Bphysics/BphysicsSetup.py | 40 + .../python/HLTMenuConfig/Menu/LS2_v1.py | 6 + .../HLTMenuConfig/Menu/SignatureDicts.py | 5 +- 28 files changed, 1663 insertions(+), 19 deletions(-) create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoConfig.py create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBmumuxComboHypoMonitoringConfig.py create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/python/TrigBphysStreamerHypoConfig.py create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/src/ITrigBphysState.h create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.cxx create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypo.h create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypoTool.cxx create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBmumuxComboHypoTool.h create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypo.cxx create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypo.h create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypoTool.cxx create mode 100644 Trigger/TrigHypothesis/TrigBphysHypo/src/TrigBphysStreamerHypoTool.h create mode 100644 Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Bphysics/BphysicsSetup.py diff --git a/Trigger/TrigHypothesis/TrigBphysHypo/CMakeLists.txt b/Trigger/TrigHypothesis/TrigBphysHypo/CMakeLists.txt index b9fea33fcd54..7277228c7933 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 000000000000..80f62a8821b1 --- /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 000000000000..74eb8c8d62ca --- /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 000000000000..fe26b09e085b --- /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 475f4edba579..1cab1e5542e6 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 000000000000..803de129bd8a --- /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 000000000000..2ad51468e677 --- /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 000000000000..2a26d34de315 --- /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 000000000000..23326121de7b --- /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 000000000000..f3b7078121ff --- /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 000000000000..579228b6404b --- /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 000000000000..581708c471d9 --- /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 000000000000..fb16e7edc6b2 --- /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 000000000000..e5cb04945781 --- /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 87237ada82ea..5103d00cf282 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 47bf5d63ce8f..d73bdaa2731b 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 50895ba0228b..5c87a3d19b88 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 547a29ede789..de541a5cd9d2 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 a9937ad8ba42..896c55006512 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 2b7f52f1bdf9..82181e34607e 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 dcad92db4df3..4e0d514b6667 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 aa2dfc634541..03ac875bbe98 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 9b8c0dcc12d0..76901fa90e34 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 984e278bab95..5db188cebf89 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 f2f0fc1b7b48..0646d2c83d60 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 000000000000..4defe524929f --- /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 06d632fc3ec5..5e099641c193 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 473d1ac38e65..e6d9f2096f91 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) -- GitLab