LDMX Software
EcalVetoProcessor.cxx
2
3// LDMX
5#include "DetDescr/SimSpecialID.h"
6#include "Ecal/Event/EcalHit.h"
8#include "SimCore/Event/SimParticle.h"
10
11/*~~~~~~~~~~~*/
12/* Tools */
13/*~~~~~~~~~~~*/
14#include "Tools/AnalysisUtils.h"
15
16// C++
17#include <stdlib.h>
18
19#include <algorithm>
20#include <cmath>
21#include <fstream>
22#include <iomanip>
23
24// ROOT (MIP tracking)
25#include "TDecompSVD.h"
26#include "TMatrixD.h"
27#include "TVector3.h"
28
29namespace ecal {
30
32 profiling_map_["setup"] = 0.;
33 profiling_map_["recoil_electron"] = 0.;
34 profiling_map_["trajectories"] = 0.;
35 profiling_map_["roc_var"] = 0.;
36 profiling_map_["fill_hitmaps"] = 0.;
37 profiling_map_["containment_var"] = 0.;
38 profiling_map_["mip_tracking_setup"] = 0.;
39 profiling_map_["straight_tracks"] = 0.;
40 profiling_map_["linreg_tracks"] = 0.;
41 profiling_map_["set_variables"] = 0.;
42 profiling_map_["bdt_variables"] = 0.;
43}
44
46 const ldmx::EcalVetoResult &result) {
47 // Base variables
48 bdtFeatures_.push_back(result.getNReadoutHits());
49 bdtFeatures_.push_back(result.getSummedDet());
50 bdtFeatures_.push_back(result.getSummedTightIso());
51 bdtFeatures_.push_back(result.getMaxCellDep());
52 bdtFeatures_.push_back(result.getShowerRMS());
53 bdtFeatures_.push_back(result.getXStd());
54 bdtFeatures_.push_back(result.getYStd());
55 bdtFeatures_.push_back(result.getAvgLayerHit());
56 bdtFeatures_.push_back(result.getStdLayerHit());
57 bdtFeatures_.push_back(result.getDeepestLayerHit());
58 bdtFeatures_.push_back(result.getEcalBackEnergy());
59 // MIP tracking
60 bdtFeatures_.push_back(result.getNStraightTracks());
61 // bdtFeatures_.push_back(result.getNLinregTracks());
62 bdtFeatures_.push_back(result.getFirstNearPhLayer());
63 bdtFeatures_.push_back(result.getNNearPhHits());
64 bdtFeatures_.push_back(result.getPhotonTerritoryHits());
65 bdtFeatures_.push_back(result.getEPSep());
66 bdtFeatures_.push_back(result.getEPDot());
67 // Longitudinal segment variables
68 bdtFeatures_.push_back(result.getEnergySeg()[0]);
69 bdtFeatures_.push_back(result.getXMeanSeg()[0]);
70 bdtFeatures_.push_back(result.getYMeanSeg()[0]);
71 bdtFeatures_.push_back(result.getLayerMeanSeg()[0]);
72 bdtFeatures_.push_back(result.getEnergySeg()[1]);
73 bdtFeatures_.push_back(result.getYMeanSeg()[2]);
75 bdtFeatures_.push_back(result.getEleContEnergy()[0][0]);
76 bdtFeatures_.push_back(result.getEleContEnergy()[1][0]);
77 bdtFeatures_.push_back(result.getEleContYMean()[0][0]);
78 bdtFeatures_.push_back(result.getEleContEnergy()[0][1]);
79 bdtFeatures_.push_back(result.getEleContEnergy()[1][1]);
80 bdtFeatures_.push_back(result.getEleContYMean()[0][1]);
82 bdtFeatures_.push_back(result.getPhContNHits()[0][0]);
83 bdtFeatures_.push_back(result.getPhContYMean()[0][0]);
84 bdtFeatures_.push_back(result.getPhContNHits()[0][1]);
86 bdtFeatures_.push_back(result.getOutContEnergy()[0][0]);
87 bdtFeatures_.push_back(result.getOutContEnergy()[1][0]);
88 bdtFeatures_.push_back(result.getOutContEnergy()[2][0]);
89 bdtFeatures_.push_back(result.getOutContNHits()[0][0]);
90 bdtFeatures_.push_back(result.getOutContXMean()[0][0]);
91 bdtFeatures_.push_back(result.getOutContYMean()[0][0]);
92 bdtFeatures_.push_back(result.getOutContYMean()[1][0]);
93 bdtFeatures_.push_back(result.getOutContYStd()[0][0]);
94 bdtFeatures_.push_back(result.getOutContEnergy()[0][1]);
95 bdtFeatures_.push_back(result.getOutContEnergy()[1][1]);
96 bdtFeatures_.push_back(result.getOutContEnergy()[2][1]);
97 bdtFeatures_.push_back(result.getOutContLayerMean()[0][1]);
98 bdtFeatures_.push_back(result.getOutContLayerStd()[0][1]);
99 bdtFeatures_.push_back(result.getOutContEnergy()[0][2]);
100 bdtFeatures_.push_back(result.getOutContLayerMean()[0][2]);
101}
102
104 featureListName_ = parameters.getParameter<std::string>("feature_list_name");
105 // Load BDT ONNX file
106 rt_ = std::make_unique<ldmx::Ort::ONNXRuntime>(
107 parameters.getParameter<std::string>("bdt_file"));
108
109 // Read in arrays holding 68% containment radius per layer
110 // for different bins in momentum/angle
111 rocFileName_ = parameters.getParameter<std::string>("roc_file");
112 if (!std::ifstream(rocFileName_).good()) {
113 EXCEPTION_RAISE(
114 "EcalVetoProcessor",
115 "The specified RoC file '" + rocFileName_ + "' does not exist!");
116 } else {
117 std::ifstream rocfile(rocFileName_);
118 std::string line, value;
119
120 // Extract the first line in the file
121 std::getline(rocfile, line);
122 std::vector<float> values;
123
124 // Read data, line by line
125 while (std::getline(rocfile, line)) {
126 std::stringstream ss(line);
127 values.clear();
128 while (std::getline(ss, value, ',')) {
129 float f_value = (value != "") ? std::stof(value) : -1.0;
130 values.push_back(f_value);
131 }
132 roc_range_values_.push_back(values);
133 }
134 }
135
136 nEcalLayers_ = parameters.getParameter<int>("num_ecal_layers");
137
138 bdtCutVal_ = parameters.getParameter<double>("disc_cut");
139 ecalLayerEdepRaw_.resize(nEcalLayers_, 0);
140 ecalLayerEdepReadout_.resize(nEcalLayers_, 0);
141 ecalLayerTime_.resize(nEcalLayers_, 0);
142
143 beamEnergyMeV_ = parameters.getParameter<double>("beam_energy");
144 run_lin_reg_ = parameters.getParameter<bool>("run_lin_reg");
145 linreg_radius_ = parameters.getParameter<double>("linreg_radius");
146
147 // Set the collection name as defined in the configuration
148 sp_pass_name_ = parameters.getParameter<std::string>("sp_pass_name", "");
149 collectionName_ = parameters.getParameter<std::string>("collection_name");
150 rec_pass_name_ = parameters.getParameter<std::string>("rec_pass_name");
151 rec_coll_name_ = parameters.getParameter<std::string>("rec_coll_name");
152 recoil_from_tracking_ = parameters.getParameter<bool>("recoil_from_tracking");
153 track_collection_ = parameters.getParameter<std::string>("track_collection");
154 track_pass_name_ =
155 parameters.getParameter<std::string>("track_pass_name", "");
156 inverse_skim_ = parameters.getParameter<bool>("inverse_skim");
157}
158
159void EcalVetoProcessor::clearProcessor() {
160 cellMap_.clear();
161 cellMapTightIso_.clear();
162 bdtFeatures_.clear();
163
164 nReadoutHits_ = 0;
165 summedDet_ = 0;
166 summedTightIso_ = 0;
167 maxCellDep_ = 0;
168 showerRMS_ = 0;
169 xStd_ = 0;
170 yStd_ = 0;
171 avgLayerHit_ = 0;
172 stdLayerHit_ = 0;
173 deepestLayerHit_ = 0;
174 ecalBackEnergy_ = 0;
175 // MIP tracking
177 nLinregTracks_ = 0;
179 nNearPhHits_ = 0;
180 epAng_ = 0;
181 epAngAtTarget_ = 0;
182 epSep_ = 0;
183 epDot_ = 0;
184 epDotAtTarget_ = 0;
186
187 std::fill(ecalLayerEdepRaw_.begin(), ecalLayerEdepRaw_.end(), 0);
188 std::fill(ecalLayerEdepReadout_.begin(), ecalLayerEdepReadout_.end(), 0);
189 std::fill(ecalLayerTime_.begin(), ecalLayerTime_.end(), 0);
190}
191
193 auto start = std::chrono::high_resolution_clock::now();
194 nevents_++;
195
196 // Get the Ecal Geometry
198 ldmx::EcalGeometry::CONDITIONS_OBJECT_NAME);
199
201
202 clearProcessor();
203
204 // Get the collection of Ecal scoring plane hits. If it doesn't exist,
205 // don't bother adding any truth tracking information.
206
207 std::array<float, 3> recoilP = {0., 0., 0.};
208 std::array<float, 3> recoilPos = {-9999., -9999., -9999.};
209 std::array<float, 3> recoilPAtTarget = {0., 0., 0.};
210 std::array<float, 3> recoilPosAtTarget = {-9999., -9999., -9999.};
211
212 auto setup = std::chrono::high_resolution_clock::now();
213 profiling_map_["setup"] +=
214 std::chrono::duration<float, std::milli>(setup - start).count();
215
216 if (!recoil_from_tracking_ &&
217 event.exists("EcalScoringPlaneHits", sp_pass_name_)) {
218 ldmx_log(trace) << " Loop through all of the sim particles and find the "
219 "recoil electron";
220 //
221 // Loop through all of the sim particles and find the recoil electron.
222 //
223
224 // Get the collection of simulated particles from the event
225 auto particleMap{event.getMap<int, ldmx::SimParticle>("SimParticles")};
226
227 // Search for the recoil electron
228 auto [recoilTrackID, recoilElectron] = Analysis::getRecoil(particleMap);
229
230 // Find ECAL SP hit for recoil electron
231 auto ecalSpHits{event.getCollection<ldmx::SimTrackerHit>(
232 "EcalScoringPlaneHits", sp_pass_name_)};
233 float pmax = 0;
234 for (ldmx::SimTrackerHit &spHit : ecalSpHits) {
235 ldmx::SimSpecialID hit_id(spHit.getID());
236 auto ecal_sp_momentum = spHit.getMomentum();
237 auto ecal_sp_position = spHit.getPosition();
238 if (hit_id.plane() != 31 || ecal_sp_momentum[2] <= 0) continue;
239
240 if (spHit.getTrackID() == recoilTrackID) {
241 // A*A is faster than pow(A,2)
242 if (sqrt((ecal_sp_momentum[0] * ecal_sp_momentum[0]) +
243 (ecal_sp_momentum[1] * ecal_sp_momentum[1]) +
244 (ecal_sp_momentum[2] * ecal_sp_momentum[2])) > pmax) {
245 recoilP = {static_cast<float>(ecal_sp_momentum[0]),
246 static_cast<float>(ecal_sp_momentum[1]),
247 static_cast<float>(ecal_sp_momentum[2])};
248 recoilPos = {(ecal_sp_position[0]), (ecal_sp_position[1]),
249 (ecal_sp_position[2])};
250 pmax = sqrt(recoilP[0] * recoilP[0] + recoilP[1] * recoilP[1] +
251 recoilP[2] * recoilP[2]);
252 }
253 }
254 }
255
256 // Find target SP hit for recoil electron
257 if (event.exists("TargetScoringPlaneHits", sp_pass_name_)) {
258 std::vector<ldmx::SimTrackerHit> targetSpHits =
259 event.getCollection<ldmx::SimTrackerHit>("TargetScoringPlaneHits",
260 sp_pass_name_);
261 pmax = 0;
262 for (ldmx::SimTrackerHit &spHit : targetSpHits) {
263 ldmx::SimSpecialID hit_id(spHit.getID());
264 auto target_sp_momentum = spHit.getMomentum();
265 auto target_sp_position = spHit.getPosition();
266 if (hit_id.plane() != 1 || target_sp_momentum[2] <= 0) continue;
267
268 if (spHit.getTrackID() == recoilTrackID) {
269 if (sqrt((target_sp_momentum[0] * target_sp_momentum[0]) +
270 (target_sp_momentum[1] * target_sp_momentum[1]) +
271 (target_sp_momentum[2] * target_sp_momentum[2])) > pmax) {
272 recoilPAtTarget = {static_cast<float>(target_sp_momentum[0]),
273 static_cast<float>(target_sp_momentum[1]),
274 static_cast<float>(target_sp_momentum[2])};
275 recoilPosAtTarget = {target_sp_position[0], target_sp_position[1],
276 target_sp_position[2]};
277 // (A*A) is faster than pow(A,2)
278 pmax = sqrt((recoilPAtTarget[0] * recoilPAtTarget[0]) +
279 (recoilPAtTarget[1] * recoilPAtTarget[1]) +
280 (recoilPAtTarget[2] * recoilPAtTarget[2]));
281 }
282 }
283 } // end loop on target SP hits
284 } // end condition on target SP
285 } // end condition on ecal SP
286
287 // Get recoilPos using recoil tracking
288 bool fiducial_in_tracker{false};
289 if (recoil_from_tracking_) {
290 ldmx_log(trace) << " Get recoil tracks collection";
291
292 // Get the recoil track collection
293 auto recoil_tracks{
294 event.getCollection<ldmx::Track>(track_collection_, track_pass_name_)};
295
296 ldmx_log(trace) << " Propagate the recoil ele to the ECAL";
297 ldmx::TrackStateType ts_type = ldmx::TrackStateType::AtECAL;
298 auto recoil_track_states_ecal = trackProp(recoil_tracks, ts_type, "ecal");
299 ldmx_log(trace) << " Propagate the recoil ele to the Target";
300 ldmx::TrackStateType ts_type_target = ldmx::TrackStateType::AtTarget;
301 auto recoil_track_states_target =
302 trackProp(recoil_tracks, ts_type_target, "target");
303
304 ldmx_log(trace) << " Set recoilPos and recoilP";
305 // Redefining recoilPos now to come from the track state
306 // track_state_loc0 is recoilPos[0] and track_state_loc1 is recoilPos[1]
307 if (!recoil_track_states_ecal.empty()) {
308 fiducial_in_tracker = true;
309 recoilPos = {recoil_track_states_ecal[0], recoil_track_states_ecal[1],
310 recoil_track_states_ecal[2]};
311 recoilP = {(recoil_track_states_ecal[3]), (recoil_track_states_ecal[4]),
312 (recoil_track_states_ecal[5])};
313 } else {
314 ldmx_log(trace) << " No recoil track at ECAL";
315 fiducial_in_tracker = false;
316 }
317 ldmx_log(debug) << " Set recoilP = (" << recoilP[0] << ", " << recoilP[1]
318 << ", " << recoilP[2] << ") and recoilPos = ("
319 << recoilPos[0] << ", " << recoilPos[1] << ", "
320 << recoilPos[2] << ")";
321
322 // Repeat the above but now for the taget states
323 if (!recoil_track_states_target.empty()) {
324 recoilPosAtTarget = {(recoil_track_states_target[0]),
325 (recoil_track_states_target[1]),
326 (recoil_track_states_target[2])};
327 recoilPAtTarget = {recoil_track_states_target[3],
328 recoil_track_states_target[4],
329 recoil_track_states_target[5]};
330 }
331 ldmx_log(debug) << " Set recoilPAtTarget = (" << recoilPAtTarget[0] << ", "
332 << recoilPAtTarget[1] << ", " << recoilPAtTarget[2]
333 << ") and recoilPosAtTarget = (" << recoilPosAtTarget[0]
334 << ", " << recoilPosAtTarget[1] << ", "
335 << recoilPosAtTarget[2] << ")";
336 } // condition to do recoil information from tracking
337
338 ldmx_log(trace) << " Get projected trajectories for electron and photon";
339
340 auto recoil_electron = std::chrono::high_resolution_clock::now();
341 profiling_map_["recoil_electron"] +=
342 std::chrono::duration<float, std::milli>(recoil_electron - setup).count();
343
344 // Get projected trajectories for electron and photon
345 std::vector<XYCoords> ele_trajectory, photon_trajectory,
346 ele_trajectory_at_target;
347 // Require that z-momentum is positive (which will also exclude the default
348 // initializaton) Require that the positions are not the default initializaton
349 if ((recoilP[2] > 0.) && (recoilPAtTarget[2] > 0.) &&
350 (recoilPos[0] != -9999.) && (recoilPosAtTarget[0] != -9999.)) {
351 ele_trajectory = getTrajectory(recoilP, recoilPos);
352 // Get the photon projection. This does not require that the photon exists
353 // tho
354 std::array<float, 3> photon_proj_momentum = {
355 -recoilPAtTarget[0], -recoilPAtTarget[1],
356 beamEnergyMeV_ - recoilPAtTarget[2]};
357 photon_trajectory = getTrajectory(photon_proj_momentum, recoilPosAtTarget);
358 } else {
359 ldmx_log(trace) << "Ele / photon trajectory cannot be determined, pZ = "
360 << recoilP[2] << " pZAtTarget = " << recoilPAtTarget[2]
361 << " X = " << recoilPos[0]
362 << " XAtTarget = " << recoilPosAtTarget[0];
363 }
364
365 float recoilPMag = (recoilP[2] > 0.) ? sqrt((recoilP[0] * recoilP[0]) +
366 (recoilP[1] * recoilP[1]) +
367 (recoilP[2] * recoilP[2]))
368 : -1.0;
369 float recoilTheta =
370 recoilPMag > 0 ? acos(recoilP[2] / recoilPMag) * 180.0 / M_PI : -1.0;
371
372 ldmx_log(trace) << " Build Radii of containment (ROC)";
373
374 auto trajectories = std::chrono::high_resolution_clock::now();
375 profiling_map_["trajectories"] +=
376 std::chrono::duration<float, std::milli>(trajectories - recoil_electron)
377 .count();
378
379 // Use the appropriate containment radii for the recoil electron
380 std::vector<float> roc_values_bin0(roc_range_values_[0].begin() + 4,
381 roc_range_values_[0].end());
382 std::vector<float> ele_radii = roc_values_bin0;
383 float theta_min, theta_max, p_min, p_max;
384 bool inrange;
385
386 for (int i = 0; i < roc_range_values_.size(); i++) {
387 theta_min = roc_range_values_[i][0];
388 theta_max = roc_range_values_[i][1];
389 p_min = roc_range_values_[i][2];
390 p_max = roc_range_values_[i][3];
391 inrange = true;
392
393 if (theta_min != -1.0) {
394 inrange = inrange && (recoilTheta >= theta_min);
395 }
396 if (theta_max != -1.0) {
397 inrange = inrange && (recoilTheta < theta_max);
398 }
399 if (p_min != -1.0) {
400 inrange = inrange && (recoilPMag >= p_min);
401 }
402 if (p_max != -1.0) {
403 inrange = inrange && (recoilPMag < p_max);
404 }
405 if (inrange) {
406 std::vector<float> roc_values_bini(roc_range_values_[i].begin() + 4,
407 roc_range_values_[i].end());
408 ele_radii = roc_values_bini;
409 }
410 }
411 // Use default RoC bin for photon
412 std::vector<float> photon_radii = roc_values_bin0;
413
414 auto roc_var = std::chrono::high_resolution_clock::now();
415 profiling_map_["roc_var"] +=
416 std::chrono::duration<float, std::milli>(roc_var - trajectories).count();
417
418 // Get the collection of digitized Ecal hits from the event.
419 const std::vector<ldmx::EcalHit> ecalRecHits =
420 event.getCollection<ldmx::EcalHit>(rec_coll_name_, rec_pass_name_);
421
422 ldmx::EcalID globalCentroid =
423 GetShowerCentroidIDAndRMS(ecalRecHits, showerRMS_);
424 /* ~~ Fill the hit map ~~ O(n) */
425 fillHitMap(ecalRecHits, cellMap_);
426 bool doTight = true;
427 /* ~~ Fill the isolated hit maps ~~ O(n) */
428 fillIsolatedHitMap(ecalRecHits, globalCentroid, cellMap_, cellMapTightIso_,
429 doTight);
430
431 auto fill_hitmaps = std::chrono::high_resolution_clock::now();
432 profiling_map_["fill_hitmaps"] +=
433 std::chrono::duration<float, std::milli>(fill_hitmaps - roc_var).count();
434
435 // Loop over the hits from the event to calculate the rest of the important
436 // quantities
437
438 float wavgLayerHit = 0;
439 float xMean = 0;
440 float yMean = 0;
441
442 // Containment variables
443 unsigned int nregions = 5;
444 std::vector<float> electronContainmentEnergy(nregions, 0.0);
445 std::vector<float> photonContainmentEnergy(nregions, 0.0);
446 std::vector<float> outsideContainmentEnergy(nregions, 0.0);
447 std::vector<int> outsideContainmentNHits(nregions, 0);
448 std::vector<float> outsideContainmentXmean(nregions, 0.0);
449 std::vector<float> outsideContainmentYmean(nregions, 0.0);
450 std::vector<float> outsideContainmentXstd(nregions, 0.0);
451 std::vector<float> outsideContainmentYstd(nregions, 0.0);
452 // Longitudinal segmentation
453 std::vector<int> segLayers = {0, 6, 17, 34};
454 unsigned int nsegments = segLayers.size() - 1;
455 std::vector<float> energySeg(nsegments, 0.0);
456 std::vector<float> xMeanSeg(nsegments, 0.0);
457 std::vector<float> xStdSeg(nsegments, 0.0);
458 std::vector<float> yMeanSeg(nsegments, 0.0);
459 std::vector<float> yStdSeg(nsegments, 0.0);
460 std::vector<float> layerMeanSeg(nsegments, 0.0);
461 std::vector<float> layerStdSeg(nsegments, 0.0);
462 std::vector<std::vector<float>> eContEnergy(
463 nregions, std::vector<float>(nsegments, 0.0));
464 std::vector<std::vector<float>> eContXMean(
465 nregions, std::vector<float>(nsegments, 0.0));
466 std::vector<std::vector<float>> eContYMean(
467 nregions, std::vector<float>(nsegments, 0.0));
468 std::vector<std::vector<float>> gContEnergy(
469 nregions, std::vector<float>(nsegments, 0.0));
470 std::vector<std::vector<int>> gContNHits(nregions,
471 std::vector<int>(nsegments, 0));
472 std::vector<std::vector<float>> gContXMean(
473 nregions, std::vector<float>(nsegments, 0.0));
474 std::vector<std::vector<float>> gContYMean(
475 nregions, std::vector<float>(nsegments, 0.0));
476 std::vector<std::vector<float>> oContEnergy(
477 nregions, std::vector<float>(nsegments, 0.0));
478 std::vector<std::vector<int>> oContNHits(nregions,
479 std::vector<int>(nsegments, 0));
480 std::vector<std::vector<float>> oContXMean(
481 nregions, std::vector<float>(nsegments, 0.0));
482 std::vector<std::vector<float>> oContYMean(
483 nregions, std::vector<float>(nsegments, 0.0));
484 std::vector<std::vector<float>> oContXStd(nregions,
485 std::vector<float>(nsegments, 0.0));
486 std::vector<std::vector<float>> oContYStd(nregions,
487 std::vector<float>(nsegments, 0.0));
488 std::vector<std::vector<float>> oContLayerMean(
489 nregions, std::vector<float>(nsegments, 0.0));
490 std::vector<std::vector<float>> oContLayerStd(
491 nregions, std::vector<float>(nsegments, 0.0));
492
493 auto containment_var = std::chrono::high_resolution_clock::now();
494 profiling_map_["containment_var"] +=
495 std::chrono::duration<float, std::milli>(containment_var - fill_hitmaps)
496 .count();
497
498 // MIP tracking: vector of hits to be used in the MIP tracking algorithm. All
499 // hits inside the electron ROC (or all hits in the ECal if the event is
500 // missing an electron) will be included.
501 std::vector<HitData> trackingHitList;
502
503 ldmx_log(trace)
504 << " Loop over the hits from the event to calculate the BDT features";
505
506 for (const ldmx::EcalHit &hit : ecalRecHits) {
507 // Layer-wise quantities
508 ldmx::EcalID id(hit.getID());
509 ecalLayerEdepRaw_[id.layer()] =
510 ecalLayerEdepRaw_[id.layer()] + hit.getEnergy();
511 if (id.layer() >= 20) ecalBackEnergy_ += hit.getEnergy();
512 if (maxCellDep_ < hit.getEnergy()) maxCellDep_ = hit.getEnergy();
513 if (hit.getEnergy() <= 0) {
514 ldmx_log(fatal)
515 << "ECal hit has negative or zero energy, this should never happen, "
516 "check the threshold settings in HgcrocEmulator";
517 continue;
518 }
519 nReadoutHits_++;
520 ecalLayerEdepReadout_[id.layer()] += hit.getEnergy();
521 ecalLayerTime_[id.layer()] += (hit.getEnergy()) * hit.getTime();
522 auto [x, y, z] = geometry_->getPosition(id);
523 xMean += x * hit.getEnergy();
524 yMean += y * hit.getEnergy();
525 avgLayerHit_ += id.layer();
526 wavgLayerHit += id.layer() * hit.getEnergy();
527 if (deepestLayerHit_ < id.layer()) {
528 deepestLayerHit_ = id.layer();
529 }
530 XYCoords xy_pair = std::make_pair(x, y);
531 float distance_ele_trajectory =
532 ele_trajectory.size()
533 ? sqrt(pow((xy_pair.first - ele_trajectory[id.layer()].first), 2) +
534 pow((xy_pair.second - ele_trajectory[id.layer()].second), 2))
535 : -1.0;
536 float distance_photon_trajectory =
537 photon_trajectory.size()
538 ? sqrt(pow((xy_pair.first - photon_trajectory[id.layer()].first),
539 2) +
540 pow((xy_pair.second - photon_trajectory[id.layer()].second),
541 2))
542 : -1.0;
543
544 // Decide which longitudinal segment the hit is in and add to sums
545 for (unsigned int iseg = 0; iseg < nsegments; iseg++) {
546 if (id.layer() >= segLayers[iseg] &&
547 id.layer() <= segLayers[iseg + 1] - 1) {
548 energySeg[iseg] += hit.getEnergy();
549 xMeanSeg[iseg] += xy_pair.first * hit.getEnergy();
550 yMeanSeg[iseg] += xy_pair.second * hit.getEnergy();
551 layerMeanSeg[iseg] += id.layer() * hit.getEnergy();
552
553 // Decide which containment region the hit is in and add to sums
554 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
555 if (distance_ele_trajectory >= ireg * ele_radii[id.layer()] &&
556 distance_ele_trajectory < (ireg + 1) * ele_radii[id.layer()]) {
557 eContEnergy[ireg][iseg] += hit.getEnergy();
558 eContXMean[ireg][iseg] += xy_pair.first * hit.getEnergy();
559 eContYMean[ireg][iseg] += xy_pair.second * hit.getEnergy();
560 }
561 if (distance_photon_trajectory >= ireg * photon_radii[id.layer()] &&
562 distance_photon_trajectory <
563 (ireg + 1) * photon_radii[id.layer()]) {
564 gContEnergy[ireg][iseg] += hit.getEnergy();
565 gContNHits[ireg][iseg] += 1;
566 gContXMean[ireg][iseg] += xy_pair.first * hit.getEnergy();
567 gContYMean[ireg][iseg] += xy_pair.second * hit.getEnergy();
568 }
569 if (distance_ele_trajectory > (ireg + 1) * ele_radii[id.layer()] &&
570 distance_photon_trajectory >
571 (ireg + 1) * photon_radii[id.layer()]) {
572 oContEnergy[ireg][iseg] += hit.getEnergy();
573 oContNHits[ireg][iseg] += 1;
574 oContXMean[ireg][iseg] += xy_pair.first * hit.getEnergy();
575 oContYMean[ireg][iseg] += xy_pair.second * hit.getEnergy();
576 oContLayerMean[ireg][iseg] += id.layer() * hit.getEnergy();
577 }
578 }
579 }
580 }
581
582 // Decide which containment region the hit is in and add to sums
583 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
584 if (distance_ele_trajectory >= ireg * ele_radii[id.layer()] &&
585 distance_ele_trajectory < (ireg + 1) * ele_radii[id.layer()])
586 electronContainmentEnergy[ireg] += hit.getEnergy();
587 if (distance_photon_trajectory >= ireg * photon_radii[id.layer()] &&
588 distance_photon_trajectory < (ireg + 1) * photon_radii[id.layer()])
589 photonContainmentEnergy[ireg] += hit.getEnergy();
590 if (distance_ele_trajectory > (ireg + 1) * ele_radii[id.layer()] &&
591 distance_photon_trajectory > (ireg + 1) * photon_radii[id.layer()]) {
592 outsideContainmentEnergy[ireg] += hit.getEnergy();
593 outsideContainmentNHits[ireg] += 1;
594 outsideContainmentXmean[ireg] += xy_pair.first * hit.getEnergy();
595 outsideContainmentYmean[ireg] += xy_pair.second * hit.getEnergy();
596 }
597 }
598
599 // MIP tracking: Decide whether hit should be added to trackingHitList
600 // If inside e- RoC or if etraj is missing, use the hit for tracking:
601 if (distance_ele_trajectory >= ele_radii[id.layer()] ||
602 distance_ele_trajectory == -1.0) {
603 HitData hd;
604 hd.pos = TVector3(xy_pair.first, xy_pair.second,
605 geometry_->getZPosition(id.layer()));
606 hd.layer = id.layer();
607 trackingHitList.push_back(hd);
608 }
609 } // end loop over rechits
610
611 for (const auto &[id, energy] : cellMapTightIso_) {
612 if (energy > 0) summedTightIso_ += energy;
613 }
614
615 for (int iLayer = 0; iLayer < ecalLayerEdepReadout_.size(); iLayer++) {
616 ecalLayerTime_[iLayer] =
617 ecalLayerTime_[iLayer] / ecalLayerEdepReadout_[iLayer];
618 summedDet_ += ecalLayerEdepReadout_[iLayer];
619 }
620
621 if (nReadoutHits_ > 0) {
622 avgLayerHit_ /= nReadoutHits_;
623 wavgLayerHit /= summedDet_;
624 xMean /= summedDet_;
625 yMean /= summedDet_;
626 } else {
627 wavgLayerHit = 0;
628 avgLayerHit_ = 0;
629 xMean = 0;
630 yMean = 0;
631 }
632
633 // If necessary, quotient out the total energy from the means
634 for (unsigned int iseg = 0; iseg < nsegments; iseg++) {
635 if (energySeg[iseg] > 0) {
636 xMeanSeg[iseg] /= energySeg[iseg];
637 yMeanSeg[iseg] /= energySeg[iseg];
638 layerMeanSeg[iseg] /= energySeg[iseg];
639 }
640 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
641 if (eContEnergy[ireg][iseg] > 0) {
642 eContXMean[ireg][iseg] /= eContEnergy[ireg][iseg];
643 eContYMean[ireg][iseg] /= eContEnergy[ireg][iseg];
644 }
645 if (gContEnergy[ireg][iseg] > 0) {
646 gContXMean[ireg][iseg] /= gContEnergy[ireg][iseg];
647 gContYMean[ireg][iseg] /= gContEnergy[ireg][iseg];
648 }
649 if (oContEnergy[ireg][iseg] > 0) {
650 oContXMean[ireg][iseg] /= oContEnergy[ireg][iseg];
651 oContYMean[ireg][iseg] /= oContEnergy[ireg][iseg];
652 oContLayerMean[ireg][iseg] /= oContEnergy[ireg][iseg];
653 }
654 }
655 }
656
657 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
658 if (outsideContainmentEnergy[ireg] > 0) {
659 outsideContainmentXmean[ireg] /= outsideContainmentEnergy[ireg];
660 outsideContainmentYmean[ireg] /= outsideContainmentEnergy[ireg];
661 }
662 }
663
664 // Loop over hits a second time to find the standard deviations.
665 for (const ldmx::EcalHit &hit : ecalRecHits) {
666 ldmx::EcalID id(hit.getID());
667 auto [x, y, z] = geometry_->getPosition(id);
668 if (hit.getEnergy() > 0) {
669 xStd_ += pow((x - xMean), 2) * hit.getEnergy();
670 yStd_ += pow((y - yMean), 2) * hit.getEnergy();
671 stdLayerHit_ += pow((id.layer() - wavgLayerHit), 2) * hit.getEnergy();
672 }
673 XYCoords xy_pair = std::make_pair(x, y);
674 float distance_ele_trajectory =
675 ele_trajectory.size()
676 ? sqrt(pow((xy_pair.first - ele_trajectory[id.layer()].first), 2) +
677 pow((xy_pair.second - ele_trajectory[id.layer()].second), 2))
678 : -1.0;
679 float distance_photon_trajectory =
680 photon_trajectory.size()
681 ? sqrt(pow((xy_pair.first - photon_trajectory[id.layer()].first),
682 2) +
683 pow((xy_pair.second - photon_trajectory[id.layer()].second),
684 2))
685 : -1.0;
686
687 for (unsigned int iseg = 0; iseg < nsegments; iseg++) {
688 if (id.layer() >= segLayers[iseg] &&
689 id.layer() <= segLayers[iseg + 1] - 1) {
690 xStdSeg[iseg] +=
691 pow((xy_pair.first - xMeanSeg[iseg]), 2) * hit.getEnergy();
692 yStdSeg[iseg] +=
693 pow((xy_pair.second - yMeanSeg[iseg]), 2) * hit.getEnergy();
694 layerStdSeg[iseg] +=
695 pow((id.layer() - layerMeanSeg[iseg]), 2) * hit.getEnergy();
696
697 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
698 if (distance_ele_trajectory > (ireg + 1) * ele_radii[id.layer()] &&
699 distance_photon_trajectory >
700 (ireg + 1) * photon_radii[id.layer()]) {
701 oContXStd[ireg][iseg] +=
702 pow((xy_pair.first - oContXMean[ireg][iseg]), 2) *
703 hit.getEnergy();
704 oContYStd[ireg][iseg] +=
705 pow((xy_pair.second - oContYMean[ireg][iseg]), 2) *
706 hit.getEnergy();
707 oContLayerStd[ireg][iseg] +=
708 pow((id.layer() - oContLayerMean[ireg][iseg]), 2) *
709 hit.getEnergy();
710 }
711 }
712 }
713 }
714
715 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
716 if (distance_ele_trajectory > (ireg + 1) * ele_radii[id.layer()] &&
717 distance_photon_trajectory > (ireg + 1) * photon_radii[id.layer()]) {
718 outsideContainmentXstd[ireg] +=
719 pow((xy_pair.first - outsideContainmentXmean[ireg]), 2) *
720 hit.getEnergy();
721 outsideContainmentYstd[ireg] +=
722 pow((xy_pair.second - outsideContainmentYmean[ireg]), 2) *
723 hit.getEnergy();
724 }
725 }
726 } // end loop over rechits (2nd time)
727
728 if (nReadoutHits_ > 0) {
729 xStd_ = sqrt(xStd_ / summedDet_);
730 yStd_ = sqrt(yStd_ / summedDet_);
731 stdLayerHit_ = sqrt(stdLayerHit_ / summedDet_);
732 } else {
733 xStd_ = 0;
734 yStd_ = 0;
735 stdLayerHit_ = 0;
736 }
737
738 // Quotient out the total energies from the standard deviations if possible
739 // and take root
740 for (unsigned int iseg = 0; iseg < nsegments; iseg++) {
741 if (energySeg[iseg] > 0) {
742 xStdSeg[iseg] = sqrt(xStdSeg[iseg] / energySeg[iseg]);
743 yStdSeg[iseg] = sqrt(yStdSeg[iseg] / energySeg[iseg]);
744 layerStdSeg[iseg] = sqrt(layerStdSeg[iseg] / energySeg[iseg]);
745 }
746 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
747 if (oContEnergy[ireg][iseg] > 0) {
748 oContXStd[ireg][iseg] =
749 sqrt(oContXStd[ireg][iseg] / oContEnergy[ireg][iseg]);
750 oContYStd[ireg][iseg] =
751 sqrt(oContYStd[ireg][iseg] / oContEnergy[ireg][iseg]);
752 oContLayerStd[ireg][iseg] =
753 sqrt(oContLayerStd[ireg][iseg] / oContEnergy[ireg][iseg]);
754 }
755 }
756 }
757
758 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
759 if (outsideContainmentEnergy[ireg] > 0) {
760 outsideContainmentXstd[ireg] =
761 sqrt(outsideContainmentXstd[ireg] / outsideContainmentEnergy[ireg]);
762 outsideContainmentYstd[ireg] =
763 sqrt(outsideContainmentYstd[ireg] / outsideContainmentEnergy[ireg]);
764 }
765 }
766
767 ldmx_log(trace) << " Find out if the recoil electron is fiducial";
768
769 // Find the location of the recoil electron
770 // Ecal face is not where the first layer starts,
771 // defined in DetDescr/python/EcalGeometry.py
772 const float dz_from_face{7.932};
773 float drifted_recoil_x{-9999.};
774 float drifted_recoil_y{-9999.};
775 if (recoilP[2] > 0.) {
776 ldmx_log(trace) << " Recoil electron pX = " << recoilP[0]
777 << " pY = " << recoilP[1] << " pZ = " << recoilP[2];
778 ldmx_log(trace) << " Recoil electron PosX = " << recoilPos[0]
779 << " PosY = " << recoilPos[1] << " PosZ = " << recoilPos[2];
780 drifted_recoil_x =
781 (dz_from_face * (recoilP[0] / recoilP[2])) + recoilPos[0];
782 drifted_recoil_y =
783 (dz_from_face * (recoilP[1] / recoilP[2])) + recoilPos[1];
784 }
785 const int recoil_layer_index = 0;
786
787 // Check if it's fiducial
788 bool inside_ecal_cell{false};
789 // At module level
790 const auto ecalID = geometry_->getID(drifted_recoil_x, drifted_recoil_y,
791 recoil_layer_index, true);
792 if (!ecalID.null()) {
793 // If fiducial at module level, check at cell level
794 const auto cellID =
795 geometry_->getID(drifted_recoil_x, drifted_recoil_y, recoil_layer_index,
796 ecalID.getModuleID(), true);
797 if (!cellID.null()) {
798 inside_ecal_cell = true;
799 }
800 }
801
802 ldmx_log(info) << " Is this event is fiducial in ECAL? "
803 << inside_ecal_cell;
804
805 // ------------------------------------------------------
806 // MIP tracking starts here
807
808 /* Goal: Calculate
809 * nStraightTracks (self-explanatory),
810 * nLinregTracks (tracks found by linreg algorithm),
811 */
812
813 // Find epAng and epSep, and prepare EP trajectory vectors:
814 TVector3 e_traj_start;
815 TVector3 e_traj_end;
816 TVector3 e_traj_target_start;
817 TVector3 e_traj_target_end;
818 TVector3 p_traj_start;
819 TVector3 p_traj_end;
820 if (!ele_trajectory.empty() && !photon_trajectory.empty()) {
821 // Create TVector3s marking the start and endpoints of each projected
822 // trajectory
823 e_traj_start.SetXYZ(ele_trajectory[0].first, ele_trajectory[0].second,
825 e_traj_end.SetXYZ(ele_trajectory[(nEcalLayers_ - 1)].first,
826 ele_trajectory[(nEcalLayers_ - 1)].second,
827 geometry_->getZPosition((nEcalLayers_ - 1)));
828 p_traj_start.SetXYZ(photon_trajectory[0].first, photon_trajectory[0].second,
830 p_traj_end.SetXYZ(photon_trajectory[(nEcalLayers_ - 1)].first,
831 photon_trajectory[(nEcalLayers_ - 1)].second,
832 geometry_->getZPosition((nEcalLayers_ - 1)));
833
834 TVector3 evec = e_traj_end - e_traj_start;
835 TVector3 e_norm = evec.Unit();
836 TVector3 pvec = p_traj_end - p_traj_start;
837 TVector3 p_norm = pvec.Unit();
838
839 // Calculate the angle between the projected electron at Ecal and the photon
840 // (at target)
841 epDot_ = e_norm.Dot(p_norm);
842 epAng_ = acos(epDot_) * 180.0 / M_PI;
843 epSep_ = sqrt(pow(e_traj_start.X() - p_traj_start.X(), 2) +
844 pow(e_traj_start.Y() - p_traj_start.Y(), 2));
845 // Calculate the electron trajectory with positions and momentum as measured
846 // at the target
847 if (!ele_trajectory_at_target.empty()) {
848 e_traj_target_start.SetXYZ(ele_trajectory_at_target[0].first,
849 ele_trajectory_at_target[0].second,
851 e_traj_target_end.SetXYZ(ele_trajectory_at_target[(0)].first,
852 ele_trajectory_at_target[(0)].second,
853 geometry_->getZPosition((nEcalLayers_ - 1)));
854 // Now calculate the ep angle at the target
855 TVector3 evec_target = e_traj_target_end - e_traj_target_start;
856 TVector3 e_norm_target = evec_target.Unit();
857 epDotAtTarget_ = e_norm_target.Dot(p_norm);
858 epAngAtTarget_ = acos(epDotAtTarget_) * 180.0 / M_PI;
859 }
860 ldmx_log(trace) << " Electron trajectory calculated";
861 } else {
862 // Electron trajectory is missing, so all hits in the Ecal are fair game.
863 // Pick e/ptraj so that they won't restrict the tracking algorithm (place
864 // them far outside the ECal).
865 ldmx_log(trace) << " Electron trajectory is missing";
866 e_traj_start = TVector3(999, 999, geometry_->getZPosition(0));
867 e_traj_end =
868 TVector3(999, 999, geometry_->getZPosition((nEcalLayers_ - 1)));
869 p_traj_start = TVector3(1000, 1000, geometry_->getZPosition(0));
870 p_traj_end =
871 TVector3(1000, 1000, geometry_->getZPosition((nEcalLayers_ - 1)));
872 /*ensures event will not be vetoed by angle/separation cut */
873 epAng_ = 999.;
874 epAngAtTarget_ = 999.;
875 epSep_ = 999.;
876 epDot_ = 999.;
877 epDotAtTarget_ = 999.;
878 }
879
880 // Near photon step: Find the first layer of the ECal where a hit near the
881 // projected photon trajectory is found Currently unusued pending further
882 // study; performance has dropped between v9 and v12. Currently used in
883 // segmipBDT
884 firstNearPhLayer_ = nEcalLayers_ - 1;
885
886 // If no photon trajectory, leave this at the default (ECal back)
887 if (!photon_trajectory.empty()) {
888 for (std::vector<HitData>::iterator it = trackingHitList.begin();
889 it != trackingHitList.end(); ++it) {
890 float ehDist =
891 sqrt(pow((*it).pos.X() - photon_trajectory[(*it).layer].first, 2) +
892 pow((*it).pos.Y() - photon_trajectory[(*it).layer].second, 2));
893 if (ehDist < 8.7) {
894 nNearPhHits_++;
895 if ((*it).layer < firstNearPhLayer_) {
896 firstNearPhLayer_ = (*it).layer;
897 }
898 }
899 }
900 }
901
902 // Territories limited to trackingHitList
903 TVector3 gToe = (e_traj_start - p_traj_start).Unit();
904 TVector3 origin = p_traj_start + 0.5 * 8.7 * gToe;
905 if (!ele_trajectory.empty()) {
906 for (auto &hitData : trackingHitList) {
907 TVector3 hitPos = hitData.pos;
908 TVector3 hitPrime = hitPos - origin;
909 if (hitPrime.Dot(gToe) <= 0) {
911 }
912 }
913 } else {
914 photonTerritoryHits_ = nReadoutHits_;
915 }
916
917 auto mip_tracking_setup = std::chrono::high_resolution_clock::now();
918 profiling_map_["mip_tracking_setup"] +=
919 std::chrono::duration<float, std::milli>(mip_tracking_setup -
920 containment_var)
921 .count();
922
923 // ------------------------------------------------------
924 // Find straight MIP tracks:
925
926 std::sort(trackingHitList.begin(), trackingHitList.end(),
927 [](HitData ha, HitData hb) { return ha.layer > hb.layer; });
928 // For merging tracks: Need to keep track of existing tracks
929 // Candidate tracks to merge in will always be in front of the current track
930 // (lower z), so only store the last hit 3-layer vector: each track = vector
931 // of 3-tuples (xy+layer).
932 std::vector<std::vector<HitData>> track_list;
933
934 // print trackingHitList
935
936 ldmx_log(trace) << "====== Tracking hit list (original) length "
937 << trackingHitList.size() << " ======";
938 for (int i = 0; i < trackingHitList.size(); i++) {
939 ldmx_log(trace) << " [" << trackingHitList[i].pos.X() << ", "
940 << trackingHitList[i].pos.Y() << ", "
941 << trackingHitList[i].layer << "]";
942 }
943 ldmx_log(trace) << "====== END OF Tracking hit list ======";
944
945 // in v14 minR is 4.17 mm
946 // while maxR is 4.81 mm
947 float cellWidth = 2 * geometry_->getCellMaxR();
948 for (int iHit = 0; iHit < trackingHitList.size(); iHit++) {
949 // list of hit numbers in track (34 = maximum theoretical length)
950 int track[34];
951 int currenthit{iHit};
952 int trackLen{1};
953
954 track[0] = iHit;
955
956 // Search for hits to add to the track:
957 // repeatedly find hits in the front two layers with same x & y positions
958 // but since v14 the odd layers are offset, so we allow half a cellWidth
959 // deviation and then add to track until no more hits are found
960 int jHit = iHit;
961 while (jHit < trackingHitList.size()) {
962 if ((trackingHitList[jHit].layer ==
963 trackingHitList[currenthit].layer - 1 ||
964 trackingHitList[jHit].layer ==
965 trackingHitList[currenthit].layer - 2) &&
966 abs(trackingHitList[jHit].pos.X() -
967 trackingHitList[currenthit].pos.X()) <= 0.5 * cellWidth &&
968 abs(trackingHitList[jHit].pos.Y() -
969 trackingHitList[currenthit].pos.Y()) <= 0.5 * cellWidth) {
970 track[trackLen] = jHit;
971 trackLen++;
972 currenthit = jHit;
973 }
974 jHit++;
975 }
976
977 // Confirm that the track is valid:
978 if (trackLen < 2) continue; // Track must contain at least 2 hits
979 float closest_e = distTwoLines(trackingHitList[track[0]].pos,
980 trackingHitList[track[trackLen - 1]].pos,
981 e_traj_start, e_traj_end);
982 float closest_p = distTwoLines(trackingHitList[track[0]].pos,
983 trackingHitList[track[trackLen - 1]].pos,
984 p_traj_start, p_traj_end);
985 // Make sure that the track is near the photon trajectory and away from the
986 // electron trajectory Details of these constraints may be revised
987 if (closest_p > cellWidth and closest_e < 2 * cellWidth) continue;
988 if (trackLen < 4 and closest_e > closest_p) continue;
989 ldmx_log(trace) << "====== After rejection for MIP tracking ======";
990 ldmx_log(trace) << "current hit: [" << trackingHitList[iHit].pos.X() << ", "
991 << trackingHitList[iHit].pos.Y() << ", "
992 << trackingHitList[iHit].layer << "]";
993
994 for (int k = 0; k < trackLen; k++) {
995 ldmx_log(trace) << " track[" << k << "] position = ["
996 << trackingHitList[track[k]].pos.X() << ", "
997 << trackingHitList[track[k]].pos.Y() << ", "
998 << trackingHitList[track[k]].layer << "]";
999 }
1000
1001 // if track found, increment nStraightTracks and remove all hits in track
1002 // from future consideration
1003 if (trackLen >= 2) {
1004 std::vector<HitData> temp_track_list;
1005 int n_remove = 0;
1006 for (int kHit = 0; kHit < trackLen; kHit++) {
1007 temp_track_list.push_back(trackingHitList[track[kHit] - n_remove]);
1008 trackingHitList.erase(trackingHitList.begin() + track[kHit] - n_remove);
1009 n_remove++;
1010 }
1011 // print trackingHitList
1012 ldmx_log(trace) << "====== Tracking hit list (after erase) length "
1013 << trackingHitList.size() << " ======";
1014 for (int i = 0; i < trackingHitList.size(); i++) {
1015 ldmx_log(trace) << " [" << trackingHitList[i].pos.X() << ", "
1016 << trackingHitList[i].pos.Y() << ", "
1017 << trackingHitList[i].layer << "] ";
1018 }
1019 ldmx_log(trace) << "====== END OF Tracking hit list ======";
1020
1021 track_list.push_back(temp_track_list);
1022 // The *current* hit will have been removed, so iHit is currently pointing
1023 // to the next hit. Decrement iHit so no hits will get skipped by iHit++
1024 iHit--;
1025 }
1026 }
1027
1028 ldmx_log(debug) << "Straight tracks found (before merge): "
1029 << track_list.size();
1030 for (int iTrack = 0; iTrack < track_list.size(); iTrack++) {
1031 ldmx_log(trace) << "Track " << iTrack << ":";
1032 for (int iHit = 0; iHit < track_list[iTrack].size(); iHit++) {
1033 ldmx_log(trace) << " Hit " << iHit << ": ["
1034 << track_list[iTrack][iHit].pos.X() << ", "
1035 << track_list[iTrack][iHit].pos.Y() << ", "
1036 << track_list[iTrack][iHit].layer << "]";
1037 }
1038 }
1039
1040 // Optional addition: Merge nearby straight tracks. Not necessary for veto.
1041 // Criteria: consider tail of track. Merge if head of next track is 1/2
1042 // layers behind, within 1 cell of xy position.
1043 ldmx_log(debug) << "Beginning track merging using " << track_list.size()
1044 << " tracks";
1045
1046 for (int track_i = 0; track_i < track_list.size(); track_i++) {
1047 // for each track, check the remainder of the track list for compatible
1048 // tracks
1049 std::vector<HitData> base_track = track_list[track_i];
1050 HitData tail_hitdata = base_track.back(); // xylayer of last hit in track
1051 ldmx_log(trace) << " Considering track " << track_i;
1052 for (int track_j = track_i + 1; track_j < track_list.size(); track_j++) {
1053 std::vector<HitData> checking_track = track_list[track_j];
1054 HitData head_hitdata = checking_track.front();
1055 // if 1-2 layers behind, and xy within one cell...
1056 if ((head_hitdata.layer == tail_hitdata.layer + 1 ||
1057 head_hitdata.layer == tail_hitdata.layer + 2) &&
1058 pow(pow(head_hitdata.pos.X() - tail_hitdata.pos.X(), 2) +
1059 pow(head_hitdata.pos.Y() - tail_hitdata.pos.Y(), 2),
1060 0.5) <= cellWidth) {
1061 // ...then append the second track to the first one and delete it
1062 // NOTE: TO ADD: (trackingHitList[iHit].pos -
1063 // trackingHitList[jHit].pos).Mag()
1064 ldmx_log(trace) << " ** Compatible track found at index "
1065 << track_j;
1066 ldmx_log(trace) << " Tail xylayer: " << head_hitdata.pos.X() << ","
1067 << head_hitdata.pos.Y() << "," << head_hitdata.layer;
1068 ldmx_log(trace) << " Head xylayer: " << tail_hitdata.pos.X() << ","
1069 << tail_hitdata.pos.Y() << "," << tail_hitdata.layer;
1070
1071 for (int hit_k = 0; hit_k < checking_track.size(); hit_k++) {
1072 base_track.push_back(track_list[track_j][hit_k]);
1073 }
1074 track_list[track_i] = base_track;
1075 track_list.erase(track_list.begin() + track_j);
1076 break;
1077 }
1078 }
1079 }
1080 nStraightTracks_ = track_list.size();
1081 // print the track list
1082 ldmx_log(debug) << "Straight tracks found (after merge): "
1084 for (int track_i = 0; track_i < track_list.size(); track_i++) {
1085 ldmx_log(debug) << "Track " << track_i << ":";
1086 for (int hit_i = 0; hit_i < track_list[track_i].size(); hit_i++) {
1087 ldmx_log(debug) << " Hit " << hit_i << ": ["
1088 << track_list[track_i][hit_i].pos.X() << ", "
1089 << track_list[track_i][hit_i].pos.Y() << ", "
1090 << track_list[track_i][hit_i].layer << "]";
1091 }
1092 }
1093
1094 auto straight_tracks = std::chrono::high_resolution_clock::now();
1095 profiling_map_["straight_tracks"] += std::chrono::duration<float, std::milli>(
1096 straight_tracks - mip_tracking_setup)
1097 .count();
1098
1099 // ------------------------------------------------------
1100 // Linreg tracking:
1101 ldmx_log(info) << "Finding linreg tracks from a total of "
1102 << trackingHitList.size() << " hits using a radius of "
1103 << linreg_radius_ << " mm";
1104
1105 int max_lin_reg_hit{0};
1106 if (run_lin_reg_) max_lin_reg_hit = trackingHitList.size();
1107 for (int iHit = 0; iHit < max_lin_reg_hit; iHit++) {
1108 // Hits being considered at a given time
1109 std::vector<int> hitsInRegion;
1110 TMatrixD Vm(3, 3);
1111 TMatrixD hdt(3, 3);
1112 TVector3 slopeVec;
1113 TVector3 hmean;
1114 TVector3 hpoint;
1115 float r_corr_best{0.0};
1116 // Temp array having 3 potential hits
1117 int hitNums[3];
1118 // From the above which are passing the correlation reqs
1119 int bestHitNums[3];
1120
1121 hitsInRegion.push_back(iHit);
1122 // Find all hits within 2 cells of the primary hit:
1123 for (int jHit = 0; jHit < trackingHitList.size(); jHit++) {
1124 // Dont try to put hits on the same layer to the lin-reg track
1125 if (trackingHitList[iHit].pos(2) == trackingHitList[jHit].pos(2)) {
1126 continue;
1127 }
1128 float dstToHit =
1129 (trackingHitList[iHit].pos - trackingHitList[jHit].pos).Mag();
1130 // This distance optimized to give the best significance
1131 // it used to be 2*cellWidth, i.e. 4.81 mm
1132 // note, the layers in the back have a separation of 22.3
1133 if (dstToHit <= 2 * linreg_radius_) {
1134 hitsInRegion.push_back(jHit);
1135 }
1136 }
1137 // Found a track that passed the lin-reg reqs
1138 bool bestLinRegFound{false};
1139
1140 ldmx_log(trace) << "There are " << hitsInRegion.size()
1141 << " hits within a radius of " << linreg_radius_ << " mm";
1142
1143 // Look at combinations of hits within the region (do not consider the same
1144 // combination twice):
1145 hitNums[0] = iHit;
1146 for (int jHitInReg = 1; jHitInReg < hitsInRegion.size() - 1; jHitInReg++) {
1147 // We require (exactly) 3 hits for the lin-reg track building
1148 if (hitsInRegion.size() < 3) break;
1149 hitNums[1] = hitsInRegion[jHitInReg];
1150 for (int kHitReg = jHitInReg + 1; kHitReg < hitsInRegion.size();
1151 kHitReg++) {
1152 hitNums[2] = hitsInRegion[kHitReg];
1153 for (int hInd = 0; hInd < 3; hInd++) {
1154 // hmean = geometric mean, subtract off from hits to improve SVD
1155 // performance
1156 hmean(hInd) = (trackingHitList[hitNums[0]].pos(hInd) +
1157 trackingHitList[hitNums[1]].pos(hInd) +
1158 trackingHitList[hitNums[2]].pos(hInd)) /
1159 3.0;
1160 }
1161 for (int hInd = 0; hInd < 3; hInd++) {
1162 for (int lInd = 0; lInd < 3; lInd++) {
1163 hdt(hInd, lInd) =
1164 trackingHitList[hitNums[hInd]].pos(lInd) - hmean(lInd);
1165 }
1166 }
1167
1168 // Perform "linreg" on selected points
1169 // Calculate the determinant of the matrix
1170 float determinant =
1171 hdt(0, 0) * (hdt(1, 1) * hdt(2, 2) - hdt(1, 2) * hdt(2, 1)) -
1172 hdt(0, 1) * (hdt(1, 0) * hdt(2, 2) - hdt(1, 2) * hdt(2, 0)) +
1173 hdt(0, 2) * (hdt(1, 0) * hdt(2, 1) - hdt(1, 1) * hdt(2, 0));
1174 // Exit early if the matrix is singular (i.e. det = 0)
1175 if (determinant == 0) continue;
1176 // Perform matrix decomposition with SVD
1177 TDecompSVD svdObj(hdt);
1178 bool decomposed = svdObj.Decompose();
1179 if (!decomposed) continue;
1180
1181 // First col of V matrix is the slope of the best-fit line
1182 Vm = svdObj.GetV();
1183 for (int hInd = 0; hInd < 3; hInd++) {
1184 slopeVec(hInd) = Vm[0][hInd];
1185 }
1186 // hmean, hpoint are points on the best-fit line
1187 hpoint = slopeVec + hmean;
1188 // linreg complete: Now have best-fit line for 3 hits under
1189 // consideration Check whether the track is valid: r^2 must be high,
1190 // and the track must plausibly originate from the photon
1191 float closest_e = distTwoLines(hmean, hpoint, e_traj_start, e_traj_end);
1192 float closest_p = distTwoLines(hmean, hpoint, p_traj_start, p_traj_end);
1193 // Projected track must be close to the photon; details may change after
1194 // future study.
1195 if (closest_p > cellWidth or closest_e < 1.5 * cellWidth) continue;
1196 // find r^2
1197 // ~variance
1198 float vrnc = (trackingHitList[hitNums[0]].pos - hmean).Mag() +
1199 (trackingHitList[hitNums[1]].pos - hmean).Mag() +
1200 (trackingHitList[hitNums[2]].pos - hmean).Mag();
1201 // sum of |errors|
1202 float sumerr =
1203 distPtToLine(trackingHitList[hitNums[0]].pos, hmean, hpoint) +
1204 distPtToLine(trackingHitList[hitNums[1]].pos, hmean, hpoint) +
1205 distPtToLine(trackingHitList[hitNums[2]].pos, hmean, hpoint);
1206 float r_corr = 1 - sumerr / vrnc;
1207 // Check whether r^2 exceeds a low minimum r_corr: "Fake" tracks are
1208 // still much more common in background, so making the algorithm
1209 // oversensitive doesn't lower performance significantly
1210 if (r_corr > r_corr_best and r_corr > .6) {
1211 r_corr_best = r_corr;
1212 // Only looking for 3-hit tracks currently
1213 bestLinRegFound = true;
1214 for (int k = 0; k < 3; k++) {
1215 bestHitNums[k] = hitNums[k];
1216 }
1217 }
1218 } // end loop on hits in the region
1219 } // end 2nd loop on hits in the region
1220
1221 // Continue early if not hits on track
1222 if (!bestLinRegFound) continue;
1223 // Otherwise increase the number of lin-reg tracks
1225 ldmx_log(debug) << " Lin-reg track " << nLinregTracks_;
1226 for (int finalHitIndx = 0; finalHitIndx < 3; finalHitIndx++) {
1227 ldmx_log(debug) << " Hit " << finalHitIndx << " ["
1228 << trackingHitList[bestHitNums[finalHitIndx]].pos(0)
1229 << ", "
1230 << trackingHitList[bestHitNums[finalHitIndx]].pos(1)
1231 << ", "
1232 << trackingHitList[bestHitNums[finalHitIndx]].pos(2)
1233 << "] ";
1234 }
1235
1236 // Exclude all hits in a found track from further consideration:
1237 for (int lHit = 0; lHit < 3; lHit++) {
1238 if (!trackingHitList.empty()) {
1239 trackingHitList.erase(trackingHitList.begin() + bestHitNums[lHit]);
1240 }
1241 }
1242 iHit--;
1243 } // end loop on all hits
1244
1245 auto linreg_tracks = std::chrono::high_resolution_clock::now();
1246 profiling_map_["linreg_tracks"] +=
1247 std::chrono::duration<float, std::milli>(linreg_tracks - straight_tracks)
1248 .count();
1249
1250 ldmx_log(info) << " MIP tracking completed; found " << nStraightTracks_
1251 << " straight tracks and " << nLinregTracks_
1252 << " lin-reg tracks";
1253
1254 result.setVariables(
1255 nReadoutHits_, deepestLayerHit_, summedDet_, summedTightIso_, maxCellDep_,
1256 showerRMS_, xStd_, yStd_, avgLayerHit_, stdLayerHit_, ecalBackEnergy_,
1259 epDotAtTarget_, electronContainmentEnergy, photonContainmentEnergy,
1260 outsideContainmentEnergy, outsideContainmentNHits, outsideContainmentXstd,
1261 outsideContainmentYstd, energySeg, xMeanSeg, yMeanSeg, xStdSeg, yStdSeg,
1262 layerMeanSeg, layerStdSeg, eContEnergy, eContXMean, eContYMean,
1263 gContEnergy, gContNHits, gContXMean, gContYMean, oContEnergy, oContNHits,
1264 oContXMean, oContYMean, oContXStd, oContYStd, oContLayerMean,
1265 oContLayerStd, ecalLayerEdepReadout_, recoilP, recoilPos);
1266
1267 auto set_variables = std::chrono::high_resolution_clock::now();
1268 profiling_map_["set_variables"] +=
1269 std::chrono::duration<float, std::milli>(set_variables - linreg_tracks)
1270 .count();
1271
1272 buildBDTFeatureVector(result);
1273 ldmx::Ort::FloatArrays inputs({bdtFeatures_});
1274 float pred = rt_->run({featureListName_}, inputs, {"probabilities"})[0].at(1);
1275 ldmx_log(info) << " BDT was ran, score is " << pred;
1276 // Other considerations were (nLinregTracks_ == 0) && (firstNearPhLayer_ >=
1277 // 6)
1278 // && (epAng_ > 3.0 && epAng_ < 900 || epSep_ > 10.0 && epSep_ < 900)
1279 bool passesTrackingVeto = (nStraightTracks_ < 3);
1280 result.setVetoResult(pred > bdtCutVal_ && passesTrackingVeto);
1281 result.setDiscValue(pred);
1282 ldmx_log(info) << " The pred > bdtCutVal = " << (pred > bdtCutVal_)
1283 << " and MIP tracking passed = " << passesTrackingVeto;
1284
1285 // Persist in the event if the recoil ele is fiducial
1286 result.setFiducial(inside_ecal_cell);
1287 result.setTrackingFiducial(fiducial_in_tracker);
1288
1289 // If the event passes the veto, keep it. Otherwise,
1290 // drop the event.
1291 if (!inverse_skim_) {
1292 if (result.passesVeto()) {
1294 } else {
1296 }
1297 } else {
1298 // Invert the skimming logic
1299 if (result.passesVeto()) {
1301 } else {
1303 }
1304 }
1305
1306 event.add(collectionName_, result);
1307
1308 auto bdt_variables = std::chrono::high_resolution_clock::now();
1309 profiling_map_["bdt_variables"] +=
1310 std::chrono::duration<float, std::milli>(bdt_variables - set_variables)
1311 .count();
1312
1313 auto end = std::chrono::high_resolution_clock::now();
1314 auto time_diff = end - start;
1315 processing_time_ +=
1316 std::chrono::duration<float, std::milli>(time_diff).count();
1317}
1318
1320 ldmx_log(info) << "AVG Time/Event: " << std::fixed << std::setprecision(2)
1321 << processing_time_ / nevents_ << " ms";
1322
1323 ldmx_log(info) << "Breakdown::";
1324
1325 ldmx_log(info) << "setup Avg Time/Event = " << std::fixed
1326 << std::setprecision(3) << profiling_map_["setup"] / nevents_
1327 << " ms";
1328
1329 ldmx_log(info) << "recoil_electron Avg Time/Event = " << std::fixed
1330 << std::setprecision(3)
1331 << profiling_map_["recoil_electron"] / nevents_ << " ms";
1332 ldmx_log(info) << "trajectories Avg Time/Event = " << std::fixed
1333 << std::setprecision(3)
1334 << profiling_map_["trajectories"] / nevents_ << " ms";
1335
1336 ldmx_log(info) << "roc_var Avg Time/Event = " << std::fixed
1337 << std::setprecision(3) << profiling_map_["roc_var"] / nevents_
1338 << " ms";
1339
1340 ldmx_log(info) << "fill_hitmaps Avg Time/Event = " << std::fixed
1341 << std::setprecision(3)
1342 << profiling_map_["fill_hitmaps"] / nevents_ << " ms";
1343 ldmx_log(info) << "containment_var Avg Time/Event = " << std::fixed
1344 << std::setprecision(3)
1345 << profiling_map_["containment_var"] / nevents_ << " ms";
1346 ldmx_log(info) << "mip_tracking_setup Avg Time/Event = " << std::fixed
1347 << std::setprecision(3)
1348 << profiling_map_["mip_tracking_setup"] / nevents_ << " ms";
1349
1350 ldmx_log(info) << "straight_tracks Avg Time/Event = " << std::fixed
1351 << std::setprecision(3)
1352 << profiling_map_["straight_tracks"] / nevents_ << " ms";
1353
1354 ldmx_log(info) << "linreg_tracks Avg Time/Event = " << std::fixed
1355 << std::setprecision(3)
1356 << profiling_map_["linreg_tracks"] / nevents_ << " ms";
1357
1358 ldmx_log(info) << "set_variables Avg Time/Event = " << std::fixed
1359 << std::setprecision(3)
1360 << profiling_map_["set_variables"] / nevents_ << " ms";
1361
1362 ldmx_log(info) << "bdt_variables Avg Time/Event = " << std::fixed
1363 << std::setprecision(3)
1364 << profiling_map_["bdt_variables"] / nevents_ << " ms";
1365}
1366/* Function to calculate the energy weighted shower centroid */
1367ldmx::EcalID EcalVetoProcessor::GetShowerCentroidIDAndRMS(
1368 const std::vector<ldmx::EcalHit> &ecalRecHits, float &showerRMS) {
1369 auto wgtCentroidCoords = std::make_pair<float, float>(0., 0.);
1370 float sumEdep = 0;
1371 ldmx::EcalID returnCellId;
1372
1373 // Calculate Energy Weighted Centroid
1374 for (const ldmx::EcalHit &hit : ecalRecHits) {
1375 ldmx::EcalID id(hit.getID());
1376 CellEnergyPair cell_energy_pair = std::make_pair(id, hit.getEnergy());
1377 auto [x, y, z] = geometry_->getPosition(id);
1378 XYCoords centroidCoords = std::make_pair(x, y);
1379 wgtCentroidCoords.first = wgtCentroidCoords.first +
1380 centroidCoords.first * cell_energy_pair.second;
1381 wgtCentroidCoords.second = wgtCentroidCoords.second +
1382 centroidCoords.second * cell_energy_pair.second;
1383 sumEdep += cell_energy_pair.second;
1384 }
1385 wgtCentroidCoords.first = (sumEdep > 1E-6) ? wgtCentroidCoords.first / sumEdep
1386 : wgtCentroidCoords.first;
1387 wgtCentroidCoords.second = (sumEdep > 1E-6)
1388 ? wgtCentroidCoords.second / sumEdep
1389 : wgtCentroidCoords.second;
1390 // Find Nearest Cell to Centroid
1391 float maxDist = 1e6;
1392 for (const ldmx::EcalHit &hit : ecalRecHits) {
1393 auto [x, y, z] = geometry_->getPosition(hit.getID());
1394 XYCoords centroidCoords = std::make_pair(x, y);
1395
1396 float deltaR =
1397 pow(pow((centroidCoords.first - wgtCentroidCoords.first), 2) +
1398 pow((centroidCoords.second - wgtCentroidCoords.second), 2),
1399 .5);
1400 showerRMS += deltaR * hit.getEnergy();
1401 if (deltaR < maxDist) {
1402 maxDist = deltaR;
1403 returnCellId = ldmx::EcalID(hit.getID());
1404 }
1405 }
1406 if (sumEdep > 0) showerRMS = showerRMS / sumEdep;
1407 // flatten
1408 return ldmx::EcalID(0, returnCellId.module(), returnCellId.cell());
1409}
1410
1415 const std::vector<ldmx::EcalHit> &ecalRecHits,
1416 std::map<ldmx::EcalID, float> &cellMap) {
1417 for (const ldmx::EcalHit &hit : ecalRecHits) {
1418 ldmx::EcalID id(hit.getID());
1419 cellMap.emplace(id, hit.getEnergy());
1420 }
1421}
1422
1423void EcalVetoProcessor::fillIsolatedHitMap(
1424 const std::vector<ldmx::EcalHit> &ecalRecHits, ldmx::EcalID globalCentroid,
1425 std::map<ldmx::EcalID, float> &cellMap,
1426 std::map<ldmx::EcalID, float> &cellMapIso, bool doTight) {
1427 for (const ldmx::EcalHit &hit : ecalRecHits) {
1428 auto isolatedHit = std::make_pair(true, ldmx::EcalID());
1429 ldmx::EcalID id(hit.getID());
1430 if (doTight) {
1431 // Disregard hits that are on the centroid.
1432 if (id == globalCentroid) continue;
1433
1434 // Skip hits that are on centroid inner ring
1435 if (geometry_->isNN(globalCentroid, id)) {
1436 continue;
1437 }
1438 }
1439
1440 // Skip hits that have a readout neighbor
1441 // Get neighboring cell id's and try to look them up in the full cell map
1442 // (constant speed algo.)
1443 // these ideas are only cell/module (must ignore layer)
1444 std::vector<ldmx::EcalID> cellNbrIds = geometry_->getNN(id);
1445
1446 for (int k = 0; k < cellNbrIds.size(); k++) {
1447 // update neighbor ID to the current layer
1448 cellNbrIds[k] = ldmx::EcalID(id.layer(), cellNbrIds[k].module(),
1449 cellNbrIds[k].cell());
1450 // look in cell hit map to see if it is there
1451 if (cellMap.find(cellNbrIds[k]) != cellMap.end()) {
1452 isolatedHit = std::make_pair(false, cellNbrIds[k]);
1453 break;
1454 }
1455 }
1456 if (!isolatedHit.first) {
1457 continue;
1458 }
1459 // Insert isolated hit
1460 cellMapIso.emplace(id, hit.getEnergy());
1461 }
1462}
1463
1464/* Calculate where trajectory intersects ECAL layers using position and momentum
1465 * at scoring plane */
1466std::vector<std::pair<float, float>> EcalVetoProcessor::getTrajectory(
1467 std::array<float, 3> momentum, std::array<float, 3> position) {
1468 std::vector<XYCoords> positions;
1469 for (int iLayer = 0; iLayer < nEcalLayers_; iLayer++) {
1470 float posX =
1471 position[0] + (momentum[0] / momentum[2]) *
1472 (geometry_->getZPosition(iLayer) - position[2]);
1473 float posY =
1474 position[1] + (momentum[1] / momentum[2]) *
1475 (geometry_->getZPosition(iLayer) - position[2]);
1476 positions.push_back(std::make_pair(posX, posY));
1477 }
1478 return positions;
1479}
1480
1481// MIP tracking functions:
1482
1483float EcalVetoProcessor::distTwoLines(TVector3 v1, TVector3 v2, TVector3 w1,
1484 TVector3 w2) {
1485 TVector3 e1 = v1 - v2;
1486 TVector3 e2 = w1 - w2;
1487 TVector3 crs = e1.Cross(e2);
1488 if (crs.Mag() == 0) {
1489 return 100.0; // arbitrary large number; edge case that shouldn't cause
1490 // problems.
1491 } else {
1492 return std::abs(crs.Dot(v1 - w1) / crs.Mag());
1493 }
1494}
1495
1496float EcalVetoProcessor::distPtToLine(TVector3 h1, TVector3 p1, TVector3 p2) {
1497 return ((h1 - p1).Cross(h1 - p2)).Mag() / (p1 - p2).Mag();
1498}
1499
1500std::vector<float> EcalVetoProcessor::trackProp(const ldmx::Tracks &tracks,
1501 ldmx::TrackStateType ts_type,
1502 const std::string &ts_title) {
1503 // Vector to hold the new track state variables
1504 std::vector<float> new_track_states;
1505
1506 // Return if no tracks
1507 if (tracks.empty()) return new_track_states;
1508
1509 // Otherwise loop on the tracks
1510 for (auto &track : tracks) {
1511 // Get track state for ts_type
1512 auto trk_ts = track.getTrackState(ts_type);
1513 // Continue if there's no value
1514 if (!trk_ts.has_value()) continue;
1515 ldmx::Track::TrackState &ecal_track_state = trk_ts.value();
1516
1517 // Check that the track state is filled
1518 if (ecal_track_state.params.size() < 5) continue;
1519
1520 float track_state_loc0 = static_cast<float>(ecal_track_state.params[0]);
1521 float track_state_loc1 = static_cast<float>(ecal_track_state.params[1]);
1522 // param 2 = phi (azimuthal), param 3 = theta (polar)
1523 // param 4 = QoP
1524 // ACTS (local) to LDMX (global) coordinates: (y,z,x)-> (x,y,z)
1525 // convert qop [1/GeV] to p [MeV]
1526 float p_track_state = (-1 / ecal_track_state.params[4]) * 1000;
1527 // p * sin(theta) * sin(phi)
1528 float recoil_mom_x = p_track_state * sin(ecal_track_state.params[3]) *
1529 sin(ecal_track_state.params[2]);
1530 // p * cos(theta)
1531 float recoil_mom_y = p_track_state * cos(ecal_track_state.params[3]);
1532 // p * sin(theta) * cos(phi)
1533 float recoil_mom_z = p_track_state * sin(ecal_track_state.params[3]) *
1534 cos(ecal_track_state.params[2]);
1535
1536 // Store the new track state variables
1537 new_track_states.push_back(track_state_loc0);
1538 new_track_states.push_back(track_state_loc1);
1539 // z-position at the ECAL (4) or Target (1)
1540 if (ts_type == 4) {
1541 // this should match `ECAL_SCORING_PLANE` in CKFProcessor
1542 new_track_states.push_back(240.5);
1543 } else if (ts_type == 1) {
1544 // This should match `target_surface` in CKFProcessor
1545 new_track_states.push_back(0.0);
1546 }
1547
1548 new_track_states.push_back(recoil_mom_x);
1549 new_track_states.push_back(recoil_mom_y);
1550 new_track_states.push_back(recoil_mom_z);
1551
1552 // Break after getting the first valid track state
1553 // TODO: interface this with CLUE to make sure the propageted track
1554 // has an associated cluster in the ECAL
1555 break;
1556 }
1557
1558 return new_track_states;
1559}
1560
1561} // namespace ecal
1562
1563DECLARE_PRODUCER_NS(ecal, EcalVetoProcessor);
Collection of utility functions useful for analysis.
Class that translates raw positions of ECal module hits into cells in a hexagonal readout.
Class that determines if event is vetoable using ECAL hit information.
Class providing string constants for the event model.
#define DECLARE_PRODUCER_NS(NS, CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class which encapsulates information from a hit in a simulated tracking detector.
std::vector< float > trackProp(const ldmx::Tracks &tracks, ldmx::TrackStateType ts_type, const std::string &ts_title)
Return a vector of parameters for a propagated recoil track.
float epSep_
Distance between the projected photon and electron trajectories at the ECal face.
float distTwoLines(TVector3 v1, TVector3 v2, TVector3 w1, TVector3 w2)
Returns the distance between the lines v and w, with v defined to pass through the points (v1,...
void configure(framework::config::Parameters &parameters) override
Configure the processor using the given user specified parameters.
void produce(framework::Event &event) override
Process the event and put new data products into it.
void onNewRun(const ldmx::RunHeader &rh) override
onNewRun is the first function called for each processor after the conditions are fully configured an...
void buildBDTFeatureVector(const ldmx::EcalVetoResult &result)
int photonTerritoryHits_
Number of hits in the photon territory.
float epDotAtTarget_
Dot product of the photon and electron momenta unit vectors at Target.
float epAng_
Angular separation between the projected photon and electron trajectories as projected at ECAL.
int nStraightTracks_
Number of "straight" tracks found in the event.
int nLinregTracks_
Number of "linreg" tracks found in the event.
int firstNearPhLayer_
First ECal layer in which a hit is found near the photon.
float epDot_
Dot product of the photon and electron momenta unit vectors at Ecal.
float distPtToLine(TVector3 h1, TVector3 p1, TVector3 p2)
Return the minimum distance between the point h1 and the line passing through points p1 and p2.
std::string collectionName_
Name of the collection which will containt the results.
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
void fillHitMap(const std::vector< ldmx::EcalHit > &ecalRecHits, std::map< ldmx::EcalID, float > &cellMap_)
Function to load up empty vector of hit maps.
float epAngAtTarget_
Angular separation between the projected photon and electron trajectories as at Target.
int nNearPhHits_
Number of hits near the photon trajectory.
const ldmx::EcalGeometry * geometry_
handle to current geometry (to share with member functions)
const T & getCondition(const std::string &condition_name)
Access a conditions object for the current event.
void setStorageHint(framework::StorageControl::Hint hint)
Mark the current event as having the given storage control hint from this module.
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:92
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
std::vector< EcalID > getNN(EcalID id) const
Get the Nearest Neighbors of the input ID.
EcalID getID(double x, double y, double z, bool fallible=false) const
Get a cell's ID number from its position.
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.
bool isNN(EcalID centroid, EcalID probe) const
Check if the probe id is one of the nearest neightbors of the centroid id.
double getCellMaxR() const
Get the center-to-corner radius of the cell hexagons.
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
int cell() const
Get the value of the cell field from the ID.
Definition EcalID.h:111
int module() const
Get the value of the module field from the ID.
Definition EcalID.h:87
bool passesVeto() const
Checks if the event passes the Ecal veto.
void setVariables(int nReadoutHits, int deepestLayerHit, float summedDet, float summedTightIso, float maxCellDep, float showerRMS, float xStd, float yStd, float avgLayerHit, float stdLayerHit, float ecalBackEnergy, int nStraightTracks, int nLinregTracks, int firstNearPhLayer, int nNearPhHits, int photonTerritoryHits, float epAng, float epAngAtTarget, float epSep, float epDot, float epDotAtTarget, std::vector< float > electronContainmentEnergy, std::vector< float > photonContainmentEnergy, std::vector< float > outsideContainmentEnergy, std::vector< int > outsideContainmentNHits, std::vector< float > outsideContainmentXStd, std::vector< float > outsideContainmentYStd, std::vector< float > energySeg, std::vector< float > xMeanSeg, std::vector< float > yMeanSeg, std::vector< float > xStdSeg, std::vector< float > yStdSeg, std::vector< float > layerMeanSeg, std::vector< float > layerStdSeg, std::vector< std::vector< float > > eContEnergy, std::vector< std::vector< float > > eContXMean, std::vector< std::vector< float > > eContYMean, std::vector< std::vector< float > > gContEnergy, std::vector< std::vector< int > > gContNHits, std::vector< std::vector< float > > gContXMean, std::vector< std::vector< float > > gContYMean, std::vector< std::vector< float > > oContEnergy, std::vector< std::vector< int > > oContNHits, std::vector< std::vector< float > > oContXMean, std::vector< std::vector< float > > oContYMean, std::vector< std::vector< float > > oContXStd, std::vector< std::vector< float > > oContYStd, std::vector< std::vector< float > > oContLayerMean, std::vector< std::vector< float > > oContLayerStd, std::vector< float > EcalLayerEdepReadout, std::array< float, 3 > recoilP, std::array< float, 3 > recoilPos)
Set the sim particle and 'is findable' flag.
int getNStraightTracks() const
Number of straight tracks found.
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:57
Class representing a simulated particle.
Definition SimParticle.h:23
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.
Implementation of a track object.
Definition Track.h:53
constexpr StorageControl::Hint hint_shouldKeep
storage control hint alias for backwards compatibility
constexpr StorageControl::Hint hint_shouldDrop
storage control hint alias for backwards compatibility