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
23// ROOT (MIP tracking)
24#include "TDecompSVD.h"
25#include "TMatrixD.h"
26#include "TVector3.h"
27
28namespace ecal {
29
31 const ldmx::EcalVetoResult &result) {
32 // Base variables
33 bdtFeatures_.push_back(result.getNReadoutHits());
34 bdtFeatures_.push_back(result.getSummedDet());
35 bdtFeatures_.push_back(result.getSummedTightIso());
36 bdtFeatures_.push_back(result.getMaxCellDep());
37 bdtFeatures_.push_back(result.getShowerRMS());
38 bdtFeatures_.push_back(result.getXStd());
39 bdtFeatures_.push_back(result.getYStd());
40 bdtFeatures_.push_back(result.getAvgLayerHit());
41 bdtFeatures_.push_back(result.getStdLayerHit());
42 bdtFeatures_.push_back(result.getDeepestLayerHit());
43 bdtFeatures_.push_back(result.getEcalBackEnergy());
44 // MIP tracking
45 bdtFeatures_.push_back(result.getNStraightTracks());
46 // bdtFeatures_.push_back(result.getNLinregTracks());
47 bdtFeatures_.push_back(result.getFirstNearPhLayer());
48 bdtFeatures_.push_back(result.getNNearPhHits());
49 bdtFeatures_.push_back(result.getPhotonTerritoryHits());
50 bdtFeatures_.push_back(result.getEPSep());
51 bdtFeatures_.push_back(result.getEPDot());
52 // Longitudinal segment variables
53 bdtFeatures_.push_back(result.getEnergySeg()[0]);
54 bdtFeatures_.push_back(result.getXMeanSeg()[0]);
55 bdtFeatures_.push_back(result.getYMeanSeg()[0]);
56 bdtFeatures_.push_back(result.getLayerMeanSeg()[0]);
57 bdtFeatures_.push_back(result.getEnergySeg()[1]);
58 bdtFeatures_.push_back(result.getYMeanSeg()[2]);
60 bdtFeatures_.push_back(result.getEleContEnergy()[0][0]);
61 bdtFeatures_.push_back(result.getEleContEnergy()[1][0]);
62 bdtFeatures_.push_back(result.getEleContYMean()[0][0]);
63 bdtFeatures_.push_back(result.getEleContEnergy()[0][1]);
64 bdtFeatures_.push_back(result.getEleContEnergy()[1][1]);
65 bdtFeatures_.push_back(result.getEleContYMean()[0][1]);
67 bdtFeatures_.push_back(result.getPhContNHits()[0][0]);
68 bdtFeatures_.push_back(result.getPhContYMean()[0][0]);
69 bdtFeatures_.push_back(result.getPhContNHits()[0][1]);
71 bdtFeatures_.push_back(result.getOutContEnergy()[0][0]);
72 bdtFeatures_.push_back(result.getOutContEnergy()[1][0]);
73 bdtFeatures_.push_back(result.getOutContEnergy()[2][0]);
74 bdtFeatures_.push_back(result.getOutContNHits()[0][0]);
75 bdtFeatures_.push_back(result.getOutContXMean()[0][0]);
76 bdtFeatures_.push_back(result.getOutContYMean()[0][0]);
77 bdtFeatures_.push_back(result.getOutContYMean()[1][0]);
78 bdtFeatures_.push_back(result.getOutContYStd()[0][0]);
79 bdtFeatures_.push_back(result.getOutContEnergy()[0][1]);
80 bdtFeatures_.push_back(result.getOutContEnergy()[1][1]);
81 bdtFeatures_.push_back(result.getOutContEnergy()[2][1]);
82 bdtFeatures_.push_back(result.getOutContLayerMean()[0][1]);
83 bdtFeatures_.push_back(result.getOutContLayerStd()[0][1]);
84 bdtFeatures_.push_back(result.getOutContEnergy()[0][2]);
85 bdtFeatures_.push_back(result.getOutContLayerMean()[0][2]);
86}
87
89 verbose_ = parameters.getParameter<bool>("verbose");
90 featureListName_ = parameters.getParameter<std::string>("feature_list_name");
91 // Load BDT ONNX file
92 rt_ = std::make_unique<ldmx::Ort::ONNXRuntime>(
93 parameters.getParameter<std::string>("bdt_file"));
94
95 // Read in arrays holding 68% containment radius per layer
96 // for different bins in momentum/angle
97 rocFileName_ = parameters.getParameter<std::string>("roc_file");
98 if (!std::ifstream(rocFileName_).good()) {
99 EXCEPTION_RAISE(
100 "EcalVetoProcessor",
101 "The specified RoC file '" + rocFileName_ + "' does not exist!");
102 } else {
103 std::ifstream rocfile(rocFileName_);
104 std::string line, value;
105
106 // Extract the first line in the file
107 std::getline(rocfile, line);
108 std::vector<double> values;
109
110 // Read data, line by line
111 while (std::getline(rocfile, line)) {
112 std::stringstream ss(line);
113 values.clear();
114 while (std::getline(ss, value, ',')) {
115 double f_value = (value != "") ? std::stof(value) : -1.0;
116 values.push_back(f_value);
117 }
118 roc_range_values_.push_back(values);
119 }
120 }
121
122 nEcalLayers_ = parameters.getParameter<int>("num_ecal_layers");
123
124 bdtCutVal_ = parameters.getParameter<double>("disc_cut");
125 ecalLayerEdepRaw_.resize(nEcalLayers_, 0);
126 ecalLayerEdepReadout_.resize(nEcalLayers_, 0);
127 ecalLayerTime_.resize(nEcalLayers_, 0);
128
129 beamEnergyMeV_ = parameters.getParameter<double>("beam_energy");
130
131 // Set the collection name as defined in the configuration
132 collectionName_ = parameters.getParameter<std::string>("collection_name");
133 rec_pass_name_ = parameters.getParameter<std::string>("rec_pass_name");
134 rec_coll_name_ = parameters.getParameter<std::string>("rec_coll_name");
135}
136
137void EcalVetoProcessor::clearProcessor() {
138 cellMap_.clear();
139 cellMapTightIso_.clear();
140 bdtFeatures_.clear();
141
142 nReadoutHits_ = 0;
143 summedDet_ = 0;
144 summedTightIso_ = 0;
145 maxCellDep_ = 0;
146 showerRMS_ = 0;
147 xStd_ = 0;
148 yStd_ = 0;
149 avgLayerHit_ = 0;
150 stdLayerHit_ = 0;
151 deepestLayerHit_ = 0;
152 ecalBackEnergy_ = 0;
153 // MIP tracking
155 nLinregTracks_ = 0;
157 nNearPhHits_ = 0;
158 epAng_ = 0;
159 epSep_ = 0;
160 epDot_ = 0;
162
163 std::fill(ecalLayerEdepRaw_.begin(), ecalLayerEdepRaw_.end(), 0);
164 std::fill(ecalLayerEdepReadout_.begin(), ecalLayerEdepReadout_.end(), 0);
165 std::fill(ecalLayerTime_.begin(), ecalLayerTime_.end(), 0);
166}
167
169 // Get the Ecal Geometry
170 geometry_ = &getCondition<ldmx::EcalGeometry>(
171 ldmx::EcalGeometry::CONDITIONS_OBJECT_NAME);
172
174
175 clearProcessor();
176
177 // Get the collection of Ecal scoring plane hits. If it doesn't exist,
178 // don't bother adding any truth tracking information.
179
180 std::vector<double> recoilP;
181 std::vector<float> recoilPos;
182 std::vector<double> recoilPAtTarget;
183 std::vector<float> recoilPosAtTarget;
184
185 if (verbose_) {
186 ldmx_log(debug) << " Loop through all of the sim particles and find the "
187 "recoil electron";
188 }
189
190 if (event.exists("EcalScoringPlaneHits")) {
191 //
192 // Loop through all of the sim particles and find the recoil electron.
193 //
194
195 // Get the collection of simulated particles from the event
196 auto particleMap{event.getMap<int, ldmx::SimParticle>("SimParticles")};
197
198 // Search for the recoil electron
199 auto [recoilTrackID, recoilElectron] = Analysis::getRecoil(particleMap);
200
201 // Find ECAL SP hit for recoil electron
202 auto ecalSpHits{
203 event.getCollection<ldmx::SimTrackerHit>("EcalScoringPlaneHits")};
204 float pmax = 0;
205 for (ldmx::SimTrackerHit &spHit : ecalSpHits) {
206 ldmx::SimSpecialID hit_id(spHit.getID());
207 if (hit_id.plane() != 31 || spHit.getMomentum()[2] <= 0) continue;
208
209 if (spHit.getTrackID() == recoilTrackID) {
210 if (sqrt(pow(spHit.getMomentum()[0], 2) +
211 pow(spHit.getMomentum()[1], 2) +
212 pow(spHit.getMomentum()[2], 2)) > pmax) {
213 recoilP = spHit.getMomentum();
214 recoilPos = spHit.getPosition();
215 pmax = sqrt(pow(recoilP[0], 2) + pow(recoilP[1], 2) +
216 pow(recoilP[2], 2));
217 }
218 }
219 }
220
221 // Find target SP hit for recoil electron
222 if (event.exists("TargetScoringPlaneHits")) {
223 std::vector<ldmx::SimTrackerHit> targetSpHits =
224 event.getCollection<ldmx::SimTrackerHit>("TargetScoringPlaneHits");
225 pmax = 0;
226 for (ldmx::SimTrackerHit &spHit : targetSpHits) {
227 ldmx::SimSpecialID hit_id(spHit.getID());
228 if (hit_id.plane() != 1 || spHit.getMomentum()[2] <= 0) continue;
229
230 if (spHit.getTrackID() == recoilTrackID) {
231 if (sqrt(pow(spHit.getMomentum()[0], 2) +
232 pow(spHit.getMomentum()[1], 2) +
233 pow(spHit.getMomentum()[2], 2)) > pmax) {
234 recoilPAtTarget = spHit.getMomentum();
235 recoilPosAtTarget = spHit.getPosition();
236 pmax =
237 sqrt(pow(recoilPAtTarget[0], 2) + pow(recoilPAtTarget[1], 2) +
238 pow(recoilPAtTarget[2], 2));
239 }
240 }
241 }
242 }
243 }
244
245 if (verbose_) {
246 ldmx_log(debug) << " Get projected trajectories for electron and photon";
247 }
248 // Get projected trajectories for electron and photon
249 std::vector<XYCoords> ele_trajectory, photon_trajectory;
250 if (recoilP.size() > 0) {
251 ele_trajectory = getTrajectory(recoilP, recoilPos);
252 std::vector<double> pvec = recoilPAtTarget.size()
253 ? recoilPAtTarget
254 : std::vector<double>{0.0, 0.0, 0.0};
255 std::vector<float> posvec = recoilPosAtTarget.size()
256 ? recoilPosAtTarget
257 : std::vector<float>{0.0, 0.0, 0.0};
258 photon_trajectory =
259 getTrajectory({-pvec[0], -pvec[1], beamEnergyMeV_ - pvec[2]}, posvec);
260 }
261
262 float recoilPMag =
263 recoilP.size()
264 ? sqrt(pow(recoilP[0], 2) + pow(recoilP[1], 2) + pow(recoilP[2], 2))
265 : -1.0;
266 float recoilTheta =
267 recoilPMag > 0 ? acos(recoilP[2] / recoilPMag) * 180.0 / M_PI : -1.0;
268
269 if (verbose_) {
270 ldmx_log(debug) << " Build Radii of containment (ROC)";
271 }
272 // Use the appropriate containment radii for the recoil electron
273 std::vector<double> roc_values_bin0(roc_range_values_[0].begin() + 4,
274 roc_range_values_[0].end());
275 std::vector<double> ele_radii = roc_values_bin0;
276 double theta_min, theta_max, p_min, p_max;
277 bool inrange;
278
279 for (int i = 0; i < roc_range_values_.size(); i++) {
280 theta_min = roc_range_values_[i][0];
281 theta_max = roc_range_values_[i][1];
282 p_min = roc_range_values_[i][2];
283 p_max = roc_range_values_[i][3];
284 inrange = true;
285
286 if (theta_min != -1.0) {
287 inrange = inrange && (recoilTheta >= theta_min);
288 }
289 if (theta_max != -1.0) {
290 inrange = inrange && (recoilTheta < theta_max);
291 }
292 if (p_min != -1.0) {
293 inrange = inrange && (recoilPMag >= p_min);
294 }
295 if (p_max != -1.0) {
296 inrange = inrange && (recoilPMag < p_max);
297 }
298 if (inrange) {
299 std::vector<double> roc_values_bini(roc_range_values_[i].begin() + 4,
300 roc_range_values_[i].end());
301 ele_radii = roc_values_bini;
302 }
303 }
304 // Use default RoC bin for photon
305 std::vector<double> photon_radii = roc_values_bin0;
306
307 // Get the collection of digitized Ecal hits from the event.
308 const std::vector<ldmx::EcalHit> ecalRecHits =
309 event.getCollection<ldmx::EcalHit>(rec_coll_name_, rec_pass_name_);
310
311 ldmx::EcalID globalCentroid =
312 GetShowerCentroidIDAndRMS(ecalRecHits, showerRMS_);
313 /* ~~ Fill the hit map ~~ O(n) */
314 fillHitMap(ecalRecHits, cellMap_);
315 bool doTight = true;
316 /* ~~ Fill the isolated hit maps ~~ O(n) */
317 fillIsolatedHitMap(ecalRecHits, globalCentroid, cellMap_, cellMapTightIso_,
318 doTight);
319
320 // Loop over the hits from the event to calculate the rest of the important
321 // quantities
322
323 float wavgLayerHit = 0;
324 float xMean = 0;
325 float yMean = 0;
326
327 // Containment variables
328 unsigned int nregions = 5;
329 std::vector<float> electronContainmentEnergy(nregions, 0.0);
330 std::vector<float> photonContainmentEnergy(nregions, 0.0);
331 std::vector<float> outsideContainmentEnergy(nregions, 0.0);
332 std::vector<int> outsideContainmentNHits(nregions, 0);
333 std::vector<float> outsideContainmentXmean(nregions, 0.0);
334 std::vector<float> outsideContainmentYmean(nregions, 0.0);
335 std::vector<float> outsideContainmentXstd(nregions, 0.0);
336 std::vector<float> outsideContainmentYstd(nregions, 0.0);
337 // Longitudinal segmentation
338 std::vector<int> segLayers = {0, 6, 17, 34};
339 unsigned int nsegments = segLayers.size() - 1;
340 std::vector<float> energySeg(nsegments, 0.0);
341 std::vector<float> xMeanSeg(nsegments, 0.0);
342 std::vector<float> xStdSeg(nsegments, 0.0);
343 std::vector<float> yMeanSeg(nsegments, 0.0);
344 std::vector<float> yStdSeg(nsegments, 0.0);
345 std::vector<float> layerMeanSeg(nsegments, 0.0);
346 std::vector<float> layerStdSeg(nsegments, 0.0);
347 std::vector<std::vector<float>> eContEnergy(
348 nregions, std::vector<float>(nsegments, 0.0));
349 std::vector<std::vector<float>> eContXMean(
350 nregions, std::vector<float>(nsegments, 0.0));
351 std::vector<std::vector<float>> eContYMean(
352 nregions, std::vector<float>(nsegments, 0.0));
353 std::vector<std::vector<float>> gContEnergy(
354 nregions, std::vector<float>(nsegments, 0.0));
355 std::vector<std::vector<int>> gContNHits(nregions,
356 std::vector<int>(nsegments, 0));
357 std::vector<std::vector<float>> gContXMean(
358 nregions, std::vector<float>(nsegments, 0.0));
359 std::vector<std::vector<float>> gContYMean(
360 nregions, std::vector<float>(nsegments, 0.0));
361 std::vector<std::vector<float>> oContEnergy(
362 nregions, std::vector<float>(nsegments, 0.0));
363 std::vector<std::vector<int>> oContNHits(nregions,
364 std::vector<int>(nsegments, 0));
365 std::vector<std::vector<float>> oContXMean(
366 nregions, std::vector<float>(nsegments, 0.0));
367 std::vector<std::vector<float>> oContYMean(
368 nregions, std::vector<float>(nsegments, 0.0));
369 std::vector<std::vector<float>> oContXStd(nregions,
370 std::vector<float>(nsegments, 0.0));
371 std::vector<std::vector<float>> oContYStd(nregions,
372 std::vector<float>(nsegments, 0.0));
373 std::vector<std::vector<float>> oContLayerMean(
374 nregions, std::vector<float>(nsegments, 0.0));
375 std::vector<std::vector<float>> oContLayerStd(
376 nregions, std::vector<float>(nsegments, 0.0));
377
378 // MIP tracking: vector of hits to be used in the MIP tracking algorithm. All
379 // hits inside the electron ROC (or all hits in the ECal if the event is
380 // missing an electron) will be included.
381 std::vector<HitData> trackingHitList;
382
383 if (verbose_) {
384 ldmx_log(debug)
385 << " Loop over the hits from the event to calculate the BDT features";
386 }
387
388 for (const ldmx::EcalHit &hit : ecalRecHits) {
389 // Layer-wise quantities
390 ldmx::EcalID id(hit.getID());
391 ecalLayerEdepRaw_[id.layer()] =
392 ecalLayerEdepRaw_[id.layer()] + hit.getEnergy();
393 if (id.layer() >= 20) ecalBackEnergy_ += hit.getEnergy();
394 if (maxCellDep_ < hit.getEnergy()) maxCellDep_ = hit.getEnergy();
395 if (hit.getEnergy() > 0) {
396 nReadoutHits_++;
397 ecalLayerEdepReadout_[id.layer()] += hit.getEnergy();
398 ecalLayerTime_[id.layer()] += (hit.getEnergy()) * hit.getTime();
399 auto [x, y, z] = geometry_->getPosition(id);
400 xMean += x * hit.getEnergy();
401 yMean += y * hit.getEnergy();
402 avgLayerHit_ += id.layer();
403 wavgLayerHit += id.layer() * hit.getEnergy();
404 if (deepestLayerHit_ < id.layer()) {
405 deepestLayerHit_ = id.layer();
406 }
407 XYCoords xy_pair = std::make_pair(x, y);
408 float distance_ele_trajectory =
409 ele_trajectory.size()
410 ? sqrt(
411 pow((xy_pair.first - ele_trajectory[id.layer()].first), 2) +
412 pow((xy_pair.second - ele_trajectory[id.layer()].second),
413 2))
414 : -1.0;
415 float distance_photon_trajectory =
416 photon_trajectory.size()
417 ? sqrt(
418 pow((xy_pair.first - photon_trajectory[id.layer()].first),
419 2) +
420 pow((xy_pair.second - photon_trajectory[id.layer()].second),
421 2))
422 : -1.0;
423
424 // Decide which longitudinal segment the hit is in and add to sums
425 for (unsigned int iseg = 0; iseg < nsegments; iseg++) {
426 if (id.layer() >= segLayers[iseg] &&
427 id.layer() <= segLayers[iseg + 1] - 1) {
428 energySeg[iseg] += hit.getEnergy();
429 xMeanSeg[iseg] += xy_pair.first * hit.getEnergy();
430 yMeanSeg[iseg] += xy_pair.second * hit.getEnergy();
431 layerMeanSeg[iseg] += id.layer() * hit.getEnergy();
432
433 // Decide which containment region the hit is in and add to sums
434 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
435 if (distance_ele_trajectory >= ireg * ele_radii[id.layer()] &&
436 distance_ele_trajectory < (ireg + 1) * ele_radii[id.layer()]) {
437 eContEnergy[ireg][iseg] += hit.getEnergy();
438 eContXMean[ireg][iseg] += xy_pair.first * hit.getEnergy();
439 eContYMean[ireg][iseg] += xy_pair.second * hit.getEnergy();
440 }
441 if (distance_photon_trajectory >= ireg * photon_radii[id.layer()] &&
442 distance_photon_trajectory <
443 (ireg + 1) * photon_radii[id.layer()]) {
444 gContEnergy[ireg][iseg] += hit.getEnergy();
445 gContNHits[ireg][iseg] += 1;
446 gContXMean[ireg][iseg] += xy_pair.first * hit.getEnergy();
447 gContYMean[ireg][iseg] += xy_pair.second * hit.getEnergy();
448 }
449 if (distance_ele_trajectory > (ireg + 1) * ele_radii[id.layer()] &&
450 distance_photon_trajectory >
451 (ireg + 1) * photon_radii[id.layer()]) {
452 oContEnergy[ireg][iseg] += hit.getEnergy();
453 oContNHits[ireg][iseg] += 1;
454 oContXMean[ireg][iseg] += xy_pair.first * hit.getEnergy();
455 oContYMean[ireg][iseg] += xy_pair.second * hit.getEnergy();
456 oContLayerMean[ireg][iseg] += id.layer() * hit.getEnergy();
457 }
458 }
459 }
460 }
461
462 // Decide which containment region the hit is in and add to sums
463 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
464 if (distance_ele_trajectory >= ireg * ele_radii[id.layer()] &&
465 distance_ele_trajectory < (ireg + 1) * ele_radii[id.layer()])
466 electronContainmentEnergy[ireg] += hit.getEnergy();
467 if (distance_photon_trajectory >= ireg * photon_radii[id.layer()] &&
468 distance_photon_trajectory < (ireg + 1) * photon_radii[id.layer()])
469 photonContainmentEnergy[ireg] += hit.getEnergy();
470 if (distance_ele_trajectory > (ireg + 1) * ele_radii[id.layer()] &&
471 distance_photon_trajectory >
472 (ireg + 1) * photon_radii[id.layer()]) {
473 outsideContainmentEnergy[ireg] += hit.getEnergy();
474 outsideContainmentNHits[ireg] += 1;
475 outsideContainmentXmean[ireg] += xy_pair.first * hit.getEnergy();
476 outsideContainmentYmean[ireg] += xy_pair.second * hit.getEnergy();
477 }
478 }
479
480 // MIP tracking: Decide whether hit should be added to trackingHitList
481 // If inside e- RoC or if etraj is missing, use the hit for tracking:
482 if (distance_ele_trajectory >= ele_radii[id.layer()] ||
483 distance_ele_trajectory == -1.0) {
484 HitData hd;
485 hd.pos = TVector3(xy_pair.first, xy_pair.second,
486 geometry_->getZPosition(id.layer()));
487 hd.layer = id.layer();
488 trackingHitList.push_back(hd);
489 }
490 }
491 } // end loop over rechits
492
493 for (const auto &[id, energy] : cellMapTightIso_) {
494 if (energy > 0) summedTightIso_ += energy;
495 }
496
497 for (int iLayer = 0; iLayer < ecalLayerEdepReadout_.size(); iLayer++) {
498 ecalLayerTime_[iLayer] =
499 ecalLayerTime_[iLayer] / ecalLayerEdepReadout_[iLayer];
500 summedDet_ += ecalLayerEdepReadout_[iLayer];
501 }
502
503 if (nReadoutHits_ > 0) {
504 avgLayerHit_ /= nReadoutHits_;
505 wavgLayerHit /= summedDet_;
506 xMean /= summedDet_;
507 yMean /= summedDet_;
508 } else {
509 wavgLayerHit = 0;
510 avgLayerHit_ = 0;
511 xMean = 0;
512 yMean = 0;
513 }
514
515 // If necessary, quotient out the total energy from the means
516 for (unsigned int iseg = 0; iseg < nsegments; iseg++) {
517 if (energySeg[iseg] > 0) {
518 xMeanSeg[iseg] /= energySeg[iseg];
519 yMeanSeg[iseg] /= energySeg[iseg];
520 layerMeanSeg[iseg] /= energySeg[iseg];
521 }
522 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
523 if (eContEnergy[ireg][iseg] > 0) {
524 eContXMean[ireg][iseg] /= eContEnergy[ireg][iseg];
525 eContYMean[ireg][iseg] /= eContEnergy[ireg][iseg];
526 }
527 if (gContEnergy[ireg][iseg] > 0) {
528 gContXMean[ireg][iseg] /= gContEnergy[ireg][iseg];
529 gContYMean[ireg][iseg] /= gContEnergy[ireg][iseg];
530 }
531 if (oContEnergy[ireg][iseg] > 0) {
532 oContXMean[ireg][iseg] /= oContEnergy[ireg][iseg];
533 oContYMean[ireg][iseg] /= oContEnergy[ireg][iseg];
534 oContLayerMean[ireg][iseg] /= oContEnergy[ireg][iseg];
535 }
536 }
537 }
538
539 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
540 if (outsideContainmentEnergy[ireg] > 0) {
541 outsideContainmentXmean[ireg] /= outsideContainmentEnergy[ireg];
542 outsideContainmentYmean[ireg] /= outsideContainmentEnergy[ireg];
543 }
544 }
545
546 // Loop over hits a second time to find the standard deviations.
547 for (const ldmx::EcalHit &hit : ecalRecHits) {
548 ldmx::EcalID id(hit.getID());
549 auto [x, y, z] = geometry_->getPosition(id);
550 if (hit.getEnergy() > 0) {
551 xStd_ += pow((x - xMean), 2) * hit.getEnergy();
552 yStd_ += pow((y - yMean), 2) * hit.getEnergy();
553 stdLayerHit_ += pow((id.layer() - wavgLayerHit), 2) * hit.getEnergy();
554 }
555 XYCoords xy_pair = std::make_pair(x, y);
556 float distance_ele_trajectory =
557 ele_trajectory.size()
558 ? sqrt(pow((xy_pair.first - ele_trajectory[id.layer()].first), 2) +
559 pow((xy_pair.second - ele_trajectory[id.layer()].second), 2))
560 : -1.0;
561 float distance_photon_trajectory =
562 photon_trajectory.size()
563 ? sqrt(pow((xy_pair.first - photon_trajectory[id.layer()].first),
564 2) +
565 pow((xy_pair.second - photon_trajectory[id.layer()].second),
566 2))
567 : -1.0;
568
569 for (unsigned int iseg = 0; iseg < nsegments; iseg++) {
570 if (id.layer() >= segLayers[iseg] &&
571 id.layer() <= segLayers[iseg + 1] - 1) {
572 xStdSeg[iseg] +=
573 pow((xy_pair.first - xMeanSeg[iseg]), 2) * hit.getEnergy();
574 yStdSeg[iseg] +=
575 pow((xy_pair.second - yMeanSeg[iseg]), 2) * hit.getEnergy();
576 layerStdSeg[iseg] +=
577 pow((id.layer() - layerMeanSeg[iseg]), 2) * hit.getEnergy();
578
579 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
580 if (distance_ele_trajectory > (ireg + 1) * ele_radii[id.layer()] &&
581 distance_photon_trajectory >
582 (ireg + 1) * photon_radii[id.layer()]) {
583 oContXStd[ireg][iseg] +=
584 pow((xy_pair.first - oContXMean[ireg][iseg]), 2) *
585 hit.getEnergy();
586 oContYStd[ireg][iseg] +=
587 pow((xy_pair.second - oContYMean[ireg][iseg]), 2) *
588 hit.getEnergy();
589 oContLayerStd[ireg][iseg] +=
590 pow((id.layer() - oContLayerMean[ireg][iseg]), 2) *
591 hit.getEnergy();
592 }
593 }
594 }
595 }
596
597 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
598 if (distance_ele_trajectory > (ireg + 1) * ele_radii[id.layer()] &&
599 distance_photon_trajectory > (ireg + 1) * photon_radii[id.layer()]) {
600 outsideContainmentXstd[ireg] +=
601 pow((xy_pair.first - outsideContainmentXmean[ireg]), 2) *
602 hit.getEnergy();
603 outsideContainmentYstd[ireg] +=
604 pow((xy_pair.second - outsideContainmentYmean[ireg]), 2) *
605 hit.getEnergy();
606 }
607 }
608 } // end loop over rechits (2nd time)
609
610 if (nReadoutHits_ > 0) {
611 xStd_ = sqrt(xStd_ / summedDet_);
612 yStd_ = sqrt(yStd_ / summedDet_);
613 stdLayerHit_ = sqrt(stdLayerHit_ / summedDet_);
614 } else {
615 xStd_ = 0;
616 yStd_ = 0;
617 stdLayerHit_ = 0;
618 }
619
620 // Quotient out the total energies from the standard deviations if possible
621 // and take root
622 for (unsigned int iseg = 0; iseg < nsegments; iseg++) {
623 if (energySeg[iseg] > 0) {
624 xStdSeg[iseg] = sqrt(xStdSeg[iseg] / energySeg[iseg]);
625 yStdSeg[iseg] = sqrt(yStdSeg[iseg] / energySeg[iseg]);
626 layerStdSeg[iseg] = sqrt(layerStdSeg[iseg] / energySeg[iseg]);
627 }
628 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
629 if (oContEnergy[ireg][iseg] > 0) {
630 oContXStd[ireg][iseg] =
631 sqrt(oContXStd[ireg][iseg] / oContEnergy[ireg][iseg]);
632 oContYStd[ireg][iseg] =
633 sqrt(oContYStd[ireg][iseg] / oContEnergy[ireg][iseg]);
634 oContLayerStd[ireg][iseg] =
635 sqrt(oContLayerStd[ireg][iseg] / oContEnergy[ireg][iseg]);
636 }
637 }
638 }
639
640 for (unsigned int ireg = 0; ireg < nregions; ireg++) {
641 if (outsideContainmentEnergy[ireg] > 0) {
642 outsideContainmentXstd[ireg] =
643 sqrt(outsideContainmentXstd[ireg] / outsideContainmentEnergy[ireg]);
644 outsideContainmentYstd[ireg] =
645 sqrt(outsideContainmentYstd[ireg] / outsideContainmentEnergy[ireg]);
646 }
647 }
648
649 if (verbose_) {
650 ldmx_log(debug) << " Find out if the recoil electron is fiducial";
651 }
652
653 // Find the location of the recoil electron
654 // Ecal face is not where the first layer starts,
655 // defined in DetDescr/python/EcalGeometry.py
656 const float dz_from_face{7.932};
657 float drifted_recoil_x{-9999.};
658 float drifted_recoil_y{-9999.};
659 if (recoilP.size() > 0) {
660 drifted_recoil_x =
661 (dz_from_face * (recoilP[0] / recoilP[2])) + recoilPos[0];
662 drifted_recoil_y =
663 (dz_from_face * (recoilP[1] / recoilP[2])) + recoilPos[1];
664 }
665 const int recoil_layer_index = 0;
666
667 // Check if it's fiducial
668 bool inside{false};
669 // At module level
670 const auto ecalID = geometry_->getID(drifted_recoil_x, drifted_recoil_y,
671 recoil_layer_index, true);
672 if (!ecalID.null()) {
673 // If fiducial at module level, check at cell level
674 const auto cellID =
675 geometry_->getID(drifted_recoil_x, drifted_recoil_y, recoil_layer_index,
676 ecalID.getModuleID(), true);
677 if (!cellID.null()) {
678 inside = true;
679 }
680 }
681
682 if (!inside) {
683 ldmx_log(info) << "This event is non-fiducial in ECAL";
684 }
685
686 // ------------------------------------------------------
687 // MIP tracking starts here
688
689 /* Goal: Calculate
690 * nStraightTracks (self-explanatory),
691 * nLinregTracks (tracks found by linreg algorithm),
692 */
693
694 // Find epAng and epSep, and prepare EP trajectory vectors:
695 TVector3 e_traj_start;
696 TVector3 e_traj_end;
697 TVector3 p_traj_start;
698 TVector3 p_traj_end;
699 if (ele_trajectory.size() > 0 && photon_trajectory.size() > 0) {
700 // Create TVector3s marking the start and endpoints of each projected
701 // trajectory
702 e_traj_start.SetXYZ(ele_trajectory[0].first, ele_trajectory[0].second,
704 e_traj_end.SetXYZ(ele_trajectory[(nEcalLayers_ - 1)].first,
705 ele_trajectory[(nEcalLayers_ - 1)].second,
706 geometry_->getZPosition((nEcalLayers_ - 1)));
707 p_traj_start.SetXYZ(photon_trajectory[0].first, photon_trajectory[0].second,
709 p_traj_end.SetXYZ(photon_trajectory[(nEcalLayers_ - 1)].first,
710 photon_trajectory[(nEcalLayers_ - 1)].second,
711 geometry_->getZPosition((nEcalLayers_ - 1)));
712
713 TVector3 evec = e_traj_end - e_traj_start;
714 TVector3 e_norm = evec.Unit();
715 TVector3 pvec = p_traj_end - p_traj_start;
716 TVector3 p_norm = pvec.Unit();
717 epDot_ = e_norm.Dot(p_norm);
718 epAng_ = acos(epDot_) * 180.0 / M_PI;
719 epSep_ = sqrt(pow(e_traj_start.X() - p_traj_start.X(), 2) +
720 pow(e_traj_start.Y() - p_traj_start.Y(), 2));
721 } else {
722 // Electron trajectory is missing, so all hits in the Ecal are fair game.
723 // Pick e/ptraj so that they won't restrict the tracking algorithm (place
724 // them far outside the ECal).
725 e_traj_start = TVector3(999, 999, geometry_->getZPosition(0)); // 0);
726 e_traj_end = TVector3(
727 999, 999, geometry_->getZPosition((nEcalLayers_ - 1))); // 999);
728 p_traj_start = TVector3(1000, 1000, geometry_->getZPosition(0)); // 0);
729 p_traj_end = TVector3(
730 1000, 1000, geometry_->getZPosition((nEcalLayers_ - 1))); // 1000);
731 /*ensures event will not be vetoed by angle/separation cut */
732 epAng_ = 999.;
733 epSep_ = 999.;
734 epDot_ = 999.;
735 }
736
737 // Near photon step: Find the first layer of the ECal where a hit near the
738 // projected photon trajectory is found Currently unusued pending further
739 // study; performance has dropped between v9 and v12. Currently used in
740 // segmipBDT
741 firstNearPhLayer_ = nEcalLayers_ - 1;
742
743 if (photon_trajectory.size() !=
744 0) { // If no photon trajectory, leave this at the default (ECal back)
745 for (std::vector<HitData>::iterator it = trackingHitList.begin();
746 it != trackingHitList.end(); ++it) {
747 float ehDist =
748 sqrt(pow((*it).pos.X() - photon_trajectory[(*it).layer].first, 2) +
749 pow((*it).pos.Y() - photon_trajectory[(*it).layer].second, 2));
750 if (ehDist < 8.7) {
751 nNearPhHits_++;
752 if ((*it).layer < firstNearPhLayer_) {
753 firstNearPhLayer_ = (*it).layer;
754 }
755 }
756 }
757 }
758
759 // Territories limited to trackingHitList
760 TVector3 gToe = (e_traj_start - p_traj_start).Unit();
761 TVector3 origin = p_traj_start + 0.5 * 8.7 * gToe;
762 if (ele_trajectory.size() > 0) {
763 for (auto &hitData : trackingHitList) {
764 TVector3 hitPos = hitData.pos;
765 TVector3 hitPrime = hitPos - origin;
766 if (hitPrime.Dot(gToe) <= 0) {
768 }
769 }
770 } else {
771 photonTerritoryHits_ = nReadoutHits_;
772 }
773
774 // ------------------------------------------------------
775 // Find straight MIP tracks:
776
777 std::sort(trackingHitList.begin(), trackingHitList.end(),
778 [](HitData ha, HitData hb) { return ha.layer > hb.layer; });
779 // For merging tracks: Need to keep track of existing tracks
780 // Candidate tracks to merge in will always be in front of the current track
781 // (lower z), so only store the last hit 3-layer vector: each track = vector
782 // of 3-tuples (xy+layer).
783 std::vector<std::vector<HitData>> track_list;
784
785 // print trackingHitList
786 if (verbose_) {
787 ldmx_log(debug) << "====== Tracking hit list (original) length "
788 << trackingHitList.size() << " ======";
789 for (int i = 0; i < trackingHitList.size(); i++) {
790 std::cout << "[" << trackingHitList[i].pos.X() << ", "
791 << trackingHitList[i].pos.Y() << ", "
792 << trackingHitList[i].layer << "], ";
793 }
794 std::cout << std::endl;
795 ldmx_log(debug) << "====== END OF Tracking hit list ======";
796 }
797
798 // in v14 minR is 4.17 mm
799 // while maxR is 4.81 mm
800 float cellWidth = 2 * geometry_->getCellMaxR();
801 for (int iHit = 0; iHit < trackingHitList.size(); iHit++) {
802 // list of hit numbers in track (34 = maximum theoretical length)
803 int track[34];
804 int currenthit{iHit};
805 int trackLen{1};
806
807 track[0] = iHit;
808
809 // Search for hits to add to the track:
810 // repeatedly find hits in the front two layers with same x & y positions
811 // but since v14 the odd layers are offset, so we allow half a cellWidth
812 // deviation and then add to track until no more hits are found
813 int jHit = iHit;
814 while (jHit < trackingHitList.size()) {
815 if ((trackingHitList[jHit].layer ==
816 trackingHitList[currenthit].layer - 1 ||
817 trackingHitList[jHit].layer ==
818 trackingHitList[currenthit].layer - 2) &&
819 abs(trackingHitList[jHit].pos.X() -
820 trackingHitList[currenthit].pos.X()) <= 0.5 * cellWidth &&
821 abs(trackingHitList[jHit].pos.Y() -
822 trackingHitList[currenthit].pos.Y()) <= 0.5 * cellWidth) {
823 track[trackLen] = jHit;
824 trackLen++;
825 currenthit = jHit;
826 }
827 jHit++;
828 }
829
830 // Confirm that the track is valid:
831 if (trackLen < 2) continue; // Track must contain at least 2 hits
832 float closest_e = distTwoLines(trackingHitList[track[0]].pos,
833 trackingHitList[track[trackLen - 1]].pos,
834 e_traj_start, e_traj_end);
835 float closest_p = distTwoLines(trackingHitList[track[0]].pos,
836 trackingHitList[track[trackLen - 1]].pos,
837 p_traj_start, p_traj_end);
838 // Make sure that the track is near the photon trajectory and away from the
839 // electron trajectory Details of these constraints may be revised
840 if (closest_p > cellWidth and closest_e < 2 * cellWidth) continue;
841 if (trackLen < 4 and closest_e > closest_p) continue;
842 if (verbose_) {
843 ldmx_log(debug) << "====== After rejection for MIP tracking ======";
844 ldmx_log(debug) << "current hit: [" << trackingHitList[iHit].pos.X()
845 << ", " << trackingHitList[iHit].pos.Y() << ", "
846 << trackingHitList[iHit].layer << "]";
847
848 for (int k = 0; k < trackLen; k++) {
849 ldmx_log(debug) << "track[" << k << "] position = ["
850 << trackingHitList[track[k]].pos.X() << ", "
851 << trackingHitList[track[k]].pos.Y() << ", "
852 << trackingHitList[track[k]].layer << "]";
853 }
854 }
855
856 // if track found, increment nStraightTracks and remove all hits in track
857 // from future consideration
858 if (trackLen >= 2) {
859 std::vector<HitData> temp_track_list;
860 int n_remove = 0;
861 for (int kHit = 0; kHit < trackLen; kHit++) {
862 temp_track_list.push_back(trackingHitList[track[kHit] - n_remove]);
863 trackingHitList.erase(trackingHitList.begin() + track[kHit] - n_remove);
864 n_remove++;
865 }
866 // print trackingHitList
867 if (verbose_) {
868 ldmx_log(debug) << "====== Tracking hit list (after erase) length "
869 << trackingHitList.size() << " ======";
870 for (int i = 0; i < trackingHitList.size(); i++) {
871 std::cout << "[" << trackingHitList[i].pos.X() << ", "
872 << trackingHitList[i].pos.Y() << ", "
873 << trackingHitList[i].layer << "] ";
874 }
875 std::cout << std::endl;
876 ldmx_log(debug) << "====== END OF Tracking hit list ======";
877 }
878
879 track_list.push_back(temp_track_list);
880 // The *current* hit will have been removed, so iHit is currently pointing
881 // to the next hit. Decrement iHit so no hits will get skipped by iHit++
882 iHit--;
883 }
884 }
885
886 ldmx_log(debug) << "Straight tracks found (before merge): "
887 << track_list.size();
888 if (verbose_) {
889 for (int iTrack = 0; iTrack < track_list.size(); iTrack++) {
890 ldmx_log(debug) << "Track " << iTrack << ":";
891 for (int iHit = 0; iHit < track_list[iTrack].size(); iHit++) {
892 std::cout << " Hit " << iHit << ": ["
893 << track_list[iTrack][iHit].pos.X() << ", "
894 << track_list[iTrack][iHit].pos.Y() << ", "
895 << track_list[iTrack][iHit].layer << "]" << std::endl;
896 }
897 std::cout << std::endl;
898 }
899 }
900
901 // Optional addition: Merge nearby straight tracks. Not necessary for veto.
902 // Criteria: consider tail of track. Merge if head of next track is 1/2
903 // layers behind, within 1 cell of xy position.
904 ldmx_log(debug) << "Beginning track merging using " << track_list.size()
905 << " tracks";
906
907 for (int track_i = 0; track_i < track_list.size(); track_i++) {
908 // for each track, check the remainder of the track list for compatible
909 // tracks
910 std::vector<HitData> base_track = track_list[track_i];
911 HitData tail_hitdata = base_track.back(); // xylayer of last hit in track
912 if (verbose_) ldmx_log(debug) << " Considering track " << track_i;
913 for (int track_j = track_i + 1; track_j < track_list.size(); track_j++) {
914 std::vector<HitData> checking_track = track_list[track_j];
915 HitData head_hitdata = checking_track.front();
916 // if 1-2 layers behind, and xy within one cell...
917 if ((head_hitdata.layer == tail_hitdata.layer + 1 ||
918 head_hitdata.layer == tail_hitdata.layer + 2) &&
919 pow(pow(head_hitdata.pos.X() - tail_hitdata.pos.X(), 2) +
920 pow(head_hitdata.pos.Y() - tail_hitdata.pos.Y(), 2),
921 0.5) <= cellWidth) {
922 // ...then append the second track to the first one and delete it
923 // NOTE: TO ADD: (trackingHitList[iHit].pos -
924 // trackingHitList[jHit].pos).Mag()
925 if (verbose_) {
926 ldmx_log(debug) << " ** Compatible track found at index "
927 << track_j;
928 ldmx_log(debug) << " Tail xylayer: " << head_hitdata.pos.X()
929 << "," << head_hitdata.pos.Y() << ","
930 << head_hitdata.layer;
931 ldmx_log(debug) << " Head xylayer: " << tail_hitdata.pos.X()
932 << "," << tail_hitdata.pos.Y() << ","
933 << tail_hitdata.layer;
934 }
935 for (int hit_k = 0; hit_k < checking_track.size(); hit_k++) {
936 base_track.push_back(track_list[track_j][hit_k]);
937 }
938 track_list[track_i] = base_track;
939 track_list.erase(track_list.begin() + track_j);
940 break;
941 }
942 }
943 }
944 nStraightTracks_ = track_list.size();
945 // print the track list
946 ldmx_log(debug) << "Straight tracks found (after merge): "
948 for (int track_i = 0; track_i < track_list.size(); track_i++) {
949 ldmx_log(debug) << "Track " << track_i << ":";
950 for (int hit_i = 0; hit_i < track_list[track_i].size(); hit_i++) {
951 ldmx_log(debug) << " Hit " << hit_i << ": ["
952 << track_list[track_i][hit_i].pos.X() << ", "
953 << track_list[track_i][hit_i].pos.Y() << ", "
954 << track_list[track_i][hit_i].layer << "]";
955 }
956 }
957
958 // ------------------------------------------------------
959 // Linreg tracking:
960 ldmx_log(debug) << "Finding linreg tracks from " << trackingHitList.size()
961 << " hits";
962
963 for (int iHit = 0; iHit < trackingHitList.size(); iHit++) {
964 int track[34];
965 int trackLen{0};
966 // Hits being considered at one time
967 // TODO: This is currently not used, but it really should be!
968 // int hitsInRegion[50];
969 // Number of hits under consideration
970 int nHitsInRegion{1};
971 TMatrixD Vm(3, 3);
972 TMatrixD hdt(3, 3);
973 TVector3 slopeVec;
974 TVector3 hmean;
975 TVector3 hpoint;
976 float r_corr_best{0.0};
977 int hitNums[3];
978
979 // hitsInRegion[0] = iHit; // TODO
980 // Find all hits within 2 cells of the primary hit:
981 for (int jHit = 0; jHit < trackingHitList.size(); jHit++) {
982 // Dont try to put hits on the same layer to the lin-reg track
983 if (trackingHitList[iHit].pos(2) == trackingHitList[jHit].pos(2)) {
984 continue;
985 }
986 float dstToHit =
987 (trackingHitList[iHit].pos - trackingHitList[jHit].pos).Mag();
988 // This distance needs to be optimized in a future study //TODO
989 // Current 2*cellWidth has no particular meaning
990 if (dstToHit <= 2 * cellWidth) {
991 // hitsInRegion[nHitsInRegion] = jHit; // TODO
992 nHitsInRegion++;
993 }
994 }
995
996 // Look at combinations of hits within the region (do not consider the same
997 // combination twice):
998 hitNums[0] = iHit;
999 for (int jHit = 1; jHit < nHitsInRegion - 1; jHit++) {
1000 if (trackingHitList.size() < 3) break;
1001 hitNums[1] = jHit;
1002 for (int kHit = jHit + 1; kHit < nHitsInRegion; kHit++) {
1003 hitNums[2] = kHit;
1004 for (int hInd = 0; hInd < 3; hInd++) {
1005 // hmean = geometric mean, subtract off from hits to improve SVD
1006 // performance
1007 hmean(hInd) = (trackingHitList[hitNums[0]].pos(hInd) +
1008 trackingHitList[hitNums[1]].pos(hInd) +
1009 trackingHitList[hitNums[2]].pos(hInd)) /
1010 3.0;
1011 }
1012 for (int hInd = 0; hInd < 3; hInd++) {
1013 for (int lInd = 0; lInd < 3; lInd++) {
1014 hdt(hInd, lInd) =
1015 trackingHitList[hitNums[hInd]].pos(lInd) - hmean(lInd);
1016 }
1017 }
1018
1019 // Perform "linreg" on selected points
1020 // Calculate the determinant of the matrix
1021 double determinant =
1022 hdt(0, 0) * (hdt(1, 1) * hdt(2, 2) - hdt(1, 2) * hdt(2, 1)) -
1023 hdt(0, 1) * (hdt(1, 0) * hdt(2, 2) - hdt(1, 2) * hdt(2, 0)) +
1024 hdt(0, 2) * (hdt(1, 0) * hdt(2, 1) - hdt(1, 1) * hdt(2, 0));
1025 // Exit early if the matrix is singular (i.e. det = 0)
1026 if (determinant == 0) continue;
1027 // Perform matrix decomposition with SVD
1028 TDecompSVD svdObj(hdt);
1029 bool decomposed = svdObj.Decompose();
1030 if (!decomposed) continue;
1031
1032 // First col of V matrix is the slope of the best-fit line
1033 Vm = svdObj.GetV();
1034 for (int hInd = 0; hInd < 3; hInd++) {
1035 slopeVec(hInd) = Vm[0][hInd];
1036 }
1037 // hmean, hpoint are points on the best-fit line
1038 hpoint = slopeVec + hmean;
1039 // linreg complete: Now have best-fit line for 3 hits under
1040 // consideration Check whether the track is valid: r^2 must be high,
1041 // and the track must plausibly originate from the photon
1042 float closest_e = distTwoLines(hmean, hpoint, e_traj_start, e_traj_end);
1043 float closest_p = distTwoLines(hmean, hpoint, p_traj_start, p_traj_end);
1044 // Projected track must be close to the photon; details may change after
1045 // future study.
1046 if (closest_p > cellWidth or closest_e < 1.5 * cellWidth) continue;
1047 // find r^2
1048 // ~variance
1049 float vrnc = (trackingHitList[hitNums[0]].pos - hmean).Mag() +
1050 (trackingHitList[hitNums[1]].pos - hmean).Mag() +
1051 (trackingHitList[hitNums[2]].pos - hmean).Mag();
1052 // sum of |errors|
1053 float sumerr =
1054 distPtToLine(trackingHitList[hitNums[0]].pos, hmean, hpoint) +
1055 distPtToLine(trackingHitList[hitNums[1]].pos, hmean, hpoint) +
1056 distPtToLine(trackingHitList[hitNums[2]].pos, hmean, hpoint);
1057 float r_corr = 1 - sumerr / vrnc;
1058 // Check whether r^2 exceeds a low minimum r_corr: "Fake" tracks are
1059 // still much more common in background, so making the algorithm
1060 // oversensitive doesn't lower performance significantly
1061 if (r_corr > r_corr_best and r_corr > .6) {
1062 r_corr_best = r_corr;
1063 trackLen = 0;
1064 // Only looking for 3-hit tracks currently
1065 for (int k = 0; k < 3; k++) {
1066 track[k] = hitNums[k];
1067 trackLen++;
1068 }
1069 }
1070 } // end loop on hits in the region
1071 } // end 2nd loop on hits in the region
1072
1073 // Continue early if not hits on track
1074 if (trackLen == 0) continue;
1075
1076 // Ordinarily, additional hits in line w/ track would be added here.
1077 // However, this doesn't affect the results of the simple veto. Exclude all
1078 // hits in a found track from further consideration:
1079 if (trackLen >= 2) {
1081 for (int kHit = 0; kHit < trackLen; kHit++) {
1082 trackingHitList.erase(trackingHitList.begin() + track[kHit]);
1083 }
1084 iHit--;
1085 }
1086 } // end loop on all hits
1087
1088 ldmx_log(info) << " MIP tracking completed; found " << nStraightTracks_
1089 << " straight tracks and " << nLinregTracks_
1090 << " lin-reg tracks";
1091
1092 result.setVariables(
1093 nReadoutHits_, deepestLayerHit_, summedDet_, summedTightIso_, maxCellDep_,
1094 showerRMS_, xStd_, yStd_, avgLayerHit_, stdLayerHit_, ecalBackEnergy_,
1096 photonTerritoryHits_, epAng_, epSep_, epDot_, electronContainmentEnergy,
1097 photonContainmentEnergy, outsideContainmentEnergy,
1098 outsideContainmentNHits, outsideContainmentXstd, outsideContainmentYstd,
1099 energySeg, xMeanSeg, yMeanSeg, xStdSeg, yStdSeg, layerMeanSeg,
1100 layerStdSeg, eContEnergy, eContXMean, eContYMean, gContEnergy, gContNHits,
1101 gContXMean, gContYMean, oContEnergy, oContNHits, oContXMean, oContYMean,
1102 oContXStd, oContYStd, oContLayerMean, oContLayerStd,
1103 ecalLayerEdepReadout_, recoilP, recoilPos);
1104
1105 buildBDTFeatureVector(result);
1106 ldmx::Ort::FloatArrays inputs({bdtFeatures_});
1107 float pred = rt_->run({featureListName_}, inputs, {"probabilities"})[0].at(1);
1108 // Other considerations were (nLinregTracks_ == 0) && (firstNearPhLayer_ >=
1109 // 6)
1110 // && (epAng_ > 3.0 && epAng_ < 900 || epSep_ > 10.0 && epSep_ < 900)
1111 bool passesTrackingVeto = (nStraightTracks_ < 3);
1112 result.setVetoResult(pred > bdtCutVal_ && passesTrackingVeto);
1113 result.setDiscValue(pred);
1114 ldmx_log(debug) << " The pred > bdtCutVal = " << (pred > bdtCutVal_);
1115
1116 // Persist in the event if the recoil ele is fiducial
1117 result.setFiducial(inside);
1118
1119 // If the event passes the veto, keep it. Otherwise,
1120 // drop the event.
1121 if (result.passesVeto()) {
1123 } else {
1125 }
1126
1127 event.add(collectionName_, result);
1128}
1129
1130/* Function to calculate the energy weighted shower centroid */
1131ldmx::EcalID EcalVetoProcessor::GetShowerCentroidIDAndRMS(
1132 const std::vector<ldmx::EcalHit> &ecalRecHits, double &showerRMS) {
1133 auto wgtCentroidCoords = std::make_pair<float, float>(0., 0.);
1134 float sumEdep = 0;
1135 ldmx::EcalID returnCellId;
1136
1137 // Calculate Energy Weighted Centroid
1138 for (const ldmx::EcalHit &hit : ecalRecHits) {
1139 ldmx::EcalID id(hit.getID());
1140 CellEnergyPair cell_energy_pair = std::make_pair(id, hit.getEnergy());
1141 auto [x, y, z] = geometry_->getPosition(id);
1142 XYCoords centroidCoords = std::make_pair(x, y);
1143 wgtCentroidCoords.first = wgtCentroidCoords.first +
1144 centroidCoords.first * cell_energy_pair.second;
1145 wgtCentroidCoords.second = wgtCentroidCoords.second +
1146 centroidCoords.second * cell_energy_pair.second;
1147 sumEdep += cell_energy_pair.second;
1148 }
1149 wgtCentroidCoords.first = (sumEdep > 1E-6) ? wgtCentroidCoords.first / sumEdep
1150 : wgtCentroidCoords.first;
1151 wgtCentroidCoords.second = (sumEdep > 1E-6)
1152 ? wgtCentroidCoords.second / sumEdep
1153 : wgtCentroidCoords.second;
1154 // Find Nearest Cell to Centroid
1155 float maxDist = 1e6;
1156 for (const ldmx::EcalHit &hit : ecalRecHits) {
1157 auto [x, y, z] = geometry_->getPosition(hit.getID());
1158 XYCoords centroidCoords = std::make_pair(x, y);
1159
1160 float deltaR =
1161 pow(pow((centroidCoords.first - wgtCentroidCoords.first), 2) +
1162 pow((centroidCoords.second - wgtCentroidCoords.second), 2),
1163 .5);
1164 showerRMS += deltaR * hit.getEnergy();
1165 if (deltaR < maxDist) {
1166 maxDist = deltaR;
1167 returnCellId = ldmx::EcalID(hit.getID());
1168 }
1169 }
1170 if (sumEdep > 0) showerRMS = showerRMS / sumEdep;
1171 // flatten
1172 return ldmx::EcalID(0, returnCellId.module(), returnCellId.cell());
1173}
1174
1179 const std::vector<ldmx::EcalHit> &ecalRecHits,
1180 std::map<ldmx::EcalID, float> &cellMap) {
1181 for (const ldmx::EcalHit &hit : ecalRecHits) {
1182 ldmx::EcalID id(hit.getID());
1183 cellMap.emplace(id, hit.getEnergy());
1184 }
1185}
1186
1187void EcalVetoProcessor::fillIsolatedHitMap(
1188 const std::vector<ldmx::EcalHit> &ecalRecHits, ldmx::EcalID globalCentroid,
1189 std::map<ldmx::EcalID, float> &cellMap,
1190 std::map<ldmx::EcalID, float> &cellMapIso, bool doTight) {
1191 for (const ldmx::EcalHit &hit : ecalRecHits) {
1192 auto isolatedHit = std::make_pair(true, ldmx::EcalID());
1193 ldmx::EcalID id(hit.getID());
1194 if (doTight) {
1195 // Disregard hits that are on the centroid.
1196 if (id == globalCentroid) continue;
1197
1198 // Skip hits that are on centroid inner ring
1199 if (geometry_->isNN(globalCentroid, id)) {
1200 continue;
1201 }
1202 }
1203
1204 // Skip hits that have a readout neighbor
1205 // Get neighboring cell id's and try to look them up in the full cell map
1206 // (constant speed algo.)
1207 // these ideas are only cell/module (must ignore layer)
1208 std::vector<ldmx::EcalID> cellNbrIds = geometry_->getNN(id);
1209
1210 for (int k = 0; k < cellNbrIds.size(); k++) {
1211 // update neighbor ID to the current layer
1212 cellNbrIds[k] = ldmx::EcalID(id.layer(), cellNbrIds[k].module(),
1213 cellNbrIds[k].cell());
1214 // look in cell hit map to see if it is there
1215 if (cellMap.find(cellNbrIds[k]) != cellMap.end()) {
1216 isolatedHit = std::make_pair(false, cellNbrIds[k]);
1217 break;
1218 }
1219 }
1220 if (!isolatedHit.first) {
1221 continue;
1222 }
1223 // Insert isolated hit
1224 cellMapIso.emplace(id, hit.getEnergy());
1225 }
1226}
1227
1228/* Calculate where trajectory intersects ECAL layers using position and momentum
1229 * at scoring plane */
1230std::vector<std::pair<float, float>> EcalVetoProcessor::getTrajectory(
1231 std::vector<double> momentum, std::vector<float> position) {
1232 std::vector<XYCoords> positions;
1233 for (int iLayer = 0; iLayer < nEcalLayers_; iLayer++) {
1234 float posX =
1235 position[0] + (momentum[0] / momentum[2]) *
1236 (geometry_->getZPosition(iLayer) - position[2]);
1237 float posY =
1238 position[1] + (momentum[1] / momentum[2]) *
1239 (geometry_->getZPosition(iLayer) - position[2]);
1240 positions.push_back(std::make_pair(posX, posY));
1241 }
1242 return positions;
1243}
1244
1245// MIP tracking functions:
1246
1247float EcalVetoProcessor::distTwoLines(TVector3 v1, TVector3 v2, TVector3 w1,
1248 TVector3 w2) {
1249 TVector3 e1 = v1 - v2;
1250 TVector3 e2 = w1 - w2;
1251 TVector3 crs = e1.Cross(e2);
1252 if (crs.Mag() == 0) {
1253 return 100.0; // arbitrary large number; edge case that shouldn't cause
1254 // problems.
1255 } else {
1256 return std::abs(crs.Dot(v1 - w1) / crs.Mag());
1257 }
1258}
1259
1260float EcalVetoProcessor::distPtToLine(TVector3 h1, TVector3 p1, TVector3 p2) {
1261 return ((h1 - p1).Cross(h1 - p2)).Mag() / (p1 - p2).Mag();
1262}
1263
1264} // namespace ecal
1265
1266DECLARE_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.
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 buildBDTFeatureVector(const ldmx::EcalVetoResult &result)
int photonTerritoryHits_
Number of hits in the photon territory.
float epAng_
Angular separation between the projected photon and electron trajectories (currently unused)
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.
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 fillHitMap(const std::vector< ldmx::EcalHit > &ecalRecHits, std::map< ldmx::EcalID, float > &cellMap_)
Function to load up empty vector of hit maps.
int nNearPhHits_
Number of hits near the photon trajectory.
const ldmx::EcalGeometry * geometry_
handle to current geometry (to share with member functions)
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:41
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:27
T getParameter(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:89
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
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 epSep, float epDot, 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::vector< double > recoilP, std::vector< float > recoilPos)
Set the sim particle and 'is findable' flag.
bool passesVeto() const
Checks if the event passes the Ecal veto.
int getNStraightTracks() const
Number of straight tracks found.
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.
constexpr StorageControl::Hint hint_shouldKeep
storage control hint alias for backwards compatibility
constexpr StorageControl::Hint hint_shouldDrop
storage control hint alias for backwards compatibility