LDMX Software
SeedFinderProcessor.cxx
1#include "Tracking/Reco/SeedFinderProcessor.h"
2
3#include <algorithm>
4#include <iostream>
5#include <set>
6#include <sstream>
7
8#include "Acts/Definitions/TrackParametrization.hpp"
9#include "Acts/Utilities/Intersection.hpp"
10#include "Eigen/Dense"
11#include "Tracking/Sim/TrackingUtils.h"
12
13/* This processor takes in input a set of 3D space points and builds seedTracks
14 * using the ACTS algorithm which is based on the ATLAS 3-space point conformal
15 * fit.
16 *
17 */
18
19using Eigen::MatrixXd;
20using Eigen::VectorXd;
21
22namespace tracking {
23namespace reco {
24
26 framework::Process& process)
27 : TrackingGeometryUser(name, process) {
28 // TODO REMOVE FROM DEFAULT
29 /*
30 output_file_ = new TFile("seeder.root", "RECREATE");
31 output_tree_ = new TTree("seeder", "seeder");
32
33 output_tree_->Branch("nevents", &nevents_);
34 output_tree_->Branch("xhit", &xhit_);
35 output_tree_->Branch("yhit", &yhit_);
36 output_tree_->Branch("zhit", &zhit_);
37
38 output_tree_->Branch("b0", &b0_);
39 output_tree_->Branch("b1", &b1_);
40 output_tree_->Branch("b2", &b2_);
41 output_tree_->Branch("b3", &b3_);
42 output_tree_->Branch("b4", &b4_);
43 */
44}
45
47 truth_matching_tool_ = std::make_shared<tracking::sim::TruthMatchingTool>();
48 target_surface_ = tracking::sim::utils::unboundSurface(0.);
49}
50
52 // Output seed name
53 out_seed_collection_ = parameters.get<std::string>("out_seed_collection",
54 getName() + "SeedTracks");
55
56 // Input strip hits
58 parameters.get<std::string>("input_hits_collection", "TaggerSimHits");
59
60 // Tagger tracks - only for Recoil Seed finding
62 parameters.get<std::string>("tagger_trks_collection", "TaggerTracks");
63
65 parameters.get<std::vector<double>>("perigee_location", {-700, 0., 0.});
66 pmin_ = parameters.get<double>("pmin", 0.05 * Acts::UnitConstants::GeV);
67 pmax_ = parameters.get<double>("pmax", 8 * Acts::UnitConstants::GeV);
68 d0max_ = parameters.get<double>("d0max", -15. * Acts::UnitConstants::mm);
69 d0min_ = parameters.get<double>("d0min", -45. * Acts::UnitConstants::mm);
70 z0max_ = parameters.get<double>("z0max", 60. * Acts::UnitConstants::mm);
71 phicut_ = parameters.get<double>("phicut", 0.1);
72 thetacut_ = parameters.get<double>("thetacut", 0.2);
73 loc0cut_ = parameters.get<double>("loc0cut", 0.1);
74 loc1cut_ = parameters.get<double>("loc1cut", 0.3);
76 parameters.get<std::vector<std::string>>("strategies", {"0,1,2,3,4"});
77 use_target_constraint_ = parameters.get<bool>("use_target_constraint", false);
79 parameters.get<bool>("prefilter_unconstrained", false);
80
82 EXCEPTION_RAISE("BadConf",
83 "use_target_constraint needs a tagger_trks_collection");
84 }
85
86 // 5 fit parameters: one equation per strip, two per target constraint
87 const size_t min_layers = use_target_constraint_ ? 4 : 5;
88
89 // parse each "l0,l1,..." string into a layer list
90 strategy_layers_.clear();
91 for (const auto& strategy : strategies_) {
92 std::vector<int> layers;
93 std::stringstream ss(strategy);
94 std::string token;
95 while (std::getline(ss, token, ',')) {
96 if (!token.empty()) layers.push_back(std::stoi(token));
97 }
98 std::set<int> distinct(layers.begin(), layers.end());
99 if (distinct.size() < min_layers) {
100 EXCEPTION_RAISE("BadConf",
101 "Seeding strategy '" + strategy + "' has fewer than " +
102 std::to_string(min_layers) + " distinct layers");
103 }
104 strategy_layers_.push_back(layers);
105 }
106 inflate_factors_ = parameters.get<std::vector<double>>(
107 "inflate_factors", {10., 10., 10., 10., 10., 10.});
108 bfield_ = parameters.get<double>("bfield", 1.5);
109 input_pass_name_ = parameters.get<std::string>("input_pass_name");
110 sim_particles_coll_name_ =
111 parameters.get<std::string>("sim_particles_coll_name");
112 sim_particles_passname_ =
113 parameters.get<std::string>("sim_particles_passname");
114 tagger_trks_event_collection_passname_ =
115 parameters.get<std::string>("tagger_trks_event_collection_passname");
116 sim_particles_event_passname_ =
117 parameters.get<std::string>("sim_particles_event_passname");
118 u_error_ = parameters.get<double>("u_error");
119 v_error_ = parameters.get<double>("v_error");
120}
121
123 // tg is unused, should it be? FIXME
124 // const auto& tg{geometry()};
125 auto start = std::chrono::high_resolution_clock::now();
126 std::vector<ldmx::Track> seed_tracks;
127
128 nevents_++;
129
130 // check if SimParticleMap is available for truth matching
131 std::map<int, ldmx::SimParticle> particle_map;
132
133 const auto& measurements = event.getCollection<ldmx::Measurement>(
134 input_hits_collection_, input_pass_name_);
135
136 // tagger tracks give the target position; an empty name disables it
137 std::vector<ldmx::Track> tagger_tracks;
138 if (!tagger_trks_collection_.empty()) {
139 // prefer this pass so a re-reco does not see two copies
140 std::string pass = tagger_trks_event_collection_passname_;
141 if (pass.empty() &&
143 pass = event.getPassName();
144 }
145 if (event.exists(tagger_trks_collection_, pass)) {
146 tagger_tracks =
147 event.getCollection<ldmx::Track>(tagger_trks_collection_, pass);
148 } else if (use_target_constraint_) {
149 // the target constraint needs the tagger tracker
150 if (event.exists(tagger_trks_collection_, pass, false)) {
151 EXCEPTION_RAISE("BadConf",
152 "Several '" + tagger_trks_collection_ +
153 "' collections found, set "
154 "tagger_trks_event_collection_passname to pick "
155 "one");
156 }
157 EXCEPTION_RAISE("NoTaggerTracks", "No '" + tagger_trks_collection_ +
158 "' collection for the target "
159 "constraint, run tagger tracking");
160 } else if (!warned_ambiguous_tagger_ &&
161 event.exists(tagger_trks_collection_, pass, false)) {
162 ldmx_log(warn) << "Several '" << tagger_trks_collection_
163 << "' collections found, set "
164 "tagger_trks_event_collection_passname to pick one";
166 }
167 }
168
169 const auto& tgt_surf = target_surface_;
170
171 // Create the pseudomeasurements at the target
172
173 ldmx::Measurements target_pseudo_meas;
174
175 for (auto tagtrk : tagger_tracks) {
176 // For Track, the perigee parameters are stored at the target surface.
177 // Use d0/z0 as local position and the perigee covariance for the
178 // pseudo measurement. Only create the pseudo measurement if cov is
179 // available.
180
181 // The covariance matrix passed to the pseudo measurement is considered as
182 // uncorrelated. This is an approx that considers that loc-u and loc-v from
183 // the track have small correlation.
184
185 const auto& perigee_cov = tagtrk.getPerigeeCov();
186 if (!perigee_cov.empty()) {
187 Acts::BoundMatrix cov = tracking::sim::utils::unpackCov(perigee_cov);
188 double locu = tagtrk.getD0();
189 double locv = tagtrk.getZ0();
190 double covuu =
191 cov(Acts::BoundIndices::eBoundLoc0, Acts::BoundIndices::eBoundLoc0);
192 double covvv =
193 cov(Acts::BoundIndices::eBoundLoc1, Acts::BoundIndices::eBoundLoc1);
194
195 ldmx::Measurement pseudo_meas;
196 pseudo_meas.setLocalPosition(locu, locv);
197 Acts::Vector3 dummy{0., 0., 0.};
198 Acts::Vector2 local_pos{locu, locv};
199 Acts::Vector3 global_pos =
200 tgt_surf->localToGlobal(geometryContext(), local_pos, dummy);
201
202 pseudo_meas.setGlobalPosition(global_pos(0), global_pos(1),
203 global_pos(2));
204 pseudo_meas.setTime(0.);
205 pseudo_meas.setLocalCovariance(covuu, covvv);
206
207 target_pseudo_meas.push_back(pseudo_meas);
208 }
209 }
210
211 // Constraints that enter the fit: tagger tracks only
212 ldmx::Measurements fit_constraints;
213 if (use_target_constraint_) fit_constraints = target_pseudo_meas;
214
215 if (event.exists(sim_particles_coll_name_, sim_particles_event_passname_)) {
216 particle_map = event.getMap<int, ldmx::SimParticle>(
217 sim_particles_coll_name_, sim_particles_passname_);
218 truth_matching_tool_->setup(particle_map, measurements);
219 }
220
221 ldmx_log(debug) << "Preparing the strategies";
222
223 // a strategy is a list of layers from which to make the seed
224 // layer_ numbering starts at 0
225 seeds_per_strategy_.resize(strategy_layers_.size(), 0);
226 for (size_t is = 0; is < strategy_layers_.size(); ++is) {
227 groups_map_.clear();
228 const size_t n_before = seed_tracks.size();
229 if (groupStrips(measurements, strategy_layers_[is]))
230 findSeedsFromMap(seed_tracks, target_pseudo_meas, fit_constraints);
231 seeds_per_strategy_[is] += seed_tracks.size() - n_before;
232 }
233
234 groups_map_.clear();
235 // output_tree_->Fill();
236 ntracks_ += seed_tracks.size();
237 event.add(out_seed_collection_, seed_tracks);
238
239 auto end = std::chrono::high_resolution_clock::now();
240
241 // long long microseconds =
242 // std::chrono::duration_cast<std::chrono::microseconds>(end-start).count();
243
244 auto diff = end - start;
245 processing_time_ += std::chrono::duration<double, std::milli>(diff).count();
246
247 // Seed finding using 2D Hits
248 // - The hits should keep track if they are already associated to a track or
249 // not. This can be used for subsequent passes of seed-finding
250
251 // This should go into a digitization producer, which takes care of producing
252 // measurements from:
253 // - raw hits in data
254 // - sim hits in MC
255 // Step 0: Get the sim hits and project them on the surfaces to mimic 2d
256 // hits Step 1: Smear the hits and associate an uncertainty to those
257 // measurements.
258
259 xhit_.clear();
260 yhit_.clear();
261 zhit_.clear();
262
263 b0_.clear();
264 b1_.clear();
265 b2_.clear();
266 b3_.clear();
267 b4_.clear();
268
269} // produce
270
271// Seed finder from Robert's in HPS
272// https://github.com/JeffersonLab/hps-java/blob/47712878302eb0c0374d077a208a6f8f0e2c3dc6/tracking/src/main/java/org/hps/recon/tracking/kalman/SeedTrack.java
273// Adapted to possible 3D hit points.
274
275// yOrigin is the location along the beam about which we fit the seed helix
276// perigee_location is where the track parameters will be extracted
277
278// the optional constraint is a 2D point on the target surface
279
281 const ldmx::Measurements& vmeas, double xOrigin,
282 const Acts::Vector3& perigee_location,
283 const ldmx::Measurement* constraint) {
284 // Fit a straight line in the non-bending plane and a parabola in the bending
285 // plane
286
287 // Each measurement is treated as a 3D point, where the v direction is in the
288 // center of the strip with sigma equal to the length of the strip / sqrt(12).
289 // In this way it's easier to incorporate the tagger track extrapolation to
290 // the fit
291
292 Acts::Matrix<5, 5> a = Acts::Matrix<5, 5>::Zero();
293 Acts::Vector<5> y = Acts::Vector<5>::Zero();
294
295 // accumulate one 2D point on a surface into the normal equations
296 auto add_point = [&](const Acts::Surface& surface, const Acts::Vector2& loc,
297 double xmeas, double var_u, double var_v) {
298 auto rot = surface.localToGlobalTransform(geometryContext()).rotation();
299 auto tr = surface.localToGlobalTransform(geometryContext()).translation();
300 auto rotl2g = rot.transpose();
301
302 Acts::Matrix<2, 5> a_i;
303 a_i(0, 0) = rotl2g(0, 1);
304 a_i(0, 1) = rotl2g(0, 1) * xmeas;
305 a_i(0, 2) = rotl2g(0, 1) * xmeas * xmeas;
306 a_i(0, 3) = rotl2g(0, 2);
307 a_i(0, 4) = rotl2g(0, 2) * xmeas;
308
309 a_i(1, 0) = rotl2g(1, 1);
310 a_i(1, 1) = rotl2g(1, 1) * xmeas;
311 a_i(1, 2) = rotl2g(1, 1) * xmeas * xmeas;
312 a_i(1, 3) = rotl2g(1, 2);
313 a_i(1, 4) = rotl2g(1, 2) * xmeas;
314
315 Acts::Vector2 offset = (rot.transpose() * tr).topRows<2>();
316 Acts::Vector2 xoffset = {rotl2g(0, 0) * xmeas, rotl2g(1, 0) * xmeas};
317
318 Acts::Matrix<2, 2> w_i = Acts::Matrix<2, 2>::Zero();
319 w_i(0, 0) = 1. / var_u;
320 w_i(1, 1) = 1. / var_v;
321
322 Acts::Vector2 yprime_i = loc + offset - xoffset;
323 y += (a_i.transpose()) * w_i * yprime_i;
324 a += a_i.transpose() * (w_i * a_i);
325 };
326
327 for (auto meas : vmeas) {
328 double xmeas = meas.getGlobalPosition()[0] - xOrigin;
329 const Acts::Surface* hit_surface = geometry().getSurface(meas.getLayerID());
330
331 xhit_.push_back(xmeas);
332 yhit_.push_back(meas.getGlobalPosition()[1]);
333 zhit_.push_back(meas.getGlobalPosition()[2]);
334
335 // strips measure u only; v sits at the strip center
336 Acts::Vector2 loc{meas.getLocalPosition()[0], 0.};
337 add_point(*hit_surface, loc, xmeas, u_error_ * u_error_,
339 }
340
341 if (constraint) {
342 double xmeas = constraint->getGlobalPosition()[0] - xOrigin;
343 Acts::Vector2 loc{constraint->getLocalPosition()[0],
344 constraint->getLocalPosition()[1]};
345 auto cov = constraint->getLocalCovariance();
346 // floor at (1 um)^2 so a missing covariance cannot blow up the weight
347 add_point(*target_surface_, loc, xmeas, std::max<double>(cov[0], 1e-6),
348 std::max<double>(cov[1], 1e-6));
349 }
350
351 Acts::Vector<5> b;
352 b = a.inverse() * y;
353
354 b0_.push_back(b(0));
355 b1_.push_back(b(1));
356 b2_.push_back(b(2));
357 b3_.push_back(b(3));
358 b4_.push_back(b(4));
359
360 // Acts::Vector<5> hlx = Acts::Vector<5>::Zero();
361 Acts::Vector<3> ref{0., 0., 0.};
362
363 // relative_perigee_x is the perigee position in the fit frame (fit-x = ACTS x
364 // - xOrigin). It is used only for evaluating the fitted curve (y, z, slopes).
365 // The PerigeeSurface and seed_pos must use the absolute ACTS x coordinate,
366 // which is perigee_location(0) directly.
367 double relative_perigee_x = perigee_location(0) - xOrigin;
368
369 std::shared_ptr<const Acts::PerigeeSurface> seed_perigee =
370 Acts::Surface::makeShared<Acts::PerigeeSurface>(Acts::Vector3(
371 perigee_location(0), perigee_location(1), perigee_location(2)));
372
373 // in mm — x is absolute ACTS x; y and z evaluated at fit-x =
374 // relative_perigee_x
375 Acts::Vector3 seed_pos{perigee_location(0),
376 b(0) + b(1) * relative_perigee_x +
377 b(2) * relative_perigee_x * relative_perigee_x,
378 b(3) + b(4) * relative_perigee_x};
379 Acts::Vector3 dir{1, b(1) + 2 * b(2) * relative_perigee_x, b(4)};
380 dir /= dir.norm();
381
382 // Momentum at xmeas
383 // R in meters, p in GeV
384 double p = 0.3 * bfield_ * (1. / (2. * abs(b(2)))) * 0.001;
385 // std::cout<<"Momentum "<< p*dir << std::endl;
386
387 // Convert it to MeV since that's what TrackUtils assumes
388 Acts::Vector3 seed_mom = p * dir / Acts::UnitConstants::MeV;
389 double q =
390 b(2) < 0 ? -1 * Acts::UnitConstants::e : +1 * Acts::UnitConstants::e;
391
392 // Linear intersection with the perigee line. TODO:: Use propagator instead
393 // Project the position on the surface.
394 // This is mainly necessary for the perigee surface, where
395 // the mean might not fulfill the perigee condition.
396
397 // mg Aug 2024 .. interect has changed, but just remove boundary check
398 // and change intersection to intersections
399 // auto intersection =
400 // (*seed_perigee).intersect(geometry_context(), seed_pos, dir, false);
401
402 // Acts::FreeVector seed_free = tracking::sim::utils::toFreeParameters(
403 // intersection.intersection.position, seed_mom, q);
404
405 auto intersection =
406 (*seed_perigee).intersect(geometryContext(), seed_pos, dir);
407
408 Acts::FreeVector seed_free = tracking::sim::utils::toFreeParameters(
409 intersection[0].position(), seed_mom, q);
410
411 auto bound_params = Acts::transformFreeToBoundParameters(
412 seed_free, *seed_perigee, geometryContext())
413 .value();
414
415 ldmx_log(trace) << "bound parameters at perigee location" << bound_params;
416
417 Acts::BoundVector stddev;
418 // sigma set to 75% of momentum
419 double sigma_p = 0.75 * p * Acts::UnitConstants::GeV;
420 stddev[Acts::eBoundLoc0] =
421 inflate_factors_[Acts::eBoundLoc0] * 2 * Acts::UnitConstants::mm;
422 stddev[Acts::eBoundLoc1] =
423 inflate_factors_[Acts::eBoundLoc1] * 5 * Acts::UnitConstants::mm;
424 stddev[Acts::eBoundPhi] =
425 inflate_factors_[Acts::eBoundPhi] * 5 * Acts::UnitConstants::degree;
426 stddev[Acts::eBoundTheta] =
427 inflate_factors_[Acts::eBoundTheta] * 5 * Acts::UnitConstants::degree;
428 stddev[Acts::eBoundQOverP] =
429 inflate_factors_[Acts::eBoundQOverP] * (1. / p) * (1. / p) * sigma_p;
430 stddev[Acts::eBoundTime] =
431 inflate_factors_[Acts::eBoundTime] * 1000 * Acts::UnitConstants::ns;
432
433 ldmx_log(debug)
434 << "Making covariance matrix as diagonal matrix with inflated terms";
435 Acts::BoundMatrix bound_cov = stddev.cwiseProduct(stddev).asDiagonal();
436
437 ldmx_log(debug) << "...now putting together the seed track ...";
438
439 ldmx::Track trk = ldmx::Track();
440 // Store the perigee surface position (absolute ACTS coordinates) converted to
441 // LDMX frame so CKFProcessor can reconstruct the same surface.
442 Acts::Vector3 perigee_ldmx =
443 tracking::sim::utils::acts2Ldmx(perigee_location);
444 trk.setPerigeeLocation(perigee_ldmx(0), perigee_ldmx(1), perigee_ldmx(2));
445 trk.setChi2(0.);
446 trk.setNhits(vmeas.size());
447 trk.setNdf(0);
448 trk.setNsharedHits(0);
449 trk.setCharge(q < 0 ? -1 : 1);
450 std::vector<double> v_seed_params(
451 (bound_params).data(),
452 bound_params.data() + bound_params.rows() * bound_params.cols());
453 std::vector<double> v_seed_cov;
454 tracking::sim::utils::flatCov(bound_cov, v_seed_cov);
455 trk.setPerigeeParameters(v_seed_params);
456 trk.setPerigeeCov(v_seed_cov);
457
458 ldmx_log(debug)
459 << "...making the ParticleHypothesis ...assume electron for now";
460 auto part_hypo{Acts::ParticleHypothesis::electron()};
461
462 ldmx_log(debug) << "Making BoundTrackParameters seedParameters";
463 Acts::BoundTrackParameters seed_parameters(
464 seed_perigee, std::move(bound_params), bound_cov, part_hypo);
465
466 ldmx_log(debug) << "Returning seed track";
467 return trk;
468}
469
471 // output_file_->cd();
472 // output_tree_->Write();
473 // output_file_->Close();
474 ldmx_log(info) << "AVG Time/Event: " << std::fixed << std::setprecision(1)
475 << processing_time_ / nevents_ << " ms";
476 ldmx_log(info) << "Total Seeds/Events: " << ntracks_ << "/" << nevents_;
477 for (size_t is = 0; is < seeds_per_strategy_.size(); ++is) {
478 ldmx_log(info) << " strategy '" << strategies_[is]
479 << "' seeds: " << seeds_per_strategy_[is];
480 }
481 ldmx_log(info) << "Seeds discarded due to multiple hits on layers "
482 << ndoubles_;
483 ldmx_log(info) << "not enough seed points " << nmissing_;
484 ldmx_log(info) << " nfailpmin=" << nfailpmin_;
485 ldmx_log(info) << " nfailpmax=" << nfailpmax_;
486 ldmx_log(info) << " nfaild0max=" << nfaild0max_;
487 ldmx_log(info) << " nfaild0min=" << nfaild0min_;
488 ldmx_log(info) << " nfailphicut=" << nfailphi_;
489 ldmx_log(info) << " nfailthetacut=" << nfailtheta_;
490 ldmx_log(info) << " nfailz0max=" << nfailz0max_;
492 ldmx_log(info) << " nfailprefilter=" << nfailprefilter_;
493 }
494}
495
496// Given a strategy, group the hits according to some options
497// Not a good algorithm. The best would be to organize all the hits in sensors
498// *first* then only select the hits that we are interested into. TODO!
499
500bool SeedFinderProcessor::groupStrips(
501 const std::vector<ldmx::Measurement>& measurements,
502 const std::vector<int> strategy) {
503 // std::cout<<"Using stratedy"<<std::endl;
504 // for (auto& e : strategy) {
505 // std::cout<<e<<" ";
506 //}
507 // std::cout<<std::endl;
508
509 for (auto& meas : measurements) {
510 ldmx_log(trace) << meas;
511
512 if (std::find(strategy.begin(), strategy.end(), meas.getLayer()) !=
513 strategy.end()) {
514 ldmx_log(debug) << "Adding measurement from layer_ = " << meas.getLayer();
515 groups_map_[meas.getLayer()].push_back(&meas);
516 }
517
518 } // loop meas
519
520 if (groups_map_.size() < strategy.size())
521 return false;
522 else
523 return true;
524}
525
526// For each strategy, form all the possible combinatorics and form a seedTrack
527// for each of those This will reshuffle all points. (issue?) Will sort the
528// meas_for_seed vector
529
530void SeedFinderProcessor::findSeedsFromMap(
531 std::vector<ldmx::Track>& seeds, const ldmx::Measurements& pmeas,
532 const ldmx::Measurements& fit_constraints) {
533 std::map<int, std::vector<const ldmx::Measurement*>>::iterator groups_iter =
534 groups_map_.begin();
535 // Vector of iterators, one per grouped layer
536 const int k = groups_map_.size();
537 if (k < 1) return;
538 std::vector<std::vector<const ldmx::Measurement*>::iterator> it;
539 it.resize(k);
540
541 unsigned int ikey = 0;
542 for (auto& key : groups_map_) {
543 it[ikey] = key.second.begin();
544 ikey++;
545 }
546
547 // K vectors in an array v[0],v[1].... v[K-1]
548
549 // Loop over all combinations
550 while (it[0] != groups_iter->second.end()) {
551 // process the pointed-to elements
552
553 /*
554 for (int j=0; j<K; j++) {
555 const ldmx::Measurement* meas = (*(it[j]));
556 std::cout<<meas->getGlobalPosition()[0]<<","
557 <<meas->getGlobalPosition()[1]<<","
558 <<meas->getGlobalPosition()[2]<<","<<std::endl;
559 }
560 */
561
562 std::vector<ldmx::Measurement> meas_for_seeds;
563 meas_for_seeds.reserve(k);
564
565 ldmx_log(debug) << " Grouping ";
566
567 for (int j = 0; j < k; j++) {
568 const ldmx::Measurement* meas = (*(it[j]));
569 meas_for_seeds.push_back(*meas);
570 }
571
572 std::sort(meas_for_seeds.begin(), meas_for_seeds.end(),
573 [](const ldmx::Measurement& m1, const ldmx::Measurement& m2) {
574 return m1.getGlobalPosition()[0] < m2.getGlobalPosition()[0];
575 });
576
577 if (meas_for_seeds.size() < k) {
578 nmissing_++;
579 return;
580 }
581
582 ldmx_log(debug) << "making seedTrack";
583
584 Acts::Vector3 perigee{perigee_location_[0], perigee_location_[1],
586
587 // one seed per constraint; nullptr is the unconstrained fit
588 std::vector<const ldmx::Measurement*> constraints;
589 for (const auto& c : fit_constraints) constraints.push_back(&c);
590 if (constraints.empty()) constraints.push_back(nullptr);
591
592 // constrained fits only for combinations whose plain fit passes the
593 // cuts; otherwise the constraint lets many fake combinations through
594 if (prefilter_unconstrained_ && constraints.front() != nullptr &&
595 meas_for_seeds.size() >= 5) {
596 ldmx::Track plain = seedTracker(
597 meas_for_seeds, meas_for_seeds.at(k / 2).getGlobalPosition()[0],
598 perigee, nullptr);
599 b0_.pop_back();
600 b1_.pop_back();
601 b2_.pop_back();
602 b3_.pop_back();
603 b4_.pop_back();
604 const double p = 1. / std::abs(plain.getQoP());
605 if (p < pmin_ || p > pmax_ || std::abs(plain.getZ0()) > z0max_ ||
606 plain.getD0() < d0min_ || plain.getD0() > d0max_ ||
607 std::abs(plain.getPhi()) > phicut_ ||
608 std::abs(plain.getTheta() - piover2_) > thetacut_) {
609 nfailprefilter_++;
610 constraints.clear();
611 }
612 }
613
614 for (const auto* constraint : constraints) {
615 // 5 parameters need 5 equations: one per strip, two per constraint
616 if (meas_for_seeds.size() + (constraint ? 2 : 0) < 5) {
617 nmissing_++;
618 continue;
619 }
620
621 ldmx::Track seed_track = seedTracker(
622 meas_for_seeds, meas_for_seeds.at(k / 2).getGlobalPosition()[0],
623 perigee, constraint);
624
625 bool fail = false;
626
627 // Remove failed fits
628 if (1. / abs(seed_track.getQoP()) < pmin_) {
629 nfailpmin_++;
630 fail = true;
631 } else if (1. / abs(seed_track.getQoP()) > pmax_) {
632 nfailpmax_++;
633 fail = true;
634 }
635
636 // Remove large part of fake tracks and duplicates with the following cuts
637 // for various compatibility checks.
638
639 else if (abs(seed_track.getZ0()) > z0max_) {
640 nfailz0max_++;
641 fail = true;
642 } else if (seed_track.getD0() < d0min_) {
643 nfaild0min_++;
644 fail = true;
645 } else if (seed_track.getD0() > d0max_) {
646 nfaild0max_++;
647 fail = true;
648 } else if (abs(seed_track.getPhi()) > phicut_) {
649 fail = true;
650 nfailphi_++;
651 } else if (abs(seed_track.getTheta() - piover2_) > thetacut_) {
652 fail = true;
653 nfailtheta_++;
654 }
655
656 // If I didn't use the target pseudo measurements in the track finding
657 // I can use them for compatibility with the tagger track
658
659 // the tagger seeder has no tagger_trks_collection, so pmeas is empty
660 if (pmeas.size() > 0) {
661 // I can have multiple target pseudo measurements
662 // A seed is rejected if it is found incompatible with all the target
663 // extrapolations
664
665 // This is set but unused, eventually we will use tagger track position
666 // at target to inform recoil tracking bool tgt_compatible = false;
667 for (auto tgt_pseudomeas : pmeas) {
668 // The d0/z0 are in a frame with the same orientation of the target
669 // surface
670 double delta_loc0 =
671 seed_track.getD0() - tgt_pseudomeas.getLocalPosition()[0];
672 double delta_loc1 =
673 seed_track.getZ0() - tgt_pseudomeas.getLocalPosition()[1];
674
675 if (abs(delta_loc0) < loc0cut_ && abs(delta_loc1) < loc1cut_) {
676 // found at least 1 compatible target location
677 // tgt_compatible = true;
678 break;
679 }
680 }
681 } // pmeas > 0
682
683 if (!fail) {
684 if (truth_matching_tool_->configured()) {
685 auto truth_info = truth_matching_tool_->truthMatch(meas_for_seeds);
686 seed_track.setTrackID(truth_info.track_id_);
687 seed_track.setPdgID(truth_info.pdg_id_);
688 seed_track.setTruthProb(truth_info.truth_prob_);
689 }
690
691 seeds.push_back(seed_track);
692 }
693
694 else {
695 b0_.pop_back();
696 b1_.pop_back();
697 b2_.pop_back();
698 b3_.pop_back();
699 b4_.pop_back();
700 }
701 } // constraints
702
703 // Go to next combination
704 ldmx_log(debug) << "Go to the next combination";
705
706 ++it[k - 1];
707 for (int i = k - 1;
708 (i > 0) && (it[i] == (std::next(groups_iter, i))->second.end()); --i) {
709 it[i] = std::next(groups_iter, i)->second.begin();
710 ++it[i - 1];
711 }
712 }
713} // find seeds
714
715} // namespace reco
716} // namespace tracking
717
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
std::string getName() const
Get the processor name.
Implements an event buffer system for storing event data.
Definition Event.h:40
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:107
std::string getPassName()
Get the current/default pass name.
Definition Event.h:492
Class which represents the process under execution.
Definition Process.h:34
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
std::array< float, 3 > getGlobalPosition() const
Definition Measurement.h:50
void setLocalPosition(const float &meas_u, const float &meas_v)
Set the local position i.e.
Definition Measurement.h:61
std::array< float, 2 > getLocalPosition() const
Definition Measurement.h:67
std::array< float, 2 > getLocalCovariance() const
Definition Measurement.h:84
void setGlobalPosition(const float &meas_x, const float &meas_y, const float &meas_z)
Set the global position i.e.
Definition Measurement.h:42
void setLocalCovariance(const float &cov_uu, const float &cov_vv)
Set cov(U,U) and cov(V, V).
Definition Measurement.h:77
void setTime(const float &meas_t)
Set the measurement time in ns.
Definition Measurement.h:93
Class representing a simulated particle.
Definition SimParticle.h:25
Implementation of a track object.
Definition Track.h:54
std::vector< std::vector< int > > strategy_layers_
Layer lists parsed from strategies_, one per strategy.
bool prefilter_unconstrained_
Constrained fits only after the plain fit passes the cuts.
SeedFinderProcessor(const std::string &name, framework::Process &process)
Constructor.
std::vector< long > seeds_per_strategy_
Seeds found per strategy, for the end of job summary.
std::string out_seed_collection_
The name of the output collection of seeds to be stored.
double pmax_
Maximum cut on the momentum of the seeds.
std::vector< std::string > strategies_
List of stragies for seed finding.
bool use_target_constraint_
Put tagger track positions at the target into the seed fit.
std::string input_hits_collection_
The name of the input hits collection to use in finding seeds..
ldmx::Track seedTracker(const ldmx::Measurements &vmeas, double xOrigin, const Acts::Vector3 &perigee_location, const ldmx::Measurement *constraint)
Fit the strips plus an optional target constraint (nullptr = none)
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
std::shared_ptr< Acts::Surface > target_surface_
Target surface the constraints live on.
void produce(framework::Event &event) override
Run the processor and create a collection of results which indicate if a charge particle can be found...
void onProcessStart() override
Callback for the EventProcessor to take any necessary action when the processing of events starts,...
double pmin_
Minimum cut on the momentum of the seeds.
std::string tagger_trks_collection_
The name of the tagger Tracks (only for Recoil Seeding)
bool warned_ambiguous_tagger_
Warn only once about several tagger track collections.
std::vector< double > perigee_location_
Location of the perigee for the helix track parameters.
double d0max_
Max d0 allowed for the seeds.
void configure(framework::config::Parameters &parameters) override
Configure the processor using the given user specified parameters.
double d0min_
Min d0 allowed for the seeds.
double z0max_
Max z0 allowed for the seeds.
a helper base class providing some methods to shorten access to common conditions used within the tra...
The measurement calibrator can be a function or a class/struct able to retrieve the sim hits containe...