LDMX Software
EcalSPTrackCompare.cxx
1#include "DQM/EcalSPTrackCompare.h"
2
3#include <cmath>
4
5#include "DetDescr/SimSpecialID.h"
7#include "Tracking/Event/Track.h"
8
9namespace dqm {
10
12 ecal_sp_coll_name_ = ps.get<std::string>("ecal_sp_coll_name");
13 ecal_sp_pass_name_ = ps.get<std::string>("ecal_sp_pass_name");
14 track_collection_ = ps.get<std::string>("track_collection");
15 track_pass_name_ = ps.get<std::string>("track_pass_name");
16}
17
19 // --- Find the primary electron at ECal scoring plane (plane 31) ---
20 if (!event.exists(ecal_sp_coll_name_, ecal_sp_pass_name_)) {
21 ldmx_log(debug) << "SP collection " << ecal_sp_coll_name_ << " not found";
22 return;
23 }
24
25 const auto& sp_hits = event.getCollection<ldmx::SimTrackerHit>(
26 ecal_sp_coll_name_, ecal_sp_pass_name_);
27
28 ldmx_log(debug) << "SP hits: " << sp_hits.size();
29
30 // Find the highest-momentum forward-going electron at plane 31.
31 // In dark brem events the recoil electron has trackID != 1.
32 const ldmx::SimTrackerHit* sp_electron = nullptr;
33 double best_pz = 0;
34 for (const auto& hit : sp_hits) {
35 ldmx::SimSpecialID hit_id(hit.getID());
36 if (hit_id.plane() != 31) continue;
37 if (hit.getPdgID() != 11) continue;
38 const auto p = hit.getMomentum();
39 if (p[2] <= 0) continue;
40 if (p[2] > best_pz) {
41 best_pz = p[2];
42 sp_electron = &hit;
43 }
44 }
45
46 if (!sp_electron) {
47 ldmx_log(debug) << "No forward electron found at ECal SP";
48 return;
49 }
50
51 // Extract SP electron kinematics
52 const auto sp_pos = sp_electron->getPosition();
53 const auto sp_mom = sp_electron->getMomentum();
54 double sp_x = sp_pos[0];
55 double sp_y = sp_pos[1];
56
57 ldmx_log(debug) << "SP electron: trackID=" << sp_electron->getTrackID()
58 << " pos=(" << sp_x << ", " << sp_y << ") pz=" << best_pz;
59 double sp_px = sp_mom[0];
60 double sp_py = sp_mom[1];
61 double sp_pz = sp_mom[2];
62 double sp_p = std::sqrt(sp_px * sp_px + sp_py * sp_py + sp_pz * sp_pz);
63 double sp_pt = std::sqrt(sp_px * sp_px + sp_py * sp_py);
64 double sp_theta = std::atan2(sp_pt, sp_pz);
65 double sp_phi = std::atan2(sp_py, sp_px);
66 if (sp_phi < 0) sp_phi += 2.0 * M_PI; // Shift to [0, 2π]
67
68 histograms_.fill("sp_x", sp_x);
69 histograms_.fill("sp_y", sp_y);
70 histograms_.fill("sp_p", sp_p);
71 histograms_.fill("sp_pt", sp_pt);
72 histograms_.fill("sp_theta", sp_theta);
73 histograms_.fill("sp_phi", sp_phi);
74
75 // --- Get reconstructed tracks ---
76 if (!event.exists(track_collection_, track_pass_name_)) {
77 histograms_.fill("n_tracks", 0);
78 histograms_.fill("has_match", 0);
79 return;
80 }
81
82 const auto& tracks =
83 event.getCollection<ldmx::Track>(track_collection_, track_pass_name_);
84
85 int n_tracks = tracks.size();
86 histograms_.fill("n_tracks", n_tracks);
87
88 if (n_tracks == 0) {
89 histograms_.fill("has_match", 0);
90 return;
91 }
92
93 // --- Find closest track to SP electron ---
94 int best_idx = -1;
95 double best_dr = 1e9;
96
97 for (int i = 0; i < n_tracks; ++i) {
98 const auto& track = tracks[i];
99 double trk_theta = track.getTheta();
100 double trk_phi = track.getPhi();
101
102 // Angular distance
103 double dtheta = trk_theta - sp_theta;
104 double dphi = trk_phi - sp_phi;
105 // Wrap dphi to [-pi, pi]
106 while (dphi > M_PI) dphi -= 2.0 * M_PI;
107 while (dphi < -M_PI) dphi += 2.0 * M_PI;
108
109 double dr = std::sqrt(dtheta * dtheta + dphi * dphi);
110 if (dr < best_dr) {
111 best_dr = dr;
112 best_idx = i;
113 }
114 }
115
116 histograms_.fill("has_match", 1);
117
118 // --- Fill residuals for the closest track ---
119 const auto& best = tracks[best_idx];
120
121 // Get track parameters at perigee
122 double trk_d0 = best.getD0();
123 double trk_phi = best.getPhi();
124 double trk_theta = best.getTheta();
125 double trk_qop = best.getQoP();
126 double trk_p = (std::abs(trk_qop) > 1e-9) ? 1.0 / std::abs(trk_qop) : 0.0;
127
128 // Track position at perigee (reference point is at ECAL front face z)
129 auto perigee = best.getPerigeeLocation();
130 double trk_x = perigee[0] + (-trk_d0 * std::sin(trk_phi));
131 double trk_y = perigee[1] + (trk_d0 * std::cos(trk_phi));
132
133 ldmx_log(debug) << "Track perigee: (" << perigee[0] << ", " << perigee[1]
134 << ", " << perigee[2] << ") d0=" << trk_d0;
135 ldmx_log(debug) << "Track pos: (" << trk_x << ", " << trk_y
136 << ") p=" << trk_p << " theta=" << trk_theta
137 << " phi=" << trk_phi;
138
139 // Derived track quantities
140 double trk_pt = trk_p * std::sin(trk_theta);
141
142 // Residuals
143 double dx = trk_x - sp_x;
144 double dy = trk_y - sp_y;
145 double dtheta = trk_theta - sp_theta;
146 double dphi = trk_phi - sp_phi;
147 while (dphi > M_PI) dphi -= 2.0 * M_PI;
148 while (dphi < -M_PI) dphi += 2.0 * M_PI;
149 double dp = trk_p - sp_p;
150 double dpt = trk_pt - sp_pt;
151 double dp_frac = (sp_p > 1.0) ? dp / sp_p : 0.0;
152
153 // Track properties
154 histograms_.fill("trk_x", trk_x);
155 histograms_.fill("trk_y", trk_y);
156 histograms_.fill("trk_p", trk_p);
157 histograms_.fill("trk_pt", trk_pt);
158 histograms_.fill("trk_theta", trk_theta);
159 histograms_.fill("trk_phi", trk_phi);
160
161 // Residuals
162 histograms_.fill("delta_x", dx);
163 histograms_.fill("delta_y", dy);
164 histograms_.fill("delta_theta", dtheta);
165 histograms_.fill("delta_phi", dphi);
166 histograms_.fill("delta_p", dp);
167 histograms_.fill("delta_pt", dpt);
168 histograms_.fill("delta_p_frac", dp_frac);
169 histograms_.fill("delta_r", std::sqrt(dx * dx + dy * dy));
170 histograms_.fill("delta_angle", best_dr);
171
172 // 2D correlations
173 histograms_.fill("sp_x_vs_trk_x", sp_x, trk_x);
174 histograms_.fill("sp_y_vs_trk_y", sp_y, trk_y);
175 histograms_.fill("sp_p_vs_trk_p", sp_p, trk_p);
176 histograms_.fill("sp_theta_vs_trk_theta", sp_theta, trk_theta);
177}
178
179} // namespace dqm
180
#define DECLARE_ANALYZER(CLASS)
Macro which allows the framework to construct an analyzer given its name during configuration.
Class which encapsulates information from a hit in a simulated tracking detector.
Compare the primary electron at the ECal scoring plane with ECAL tracks reconstructed by ACTS CKF.
void configure(framework::config::Parameters &ps) override
Callback for the EventProcessor to configure itself from the given set of parameters.
void analyze(const framework::Event &event) override
Process the event and make histograms or summaries.
HistogramPool histograms_
helper object for making and filling histograms
Implements an event buffer system for storing event data.
Definition Event.h:42
bool exists(const std::string &name, const std::string &passName, bool unique=true) const
Check for the existence of an object or collection with the given name and pass name in the event.
Definition Event.cxx:105
void fill(const std::string &name, const T &val)
Fill a 1D histogram.
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:78
Implements detector ids for special simulation-derived hits like scoring planes.
int plane() const
Get the value of the plane field from the ID, if it is a scoring plane.
Represents a simulated tracker hit in the simulation.
std::vector< float > getPosition() const
Get the XYZ position of the hit [mm].
std::vector< double > getMomentum() const
Get the XYZ momentum of the particle at the position at which the hit took place [MeV].
int getTrackID() const
Get the Sim particle track ID of the hit.
Implementation of a track object.
Definition Track.h:53