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 
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 
114  const EventContext &ctx = Gaudi::Hive::currentContext();
116 
117  // Now, get all the possible offline candidates
118  std::map<xAOD::Type::ObjectType, const xAOD::IParticleContainer *> offlineContainers;
119  std::map<xAOD::Type::ObjectType, particleVec_t> offlineCandidates;
120  for (const auto& p : m_offlineInputs) {
121  // Skip any that may have been disabled by providing an empty string to
122  // the corresponding property
123  if (p.second.empty() )
124  continue;
125  auto handle = SG::makeHandle(p.second, ctx);
126  if (!handle.isValid())
127  {
128  ATH_MSG_ERROR("Failed to retrieve " << p.second);
129  return StatusCode::FAILURE;
130  }
131  offlineContainers[p.first] = handle.ptr();
132  offlineCandidates.emplace(std::make_pair(
133  p.first,
134  particleVec_t(handle->begin(), handle->end() )
135  ) );
136  }
137 
138  // Store possible matches from online to offline particles here
139  // We do this as multiple chains may use the same online particles so we can
140  // reuse this information.
141  std::map<const xAOD::IParticle*, particleVec_t> matches;
142 
143  // Iterate through the chains to get the matchings
144  for (const std::string& chain : m_chainNames) {
145  // Create the output
146  xAOD::TrigCompositeContainer* container(nullptr);
147  ATH_CHECK( createOutputContainer(container, chain) );
148 
149  // Get the list of online combinations
150  std::vector<particleVec_t> onlineCombinations;
151  ATH_CHECK( m_trigParticleTool->retrieveParticles(onlineCombinations, chain, m_rerun) );
152 
154  onlineCombinations.size() << " combinations found for chain" << chain);
155 
156  // If no combinations were found (because the trigger was not passed) then
157  // we can skip this trigger
158  if (onlineCombinations.size() == 0)
159  continue;
160 
162  // Now build up the list of offline combinations;
163  std::vector<particleVec_t> offlineCombinations;
164 
165  // Projection operator to use for comparing vectors
166  // of IParticle*. Compare by using the sum of the pts
167  // of all particles in the vector.
168  struct outerproj {
169  double operator() (const particleVec_t& v) const
170  {
171  // Unfortunately std::ranges::accumulate hasn't made it into C++
172  // as of C++23 or we could use that directly.
174  return std::accumulate (std::begin(r), std::end(r), 0);
175  }
176  };
177 
178  for (const particleVec_t& combination : onlineCombinations) {
179  // Here we store the possible candidates for the matching. We use the
180  // range type as a lightweight method to carry around a view of a vector
181  // without copying it (the RangedItr is essentially a combination of 3
182  // iterators).
183  std::vector<particleRange_t> matchCandidates;
184  for (const xAOD::IParticle* part : combination) {
185  const particleVec_t& possibleMatches = getCandidateMatchesFor(
186  part, offlineCandidates, matches);
187  matchCandidates.emplace_back(possibleMatches.begin(), possibleMatches.end() );
188  } //> end loop over particles
189  // Get all possible combinations of offline objects that could match to
190  // this particular online combination.
191  auto theseOfflineCombinations =
192  TriggerMatchingUtils::getAllDistinctCombinations<const xAOD::IParticle*>(
193  matchCandidates,
195  outerproj());
196  if (msgLvl(MSG::VERBOSE) ) {
197  // Spit out some verbose information
199  "Checking matching for chain " << chain
200  << " with " << matchCandidates.size() << " legs");
201  std::size_t idx = 0;
202  for (const particleRange_t& range : matchCandidates)
203  ATH_MSG_VERBOSE( "Leg #" << idx++ << " has " << range.size()
204  << " offline candidates." );
206  "Matching generated " << theseOfflineCombinations.size()
207  << " offline combinations");
208  }
209  // Now push them back into the output. Use a specialised function for
210  // inserting into a sorted vector that ensures that we only output
211  // unique combinations
212  for (const particleVec_t& vec : theseOfflineCombinations)
213  TriggerMatchingUtils::insertIntoSortedVector(offlineCombinations, vec, outerproj());
214  } //> end loop over combinations
215 
216 
217  // Decorate the found combinations onto trigger composites.
218  for (const particleVec_t& foundCombination : offlineCombinations) {
219  xAOD::TrigComposite* composite = new xAOD::TrigComposite();
220  container->push_back(composite);
221  static const acc_t<vecLink_t<xAOD::IParticleContainer>> dec_links(
222  "TrigMatchedObjects");
223  vecLink_t<xAOD::IParticleContainer>& links = dec_links(*composite);
224  for (const xAOD::IParticle* part : foundCombination) {
225  const xAOD::IParticleContainer *container = offlineContainers.at(part->type());
226  // If we have an owning container then things are relatively simple:
227  // we can just construct the element link from its SG key and the
228  // index. Otherwise we have to locate the correct proxy from the element
229  if (container->trackIndices())
230  links.emplace_back(
231  m_offlineInputs.at(part->type()).key(),
232  part->index(),
233  extendedCtx.proxy());
234  else
235  links.push_back(makeLink<xAOD::IParticleContainer>(part, extendedCtx.proxy()));
236  }
237  } //> end loop over the found combinations
238  } //> end loop over chains
239  return StatusCode::SUCCESS;
240  }
241 
243  xAOD::TrigCompositeContainer*& container,
244  const std::string& chain) const
245  {
246  auto uniqueContainer = std::make_unique<xAOD::TrigCompositeContainer>();
247  auto aux = std::make_unique<xAOD::AuxContainerBase>();
248  uniqueContainer->setStore(aux.get() );
249  container = uniqueContainer.get();
250  std::string name = m_outputPrefix+chain;
251  // We have to replace '.' characters with '_' characters so that these are
252  // valid container names...
253  std::replace(name.begin(), name.end(), '.', '_');
254  ATH_CHECK( evtStore()->record(std::move(uniqueContainer), name) );
255  ATH_CHECK( evtStore()->record(std::move(aux), name+"Aux.") );
256  return StatusCode::SUCCESS;
257  }
258 
260  const xAOD::IParticle* part,
261  std::map<xAOD::Type::ObjectType, particleVec_t>& offlineParticles,
262  std::map<const xAOD::IParticle*, particleVec_t>& cache) const
263  {
264  // Build up all the possible matches between online and offline particles
265  auto cacheItr = cache.find(part);
266  if (cacheItr == cache.end() ) {
267  xAOD::Type::ObjectType type = part->type();
269  if (type == xAOD::Type::CaloCluster) {
270  // If it's a calo cluster then we need to get the cluster from the
271  // egamma types.
272  static const constAcc_t<vecLink_t<xAOD::CaloClusterContainer>> acc_calo("caloClusterLinks");
273  for (xAOD::Type::ObjectType egType : {
275  {
276  for (const xAOD::IParticle* cand : offlineParticles[egType]) {
277  const vecLink_t<xAOD::CaloClusterContainer>& links = acc_calo(*cand);
278  if (links.size() == 0 || !links.at(0).isValid() )
279  continue;
280  const xAOD::CaloCluster* clus = *links.at(0);
281  if (matchParticles(part, clus) )
282  candidates.push_back(cand);
283  }
284  }
285  }
286  else {
287  for (const xAOD::IParticle* cand : offlineParticles[type])
288  if (matchParticles(part, cand) )
289  candidates.push_back(cand);
290  }
291  cacheItr = cache.emplace(
292  std::make_pair(part, std::move(candidates) ) ).first;
293  }
294  return cacheItr->second;
295  }
296 
298  const xAOD::IParticle* lhs,
299  const xAOD::IParticle* rhs) const
300  {
301  return m_scoreTool->score(*lhs, *rhs) < m_drThreshold;
302  }
303 
304 } //> 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::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:297
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
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:274
Atlas::getExtendedEventContext
const ExtendedEventContext & getExtendedEventContext(const EventContext &ctx)
Retrieve an extended context from a context object.
Definition: ExtendedEventContext.cxx:32
DerivationFramework::TriggerMatchingTool::addBranches
virtual StatusCode addBranches() const override
Calculate the matchings.
Definition: TriggerMatchingTool.cxx:96
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:794
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:259
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
runIDAlign.accumulate
accumulate
Update flags based on parser line args.
Definition: runIDAlign.py:107
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:30
DerivationFramework::TriggerMatchingTool::createOutputContainer
StatusCode createOutputContainer(xAOD::TrigCompositeContainer *&container, const std::string &chain) const
Create an output container for the named chain.
Definition: TriggerMatchingTool.cxx:242