ATLAS Offline Software
Loading...
Searching...
No Matches
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
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>
18
19namespace {
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
40namespace 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",
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
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.
172 auto r = std::views::transform (v, &xAOD::IParticle::pt);
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 =
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
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();
267 particleVec_t candidates;
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
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
std::vector< size_t > vec
Handle class for reading from StoreGate.
bool m_checkEmptyChainGroups
If set, discard any triggers with empty chain groups (break the job otherwise).
std::map< xAOD::Type::ObjectType, SG::ReadHandleKey< xAOD::IParticleContainer > > m_offlineInputs
The input containers to use. These are keyed by xAOD object type.
virtual StatusCode addBranches(const EventContext &ctx) const override
Calculate the matchings.
bool matchParticles(const xAOD::IParticle *lhs, const xAOD::IParticle *rhs) const
Check if the dR between two particles is below threshold.
virtual StatusCode initialize() override
Initialize the tool.
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.
std::vector< const xAOD::IParticle * > particleVec_t
Helper typedefs.
std::string m_outputPrefix
The prefix to place at the beginning of the output containers.
ToolHandle< Trig::TrigDecisionTool > m_tdt
The trig decision tool.
StatusCode createOutputContainer(xAOD::TrigCompositeContainer *&container, const std::string &chain) const
Create an output container for the named chain.
bool m_rerun
Whether to match in rerun mode or not.
ToolHandle< Trig::IIParticleRetrievalTool > m_trigParticleTool
The tool to retrieve the online candidates.
TriggerMatchingTool(const std::string &type, const std::string &name, const IInterface *pSvcLocator)
Constructor.
bool m_inputDependentConfig
If using an input-file-dependent config then we warn when triggers are removed.
float m_drThreshold
The DR threshold to use.
ToolHandle< Trig::IMatchScoringTool > m_scoreTool
The pair scoring tool.
utility class that acts wraps a bidirectional iterator.
Definition RangedItr.h:18
virtual SG::DataProxy * proxy(const CLID &id, const std::string &key) const =0
Get proxy with given id and key.
SG::ConstAccessor< T, ALLOC > ConstAccessor
Definition AuxElement.h:569
SG::Accessor< T, ALLOC > Accessor
Definition AuxElement.h:572
std::vector< std::string > getListOfTriggers() const
Class providing the definition of the 4-vector interface.
virtual double pt() const =0
The transverse momentum ( ) of the particle.
int r
Definition globals.cxx:22
const ExtendedEventContext & getExtendedEventContext(const EventContext &ctx)
Retrieve an extended context from a context object.
std::vector< std::vector< T > > getAllDistinctCombinations(std::vector< RangedItr< typename std::vector< T >::const_iterator > > &inputs, INNERPROJ innerproj={}, OUTERPROJ outerproj={})
Get all valid, unique combinations of distinct elements from the input ranges.
bool insertIntoSortedVector(std::vector< T > &vec, const T &ele, PROJ proj={})
Helper function for inserting an element into a sorted vector.
THE reconstruction tool.
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
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
DataModel_detail::iterator< DVL > unique(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of unique for DataVector/List.
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
ObjectType
Type of objects that have a representation in the xAOD EDM.
Definition ObjectType.h:32
@ Photon
The object is a photon.
Definition ObjectType.h:47
@ CaloCluster
The object is a calorimeter cluster.
Definition ObjectType.h:39
@ Muon
The object is a muon.
Definition ObjectType.h:48
@ Electron
The object is an electron.
Definition ObjectType.h:46
@ Tau
The object is a tau (jet)
Definition ObjectType.h:49
TrigCompositeContainer_v1 TrigCompositeContainer
Declare the latest version of the container.
CaloCluster_v1 CaloCluster
Define the latest version of the calorimeter cluster class.
TrigComposite_v1 TrigComposite
Declare the latest version of the class.
DataVector< IParticle > IParticleContainer
Simple convenience declaration of IParticleContainer.