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