ATLAS Offline Software
TriggerMatchingTool.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 #include "TriggerMatchingTool.h"
6 #include "BuildCombinations.h"
7 #include "GaudiKernel/ServiceHandle.h"
8 #include "GaudiKernel/IIncidentSvc.h"
9 #include "GaudiKernel/ThreadLocalContext.h"
13 #include <memory>
14 #include <algorithm>
17 #include "StoreGate/ReadHandle.h"
18 
19 namespace {
21  template <typename T>
22  using constAcc_t = SG::AuxElement::ConstAccessor<T>;
23  template <typename T>
24  using acc_t = SG::AuxElement::Accessor<T>;
25  template <typename T>
26  using vecLink_t = std::vector<ElementLink<T>>;
27 
28  template <typename T>
29  ElementLink<T> makeLink(typename T::const_value_type element, IProxyDict *proxyDct)
30  {
31  SG::DataProxy *proxy = proxyDct->proxy(element->container());
32  // This will be null if the container isn't in the store. This shouldn't really happen
33  if (!proxy)
34  return {};
35  else
36  return ElementLink<T>(proxy->sgkey(), element->index(), proxyDct);
37  }
38 } //> end anonymous namespace
39 
40 namespace DerivationFramework {
41 
43  const std::string& type,
44  const std::string& name,
45  const IInterface* pSvcLocator) :
46  base_class(type, name, pSvcLocator)
47  {
48  declareProperty("ChainNames", m_chainNames,
49  "The list of trigger chains to match.");
50  declareProperty("OnlineParticleTool", m_trigParticleTool,
51  "The tool to retrieve online particles from the navigation.");
52  declareProperty("InputElectrons",
54  "Offline electron candidates for matching.");
55  declareProperty("InputPhotons",
57  "Offline photon candidates for matching.");
58  declareProperty("InputMuons",
60  "Offline muon candidates for matching.");
61  declareProperty("InputTaus",
62  m_offlineInputs[xAOD::Type::Tau] = "TauJets",
63  "Offline tau candidates for matching.");
64  declareProperty("DRThreshold", m_drThreshold = 0.1,
65  "The maximum dR between an offline and an online particle to consider "
66  "a match between them.");
67  declareProperty("Rerun", m_rerun = true,
68  "Whether to match triggers in rerun mode.");
69  declareProperty("OutputContainerPrefix", m_outputPrefix="TrigMatch_",
70  "The prefix to add to the output containers.");
71  declareProperty("CheckEmptyChainGroups", m_checkEmptyChainGroups = true,
72  "If set, discard any empty chain groups. Otherwise these will cause "
73  "a job failure.");
74  declareProperty("InputDependentConfig", m_inputDependentConfig=false,
75  "Warn when a trigger is removed (if the configuration is dependent "
76  "on the inputs, removal is not expected).");
77  }
78 
80  {
81  ATH_MSG_INFO( "Initializing " << name() );
82 
83  // Remove any duplicates from the list of chain names
84  std::sort(m_chainNames.begin(), m_chainNames.end() );
85  m_chainNames.erase(std::unique(m_chainNames.begin(), m_chainNames.end() ), m_chainNames.end() );
86 
87  ATH_CHECK( m_trigParticleTool.retrieve() );
88  ATH_CHECK( m_scoreTool.retrieve() );
89  for (auto &p : m_offlineInputs)
90  {
91  ATH_CHECK(p.second.initialize(SG::AllowEmpty));
92  }
93  return StatusCode::SUCCESS;
94  }
95 
96  StatusCode TriggerMatchingTool::addBranches(const EventContext& ctx) const
97  {
98  [[maybe_unused]] static const bool firstEvent = [&](){
99  auto itr = m_chainNames.begin();
100  while (itr != m_chainNames.end() ) {
101  const Trig::ChainGroup* cg = m_tdt->getChainGroup(*itr);
102  if (cg->getListOfTriggers().size() == 0){
104  ATH_MSG_WARNING("Removing trigger " << (*itr) << " -- suggests a bad tool configuration (asking for triggers not in the menu)");
105  // We are now modifying the mutable m_chainNames but since it is done
106  // within this static initialization this is thread-safe:
107  itr = m_chainNames.erase(itr);
108  } else
109  ++itr;
110  }
111  return false;
112  }();
113 
115 
116  // Now, get all the possible offline candidates
117  std::map<xAOD::Type::ObjectType, const xAOD::IParticleContainer *> offlineContainers;
118  std::map<xAOD::Type::ObjectType, particleVec_t> offlineCandidates;
119  for (const auto& p : m_offlineInputs) {
120  // Skip any that may have been disabled by providing an empty string to
121  // the corresponding property
122  if (p.second.empty() )
123  continue;
124  auto handle = SG::makeHandle(p.second, ctx);
125  if (!handle.isValid())
126  {
127  ATH_MSG_ERROR("Failed to retrieve " << p.second);
128  return StatusCode::FAILURE;
129  }
130  offlineContainers[p.first] = handle.ptr();
131  offlineCandidates.emplace(std::make_pair(
132  p.first,
133  particleVec_t(handle->begin(), handle->end() )
134  ) );
135  }
136 
137  // Store possible matches from online to offline particles here
138  // We do this as multiple chains may use the same online particles so we can
139  // reuse this information.
140  std::map<const xAOD::IParticle*, particleVec_t> matches;
141 
142  // Iterate through the chains to get the matchings
143  for (const std::string& chain : m_chainNames) {
144  // Create the output
145  xAOD::TrigCompositeContainer* container(nullptr);
146  ATH_CHECK( createOutputContainer(container, chain) );
147 
148  // Get the list of online combinations
149  std::vector<particleVec_t> onlineCombinations;
150  ATH_CHECK( m_trigParticleTool->retrieveParticles(onlineCombinations, chain, m_rerun) );
151 
153  onlineCombinations.size() << " combinations found for chain" << chain);
154 
155  // If no combinations were found (because the trigger was not passed) then
156  // we can skip this trigger
157  if (onlineCombinations.size() == 0)
158  continue;
159 
161  // Now build up the list of offline combinations;
162  std::vector<particleVec_t> offlineCombinations;
163 
164  // Projection operator to use for comparing vectors
165  // of IParticle*. Compare by using the sum of the pts
166  // of all particles in the vector.
167  struct outerproj {
168  double operator() (const particleVec_t& v) const
169  {
170  // Unfortunately std::ranges::accumulate hasn't made it into C++
171  // as of C++23 or we could use that directly.
173  return std::accumulate (std::begin(r), std::end(r), 0);
174  }
175  };
176 
177  for (const particleVec_t& combination : onlineCombinations) {
178  // Here we store the possible candidates for the matching. We use the
179  // range type as a lightweight method to carry around a view of a vector
180  // without copying it (the RangedItr is essentially a combination of 3
181  // iterators).
182  std::vector<particleRange_t> matchCandidates;
183  for (const xAOD::IParticle* part : combination) {
184  const particleVec_t& possibleMatches = getCandidateMatchesFor(
185  part, offlineCandidates, matches);
186  matchCandidates.emplace_back(possibleMatches.begin(), possibleMatches.end() );
187  } //> end loop over particles
188  // Get all possible combinations of offline objects that could match to
189  // this particular online combination.
190  auto theseOfflineCombinations =
191  TriggerMatchingUtils::getAllDistinctCombinations<const xAOD::IParticle*>(
192  matchCandidates,
194  outerproj());
195  if (msgLvl(MSG::VERBOSE) ) {
196  // Spit out some verbose information
198  "Checking matching for chain " << chain
199  << " with " << matchCandidates.size() << " legs");
200  std::size_t idx = 0;
201  for (const particleRange_t& range : matchCandidates)
202  ATH_MSG_VERBOSE( "Leg #" << idx++ << " has " << range.size()
203  << " offline candidates." );
205  "Matching generated " << theseOfflineCombinations.size()
206  << " offline combinations");
207  }
208  // Now push them back into the output. Use a specialised function for
209  // inserting into a sorted vector that ensures that we only output
210  // unique combinations
211  for (const particleVec_t& vec : theseOfflineCombinations)
212  TriggerMatchingUtils::insertIntoSortedVector(offlineCombinations, vec, outerproj());
213  } //> end loop over combinations
214 
215 
216  // Decorate the found combinations onto trigger composites.
217  for (const particleVec_t& foundCombination : offlineCombinations) {
218  xAOD::TrigComposite* composite = new xAOD::TrigComposite();
219  container->push_back(composite);
220  static const acc_t<vecLink_t<xAOD::IParticleContainer>> dec_links(
221  "TrigMatchedObjects");
222  vecLink_t<xAOD::IParticleContainer>& links = dec_links(*composite);
223  for (const xAOD::IParticle* part : foundCombination) {
224  const xAOD::IParticleContainer *container = offlineContainers.at(part->type());
225  // If we have an owning container then things are relatively simple:
226  // we can just construct the element link from its SG key and the
227  // index. Otherwise we have to locate the correct proxy from the element
228  if (container->trackIndices())
229  links.emplace_back(
230  m_offlineInputs.at(part->type()).key(),
231  part->index(),
232  extendedCtx.proxy());
233  else
234  links.push_back(makeLink<xAOD::IParticleContainer>(part, extendedCtx.proxy()));
235  }
236  } //> end loop over the found combinations
237  } //> end loop over chains
238  return StatusCode::SUCCESS;
239  }
240 
242  xAOD::TrigCompositeContainer*& container,
243  const std::string& chain) const
244  {
245  auto uniqueContainer = std::make_unique<xAOD::TrigCompositeContainer>();
246  auto aux = std::make_unique<xAOD::AuxContainerBase>();
247  uniqueContainer->setStore(aux.get() );
248  container = uniqueContainer.get();
249  std::string name = m_outputPrefix+chain;
250  // We have to replace '.' characters with '_' characters so that these are
251  // valid container names...
252  std::replace(name.begin(), name.end(), '.', '_');
253  ATH_CHECK( evtStore()->record(std::move(uniqueContainer), name) );
254  ATH_CHECK( evtStore()->record(std::move(aux), name+"Aux.") );
255  return StatusCode::SUCCESS;
256  }
257 
259  const xAOD::IParticle* part,
260  std::map<xAOD::Type::ObjectType, particleVec_t>& offlineParticles,
261  std::map<const xAOD::IParticle*, particleVec_t>& cache) const
262  {
263  // Build up all the possible matches between online and offline particles
264  auto cacheItr = cache.find(part);
265  if (cacheItr == cache.end() ) {
266  xAOD::Type::ObjectType type = part->type();
268  if (type == xAOD::Type::CaloCluster) {
269  // If it's a calo cluster then we need to get the cluster from the
270  // egamma types.
271  static const constAcc_t<vecLink_t<xAOD::CaloClusterContainer>> acc_calo("caloClusterLinks");
272  for (xAOD::Type::ObjectType egType : {
274  {
275  for (const xAOD::IParticle* cand : offlineParticles[egType]) {
276  const vecLink_t<xAOD::CaloClusterContainer>& links = acc_calo(*cand);
277  if (links.size() == 0 || !links.at(0).isValid() )
278  continue;
279  const xAOD::CaloCluster* clus = *links.at(0);
280  if (matchParticles(part, clus) )
281  candidates.push_back(cand);
282  }
283  }
284  }
285  else {
286  for (const xAOD::IParticle* cand : offlineParticles[type])
287  if (matchParticles(part, cand) )
288  candidates.push_back(cand);
289  }
290  cacheItr = cache.emplace(
291  std::make_pair(part, std::move(candidates) ) ).first;
292  }
293  return cacheItr->second;
294  }
295 
297  const xAOD::IParticle* lhs,
298  const xAOD::IParticle* rhs) const
299  {
300  return m_scoreTool->score(*lhs, *rhs) < m_drThreshold;
301  }
302 
303 } //> end namespace DerivationFramework
LArG4FSStartPointFilter.part
part
Definition: LArG4FSStartPointFilter.py:21
replace
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition: hcg.cxx:307
beamspotman.r
def r
Definition: beamspotman.py:672
DerivationFramework::TriggerMatchingTool::addBranches
virtual StatusCode addBranches(const EventContext &ctx) const override
Calculate the matchings.
Definition: TriggerMatchingTool.cxx:96
DerivationFramework::TriggerMatchingTool::m_drThreshold
float m_drThreshold
The DR threshold to use.
Definition: TriggerMatchingTool.h:73
DerivationFramework::TriggerMatchingTool::matchParticles
bool matchParticles(const xAOD::IParticle *lhs, const xAOD::IParticle *rhs) const
Check if the dR between two particles is below threshold.
Definition: TriggerMatchingTool.cxx:296
xAOD::Electron
Electron_v1 Electron
Definition of the current "egamma version".
Definition: Event/xAOD/xAODEgamma/xAODEgamma/Electron.h:17
BuildCombinations.h
IProxyDict::proxy
virtual SG::DataProxy * proxy(const CLID &id, const std::string &key) const =0
Get proxy with given id and key.
StateLessPT_NewConfig.proxy
proxy
Definition: StateLessPT_NewConfig.py:407
runLayerRecalibration.chain
chain
Definition: runLayerRecalibration.py:175
ATH_MSG_INFO
#define ATH_MSG_INFO(x)
Definition: AthMsgStreamMacros.h:31
DerivationFramework::TriggerMatchingTool::m_outputPrefix
std::string m_outputPrefix
The prefix to place at the beginning of the output containers.
Definition: TriggerMatchingTool.h:79
xAOD::TrigComposite
TrigComposite_v1 TrigComposite
Declare the latest version of the class.
Definition: Event/xAOD/xAODTrigger/xAODTrigger/TrigComposite.h:16
ObjectType
ObjectType
Definition: BaseObject.h:11
SG::Accessor
Helper class to provide type-safe access to aux data.
Definition: Control/AthContainers/AthContainers/Accessor.h:68
drawFromPickle.candidates
candidates
Definition: drawFromPickle.py:271
xAODP4Helpers.h
runITkAlign.accumulate
accumulate
Update flags based on parser line args.
Definition: runITkAlign.py:62
ExtendedEventContext.h
ViewHelper::makeLink
ElementLink< T > makeLink(const SG::View *view, const SG::ReadHandle< T > &handle, size_t index)
Create EL to a collection in view.
Definition: ViewHelper.h:309
PlotCalibFromCool.begin
begin
Definition: PlotCalibFromCool.py:94
AuxContainerBase.h
DerivationFramework::TriggerMatchingUtils::insertIntoSortedVector
bool insertIntoSortedVector(std::vector< T > &vec, const T &ele, PROJ proj={})
Helper function for inserting an element into a sorted vector.
DataVector::get
const T * get(size_type n) const
Access an element, as an rvalue.
DerivationFramework::TriggerMatchingTool::m_scoreTool
ToolHandle< Trig::IMatchScoringTool > m_scoreTool
The pair scoring tool.
Definition: TriggerMatchingTool.h:93
vec
std::vector< size_t > vec
Definition: CombinationsGeneratorTest.cxx:9
DerivationFramework::TriggerMatchingTool::TriggerMatchingTool
TriggerMatchingTool(const std::string &type, const std::string &name, const IInterface *pSvcLocator)
Constructor.
Definition: TriggerMatchingTool.cxx:42
SG::ConstAccessor
Helper class to provide constant type-safe access to aux data.
Definition: ConstAccessor.h:55
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
xAOD::IParticle
Class providing the definition of the 4-vector interface.
Definition: Event/xAOD/xAODBase/xAODBase/IParticle.h:41
IProxyDict
A proxy dictionary.
Definition: AthenaKernel/AthenaKernel/IProxyDict.h:47
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
xAOD::CaloCluster
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
Definition: Event/xAOD/xAODCaloEvent/xAODCaloEvent/CaloCluster.h:19
mergePhysValFiles.end
end
Definition: DataQuality/DataQualityUtils/scripts/mergePhysValFiles.py:92
DerivationFramework::TriggerMatchingTool::m_checkEmptyChainGroups
bool m_checkEmptyChainGroups
If set, discard any triggers with empty chain groups (break the job otherwise).
Definition: TriggerMatchingTool.h:83
SG::makeHandle
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
Definition: ReadCondHandle.h:270
Atlas::getExtendedEventContext
const ExtendedEventContext & getExtendedEventContext(const EventContext &ctx)
Retrieve an extended context from a context object.
Definition: ExtendedEventContext.cxx:32
xAOD::CaloCluster_v1
Description of a calorimeter cluster.
Definition: CaloCluster_v1.h:62
python.utils.AtlRunQueryDQUtils.p
p
Definition: AtlRunQueryDQUtils.py:209
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
DerivationFramework::TriggerMatchingTool::m_trigParticleTool
ToolHandle< Trig::IIParticleRetrievalTool > m_trigParticleTool
The tool to retrieve the online candidates.
Definition: TriggerMatchingTool.h:67
Atlas::ExtendedEventContext
Definition: ExtendedEventContext.h:23
DerivationFramework::TriggerMatchingTool::particleVec_t
std::vector< const xAOD::IParticle * > particleVec_t
Helper typedefs.
Definition: TriggerMatchingTool.h:42
DerivationFramework::TriggerMatchingTool::m_rerun
bool m_rerun
Whether to match in rerun mode or not.
Definition: TriggerMatchingTool.h:76
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
DMTest::links
links
Definition: CLinks_v1.cxx:22
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
Amg::transform
Amg::Vector3D transform(Amg::Vector3D &v, Amg::Transform3D &tr)
Transform a point from a Trasformation3D.
Definition: GeoPrimitivesHelpers.h:156
DerivationFramework::TriggerMatchingTool::m_tdt
ToolHandle< Trig::TrigDecisionTool > m_tdt
The trig decision tool.
Definition: TriggerMatchingTool.h:90
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:194
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
DerivationFramework::TriggerMatchingUtils::RangedItr
utility class that acts wraps a bidirectional iterator.
Definition: RangedItr.h:18
DerivationFramework
THE reconstruction tool.
Definition: ParticleSortingAlg.h:24
xAOD::TrigComposite_v1
Class used to describe composite objects in the HLT.
Definition: TrigComposite_v1.h:49
DataVector
Derived DataVector<T>.
Definition: DataVector.h:795
xAOD::IParticle::pt
virtual double pt() const =0
The transverse momentum ( ) of the particle.
Trig::ChainGroup
Definition: ChainGroup.h:51
IProxyDict.h
DerivationFramework::TriggerMatchingTool::getCandidateMatchesFor
const particleVec_t & getCandidateMatchesFor(const xAOD::IParticle *part, std::map< xAOD::Type::ObjectType, particleVec_t > &offlineParticles, std::map< const xAOD::IParticle *, particleVec_t > &cache) const
Get all offline particles that could match a given online one.
Definition: TriggerMatchingTool.cxx:258
DerivationFramework::TriggerMatchingTool::m_offlineInputs
std::map< xAOD::Type::ObjectType, SG::ReadHandleKey< xAOD::IParticleContainer > > m_offlineInputs
The input containers to use. These are keyed by xAOD object type.
Definition: TriggerMatchingTool.h:70
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:240
DataVector::push_back
value_type push_back(value_type pElem)
Add an element to the end of the collection.
DerivationFramework::TriggerMatchingTool::m_inputDependentConfig
bool m_inputDependentConfig
If using an input-file-dependent config then we warn when triggers are removed.
Definition: TriggerMatchingTool.h:87
xAOD::Photon
Photon_v1 Photon
Definition of the current "egamma version".
Definition: Event/xAOD/xAODEgamma/xAODEgamma/Photon.h:17
Muon
struct TBPatternUnitContext Muon
TriggerMatchingTool.h
python.PyAthena.v
v
Definition: PyAthena.py:154
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
LArNewCalib_DelayDump_OFC_Cali.idx
idx
Definition: LArNewCalib_DelayDump_OFC_Cali.py:69
CaloClusterContainer.h
xAODType::Tau
@ Tau
The object is a tau (jet)
Definition: ObjectType.h:49
DerivationFramework::TriggerMatchingTool::initialize
virtual StatusCode initialize() override
Initialize the tool.
Definition: TriggerMatchingTool.cxx:79
python.Constants.VERBOSE
int VERBOSE
Definition: Control/AthenaCommon/python/Constants.py:13
ReadHandle.h
Handle class for reading from StoreGate.
Trig::ChainGroup::getListOfTriggers
std::vector< std::string > getListOfTriggers() const
Definition: ChainGroup.cxx:467
columnar::operator()
decltype(auto) operator()(ObjectId< CI, CM > id) const noexcept
Definition: ColumnAccessor.h:173
SG::DataProxy
Definition: DataProxy.h:45
SG::AllowEmpty
@ AllowEmpty
Definition: StoreGate/StoreGate/VarHandleKey.h:27
DerivationFramework::TriggerMatchingTool::createOutputContainer
StatusCode createOutputContainer(xAOD::TrigCompositeContainer *&container, const std::string &chain) const
Create an output container for the named chain.
Definition: TriggerMatchingTool.cxx:241