ATLAS Offline Software
Loading...
Searching...
No Matches
ChargedHadronSubtractionTool.h
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#ifndef JETRECTOOLS_CHARGEDHADRONSUBTRACTIONTOOL_H
6#define JETRECTOOLS_CHARGEDHADRONSUBTRACTIONTOOL_H
7
15
16
19
26
28#include <string>
29
30
33
34 public:
35
36 ChargedHadronSubtractionTool(const std::string& name);
37
38 // Check that the configuration is reasonable
39 StatusCode initialize();
40
41 private:
42 // Implement the correction
43 StatusCode process_impl(xAOD::IParticleContainer* cont) const;
44 template <class T, class U> StatusCode matchToPrimaryVertex(T &) const;
45 template <class T, class U> StatusCode matchByPrimaryVertex(T &) const;
46
47 const xAOD::Vertex* getPrimaryVertex() const;
48
49 // properties -----------------
50
51 Gaudi::Property<bool> m_useTrackToVertexTool = {this, "UseTrackToVertexTool", false, "True if we will use the track to vertex tool"};
52 Gaudi::Property<bool> m_ignoreVertex = {this, "IgnoreVertex", false, "Dummy option for cosmics - accept everything"};
53 Gaudi::Property<float> m_z0sinThetaCutValue = {this, "Z0sinThetaCutValue", 2.0, "True if we will use the track to vertex tool"};
54 Gaudi::Property<bool> m_byVertex = {this, "DoByVertex", false, "True if we should match to each primary vertex, not just PV0"};
55
56 static double calcAbsZ0SinTheta(const xAOD::TrackParticle& trk, const xAOD::Vertex& vtx);
57
58 SG::ReadHandleKey<xAOD::VertexContainer> m_vertexContainer_key = {this, "VertexContainerKey", "PrimaryVertices", "key for the primary vertex container"};
59 SG::ReadHandleKey<jet::TrackVertexAssociation> m_trkVtxAssoc_key = {this, "TrackVertexAssociation", "JetTrackVtxAssoc", "key for the TrackVertexAssociation object"};
60
61
62
63};
64
65
66template <class T, class U> StatusCode ChargedHadronSubtractionTool::matchToPrimaryVertex(T& cont) const
67{
68 const static SG::AuxElement::Accessor<char> PVMatchedAcc("matchedToPV");
69 const static SG::AuxElement::Accessor<char> PUsidebandMatchedAcc("matchedToPUsideband");
70
71 // Use only one of TVA or PV
72 const jet::TrackVertexAssociation *trkVtxAssoc = nullptr;
73 const xAOD::Vertex *vtx = nullptr;
74 if (!m_ignoreVertex)
75 {
76 // In cosmics, there's no PV container so we need to avoid attempting
77 // to retrieve anything related to it
79 {
80 auto handle = SG::makeHandle(m_trkVtxAssoc_key);
81 if (!handle.isValid())
82 {
83 ATH_MSG_ERROR("Can't retrieve TrackVertexAssociation : " << m_trkVtxAssoc_key.key());
84 return StatusCode::FAILURE;
85 }
86 trkVtxAssoc = handle.cptr();
87 }
88 else
89 {
90 vtx = getPrimaryVertex();
91 if (vtx == nullptr)
92 {
93 ATH_MSG_ERROR("Primary vertex container was empty or no valid vertex found!");
94 return StatusCode::FAILURE;
95 }
96 else if (vtx->vertexType() == xAOD::VxType::NoVtx)
97 {
98 ATH_MSG_VERBOSE("No genuine primary vertex found. Will consider all PFOs matched.");
99 }
100 }
101 }
102
103 for (U *ppfo : cont)
104 {
105 // Ignore neutral PFOs
106 if constexpr (std::is_same_v<U, xAOD::PFO>) {
107 if (std::abs(ppfo->charge()) < FLT_MIN)
108 continue;
109 } else if constexpr (std::is_same_v<U, xAOD::FlowElement>) {
110 if (!ppfo->isCharged())
111 continue;
112 }
113
114 bool matchedToPrimaryVertex = false;
115 bool matchedToPileupSideband = false;
116 if (m_ignoreVertex)
117 {
118 // If we don't use vertex information, don't bother computing the decision
119 // Just pass every cPFO -- there shouldn't be many in cosmics!
120 matchedToPrimaryVertex = true;
121 }
122 else
123 {
124 const xAOD::TrackParticle* ptrk = nullptr;
125
126 // Use different methods to get TrackParticle based on U
127 if constexpr (std::is_same_v<U, xAOD::PFO>) {
128 ptrk = ppfo->track(0);
129 } else if constexpr (std::is_same_v<U, xAOD::FlowElement>) {
130 ptrk = dynamic_cast<const xAOD::TrackParticle*>(ppfo->chargedObject(0));
131 }
132 if (ptrk == nullptr)
133 {
134 ATH_MSG_WARNING("Charged PFO with index " << ppfo->index() << " has no ID track!");
135 continue;
136 }
137 if (trkVtxAssoc)
138 { // Use TrackVertexAssociation
139 const xAOD::Vertex *thisTracksVertex = trkVtxAssoc->associatedVertex(ptrk);
140 if (thisTracksVertex == nullptr)
141 {
142 ATH_MSG_DEBUG("No vertex associated to track " << ptrk->index() << "! So it cannot be associated to the primary vertex");
143 matchedToPrimaryVertex = false;
144 }
145 else
146 {
147 matchedToPrimaryVertex = (xAOD::VxType::PriVtx == thisTracksVertex->vertexType());
148
149 // r21 PU sideband definition (see below)
150 // needed for comparisons with new r22 definition (neutral only)
151 vtx = getPrimaryVertex();
152 if (vtx != nullptr && vtx->vertexType() != xAOD::VxType::NoVtx)
153 {
154 const double absZ0sinTheta = calcAbsZ0SinTheta(*ptrk, *vtx);
155 if (absZ0sinTheta < 2.0 * m_z0sinThetaCutValue && absZ0sinTheta >= m_z0sinThetaCutValue)
156 matchedToPileupSideband = true;
157 }
158 }
159 }
160 else if (vtx) { // Use Primary Vertex
161 if (vtx->vertexType() == xAOD::VxType::NoVtx)
162 { // No reconstructed vertices
163 matchedToPrimaryVertex = true; // simply match all cPFOs in this case
164 }
165 else
166 { // Had a good reconstructed vertex.
167 // vtz.z() provides z of that vertex w.r.t the center of the beamspot (z = 0).
168 // Thus we correct the track z0 to be w.r.t z = 0
169 float z0 = ptrk->z0() + ptrk->vz() - vtx->z();
170 float theta = ptrk->theta();
171 matchedToPrimaryVertex = (std::abs(z0 * sin(theta)) < m_z0sinThetaCutValue);
172 if (std::abs(z0 * sin(theta)) < 2.0 * m_z0sinThetaCutValue && std::abs(z0 * sin(theta)) >= m_z0sinThetaCutValue)
173 matchedToPileupSideband = true;
174 }
175 } // TVA vs PV decision
176 }
177 PVMatchedAcc(*ppfo) = matchedToPrimaryVertex;
178 PUsidebandMatchedAcc(*ppfo) = matchedToPileupSideband;
179 }
180
181 return StatusCode::SUCCESS;
182}
183
184template <class T, class U> StatusCode ChargedHadronSubtractionTool::matchByPrimaryVertex(T& cont) const
185{
186 const static SG::AuxElement::Accessor<std::vector<unsigned>> matchingPVs("MatchingPVs");
187 const static SG::AuxElement::Accessor<std::vector<unsigned>> matchingPUSBs("MatchingPUsidebands");
188
189 // Retrieve Primary Vertices
191 if (!handle.isValid())
192 {
193 ATH_MSG_WARNING(" This event has no primary vertex container");
194 return StatusCode::FAILURE;
195 }
196
197 const xAOD::VertexContainer *pvtxs = handle.cptr();
198 if (pvtxs->empty())
199 {
200 ATH_MSG_WARNING(" Failed to retrieve valid primary vertex container");
201 return StatusCode::FAILURE;
202 }
203
204 // Use only one of TVA or PV
205 const jet::TrackVertexAssociation *trkVtxAssoc = nullptr;
207 {
208 auto handle = SG::makeHandle(m_trkVtxAssoc_key);
209 if (!handle.isValid())
210 {
211 ATH_MSG_ERROR("Can't retrieve TrackVertexAssociation : " << m_trkVtxAssoc_key.key());
212 return StatusCode::FAILURE;
213 }
214 trkVtxAssoc = handle.cptr();
215 }
216
217 for (U *ppfo : cont)
218 {
219 // Ignore neutral PFOs
220 if constexpr (std::is_same_v<U, xAOD::PFO>) {
221 if (std::abs(ppfo->charge()) < FLT_MIN)
222 continue;
223 } else if constexpr (std::is_same_v<U, xAOD::FlowElement>) {
224 if (!ppfo->isCharged())
225 continue;
226 }
227
228 // Get the track for this charged PFO
229 const xAOD::TrackParticle* ptrk = nullptr;
230
231 // Use different methods to get TrackParticle based on U
232 if constexpr (std::is_same_v<U, xAOD::PFO>) {
233 ptrk = ppfo->track(0);
234 } else if constexpr (std::is_same_v<U, xAOD::FlowElement>) {
235 ptrk = dynamic_cast<const xAOD::TrackParticle*>(ppfo->chargedObject(0));
236 }
237 if (ptrk == nullptr)
238 {
239 ATH_MSG_WARNING("Charged PFO with index " << ppfo->index() << " has no ID track!");
240 continue;
241 }
242
243 std::vector<unsigned> matchingVertexList;
244 std::vector<unsigned> matchingPUSBList;
245
246 if (trkVtxAssoc)
247 {
248 // Use TrackVertexAssociation
249 const xAOD::Vertex *thisTracksVertex = trkVtxAssoc->associatedVertex(ptrk);
250 if (thisTracksVertex == nullptr)
251 {
252 ATH_MSG_DEBUG("No vertex associated to track " << ptrk->index() << "! So it cannot be associated to the primary vertex");
253 }
254 else
255 {
256 matchingVertexList.push_back(thisTracksVertex->index());
257 for (const xAOD::Vertex *vtx : *pvtxs){
258 if (vtx != nullptr && vtx->vertexType() != xAOD::VxType::NoVtx)
259 {
260 float z0 = ptrk->z0() + ptrk->vz() - vtx->z();
261 float theta = ptrk->theta();
262
263 if (std::abs(z0 * sin(theta)) < 2.0 * m_z0sinThetaCutValue && std::abs(z0 * sin(theta)) >= m_z0sinThetaCutValue)
264 matchingPUSBList.push_back(vtx->index());
265 }
266 }
267 }
268 }
269 else{
270 // Use z0sinThetaCutValue
271 // Loop over the primary vertices to determine which ones potentially match
272 for (const xAOD::Vertex *vtx : *pvtxs)
273 {
274 bool matchedToVertex = false;
275 bool matchedToPileupSideband = false;
276
277 if (vtx == nullptr)
278 {
279 ATH_MSG_WARNING("Encountered a nullptr vertex when trying to match charged PFOs to vertices");
280 continue;
281 }
282 else if (vtx->vertexType() == xAOD::VxType::NoVtx)
283 { // No reconstructed vertices
284 matchedToVertex = true; // simply match all cPFOs in this case
285 }
286 else
287 { // Had a good reconstructed vertex
288 const double absZ0sinTheta = calcAbsZ0SinTheta(*ptrk, *vtx);
289 matchedToVertex = (absZ0sinTheta < m_z0sinThetaCutValue);
290 if (absZ0sinTheta < 2.0 * m_z0sinThetaCutValue && absZ0sinTheta >= m_z0sinThetaCutValue)
291 matchedToPileupSideband = true;
292 }
293
294 if (matchedToVertex)
295 matchingVertexList.push_back(vtx->index());
296 if (matchedToPileupSideband)
297 matchingPUSBList.push_back(vtx->index());
298 }
299 }
300
301 matchingPVs(*ppfo) = std::move(matchingVertexList);
302 matchingPUSBs(*ppfo) = std::move(matchingPUSBList);
303 }
304
305 return StatusCode::SUCCESS;
306}
307
308#endif
Scalar theta() const
theta method
#define ASG_TOOL_CLASS(CLASSNAME, INT1)
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
Property holding a SG store/key/clid from which a ReadHandle is made.
ChargedHadronSubtractionTool(const std::string &name)
StatusCode initialize()
Dummy implementation of the initialisation function.
static double calcAbsZ0SinTheta(const xAOD::TrackParticle &trk, const xAOD::Vertex &vtx)
SG::ReadHandleKey< jet::TrackVertexAssociation > m_trkVtxAssoc_key
const xAOD::Vertex * getPrimaryVertex() const
Gaudi::Property< bool > m_useTrackToVertexTool
SG::ReadHandleKey< xAOD::VertexContainer > m_vertexContainer_key
Gaudi::Property< float > m_z0sinThetaCutValue
StatusCode process_impl(xAOD::IParticleContainer *cont) const
bool empty() const noexcept
Returns true if the collection is empty.
JetConstituentModifierBase(const std::string &name)
Property holding a SG store/key/clid from which a ReadHandle is made.
Class to hold N-to-one aassociations between tracks and vertices.
const xAOD::Vertex * associatedVertex(const xAOD::TrackParticle *trk) const
float z0() const
Returns the parameter.
float theta() const
Returns the parameter, which has range 0 to .
const Trk::Track * track() const
Returns a pointer (which can be NULL) to the Trk::Track which was used to make this TrackParticle.
float vz() const
The z origin for the parameters.
float z() const
Returns the z position.
VxType::VertexType vertexType() const
The type of the vertex.
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
@ PriVtx
Primary vertex.
@ NoVtx
Dummy vertex. TrackParticle was not used in vertex fit.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
VertexContainer_v1 VertexContainer
Definition of the current "Vertex container version".
Vertex_v1 Vertex
Define the latest version of the vertex class.
DataVector< IParticle > IParticleContainer
Simple convenience declaration of IParticleContainer.