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