LDMX Software
EcalRecoilRemovalProcessor.cxx
2
3#include <chrono>
4#include <fstream>
5#include <iomanip>
6
7#include "DetDescr/EcalID.h"
8#include "Ecal/EcalHelper.h"
9#include "Ecal/Event/EcalHit.h"
10
11namespace ecal {
12
14 profiling_map_["setup"] = 0.;
15 profiling_map_["recoil_electron"] = 0.;
16 profiling_map_["trajectories"] = 0.;
17 profiling_map_["rem_dist"] = 0.;
18 profiling_map_["recoil_removal"] = 0.;
19}
20
22 ldmx_log(info) << "Total Avg Time/Event: " << std::fixed
23 << std::setprecision(2) << processing_time_ / nevents_
24 << " ms";
25 ldmx_log(info) << "Breakdown::";
26
27 for (const auto& [key, value] : profiling_map_) {
28 ldmx_log(info) << std::left << std::setw(20) << key
29 << "Avg Time/Event = " << std::fixed << std::setprecision(3)
30 << value / nevents_ << " ms";
31 }
32}
33
36 beam_energy_mev_ = parameters.get<double>("beam_energy");
37 num_ecal_layers_ = parameters.get<int>("num_ecal_layers");
38 rem_dist_file_name_ = parameters.get<std::string>("rem_dist_file");
40 parameters.get<std::string>("collection_name_included");
41 collection_name_excluded_ =
42 parameters.get<std::string>("collection_name_excluded");
43 rec_coll_name_ = parameters.get<std::string>("rec_coll_name");
44 rec_pass_name_ = parameters.get<std::string>("rec_pass_name");
45 ecal_sp_hits_pass_name_ =
46 parameters.get<std::string>("ecal_sp_hits_pass_name");
47 ecal_sim_pass_name_ = parameters.get<std::string>("ecal_sim_pass_name");
48 recoil_from_tracking_ = parameters.get<bool>("recoil_from_tracking");
49 track_coll_name_ = parameters.get<std::string>("track_coll_name");
50 track_pass_name_ = parameters.get<std::string>("track_pass_name");
51 n_electrons_ = parameters.get<int>(
52 "n_electrons"); // number of electrons in the event; TODO: replace with
53 // the ElectronCounter processor result
54
55 // Read in array holding the removal distances for Ecal hit removals
56 if (!std::ifstream(rem_dist_file_name_).good()) {
57 EXCEPTION_RAISE("EcalRecoilRemovalProcessor",
58 "The specified removal distances file '" +
59 rem_dist_file_name_ + "' does not exist!");
60 } else {
61 std::ifstream remdistfile(rem_dist_file_name_);
62 std::string line, value;
63
64 // Extract the first line in the file
65 std::getline(remdistfile, line);
66 std::vector<float> values;
67
68 // Read data, line by line
69 while (std::getline(remdistfile, line)) {
70 std::stringstream ss(line);
71 values.clear();
72 while (std::getline(ss, value, ',')) {
73 float f_value = (value != "") ? std::stof(value) : -1.0;
74 values.push_back(f_value);
75 }
76 rem_dist_values_.push_back(values);
77 }
78 }
79
80 if (!recoil_from_tracking_) {
81 EXCEPTION_RAISE("EcalRecoilRemovalProcessor",
82 "The processor is currently not configured to use sim "
83 "information! Please set recoil_from_tracking = True");
84 }
85}
86
91 auto start = std::chrono::high_resolution_clock::now();
92 nevents_++;
93
94 // Get the Ecal Geometry
96 ldmx::EcalGeometry::CONDITIONS_OBJECT_NAME);
97
98 std::vector<std::array<float, 3>> ele_p;
99 std::vector<std::array<float, 3>> ele_pos;
100 std::vector<bool> fiducial_in_tracker;
101
102 auto setup_finish = std::chrono::high_resolution_clock::now();
103 profiling_map_["setup"] +=
104 std::chrono::duration<float, std::milli>(setup_finish - start).count();
105
109
110 // TODO: Gear this for multiple electron tracks in the Ecal, right now it can
111 // only handle one
112 // if (!recoil_from_tracking_ &&
113 // event.exists("EcalScoringPlaneHits", ecal_sp_hits_pass_name_) &&
114 // n_electrons_ == 1) {
115 // ldmx_log(trace) << " Loop through all of the sim particles and find
116 // the "
117 // "recoil electron";
118
119 // // Get the collection of simulated particles from the event
120 // auto particle_map{event.getMap<int, ldmx::SimParticle>(
121 // "SimParticles", ecal_sim_pass_name_)};
122
123 // // Loop through all of the sim particles and find the recoil electron.
124 // auto [recoil_track_id, recoil_electron] =
125 // analysis::getRecoil(particle_map);
126
127 // // Find ECAL SP hit for recoil electron
128 // auto ecal_sp_hits{event.getCollection<ldmx::SimTrackerHit>(
129 // "EcalScoringPlaneHits", ecal_sp_hits_pass_name_)};
130 // float pmax = 0;
131 // for (ldmx::SimTrackerHit &sp_hit : ecal_sp_hits) {
132 // ldmx::SimSpecialID hit_id(sp_hit.getID());
133 // auto ecal_sp_momentum = sp_hit.getMomentum();
134 // auto ecal_sp_position = sp_hit.getPosition();
135 // if (hit_id.plane() != 31 || ecal_sp_momentum[2] <= 0) continue;
136
137 // if (sp_hit.getTrackID() == recoil_track_id) {
138 // // A*A is faster than pow(A,2)
139 // if (sqrt((ecal_sp_momentum[0] * ecal_sp_momentum[0]) +
140 // (ecal_sp_momentum[1] * ecal_sp_momentum[1]) +
141 // (ecal_sp_momentum[2] * ecal_sp_momentum[2])) > pmax) {
142 // recoil_p = {static_cast<float>(ecal_sp_momentum[0]),
143 // static_cast<float>(ecal_sp_momentum[1]),
144 // static_cast<float>(ecal_sp_momentum[2])};
145 // recoil_pos = {(ecal_sp_position[0]), (ecal_sp_position[1]),
146 // (ecal_sp_position[2])};
147 // pmax = sqrt(recoil_p[0] * recoil_p[0] + recoil_p[1] * recoil_p[1] +
148 // recoil_p[2] * recoil_p[2]);
149 // ldmx_log(debug) << " Set recoil_p = (" << recoil_p[0] << ", "
150 // << recoil_p[1] << ", " << recoil_p[2]
151 // << ") and recoil_pos = (" << recoil_pos[0] << ", "
152 // << recoil_pos[1] << ", " << recoil_pos[2] << ")";
153 // }
154 // }
155 // }
156 // } else if (!event.exists(
157 // "EcalScoringPlaneHits",
158 // ecal_sp_hits_pass_name_)) { // end condition on ecal SP
159 // ldmx_log(debug)
160 // << "Event does not exist in collection EcalScoringPlaneHits";
161 // }
162
163 // Get recoil_pos using recoil tracking
164 if (recoil_from_tracking_) {
165 ldmx_log(trace) << " Get recoil tracks collection";
166
167 // Get the recoil track collection
168 auto recoil_tracks{
169 event.getCollection<ldmx::Track>(track_coll_name_, track_pass_name_)};
170
171 ldmx_log(trace) << " Propagate the recoil ele to the ECAL";
172 auto ele_track_states = // std::vector<std::vector<float>> OR empty vector
173 ecal::pTTrackProp(recoil_tracks, n_electrons_);
174 if (!ele_track_states.empty()) {
175 for (int i = 0; i < ele_track_states.size(); ++i) {
176 std::vector<float>& recoil_track_states_ecal = ele_track_states[i];
177 std::array<float, 3> recoil_pos;
178 std::array<float, 3> recoil_p;
179 // track_state_loc0 is recoil_pos[0] and track_state_loc1 is
180 // recoil_pos[1]
181 if (!recoil_track_states_ecal.empty()) {
182 recoil_pos = {recoil_track_states_ecal[0],
183 recoil_track_states_ecal[1],
184 recoil_track_states_ecal[2]};
185 recoil_p = {(recoil_track_states_ecal[3]),
186 (recoil_track_states_ecal[4]),
187 (recoil_track_states_ecal[5])};
188 fiducial_in_tracker.push_back(true);
189 } else {
190 recoil_pos = {-9999.f, -9999.f, -9999.f};
191 recoil_p = {0.f, 0.f, 0.f};
192 fiducial_in_tracker.push_back(false);
193 ldmx_log(info) << " Electron trajectory is empty!";
194 }
195 ldmx_log(debug) << " Electron " << i + 1 << ": set recoil_p = ("
196 << recoil_p[0] << ", " << recoil_p[1] << ", "
197 << recoil_p[2] << ") and recoil_pos = ("
198 << recoil_pos[0] << ", " << recoil_pos[1] << ", "
199 << recoil_pos[2] << ")";
200 ele_p.push_back(recoil_p);
201 ele_pos.push_back(recoil_pos);
202 } // end loop on electron track states
203 } else { // end condition on nonempty electron track states
204 ldmx_log(info) << " No valid electron tracks found in recoil tracking "
205 "information";
206 }
207 } // end condition to do recoil information from tracking
208
209 auto recoil_electron_finish = std::chrono::high_resolution_clock::now();
210 profiling_map_["recoil_electron"] +=
211 std::chrono::duration<float, std::milli>(recoil_electron_finish -
212 setup_finish)
213 .count();
214
218
219 ldmx_log(trace) << " Get projected trajectories for electron and photon";
220
221 std::vector<std::vector<XYCoords>> ele_trajectories;
222 std::vector<float> ele_p_mag;
223 std::vector<float> ele_theta;
224
225 if (!ele_p.empty() && !ele_pos.empty()) {
226 for (int i = 0; i < ele_p.size(); ++i) {
227 std::array<float, 3>& recoil_p = ele_p[i];
228 std::array<float, 3>& recoil_pos = ele_pos[i];
229 std::vector<XYCoords> ele_trajectory;
230
231 // Require that z-momentum is positive (which will also exclude the
232 // default initializaton) Require that the positions are not the default
233 // initializaton
234 if ((recoil_p[2] > 0.) && (recoil_pos[0] != -9999.)) {
235 ele_trajectory = getTrajectory(recoil_p, recoil_pos);
236 } else {
237 ldmx_log(trace) << "Ele trajectory cannot be determined, pZ = "
238 << recoil_p[2] << " X = " << recoil_pos[0];
239 }
240
241 // calculate removal distance binning variables
242 float recoil_p_mag =
243 (recoil_p[2] > 0.)
244 ? sqrt((recoil_p[0] * recoil_p[0]) + (recoil_p[1] * recoil_p[1]) +
245 (recoil_p[2] * recoil_p[2]))
246 : -1.0;
247 float recoil_theta = recoil_p_mag > 0
248 ? acos(recoil_p[2] / recoil_p_mag) * 180.0 / M_PI
249 : -1.0;
250
251 // push back variable values
252 ele_trajectories.push_back(ele_trajectory);
253 ele_p_mag.push_back(recoil_p_mag);
254 ele_theta.push_back(recoil_theta);
255
256 } // end loop on track states
257 } // end condition on ele_p and ele_pos emptiness
258
259 auto trajectories_finish = std::chrono::high_resolution_clock::now();
260 profiling_map_["trajectories"] +=
261 std::chrono::duration<float, std::milli>(trajectories_finish -
262 recoil_electron_finish)
263 .count();
264
268
269 ldmx_log(trace) << " Build recoil removal distances vector";
270 std::vector<float> rem_dist_values_bin_0(rem_dist_values_[0].begin() + 4,
271 rem_dist_values_[0].end());
272 std::vector<std::vector<float>> removal_distances;
273
274 if (!ele_trajectories.empty()) {
275 for (int k = 0; k < ele_p_mag.size(); ++k) {
276 float theta_min, theta_max, p_min, p_max;
277 bool inrange;
278 std::vector<float> rec_rem_dists = rem_dist_values_bin_0;
279 float& recoil_p_mag = ele_p_mag[k];
280 float& recoil_theta = ele_theta[k];
281
282 // Use the appropriate containment radii for the recoil electron
283 for (int i = 0; i < rem_dist_values_.size(); i++) {
284 theta_min = rem_dist_values_[i][0];
285 theta_max = rem_dist_values_[i][1];
286 p_min = rem_dist_values_[i][2];
287 p_max = rem_dist_values_[i][3];
288 inrange = true;
289
290 if (theta_min != -1.0) {
291 inrange = inrange && (recoil_theta >= theta_min);
292 }
293 if (theta_max != -1.0) {
294 inrange = inrange && (recoil_theta < theta_max);
295 }
296 if (p_min != -1.0) {
297 inrange = inrange && (recoil_p_mag >= p_min);
298 }
299 if (p_max != -1.0) {
300 inrange = inrange && (recoil_p_mag < p_max);
301 }
302 if (inrange) {
303 std::vector<float> rem_dist_values_bini(
304 rem_dist_values_[i].begin() + 4, rem_dist_values_[i].end());
305 rec_rem_dists = rem_dist_values_bini;
306 }
307 }
308 removal_distances.push_back(rec_rem_dists);
309 } // end loop on ele_trajectories
310 } // end condition on nonempty ele_trajectories
311
312 auto rem_dist_finish = std::chrono::high_resolution_clock::now();
313 profiling_map_["rem_dist"] += std::chrono::duration<float, std::milli>(
314 rem_dist_finish - trajectories_finish)
315 .count();
316
320
321 // Get the collection of digitized Ecal hits from the event.
322 const std::vector<ldmx::EcalHit> ecal_rec_hits =
323 event.getCollection<ldmx::EcalHit>(rec_coll_name_, rec_pass_name_);
324
325 std::vector<ldmx::EcalHit> ecal_rec_hits_inc;
326 std::vector<ldmx::EcalHit> ecal_rec_hits_exc;
327
328 // the time complexity of this should just be O(n_ecal_rec_hits *
329 // n_ele_trajectories) if I've done things right
330 if (!ele_trajectories.empty()) {
331 ldmx_log(trace) << " ======== EcalRecHitInc List (length"
332 << ecal_rec_hits_inc.size() << ") ========";
333 for (const ldmx::EcalHit& hit :
334 ecal_rec_hits) { // loops through reconstructed hits in the ecal and
335 // discards all those inside the electron's RoC
336 ldmx::EcalID id(hit.getID());
337 auto [x, y, z] = geometry_->getPosition(id);
338 XYCoords xy_pair = std::make_pair(x, y);
339
340 bool include = true;
341 for (int i = 0; i < ele_trajectories.size(); ++i) {
342 std::vector<XYCoords>& ele_trajectory = ele_trajectories[i];
343 std::vector<float>& rec_rem_dists = removal_distances[i];
344
345 float dist_ele_traj = // calculates distance between hit location and
346 // projected particle positions
347 ele_trajectory.size()
348 ? sqrt(pow((xy_pair.first - ele_trajectory[id.layer()].first),
349 2) +
350 pow((xy_pair.second - ele_trajectory[id.layer()].second),
351 2))
352 : -1.0;
353 if (dist_ele_traj == -1.0) {
354 ldmx_log(trace) << " ele_trajectory does not exist; KEEP";
355 continue;
356 } else if (dist_ele_traj <=
357 (rec_rem_dists[id.layer()])) { // if the hit is inside some
358 // distance of the electron,
359 // discard it; else keep it
360 ldmx_log(trace) << " dist_ele_traj = " << dist_ele_traj
361 << " <= " << rec_rem_dists[id.layer()] << "; DISCARD";
362 include = false;
363 } else {
364 ldmx_log(trace) << " dist_ele_traj = " << dist_ele_traj
365 << " > " << rec_rem_dists[id.layer()] << "; KEEP";
366 continue;
367 }
368 } // end loop on electron trajectories
369
370 // drop or keep hit
371 if (include) {
372 ecal_rec_hits_inc.emplace_back(hit);
373 } else {
374 ecal_rec_hits_exc.emplace_back(hit);
375 }
376
377 } // end loop on ecal_rec_hits
378 ldmx_log(trace) << " ======== END OF ecal_rec_hit List ========";
379 } else { // end condition on nonempty ele_trajectories
380 ecal_rec_hits_inc = ecal_rec_hits;
381 }
382 ldmx_log(info) << " Removed " << ecal_rec_hits_exc.size()
383 << " hits within recoil electron RoC; "
384 << ecal_rec_hits_inc.size() << " hits remaining of "
385 << ecal_rec_hits.size();
386
387 // creates collection for reduced set of rec hits for analysis
388 event.add(collection_name_included_, ecal_rec_hits_inc);
389 // creates collection of discarded rec hits
390 event.add(collection_name_excluded_, ecal_rec_hits_exc);
391
392 auto recoil_removal_finish = std::chrono::high_resolution_clock::now();
393 profiling_map_["recoil_removal"] +=
394 std::chrono::duration<float, std::milli>(recoil_removal_finish -
395 rem_dist_finish)
396 .count();
397
398 auto end = std::chrono::high_resolution_clock::now();
399 auto time_diff = end - start;
400 processing_time_ +=
401 std::chrono::duration<float, std::milli>(time_diff).count();
402
403} // end EcalRecoilRemovalProcessor::produce
404
405/* Calculate where trajectory intersects ECAL layers using position and
406 * momentum at scoring plane */
407std::vector<ldmx::XYCoords> EcalRecoilRemovalProcessor::getTrajectory(
408 std::array<float, 3> momentum, std::array<float, 3> position) {
409 std::vector<XYCoords> positions;
410 for (int i_layer = 0; i_layer < num_ecal_layers_; i_layer++) {
411 float pos_x =
412 position[0] + (momentum[0] / momentum[2]) *
413 (geometry_->getZPosition(i_layer) - position[2]);
414 float pos_y =
415 position[1] + (momentum[1] / momentum[2]) *
416 (geometry_->getZPosition(i_layer) - position[2]);
417 positions.push_back(std::make_pair(pos_x, pos_y));
418 }
419 return positions;
420} // end EcalRecoilRemovalProcessor::getTrajectory
421
422} // end namespace ecal
423
Class that propagates tracks to the ECAL face.
std::vector< std::vector< float > > pTTrackProp(const ldmx::Tracks &tracks, int ele_count)
Return a vector of ele_count valid track states with the greatest transverse momentum.
Class that defines an ECal detector ID with a cell number.
Class that discards Ecal reconstructed hits from the recoil electron for WAB event processing.
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Discards Ecal reconstructed hits from the recoil electron for WAB event processing.
void configure(framework::config::Parameters &parameters) override
Configure the processor using the given user specified parameters.
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
void produce(framework::Event &event) override
Process the event and put new data products into it.
std::string collection_name_included_
Name of the collection which will containt the results.
void onNewRun(const ldmx::RunHeader &rh) override
onNewRun is the first function called for each processor after the conditions are fully configured an...
const T & getCondition(const std::string &condition_name)
Access a conditions object for the current event.
Implements an event buffer system for storing event data.
Definition Event.h:40
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
std::tuple< double, double, double > getPosition(EcalID id) const
Get a cell's position from its ID number.
double getZPosition(int layer) const
Get the z-coordinate given the layer id.
Stores reconstructed hit information from the ECAL.
Definition EcalHit.h:19
Extension of DetectorID providing access to ECal layers and cell numbers in a hex grid.
Definition EcalID.h:20
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:68
Implementation of a track object.
Definition Track.h:54