ATLAS Offline Software
Loading...
Searching...
No Matches
MuSAVtxJPsiValidationAlg.cxx
Go to the documentation of this file.
1/*
2Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5// this algorithm produces a J/Psi candidate muons container to be passed to the MuSAVtxFitter
6// to be used in "tag and probe" style studies on the MuSA performance
10#include "xAODMuon/Muon.h"
13#include "xAODTracking/Vertex.h"
15#include "AthLinks/ElementLink.h"
18#include "VrtSecInclusive/Constants.h"
20
21namespace {
22 // Accessors for vertex properties
23 const SG::Accessor<float> vtx_pxAcc("vtx_px");
24 const SG::Accessor<float> vtx_pyAcc("vtx_py");
25 const SG::Accessor<float> vtx_pzAcc("vtx_pz");
26 const SG::Accessor<float> vtx_massAcc("vtx_mass");
27 const SG::Accessor<float> vtx_chargeAcc("vtx_charge");
28 const SG::Accessor<float> minOpAngAcc("minOpAng");
29 const SG::Accessor<float> chi2_coreAcc("chi2_core");
30 const SG::Accessor<float> ndof_coreAcc("ndof_core");
31 const SG::Accessor<float> chi2_assocAcc("chi2_assoc");
32 const SG::Accessor<float> ndof_assocAcc("ndof_assoc");
33 const SG::Accessor<float> massAcc("mass");
34 const SG::Accessor<float> mass_eAcc("mass_e");
35 const SG::Accessor<float> mass_selectedTracksAcc("mass_selectedTracks");
36 const SG::Accessor<int> num_trksAcc("num_trks");
37 const SG::Accessor<int> num_selectedTracksAcc("num_selectedTracks");
38 const SG::Accessor<int> num_associatedTracksAcc("num_associatedTracks");
39 const SG::Accessor<float> dCloseVrtAcc("dCloseVrt");
40}
41
42namespace Rec {
43
44MuSAVtxJPsiValidationAlg::MuSAVtxJPsiValidationAlg(const std::string& name, ISvcLocator* p)
45 : AthAlgorithm(name,p)
46{}
47
49 ATH_CHECK(m_muonContainer.initialize());
50 ATH_CHECK(m_eventInfo.initialize());
51 ATH_CHECK(m_JPsiMuonContainer.initialize());
52 ATH_CHECK(m_JPsiVertexContainer.initialize());
54
55 ATH_CHECK(m_JPsiFinderTool.retrieve());
56 ATH_MSG_DEBUG("Retrieved J/Psi finder tool: " << m_JPsiFinderTool);
57
58 ATH_CHECK(m_tdt.retrieve(DisableTool{m_triggerChains.empty()}));
59 ATH_CHECK(m_matchTool.retrieve(DisableTool{m_triggerChains.empty()}));
60
61 for(const std::string& chain : m_triggerChains) {
62 m_trigDecorKeys.emplace_back(m_JPsiVertexContainer, std::format("muonsMatched_{:}", chain));
63 }
64 ATH_CHECK(m_trigDecorKeys.initialize());
65
66 ATH_MSG_DEBUG("Initialization successful");
67
68 return StatusCode::SUCCESS;
69}
70
71StatusCode MuSAVtxJPsiValidationAlg::execute(const EventContext& ctx) {
72
73 ATH_MSG_DEBUG("Executing MuSAVtxJPsiValidationAlg for event");
75 ATH_CHECK(muonContainer.isValid());
76
78 ATH_CHECK(evtInfo.isValid());
79
81 ATH_CHECK(JPsiMuonContainer.record(std::make_unique<xAOD::MuonContainer>(), std::make_unique<xAOD::MuonAuxContainer>()));
82
84 ATH_CHECK(JPsiVertexContainer.record(std::make_unique<xAOD::VertexContainer>(), std::make_unique<xAOD::VertexAuxContainer>()));
85
87 ATH_CHECK(JPsiTrackParticleContainer.record(std::make_unique<xAOD::TrackParticleContainer>(), std::make_unique<xAOD::TrackParticleAuxContainer>()));
88
89 // 1) Run J/Psi finder
90 auto jpsiVtxs = std::make_unique<xAOD::VertexContainer>();
91 auto jpsiAux = std::make_unique<xAOD::VertexAuxContainer>();
92 jpsiVtxs->setStore(jpsiAux.get());
93 ATH_CHECK(m_JPsiFinderTool->performSearch(ctx, *jpsiVtxs));
94
95 if (jpsiVtxs->empty()) {
96 ATH_MSG_DEBUG("No J/Psi candidates found");
97 return StatusCode::SUCCESS;
98 } else {
99 ATH_MSG_DEBUG("Found " << jpsiVtxs->size() << " J/Psi candidates");
100 }
101
102 // Create all trigger decor handles once, outside the vertex loop
103 std::vector<SG::WriteDecorHandle<xAOD::VertexContainer, char>> decors;
104 decors.reserve(m_trigDecorKeys.size());
105 for (const auto& key : m_trigDecorKeys){
106 ATH_MSG_DEBUG("Creating WriteDecorHandle for key: " << key.key());
107 decors.emplace_back(key, ctx);
108 }
109
110 for (const xAOD::Vertex* vtx : *jpsiVtxs) {
111 const auto& links = vtx->trackParticleLinks();
112 std::vector<char> isTriggerMatched(m_triggerChains.size(), 0);
113 if (links.size() == 2) {
114 const xAOD::TrackParticle* tp1 = links[0].isValid() ? *links[0] : nullptr;
115 const xAOD::TrackParticle* tp2 = links[1].isValid() ? *links[1] : nullptr;
116 const xAOD::Muon* mu1 = tp1 ? findMuonFromTrack(tp1, muonContainer.cptr()) : nullptr;
117 const xAOD::Muon* mu2 = tp2 ? findMuonFromTrack(tp2, muonContainer.cptr()) : nullptr;
118
119 if (!mu1 && !mu2) {
120 ATH_MSG_WARNING("No muons found for J/Psi vertex with index " << vtx->index());
121 continue;
122 } else if (!mu1 || !mu2) {
123 ATH_MSG_WARNING("Only one muon found for J/Psi vertex with index " << vtx->index());
124 } else {
125 ATH_MSG_DEBUG("Found J/Psi vertex with index " << vtx->index() << " and muons: " << mu1->index() << ", " << mu2->index());
126 }
127
128 if (mu1) JPsiMuonContainer->push_back(new xAOD::Muon(*mu1));
129 if (mu2) JPsiMuonContainer->push_back(new xAOD::Muon(*mu2));
130
131 if(mu1 && mu2) {
132 for(size_t iChain = 0; iChain < m_triggerChains.size(); ++iChain) {
133 const std::string& chain = m_triggerChains[iChain];
134 if (m_tdt->isPassed(chain)) {
135 // Check for matching muons
136 bool matched = m_matchTool->match(std::vector<const xAOD::IParticle*>{mu1, mu2}, chain, m_dR);
137 ATH_MSG_DEBUG("Trigger chain " << chain << " matched: " << (matched ? "Yes" : "No"));
138 isTriggerMatched[iChain] = matched;
139 }
140 }
141 }
142 }
143
144 // vertices right out of JPsiFinder have vxTrackAtVertex decorators that can't be saved to container
145 // thus we make a fresh copy to avoid this issue
146 xAOD::Vertex* newVtx = new xAOD::Vertex();
147 JPsiVertexContainer->push_back(newVtx); // add the vertex to the container first
148
149 // Set properties of the new vertex
150 newVtx->setPosition(vtx->position());
151 newVtx->setCovariance(vtx->covariance());
152 newVtx->setFitQuality(vtx->chiSquared(), vtx->numberDoF());
153 newVtx->setVertexType(vtx->vertexType());
154
155 //add trigger matching decorations to the new vertex
156 for (size_t iChain = 0; iChain < m_triggerChains.size(); ++iChain) {
157 decors[iChain](*newVtx) = isTriggerMatched[iChain];
158 }
159
160 // copy track particle links
161 for (const auto& link : vtx->trackParticleLinks()) {
162 if (!link.isValid()) continue;
163 const xAOD::TrackParticle* oldTrack = *link;
165 newTrack->makePrivateStore(*oldTrack);
166 JPsiTrackParticleContainer->push_back(newTrack);
167 ElementLink<xAOD::TrackParticleContainer> newLink(*JPsiTrackParticleContainer, newTrack->index(), ctx);
168 newVtx->addTrackAtVertex(newLink, 1.0); // weight is set to 1.0 for simplicity
169 }
170
171 // add identical decos to musa for comparison
172 constexpr double muonMass = 105.658; // Muon mass in MeV
173 TLorentzVector sumP4_muon, sumP4_electron, sumP4_selected;
174 std::vector<const xAOD::TrackParticle*> vtxTracks;
175
176 int vtxCharge = 0;
177
178 for (const auto& link : newVtx->trackParticleLinks()) {
179 const xAOD::TrackParticle* track = link.isValid() ? *link : nullptr;
180 if (!track) continue;
181
182 double pt = track->pt();
183 double eta = track->eta();
184 double phi = track->phi();
185
186 TLorentzVector p4_muon, p4_electron;
187 p4_muon.SetPtEtaPhiM(pt, eta, phi, muonMass);
188 p4_electron.SetPtEtaPhiM(pt, eta, phi, VKalVrtAthena::PhysConsts::mass_electron);
189
190 sumP4_muon += p4_muon;
191 sumP4_electron += p4_electron;
192 sumP4_selected += p4_muon; // assuming selected tracks are muons...
193
194 vtxCharge += track->charge();
195 vtxTracks.push_back(track);
196 }
197
198 //calculate opening angle between two tracks
199 float vtxDeltaR = 0;
200 if (vtxTracks.size() == 2) {
201 float dEta = vtxTracks[0]->eta() - vtxTracks[1]->eta();
202 float dPhi = std::abs(vtxTracks[0]->phi() - vtxTracks[1]->phi());
203 if (dPhi > M_PI) dPhi = 2 * M_PI - dPhi;
204 vtxDeltaR = std::sqrt(dEta * dEta + dPhi * dPhi);
205 }
206
207 vtx_pxAcc(*newVtx) = sumP4_muon.Px();
208 vtx_pyAcc(*newVtx) = sumP4_muon.Py();
209 vtx_pzAcc(*newVtx) = sumP4_muon.Pz();
210 vtx_massAcc(*newVtx) = sumP4_muon.M();
211 vtx_chargeAcc(*newVtx) = vtxCharge;
212 minOpAngAcc(*newVtx) = vtxDeltaR;
213 chi2_coreAcc(*newVtx) = vtx->chiSquared();
214 ndof_coreAcc(*newVtx) = vtx->numberDoF();
215 chi2_assocAcc(*newVtx) = vtx->chiSquared();
216 ndof_assocAcc(*newVtx) = vtx->numberDoF();
217 massAcc(*newVtx) = sumP4_muon.M();
218 mass_eAcc(*newVtx) = sumP4_electron.M();
219 mass_selectedTracksAcc(*newVtx) = sumP4_selected.M();
220 num_trksAcc(*newVtx) = links.size();
221 num_selectedTracksAcc(*newVtx) = links.size(); // Assuming all tracks are selected
222 num_associatedTracksAcc(*newVtx) = links.size(); // Assuming all tracks are associated
223 dCloseVrtAcc(*newVtx) = 0; // not implemented but assigned for comparison to MuSA
224 }
225
226 return StatusCode::SUCCESS;
227}
228
230 for (const xAOD::Muon* mu : *muons) {
231 if (mu->trackParticle(xAOD::Muon::CombinedTrackParticle) == tp) return mu;
232 if (mu->trackParticle(xAOD::Muon::InnerDetectorTrackParticle) == tp) return mu;
233 }
234 return nullptr;
235}
236
237} // namespace Rec
#define M_PI
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_WARNING(x,...)
xAOD::MuonContainer * muonContainer
AthAlgorithm(const std::string &name, ISvcLocator *pSvcLocator)
Constructor.
virtual StatusCode execute(const EventContext &ctx) override
Execute method.
SG::WriteHandleKey< xAOD::TrackParticleContainer > m_JPsiTrackParticleContainer
SG::ReadHandleKey< xAOD::MuonContainer > m_muonContainer
virtual StatusCode initialize() override
ToolHandle< Trig::IMatchingTool > m_matchTool
SG::WriteHandleKey< xAOD::VertexContainer > m_JPsiVertexContainer
SG::WriteDecorHandleKeyArray< xAOD::VertexContainer > m_trigDecorKeys
const xAOD::Muon * findMuonFromTrack(const xAOD::TrackParticle *tp, const xAOD::MuonContainer *muons)
PublicToolHandle< Trig::TrigDecisionTool > m_tdt
ToolHandle< Analysis::JpsiFinder > m_JPsiFinderTool
SG::WriteHandleKey< xAOD::MuonContainer > m_JPsiMuonContainer
MuSAVtxJPsiValidationAlg(const std::string &name, ISvcLocator *pSvcLocator)
SG::ReadHandleKey< xAOD::EventInfo > m_eventInfo
Gaudi::Property< std::vector< std::string > > m_triggerChains
Helper class to provide type-safe access to aux data.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
void addTrackAtVertex(const ElementLink< TrackParticleContainer > &tr, float weight=1.0)
Add a new track to the vertex.
const TrackParticleLinks_t & trackParticleLinks() const
Get all the particles associated with the vertex.
void setVertexType(VxType::VertexType vType)
Set the type of the vertex.
void setCovariance(const std::vector< float > &value)
Sets the covariance matrix as a simple vector of values.
void setPosition(const Amg::Vector3D &position)
Sets the 3-position.
void setFitQuality(float chiSquared, float numberDoF)
Set the 'Fit Quality' information.
namespace for combined reconstruction tools and interfaces
TrackParticle_v1 TrackParticle
Reference the current persistent version:
Vertex_v1 Vertex
Define the latest version of the vertex class.
Muon_v1 Muon
Reference the current persistent version:
Definition Muon.h:13
MuonContainer_v1 MuonContainer
Definition of the current "Muon container version".