LDMX Software
TruthSeedProcessor.cxx
1#include "Tracking/Reco/TruthSeedProcessor.h"
2
3#include "Tracking/Sim/GeometryContainers.h"
4
5namespace tracking::reco {
6
8 framework::Process& process)
9 : TrackingGeometryUser(name, process) {}
10
12 gctx_ = Acts::GeometryContext();
13 normal_ = std::make_shared<std::normal_distribution<float>>(0., 1.);
14
15 Acts::StraightLineStepper stepper;
16 Acts::Navigator::Config nav_cfg{geometry().getTG()};
17 const Acts::Navigator navigator(nav_cfg);
18
19 linpropagator_ = std::make_shared<LinPropagator>(stepper, navigator);
20 trk_extrap_ = std::make_shared<std::decay_t<decltype(*trk_extrap_)>>(
21 *linpropagator_, geometryContext(), magneticFieldContext());
22}
23
26 parameters.get<std::string>("scoring_hits_coll_name");
27 sp_pass_name_ = parameters.get<std::string>("sp_pass_name");
29 parameters.get<std::string>("recoil_sim_hits_coll_name");
31 parameters.get<std::string>("tagger_sim_hits_coll_name");
32 input_pass_name_ = parameters.get<std::string>("input_pass_name");
33 sim_particles_passname_ =
34 parameters.get<std::string>("sim_particles_passname");
35
36 n_min_hits_tagger_ = parameters.get<int>("n_min_hits_tagger", 11);
37 n_min_hits_recoil_ = parameters.get<int>("n_min_hits_recoil", 7);
38 pdg_ids_ = parameters.get<std::vector<int>>("pdg_ids", {11});
39 z_min_ = parameters.get<double>("z_min", -9999); // mm
40 track_id_ = parameters.get<int>("track_id", -9999);
41 pz_cut_ = parameters.get<double>("pz_cut", -9999); // MeV
42 p_cut_ = parameters.get<double>("p_cut", 0.);
43 p_cut_max_ = parameters.get<double>("p_cut_max", 100000.); // MeV
44 p_cut_ecal_ = parameters.get<double>("p_cut_ecal", -1.); // MeV
45 recoil_sp_ = parameters.get<double>("recoil_sp", true);
46 target_sp_ = parameters.get<double>("tagger_sp", true);
47 seed_smearing_ = parameters.get<bool>("seedSmearing", false);
48 max_track_id_ = parameters.get<int>("max_track_id", 5);
49
50 ldmx_log(info) << "Seed Smearing is set to " << seed_smearing_;
51
52 d0smear_ = parameters.get<std::vector<double>>("d0smear", {0.01, 0.01, 0.01});
53 z0smear_ = parameters.get<std::vector<double>>("z0smear", {0.1, 0.1, 0.1});
54 phismear_ = parameters.get<double>("phismear", 0.001);
55 thetasmear_ = parameters.get<double>("thetasmear", 0.001);
56 relpsmear_ = parameters.get<double>("relpsmear", 0.1);
57
58 // Relative smear factor terms, only used if seed_smearing_ is true.
59 rel_smearfactors_ = parameters.get<std::vector<double>>(
60 "rel_smearfactors", {0.1, 0.1, 0.1, 0.1, 0.1, 0.1});
61 inflate_factors_ = parameters.get<std::vector<double>>(
62 "inflate_factors", {10., 10., 10., 10., 10., 10.});
63
64 // In tracking frame: where do these numbers come from?
65 // These numbers come from approximating the path of the beam up
66 // until it is about to enter the first detector volume (TriggerPad1).
67 // In detector coordinates, (x_,y_,z_) = (-21.7, -883) is
68 // where the beam arrives (if no smearing is applied) and we simply
69 // reorder these values so that they are in tracking coordinates.
70 beam_origin_ = parameters.get<std::vector<double>>("beamOrigin",
71 {-883.0, -21.745876, 0.0});
72
73 // Skip the tagger or recoil trackers if wanted
74 skip_tagger_ = parameters.get<bool>("skip_tagger", false);
75 skip_recoil_ = parameters.get<bool>("skip_recoil", false);
76 particle_hypothesis_ = parameters.get<int>("particle_hypothesis");
77}
78
80 const ldmx::SimParticle& particle, const ldmx::SimTrackerHit& hit,
81 ldmx::Track& trk, const std::shared_ptr<Acts::Surface>& target_surface) {
82 std::vector<double> pos{static_cast<double>(hit.getPosition()[0]),
83 static_cast<double>(hit.getPosition()[1]),
84 static_cast<double>(hit.getPosition()[2])};
85 createTruthTrack(pos, hit.getMomentum(), particle.getCharge(), trk,
86 target_surface);
87
88 trk.setTrackID(hit.getTrackID());
89 trk.setPdgID(hit.getPdgID());
90}
91
93 const ldmx::SimParticle& particle, ldmx::Track& trk,
94 const std::shared_ptr<Acts::Surface>& target_surface) {
95 createTruthTrack(particle.getVertex(), particle.getMomentum(),
96 particle.getCharge(), trk, target_surface);
97
98 trk.setPdgID(particle.getPdgID());
99}
100
102 const std::vector<double>& pos_vec, const std::vector<double>& p_vec,
103 int charge, ldmx::Track& trk,
104 const std::shared_ptr<Acts::Surface>& target_surface) {
105 // Get the position and momentum of the particle at the point of creation.
106 // This only works for the incident electron when creating a tagger tracker
107 // seed. For the recoil tracker, the scoring plane position will need to
108 // be used. For other particles created in the target or tracker planes,
109 // this version of the method can also be used.
110 // These are just Eigen vectors defined as
111 // Eigen::Matrix<double, kSize, 1>;
112 Acts::Vector3 pos{pos_vec[0], pos_vec[1], pos_vec[2]};
113 Acts::Vector3 mom{p_vec[0], p_vec[1], p_vec[2]};
114
115 // Rotate the position and momentum into the ACTS frame.
116 pos = tracking::sim::utils::ldmx2Acts(pos);
117 mom = tracking::sim::utils::ldmx2Acts(mom);
118
119 // Get the charge of the particle.
120 // TODO: Add function that uses the PDG ID to calculate this.
121 double q{charge * Acts::UnitConstants::e};
122
123 // The idea here is:
124 // 1 - Define a bound track state parameters at point P on track. Basically a
125 // curvilinear representation. 2 - Propagate to target surface to obtain the
126 // BoundTrackState there.
127
128 // Transform the position, momentum and charge to free parameters.
129 auto free_params{tracking::sim::utils::toFreeParameters(pos, mom, q)};
130
131 // Create a line surface at the perigee. The perigee position is extracted
132 // from a particle's vertex or the particle's position at a specific
133 // scoring plane.
134 auto gen_surface{Acts::Surface::makeShared<Acts::PerigeeSurface>(
135 Acts::Vector3(free_params[Acts::eFreePos0], free_params[Acts::eFreePos1],
136 free_params[Acts::eFreePos2]))};
137
138 // ldmx_log(trace)<<"PF:: gen_surface"<<free_params[Acts::eFreePos0]<<"
139 // " <<free_params[Acts::eFreePos1]<<" " <<free_params[Acts::eFreePos2];
140
141 // Transform the parameters to local positions on the perigee surface.
142 auto bound_params{
143 Acts::transformFreeToBoundParameters(free_params, *gen_surface, gctx_)
144 .value()};
145 // Create a particle hypothesis
146 auto part{Acts::GenericParticleHypothesis(
147 Acts::ParticleHypothesis(Acts::PdgParticle(particle_hypothesis_)))};
148 Acts::BoundTrackParameters bound_trk_pars(gen_surface, bound_params,
149 std::nullopt, part);
150
151 // CAUTION:: The target surface should be close to the gen surface
152 // Linear propagation to the target surface. I assume 1mm of tolerance
153 Acts::Vector3 tgt_surf_center = target_surface->center(geometryContext());
154 Acts::Vector3 gen_surf_center = gen_surface->center(geometryContext());
155 // Tolerance
156 double tol = 1; // mm
157
158 if (abs(tgt_surf_center(0) - gen_surf_center(0)) > tol)
159 ldmx_log(error) << "Linear extrapolation to a far away surface in B field."
160 << " This will cause inaccuracies in track parameters"
161 << " Distance extrapolated = "
162 << (tgt_surf_center(0) - gen_surf_center(0));
163
164 auto prop_bound_state =
165 trk_extrap_->extrapolate(bound_trk_pars, target_surface);
166
167 // Create the seed track object.
168 Acts::Vector3 ref = target_surface->center(geometryContext());
169 // trk.setPerigeeLocation(free_params[Acts::eFreePos0],
170 // free_params[Acts::eFreePos1],
171 // free_params[Acts::eFreePos2]);
172
173 trk.setPerigeeLocation(ref(0), ref(1), ref(2));
174
175 auto prop_bound_vec = (prop_bound_state.value()).parameters();
176
178 tracking::sim::utils::convertActsToLdmxPars(prop_bound_vec));
179
180 trk.setPosition(pos(0), pos(1), pos(2));
181 trk.setMomentum(mom(0), mom(1), mom(2));
182}
183
184// origin_surface is the perigee
185// target_surface is the ecal
186ldmx::Track TruthSeedProcessor::recoilFullSeed(
187 const ldmx::SimParticle& particle, const int trackID,
188 const ldmx::SimTrackerHit& hit, const ldmx::SimTrackerHit& ecal_hit,
189 const std::map<int, std::vector<int>>& hit_count_map,
190 const std::shared_ptr<Acts::Surface>& origin_surface,
191 const std::shared_ptr<Acts::Surface>& target_surface,
192 const std::shared_ptr<Acts::Surface>& ecal_surface) {
193 ldmx::Track truth_recoil_track;
194 createTruthTrack(particle, hit, truth_recoil_track, origin_surface);
195 truth_recoil_track.setTrackID(trackID);
196
197 // Seed at the target location
198 ldmx::Track smeared_truth_track = seedFromTruth(truth_recoil_track, false);
199
200 // Add the track state at the target
201 ldmx::Track truth_track_target;
202 createTruthTrack(particle, hit, truth_track_target, target_surface);
203
204 // Store the truth track state on the seed track
205 ldmx::Track::TrackState ts_truth_target;
206 Acts::Vector3 ref = target_surface->center(geometryContext());
207 ts_truth_target.ref_x_ = ref(0);
208 ts_truth_target.ref_y_ = ref(1);
209 ts_truth_target.ref_z_ = ref(2);
210 ts_truth_target.params_ = truth_track_target.getPerigeeParameters();
211 // empty cov
212 ts_truth_target.ts_type_ = ldmx::TrackStateType::AtTarget;
213 smeared_truth_track.addTrackState(ts_truth_target);
214
215 // Add the track state at the ecal
216 ldmx::Track truth_track_ecal;
217 createTruthTrack(particle, ecal_hit, truth_track_ecal, ecal_surface);
218
219 ldmx::Track::TrackState ts_truth_ecal;
220 Acts::Vector3 ref_ecal = ecal_surface->center(geometryContext());
221 ts_truth_ecal.ref_x_ = ref_ecal(0);
222 ts_truth_ecal.ref_y_ = ref_ecal(1);
223 ts_truth_ecal.ref_z_ = ref_ecal(2);
224 ts_truth_ecal.params_ = truth_track_ecal.getPerigeeParameters();
225 // empty cov
226 ts_truth_ecal.ts_type_ = ldmx::TrackStateType::AtECAL;
227 smeared_truth_track.addTrackState(ts_truth_ecal);
228
229 // Add the hits
230 int nhits = 0;
231
232 for (auto sim_hit_idx : hit_count_map.at(smeared_truth_track.getTrackID())) {
233 smeared_truth_track.addMeasurementIndex(sim_hit_idx);
234 nhits += 1;
235 }
236
237 smeared_truth_track.setNhits(nhits);
238
239 return smeared_truth_track;
240}
241
243 const ldmx::SimParticle& beam_electron, const int trackID,
244 const ldmx::SimTrackerHit& hit,
245 const std::map<int, std::vector<int>>& hit_count_map,
246 const std::shared_ptr<Acts::Surface>& origin_surface,
247 const std::shared_ptr<Acts::Surface>& target_surface) {
248 ldmx::Track truth_track;
249
250 createTruthTrack(beam_electron, truth_track, origin_surface);
251 truth_track.setTrackID(trackID);
252
253 // Smeared track at the beam origin
254 ldmx::Track smeared_truth_track = seedFromTruth(truth_track, true);
255 // ldmx_log(trace) << "!!!!! Smeared truth track momentums: "
256 // << smearedTruthTrack.getMomentum()[0] << " "
257 // << smearedTruthTrack.getMomentum()[1] << " "
258 // << smearedTruthTrack.getMomentum()[2];
259 // ldmx_log(trace) << "!!!!! Smeared truth track Q/P: " <<
260 // smearedTruthTrack.getQoP()
261 // ;
262 ldmx_log(debug) << "Truth parameters at beam origin";
263 for (auto par : truth_track.getPerigeeParameters())
264 ldmx_log(debug) << par << " ";
265 ldmx_log(debug);
266
267 // Add the truth track state at the target
268 // Truth track target will be obtained from the scoring plane hit then
269 // extrapolated linearly to the target surface
270
271 ldmx::Track truth_track_target;
272 createTruthTrack(beam_electron, hit, truth_track_target, target_surface);
273
274 // Store the truth track state on the seed track
275 ldmx::Track::TrackState ts_truth_target;
276 Acts::Vector3 ref = target_surface->center(geometryContext());
277 ts_truth_target.ref_x_ = ref(0);
278 ts_truth_target.ref_y_ = ref(1);
279 ts_truth_target.ref_z_ = ref(2);
280 ts_truth_target.params_ = truth_track_target.getPerigeeParameters();
281 // empty cov
282 ts_truth_target.ts_type_ = ldmx::TrackStateType::AtTarget;
283 smeared_truth_track.addTrackState(ts_truth_target);
284
285 ldmx_log(debug) << "Truth parameters at target";
286 for (auto par : truth_track_target.getPerigeeParameters())
287 ldmx_log(debug) << par << " ";
288 ldmx_log(debug);
289
290 // This is the un-smeared truth track that can be used for pulls and residuals
291 ldmx::Track seed_truth_track = seedFromTruth(truth_track, false);
292
293 ldmx::Track::TrackState ts_truth_beam_origin;
294 Acts::Vector3 ref_origin = origin_surface->center(geometryContext());
295 ts_truth_beam_origin.ref_x_ = ref_origin(0);
296 ts_truth_beam_origin.ref_y_ = ref_origin(1);
297 ts_truth_beam_origin.ref_z_ = ref_origin(2);
298 ts_truth_beam_origin.params_ = seed_truth_track.getPerigeeParameters();
299 // ts_truth_beam_origin.cov = seedTruthTrack.getPerigeeCov();
300 ts_truth_beam_origin.ts_type_ = ldmx::TrackStateType::AtBeamOrigin;
301 smeared_truth_track.addTrackState(ts_truth_beam_origin);
302
303 ldmx_log(debug) << "Smeared parameters at origin";
304 for (auto par : smeared_truth_track.getPerigeeParameters())
305 ldmx_log(debug) << par << " ";
306
307 // assign the sim hit indices
308 // TODO this is not fully correct as the sim hits
309 // might be duplicated on sensors
310 // and should be merged if that is the case
311
312 int nhits = 0;
313
314 for (auto sim_hit_idx : hit_count_map.at(smeared_truth_track.getTrackID())) {
315 smeared_truth_track.addMeasurementIndex(sim_hit_idx);
316 nhits += 1;
317 }
318
319 smeared_truth_track.setNhits(nhits);
320
321 return smeared_truth_track;
322}
323
325 bool seed_smearing) {
326 ldmx::Track seed = ldmx::Track();
327 seed.setPerigeeLocation(tt.getPerigeeLocation()[0],
328 tt.getPerigeeLocation()[1],
329 tt.getPerigeeLocation()[2]);
330 seed.setChi2(0.);
331 seed.setNhits(tt.getNhits());
332 seed.setNdf(0);
333 seed.setNsharedHits(0);
334 seed.setTrackID(tt.getTrackID());
335 seed.setPdgID(tt.getPdgID());
336 seed.setTruthProb(1.);
337
338 Acts::BoundVector bound_params;
339 Acts::BoundVector stddev;
340
341 if (seed_smearing) {
342 ldmx_log(debug) << "Smear track and inflate covariance";
343
344 /*
345 double sigma_d0 = rel_smearfactors_[Acts::eBoundLoc0] * tt.getD0();
346 double sigma_z0 = rel_smearfactors_[Acts::eBoundLoc1] * tt.getZ0();
347 double sigma_phi = rel_smearfactors_[Acts::eBoundPhi] * tt.getPhi();
348 double sigma_theta = rel_smearfactors_[Acts::eBoundTheta] *
349 tt.getTheta(); double sigma_p = rel_smearfactors_[Acts::eBoundQOverP]
350 * abs(1/tt.getQoP()); double sigma_t =
351 rel_smearfactors_[Acts::eBoundTime] * tt.getT();
352 */
353
354 double sigma_d0 = d0smear_[0];
355 double sigma_z0 = z0smear_[0];
356 double sigma_phi = phismear_;
357 double sigma_theta = thetasmear_;
358 double sigma_p = relpsmear_ * abs(1 / tt.getQoP());
359 double sigma_t = 1. * Acts::UnitConstants::ns;
360
361 double smear = (*normal_)(generator_);
362 double d0smear = tt.getD0() + smear * sigma_d0;
363
364 smear = (*normal_)(generator_);
365 double z0smear = tt.getZ0() + smear * sigma_z0;
366
367 smear = (*normal_)(generator_);
368 double phismear = tt.getPhi() + smear * sigma_phi;
369
370 smear = (*normal_)(generator_);
371 double thetasmear = tt.getTheta() + smear * sigma_theta;
372
373 double p = std::abs(1. / tt.getQoP());
374 smear = (*normal_)(generator_);
375 double psmear = p + smear * sigma_p;
376
377 double q = tt.getQoP() < 0 ? -1. : 1.;
378 double qo_psmear = q / psmear;
379
380 smear = (*normal_)(generator_);
381 double tsmear = tt.getT() + smear * sigma_t;
382
383 bound_params << d0smear, z0smear, phismear, thetasmear, qo_psmear, tsmear;
384
385 stddev[Acts::eBoundLoc0] =
386 inflate_factors_[Acts::eBoundLoc0] * sigma_d0 * Acts::UnitConstants::mm;
387 stddev[Acts::eBoundLoc1] =
388 inflate_factors_[Acts::eBoundLoc1] * sigma_z0 * Acts::UnitConstants::mm;
389 stddev[Acts::eBoundPhi] = inflate_factors_[Acts::eBoundPhi] * sigma_phi;
390 stddev[Acts::eBoundTheta] =
391 inflate_factors_[Acts::eBoundTheta] * sigma_theta;
392 stddev[Acts::eBoundQOverP] =
393 inflate_factors_[Acts::eBoundQOverP] * (1. / p) * (1. / p) * sigma_p;
394 stddev[Acts::eBoundTime] =
395 inflate_factors_[Acts::eBoundTime] * sigma_t * Acts::UnitConstants::ns;
396
397 ldmx_log(debug) << stddev;
398
399 std::vector<double> v_seed_params(
400 (bound_params).data(),
401 bound_params.data() + bound_params.rows() * bound_params.cols());
402
403 Acts::BoundSquareMatrix bound_cov =
404 stddev.cwiseProduct(stddev).asDiagonal();
405 std::vector<double> v_seed_cov;
406 tracking::sim::utils::flatCov(bound_cov, v_seed_cov);
407 seed.setPerigeeParameters(v_seed_params);
408 seed.setPerigeeCov(v_seed_cov);
409
410 } else {
411 // Do not smear the seed
412
413 bound_params << tt.getD0(), tt.getZ0(), tt.getPhi(), tt.getTheta(),
414 tt.getQoP(), tt.getT();
415
416 std::vector<double> v_seed_params(
417 (bound_params).data(),
418 bound_params.data() + bound_params.rows() * bound_params.cols());
419
420 double p = std::abs(1. / tt.getQoP());
421 double sigma_p = 0.75 * p * Acts::UnitConstants::GeV;
422 stddev[Acts::eBoundLoc0] = 2 * Acts::UnitConstants::mm;
423 stddev[Acts::eBoundLoc1] = 5 * Acts::UnitConstants::mm;
424 stddev[Acts::eBoundTime] = 1000 * Acts::UnitConstants::ns;
425 stddev[Acts::eBoundPhi] = 5 * Acts::UnitConstants::degree;
426 stddev[Acts::eBoundTheta] = 5 * Acts::UnitConstants::degree;
427 stddev[Acts::eBoundQOverP] = (1. / p) * (1. / p) * sigma_p;
428
429 Acts::BoundSquareMatrix bound_cov =
430 stddev.cwiseProduct(stddev).asDiagonal();
431 std::vector<double> v_seed_cov;
432 tracking::sim::utils::flatCov(bound_cov, v_seed_cov);
433 seed.setPerigeeParameters(v_seed_params);
434 seed.setPerigeeCov(v_seed_cov);
435 }
436
437 return seed;
438}
439
441 const std::vector<ldmx::SimTrackerHit>& sim_hits,
442 std::map<int, std::vector<int>>& hit_count_map) {
443 for (int i_sim_hit = 0; i_sim_hit < sim_hits.size(); i_sim_hit++) {
444 auto& sim_hit = sim_hits.at(i_sim_hit);
445 // This track never left a hit before
446 if (!hit_count_map.count(sim_hit.getTrackID())) {
447 hit_count_map[sim_hit.getTrackID()].push_back(i_sim_hit);
448 }
449
450 // This track left a hit before.
451 // Check if it's on a different sensor than the others
452
453 else {
454 int sensor_id = tracking::sim::utils::getSensorID(sim_hit);
455 bool found_hit = false;
456
457 for (auto& i_rhit : hit_count_map[sim_hit.getTrackID()]) {
458 int tmp_sensor_id =
459 tracking::sim::utils::getSensorID(sim_hits.at(i_rhit));
460
461 if (sensor_id == tmp_sensor_id) {
462 found_hit = true;
463 break;
464 }
465 } // loop on the already recorded hits
466
467 if (!found_hit) {
468 hit_count_map[sim_hit.getTrackID()].push_back(i_sim_hit);
469 }
470 }
471 } // loop on sim hits
472}
473
475 const ldmx::SimTrackerHit& hit,
476 const std::vector<ldmx::SimTrackerHit>& ecal_sp_hits) {
477 // Clean some of the hits we don't want
478 if (hit.getPosition()[2] < z_min_) return false;
479
480 // Check if the track_id was requested
481 if (track_id_ > 0 && hit.getTrackID() != track_id_) return false;
482
483 // Check if we are requesting particular particles
484 if (std::find(pdg_ids_.begin(), pdg_ids_.end(), hit.getPdgID()) ==
485 pdg_ids_.end())
486 return false;
487
488 Acts::Vector3 p_vec{hit.getMomentum()[0], hit.getMomentum()[1],
489 hit.getMomentum()[2]};
490
491 // p cut
492 if (p_cut_ >= 0. && p_vec.norm() < p_cut_) return false;
493
494 // p cut Max
495 if (p_cut_ < 100000. && p_vec.norm() > p_cut_max_) return false;
496
497 // pz cut
498 if (pz_cut_ > -9999 && p_vec(2) < pz_cut_) return false;
499
500 // Check the ecal scoring plane
501 bool pass_ecal_scoring_plane = true;
502
503 if (p_cut_ecal_ > 0) { // only check if we care about it.
504
505 for (auto& e_sp_hit : ecal_sp_hits) {
506 if (e_sp_hit.getTrackID() == hit.getTrackID() &&
507 e_sp_hit.getPdgID() == hit.getPdgID()) {
508 Acts::Vector3 e_sp_p{e_sp_hit.getMomentum()[0],
509 e_sp_hit.getMomentum()[1],
510 e_sp_hit.getMomentum()[2]};
511
512 if (e_sp_p.norm() < p_cut_ecal_) pass_ecal_scoring_plane = false;
513
514 // Skip the rest of the scoring plane hits since we already found the
515 // track we care about
516 break;
517
518 } // check that the hit belongs to the inital particle from the target
519 // scoring plane hit
520 } // loop on Ecal scoring plane hits
521 } // pcutEcal
522
523 if (!pass_ecal_scoring_plane) return false;
524
525 return true;
526}
527
529 // Retrieve the particleMap
530 auto particle_map{event.getMap<int, ldmx::SimParticle>(
531 "SimParticles", sim_particles_passname_)};
532
533 // Retrieve the target scoring hits
534 // Information is extracted using the
535 // scoring plane hit left by the particle at the target.
536
537 const std::vector<ldmx::SimTrackerHit> scoring_hits{
538 event.getCollection<ldmx::SimTrackerHit>(scoring_hits_coll_name_,
539 sp_pass_name_)};
540
541 // Retrieve the scoring plane hits at the ECAL
542 const std::vector<ldmx::SimTrackerHit> scoring_hits_ecal{
543 event.getCollection<ldmx::SimTrackerHit>("EcalScoringPlaneHits",
544 sp_pass_name_)};
545
546 // Retrieve the sim hits in the tagger tracker
547 const std::vector<ldmx::SimTrackerHit> tagger_sim_hits =
550
551 // Retrieve the sim hits in the recoil tracker
552 const std::vector<ldmx::SimTrackerHit> recoil_sim_hits =
555
556 // If sim hit collections are empty throw a warning
557 if (tagger_sim_hits.size() == 0 && !skip_tagger_) {
558 ldmx_log(error) << "Tagger sim hits collection empty for event ";
559 }
560 if (recoil_sim_hits.size() == 0 && !skip_recoil_) {
561 ldmx_log(error) << "Recoil sim hits collection empty for event ";
562 }
563
564 // The map stores which track leaves which sim hits
565 std::map<int, std::vector<int>> hit_count_map_recoil;
566 makeHitCountMap(recoil_sim_hits, hit_count_map_recoil);
567
568 std::map<int, std::vector<int>> hit_count_map_tagger;
569 makeHitCountMap(tagger_sim_hits, hit_count_map_tagger);
570
571 // to keep track of how many sim particles leave hits on the scoring plane
572 std::vector<int> recoil_sh_idxs;
573 std::unordered_map<int, std::vector<int>> recoil_sh_count_map;
574
575 std::vector<int> tagger_sh_idxs;
576 std::unordered_map<int, std::vector<int>> tagger_sh_count_map;
577
578 // Target scoring hits for Tagger will have Z<0, Recoil scoring hits will
579 // have Z>0
580 for (unsigned int i_sh = 0; i_sh < scoring_hits.size(); i_sh++) {
581 const ldmx::SimTrackerHit& hit = scoring_hits.at(i_sh);
582 double zhit = hit.getPosition()[2];
583
584 Acts::Vector3 p_vec{hit.getMomentum()[0], hit.getMomentum()[1],
585 hit.getMomentum()[2]};
586 double tagger_p_max = 0.;
587
588 // Check if it is a tagger track going fwd that passes basic cuts
589 if (zhit < 0.) {
590 // Tagger selection cuts
591 // Negative scoring plane hit, with momentum > p_cut
592 if (p_vec(2) < 0. || p_vec.norm() < p_cut_) continue;
593
594 // Check that the hit was left by a charged particle
595 if (abs(particle_map[hit.getTrackID()].getCharge()) < 1e-8) continue;
596
597 if (p_vec.norm() > tagger_p_max) {
598 tagger_sh_count_map[hit.getTrackID()].push_back(i_sh);
599 }
600 } // Tagger loop
601
602 // Check the recoil hits
603 else {
604 // Recoil selection cuts
605 // Positive scoring plane hit, forward direction with momentum > p_cut
606 if (p_vec(2) < 0. || p_vec.norm() < p_cut_) continue;
607
608 // Check that the hit was left by a charged particle
609 if (abs(particle_map[hit.getTrackID()].getCharge()) < 1e-8) continue;
610
611 recoil_sh_count_map[hit.getTrackID()].push_back(i_sh);
612
613 } // Recoil
614 } // loop on Target scoring plane hits
615
616 for (std::pair<int, std::vector<int>> element : recoil_sh_count_map) {
617 std::sort(
618 element.second.begin(), element.second.end(),
619 [&](const int idx1, int idx2) -> bool {
620 const ldmx::SimTrackerHit& hit1 = scoring_hits.at(idx1);
621 const ldmx::SimTrackerHit& hit2 = scoring_hits.at(idx2);
622
623 Acts::Vector3 phit1{hit1.getMomentum()[0], hit1.getMomentum()[1],
624 hit1.getMomentum()[2]};
625 Acts::Vector3 phit2{hit2.getMomentum()[0], hit2.getMomentum()[1],
626 hit2.getMomentum()[2]};
627
628 return phit1.norm() > phit2.norm();
629 });
630 }
631
632 // Sort tagger hits.
633 for (auto& [_track_id, hit_indices] : tagger_sh_count_map) {
634 std::sort(
635 hit_indices.begin(), hit_indices.end(),
636 [&](const int idx1, int idx2) -> bool {
637 const ldmx::SimTrackerHit& hit1 = scoring_hits.at(idx1);
638 const ldmx::SimTrackerHit& hit2 = scoring_hits.at(idx2);
639
640 Acts::Vector3 phit1{hit1.getMomentum()[0], hit1.getMomentum()[1],
641 hit1.getMomentum()[2]};
642 Acts::Vector3 phit2{hit2.getMomentum()[0], hit2.getMomentum()[1],
643 hit2.getMomentum()[2]};
644
645 return phit1.norm() > phit2.norm();
646 });
647 }
648
649 // Building of the event truth information and the truth seeds
650 // TODO remove the truthtracks in the future as the truth seeds are enough
651
652 std::vector<ldmx::Track> tagger_truth_tracks;
653 std::vector<ldmx::Track> tagger_truth_seeds;
654 std::vector<ldmx::Track> recoil_truth_tracks;
655 std::vector<ldmx::Track> recoil_truth_seeds;
656 ldmx::Tracks beam_electrons;
657
658 // TODO:: The target should be taken from some conditions DB in the future.
659 // Define the perigee_surface at 0.0.0
660 auto target_surface{Acts::Surface::makeShared<Acts::PerigeeSurface>(
661 Acts::Vector3(0., 0., 0.))};
662
663 // Define the target_surface
664 auto target_unbound_surface = tracking::sim::utils::unboundSurface(0.);
665
666 // ecal
667 auto ecal_surface = tracking::sim::utils::unboundSurface(240.5);
668
669 auto beam_origin_surface{Acts::Surface::makeShared<Acts::PerigeeSurface>(
670 Acts::Vector3(beam_origin_[0], beam_origin_[1], beam_origin_[2]))};
671
672 if (!skip_tagger_) {
673 for (const auto& [track_id, hit_indices] : tagger_sh_count_map) {
674 const ldmx::SimTrackerHit& hit = scoring_hits.at(hit_indices.at(0));
675 const ldmx::SimParticle& phit = particle_map[hit.getTrackID()];
676
677 if (hit_count_map_tagger[hit.getTrackID()].size() > n_min_hits_tagger_) {
678 ldmx::Track truth_tagger_track;
679 createTruthTrack(phit, hit, truth_tagger_track, target_surface);
680 truth_tagger_track.setNhits(
681 hit_count_map_tagger[hit.getTrackID()].size());
682 tagger_truth_tracks.push_back(truth_tagger_track);
683
684 if (hit.getPdgID() == 11 && hit.getTrackID() < max_track_id_) {
685 ldmx::Track beam_e_truth_seed =
686 taggerFullSeed(particle_map[hit.getTrackID()], hit.getTrackID(),
687 hit, hit_count_map_tagger, beam_origin_surface,
688 target_unbound_surface);
689 beam_electrons.push_back(beam_e_truth_seed);
690 }
691 }
692 }
693 }
694
695 // Recover the EcalScoring hits
696 std::vector<ldmx::SimTrackerHit> ecal_sp_hits =
697 event.getCollection<ldmx::SimTrackerHit>("EcalScoringPlaneHits",
698 sp_pass_name_);
699 // Select ECAL hits
700 std::vector<ldmx::SimTrackerHit> sel_ecal_sp_hits;
701
702 for (auto sp_hit : ecal_sp_hits) {
703 if (sp_hit.getMomentum()[2] > 0 && ((sp_hit.getID() & 0xfff) == 31)) {
704 sel_ecal_sp_hits.push_back(sp_hit);
705 }
706 }
707
708 // Recoil target surface for truth and seed tracks is the target
709
710 for (std::pair<int, std::vector<int>> element : recoil_sh_count_map) {
711 // Only take the first entry of the vector: it should be the scoring plane
712 // hit with the highest momentum.
713 const ldmx::SimTrackerHit& hit = scoring_hits.at(element.second.at(0));
714 [[maybe_unused]] const ldmx::SimParticle& phit =
715 particle_map[hit.getTrackID()];
716 ldmx::SimTrackerHit ecal_hit;
717
718 bool found_ecal_hit = false;
719 for (auto ecal_sp_hit : sel_ecal_sp_hits) {
720 if (ecal_sp_hit.getTrackID() == hit.getTrackID()) {
721 ecal_hit = ecal_sp_hit;
722 found_ecal_hit = true;
723 break;
724 }
725 }
726
727 // Findable particle selection
728 // ldmx_log(trace) << "!!! n_recoil_sim_hits found: " <<
729 // hit_count_map_recoil[hit.getTrackID()].size();
730 if (hit_count_map_recoil[hit.getTrackID()].size() > n_min_hits_recoil_ &&
731 found_ecal_hit && !skip_recoil_) {
732 ldmx::Track truth_recoil_track =
733 recoilFullSeed(particle_map[hit.getTrackID()], hit.getTrackID(), hit,
734 ecal_hit, hit_count_map_recoil, target_surface,
735 target_unbound_surface, ecal_surface);
736 // ldmx_log(trace) << "!!! Recoil track created";
737 // ldmx_log(trace) << "!!! Recoil track momentum: " <<
738 // truth_recoil_track.getMomentum()[0] << " " <<
739 // truth_recoil_track.getMomentum()[1] << " " <<
740 // truth_recoil_track.getMomentum()[2]; ldmx_log(trace) << "!!! Hit
741 // momentum: " << hit.getMomentum()[0] << " " << hit.getMomentum()[1] << "
742 // " << hit.getMomentum()[2];
743 recoil_truth_tracks.push_back(truth_recoil_track);
744 }
745 }
746
747 /*
748 for (std::pair<int,std::vector<int>> element : recoil_sh_count_map) {
749
750 const ldmx::SimTrackerHit& hit = scoring_hits.at(element.second.at(0));
751 const ldmx::SimParticle& phit = particleMap[hit.getTrackID()];
752
753 if (hit_count_map_recoil[hit.getTrackID()].size() > n_min_hits_recoil_) {
754 ldmx::Track truth_recoil_track;
755 createTruthTrack(phit,hit,truth_recoil_track,targetSurface);
756 truth_recoil_track.setNhits(hit_count_map_recoil[hit.getTrackID()].size());
757 recoil_truth_tracks.push_back(truth_recoil_track);
758 }
759 }
760 */
761
762 // Form a truth seed from a truth track
763
764 for (auto& tt : tagger_truth_tracks) {
765 ldmx::Track seed = seedFromTruth(tt, seed_smearing_);
766
767 tagger_truth_seeds.push_back(seed);
768 }
769
770 ldmx_log(debug) << "Forming seeds from truth";
771 for (auto& tt : recoil_truth_tracks) {
772 ldmx_log(debug) << "Smearing truth track";
773
774 ldmx::Track seed = seedFromTruth(tt, seed_smearing_);
775
776 recoil_truth_seeds.push_back(seed);
777 }
778
779 // even if skip_tagger/recoil_ is true, still make the collections in the
780 // event
781 event.add("beamElectrons", beam_electrons);
782 event.add("TaggerTruthTracks", tagger_truth_tracks);
783 event.add("RecoilTruthTracks", recoil_truth_tracks);
784 event.add("TaggerTruthSeeds", tagger_truth_seeds);
785 event.add("RecoilTruthSeeds", recoil_truth_seeds);
786}
787} // namespace tracking::reco
788
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Implements an event buffer system for storing event data.
Definition Event.h:42
Class which represents the process under execution.
Definition Process.h:36
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:78
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
double getCharge() const
Get the charge of this particle.
std::vector< double > getVertex() const
Get a vector containing the vertex of this particle in mm.
int getPdgID() const
Get the PDG ID of this particle.
Definition SimParticle.h:85
std::vector< double > getMomentum() const
Get a vector containing the momentum of this particle [MeV].
Represents a simulated tracker hit in the simulation.
int getPdgID() const
Get the Sim particle track ID of the hit.
std::vector< float > getPosition() const
Get the XYZ position of the hit [mm].
std::vector< double > getMomentum() const
Get the XYZ momentum of the particle at the position at which the hit took place [MeV].
int getTrackID() const
Get the Sim particle track ID of the hit.
Implementation of a track object.
Definition Track.h:53
void setPerigeeParameters(const std::vector< double > &par)
d_0 z_0 phi_0 theta q/p t
Definition Track.h:156
a helper base class providing some methods to shorten access to common conditions used within the tra...
Create a track seed using truth information extracted from the corresponding SimParticle or SimTracke...
float z_min_
Min cut on the z_ of the scoring hit.
void createTruthTrack(const ldmx::SimParticle &particle, ldmx::Track &trk, const std::shared_ptr< Acts::Surface > &target_surface)
Use the vertex position of the SimParticle to extract (x_, y_, z_, px, py, pz, q) and create a track ...
std::string input_pass_name_
Pass name for the sim hit collections.
std::string recoil_sim_hits_coll_name_
Sim hits to check if the truth seed is findable.
void onNewRun(const ldmx::RunHeader &rh) override
onNewRun is the first function called for each processor after the conditions are fully configured an...
std::vector< int > pdg_ids_
pdg_ids of the particles we want to select for the seeds
void produce(framework::Event &event) override
Main loop that creates the seed tracks for both the tagger and recoil tracker.
int track_id_
Only select a particular trackID.
void configure(framework::config::Parameters &parameters) override
Callback for the EventProcessor to configure itself from the given set of parameters.
double pz_cut_
Ask for a minimum pz for the seeds.
TruthSeedProcessor(const std::string &name, framework::Process &process)
Constructor.
double p_cut_
Ask for a minimum p for the seeds.
void makeHitCountMap(const std::vector< ldmx::SimTrackerHit > &sim_hits, std::map< int, std::vector< int > > &hit_count_map)
Create a mapping from the selected scoring plane hit objects to the number of hits they associated pa...
std::string scoring_hits_coll_name_
Which scoring plane hits to use for the truth seeds generation.
std::string tagger_sim_hits_coll_name_
Sim hits to check if the truth seed is findable.
ldmx::Track taggerFullSeed(const ldmx::SimParticle &beam_electron, const int trackID, const ldmx::SimTrackerHit &hit, const std::map< int, std::vector< int > > &hit_count_map, const std::shared_ptr< Acts::Surface > &origin_surface, const std::shared_ptr< Acts::Surface > &target_surface)
This method retrieves the beam electron and forms a full seed The seed parameters are the truth param...
bool scoringPlaneHitFilter(const ldmx::SimTrackerHit &hit, const std::vector< ldmx::SimTrackerHit > &ecal_sp_hits)
Filter that checks if a scoring plane passes specified momentum cuts as well as if the associated Sim...
double p_cut_max_
Ask for a maximum p for the seeds.
int n_min_hits_tagger_
Minimum number of hits left in the recoil tracker to consider the seed as findable.
int n_min_hits_recoil_
Minimum number of hits left in the recoil tracker to consider the seed as findable.
ldmx::Track seedFromTruth(const ldmx::Track &tt, bool seed_smearing)
Create a track seed from a truth track applying a smearing to the truth parameters as well as an infl...
Acts::GeometryContext gctx_
The ACTS geometry context properly.