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
161 { // Use Primary Vertex
162 if (vtx->vertexType() == xAOD::VxType::NoVtx)
163 { // No reconstructed vertices
164 matchedToPrimaryVertex = true; // simply match all cPFOs in this case
165 }
166 else
167 { // Had a good reconstructed vertex.
168 // vtz.z() provides z of that vertex w.r.t the center of the beamspot (z = 0).
169 // Thus we correct the track z0 to be w.r.t z = 0
170 float z0 = ptrk->z0() + ptrk->vz() - vtx->z();
171 float theta = ptrk->theta();
172 matchedToPrimaryVertex = (std::abs(z0 * sin(theta)) < m_z0sinThetaCutValue);
173 if (std::abs(z0 * sin(theta)) < 2.0 * m_z0sinThetaCutValue && std::abs(z0 * sin(theta)) >= m_z0sinThetaCutValue)
174 matchedToPileupSideband = true;
175 }
176 } // TVA vs PV decision
177 }
178 PVMatchedAcc(*ppfo) = matchedToPrimaryVertex;
179 PUsidebandMatchedAcc(*ppfo) = matchedToPileupSideband;
180 }
181
182 return StatusCode::SUCCESS;
183}
184
185template <class T, class U> StatusCode ChargedHadronSubtractionTool::matchByPrimaryVertex(T& cont) const
186{
187 const static SG::AuxElement::Accessor<std::vector<unsigned>> matchingPVs("MatchingPVs");
188 const static SG::AuxElement::Accessor<std::vector<unsigned>> matchingPUSBs("MatchingPUsidebands");
189
190 // Retrieve Primary Vertices
192 if (!handle.isValid())
193 {
194 ATH_MSG_WARNING(" This event has no primary vertex container");
195 return StatusCode::FAILURE;
196 }
197
198 const xAOD::VertexContainer *pvtxs = handle.cptr();
199 if (pvtxs->empty())
200 {
201 ATH_MSG_WARNING(" Failed to retrieve valid primary vertex container");
202 return StatusCode::FAILURE;
203 }
204
205 // Use only one of TVA or PV
206 const jet::TrackVertexAssociation *trkVtxAssoc = nullptr;
208 {
209 auto handle = SG::makeHandle(m_trkVtxAssoc_key);
210 if (!handle.isValid())
211 {
212 ATH_MSG_ERROR("Can't retrieve TrackVertexAssociation : " << m_trkVtxAssoc_key.key());
213 return StatusCode::FAILURE;
214 }
215 trkVtxAssoc = handle.cptr();
216 }
217
218 for (U *ppfo : cont)
219 {
220 // Ignore neutral PFOs
221 if constexpr (std::is_same_v<U, xAOD::PFO>) {
222 if (std::abs(ppfo->charge()) < FLT_MIN)
223 continue;
224 } else if constexpr (std::is_same_v<U, xAOD::FlowElement>) {
225 if (!ppfo->isCharged())
226 continue;
227 }
228
229 // Get the track for this charged PFO
230 const xAOD::TrackParticle* ptrk = nullptr;
231
232 // Use different methods to get TrackParticle based on U
233 if constexpr (std::is_same_v<U, xAOD::PFO>) {
234 ptrk = ppfo->track(0);
235 } else if constexpr (std::is_same_v<U, xAOD::FlowElement>) {
236 ptrk = dynamic_cast<const xAOD::TrackParticle*>(ppfo->chargedObject(0));
237 }
238 if (ptrk == nullptr)
239 {
240 ATH_MSG_WARNING("Charged PFO with index " << ppfo->index() << " has no ID track!");
241 continue;
242 }
243
244 std::vector<unsigned> matchingVertexList;
245 std::vector<unsigned> matchingPUSBList;
246
247 if (trkVtxAssoc)
248 {
249 // Use TrackVertexAssociation
250 const xAOD::Vertex *thisTracksVertex = trkVtxAssoc->associatedVertex(ptrk);
251 if (thisTracksVertex == nullptr)
252 {
253 ATH_MSG_DEBUG("No vertex associated to track " << ptrk->index() << "! So it cannot be associated to the primary vertex");
254 }
255 else
256 {
257 matchingVertexList.push_back(thisTracksVertex->index());
258 for (const xAOD::Vertex *vtx : *pvtxs){
259 if (vtx != nullptr && vtx->vertexType() != xAOD::VxType::NoVtx)
260 {
261 float z0 = ptrk->z0() + ptrk->vz() - vtx->z();
262 float theta = ptrk->theta();
263
264 if (std::abs(z0 * sin(theta)) < 2.0 * m_z0sinThetaCutValue && std::abs(z0 * sin(theta)) >= m_z0sinThetaCutValue)
265 matchingPUSBList.push_back(vtx->index());
266 }
267 }
268 }
269 }
270 else{
271 // Use z0sinThetaCutValue
272 // Loop over the primary vertices to determine which ones potentially match
273 for (const xAOD::Vertex *vtx : *pvtxs)
274 {
275 bool matchedToVertex = false;
276 bool matchedToPileupSideband = false;
277
278 if (vtx == nullptr)
279 {
280 ATH_MSG_WARNING("Encountered a nullptr vertex when trying to match charged PFOs to vertices");
281 continue;
282 }
283 else if (vtx->vertexType() == xAOD::VxType::NoVtx)
284 { // No reconstructed vertices
285 matchedToVertex = true; // simply match all cPFOs in this case
286 }
287 else
288 { // Had a good reconstructed vertex
289 const double absZ0sinTheta = calcAbsZ0SinTheta(*ptrk, *vtx);
290 matchedToVertex = (absZ0sinTheta < m_z0sinThetaCutValue);
291 if (absZ0sinTheta < 2.0 * m_z0sinThetaCutValue && absZ0sinTheta >= m_z0sinThetaCutValue)
292 matchedToPileupSideband = true;
293 }
294
295 if (matchedToVertex)
296 matchingVertexList.push_back(vtx->index());
297 if (matchedToPileupSideband)
298 matchingPUSBList.push_back(vtx->index());
299 }
300 }
301
302 matchingPVs(*ppfo) = std::move(matchingVertexList);
303 matchingPUSBs(*ppfo) = std::move(matchingPUSBList);
304 }
305
306 return StatusCode::SUCCESS;
307}
308
309#endif
Scalar theta() const
theta method
#define ASG_TOOL_CLASS(CLASSNAME, INT1)
#define ATH_MSG_ERROR(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(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.