LDMX Software
CKFProcessor.cxx
1#include "Tracking/Reco/CKFProcessor.h"
2
3#include "Acts/EventData/TrackContainer.hpp"
4#include "Acts/Utilities/TrackHelpers.hpp"
5#include "SimCore/Event/SimParticle.h"
6#include "Tracking/Event/Track.h"
7#include "Tracking/Reco/TruthMatchingTool.h"
8#include "Tracking/Sim/GeometryContainers.h"
9
10//--- C++ StdLib ---//
11#include <algorithm> //std::vector reverse
12#include <iostream>
13#include <typeinfo>
14// eN files
15#include <fstream>
16
17namespace tracking {
18namespace reco {
19
20CKFProcessor::CKFProcessor(const std::string& name, framework::Process& process)
21 : TrackingGeometryUser(name, process) {}
22
24 profiling_map_["setup"] = 0.;
25 profiling_map_["hits"] = 0.;
26 profiling_map_["seeds"] = 0.;
27 profiling_map_["ckf_setup"] = 0.;
28 profiling_map_["ckf_run"] = 0.;
29 profiling_map_["result_loop"] = 0.;
30
31 // Initialize counters
32 nseeds_ = 0;
33 ntracks_ = 0;
34 eventnr_ = 0;
35
36 // Generate a constant magnetic field
37 Acts::Vector3 b_field(0., 0., bfield_ * Acts::UnitConstants::T);
38
39 // Setup a constant magnetic field
40 const auto const_b_field = std::make_shared<Acts::ConstantBField>(b_field);
41
42 // Define the target surface - be careful:
43 // x - downstream
44 // y - left (when looking along x)
45 // z - up
46 // Passing identity here means that your target surface is oriented in the
47 // same way
48 surf_rotation_ = Acts::RotationMatrix3::Zero();
49 // u direction along +Y
50 surf_rotation_(1, 0) = 1;
51 // v direction along +Z
52 surf_rotation_(2, 1) = 1;
53 // w direction along +X
54 surf_rotation_(0, 2) = 1;
55
56 Acts::Vector3 target_pos(0., 0., 0.);
57 Acts::Translation3 target_translation(target_pos);
58 Acts::Transform3 target_transform(target_translation * surf_rotation_);
59
60 // Unbounded surface
61 target_surface_ =
62 Acts::Surface::makeShared<Acts::PlaneSurface>(target_transform);
63
64 // Custom transformation of the interpolated bfield map
65 bool debug_transform = false;
66 auto transform_pos = [this, debug_transform](const Acts::Vector3& pos_) {
67 Acts::Vector3 rot_pos;
68 rot_pos(0) = pos_(1);
69 rot_pos(1) = pos_(2);
70 rot_pos(2) = pos_(0) + DIPOLE_OFFSET;
71
72 // Systematic effect
73 rot_pos(0) += this->map_offset_[0];
74 rot_pos(1) += this->map_offset_[1];
75 rot_pos(2) += this->map_offset_[2];
76
77 // Apply A rotation around the center of the magnet. (I guess offset first
78 // and then rotation)
79
80 if (debug_transform) {
81 std::cout << "PF::DEFAULT3 TRANSFORM" << std::endl;
82 std::cout << "PF::Check:: transforming Pos" << std::endl;
83 std::cout << pos_ << std::endl;
84 std::cout << "TO" << std::endl;
85 std::cout << rot_pos << std::endl;
86 }
87
88 return rot_pos;
89 };
90
91 Acts::RotationMatrix3 rotation = Acts::RotationMatrix3::Identity();
92 double scale = 1.;
93
94 auto transform_b_field = [rotation, scale, debug_transform](
95 const Acts::Vector3& field,
96 const Acts::Vector3& /*pos_*/) {
97 // Rotate the field in tracking coordinates
98 Acts::Vector3 rot_field;
99 rot_field(0) = field(2);
100 rot_field(1) = field(0);
101 rot_field(2) = field(1);
102
103 // Scale the field
104 rot_field = scale * rot_field;
105
106 // Rotate the field
107 rot_field = rotation * rot_field;
108
109 // A distortion scaled by position.
110 if (debug_transform) {
111 std::cout << "PF::DEFAULT3 TRANSFORM" << std::endl;
112 std::cout << "PF::Check:: transforming" << std::endl;
113 std::cout << field << std::endl;
114 std::cout << "TO" << std::endl;
115 std::cout << rot_field << std::endl;
116 }
117
118 return rot_field;
119 };
120
121 // Setup a interpolated bfield map
122 const auto map = std::make_shared<InterpolatedMagneticField3>(
123 loadDefaultBField(field_map_,
124 // default_transformPos,
125 // default_transformBField));
126 transform_pos, transform_b_field));
127
128 auto acts_logging_level = Acts::Logging::FATAL;
129 if (debug_acts_) acts_logging_level = Acts::Logging::VERBOSE;
130
131 // Setup the steppers
132 const auto stepper = Acts::EigenStepper<>{map};
133 const auto const_stepper = Acts::EigenStepper<>{const_b_field};
134 const auto multi_stepper = Acts::MultiEigenStepperLoop{map};
135
136 // Setup the navigator
137 Acts::Navigator::Config nav_cfg{geometry().getTG()};
138 nav_cfg.resolveMaterial = true;
139 nav_cfg.resolvePassive = true;
140 nav_cfg.resolveSensitive = true;
141 const Acts::Navigator navigator(nav_cfg);
142
143 propagator_ = std::make_unique<CkfPropagator>(
144 stepper, navigator,
145 Acts::getDefaultLogger("CKF_PROP", acts_logging_level));
146
147 // Setup the finder / fitters
148 ckf_ = std::make_unique<std::decay_t<decltype(*ckf_)>>(
149 *propagator_, Acts::getDefaultLogger("CKF", acts_logging_level));
150 trk_extrap_ = std::make_shared<std::decay_t<decltype(*trk_extrap_)>>(
151 *propagator_, geometryContext(), magneticFieldContext());
152
153 // Setup zero-B CKF as fallback
154 Acts::ConstantBField zero_b_field(Acts::Vector3(0., 0., 0.));
155 const auto zero_b_stepper = Acts::EigenStepper<>{
156 std::make_shared<Acts::ConstantBField>(zero_b_field)};
157 propagator_zero_b_ =
158 std::make_unique<CkfPropagator>(zero_b_stepper, navigator);
159 ckf_zero_b_ = std::make_unique<std::decay_t<decltype(*ckf_zero_b_)>>(
160 *propagator_zero_b_,
161 Acts::getDefaultLogger("CKF_ZERO_B", acts_logging_level));
162 trk_extrap_zero_b_ =
163 std::make_shared<std::decay_t<decltype(*trk_extrap_zero_b_)>>(
164 *propagator_zero_b_, geometryContext(), magneticFieldContext());
165
166 // Setup const-B (1.5T) CKF as fallback for tagger
167 propagator_const_b_ =
168 std::make_unique<CkfPropagator>(const_stepper, navigator);
169 ckf_const_b_ = std::make_unique<std::decay_t<decltype(*ckf_const_b_)>>(
170 *propagator_const_b_,
171 Acts::getDefaultLogger("CKF_CONST_B", acts_logging_level));
172 trk_extrap_const_b_ =
173 std::make_shared<std::decay_t<decltype(*trk_extrap_const_b_)>>(
174 *propagator_const_b_, geometryContext(), magneticFieldContext());
175} // end of CKFProcessor::onNewRun()
176
178 eventnr_++;
179 // get the tracking geometry from conditions
180 auto tg{geometry()};
181
182 // TODO use global variable instead and call clear;
183
184 std::vector<ldmx::Track> tracks;
185
186 auto start = std::chrono::high_resolution_clock::now();
187
188 nevents_++;
189
190 ACTS_LOCAL_LOGGER(Acts::getDefaultLogger("LDMX Tracking Geometry Maker",
191 Acts::Logging::DEBUG));
192
193 // Move this at the start of the producer
194 Acts::PropagatorOptions<Acts::StepperPlainOptions,
195 Acts::NavigatorPlainOptions, ActionList, AbortList>
196 propagator_options(geometryContext(), magneticFieldContext());
197
198 propagator_options.pathLimit = std::numeric_limits<double>::max();
199 // Activate loop protection at some pt value
200 propagator_options.loopProtection = false;
201 //(startParameters.transverseMomentum() < cfg.ptLoopers);
202
203 // Switch the material interaction on/off & eventually into logging mode
204 auto& m_interactor =
205 propagator_options.actionList.get<Acts::MaterialInteractor>();
206 m_interactor.multipleScattering = true;
207 m_interactor.energyLoss = true;
208 m_interactor.recordInteractions = false;
209
210 // The logger can be switched to sterile, e.g. for timing logging
211 auto& s_logger =
212 propagator_options.actionList.get<Acts::detail::SteppingLogger>();
213 s_logger.sterile = true;
214 // Set a maximum step size
215 propagator_options.stepping.maxStepSize =
216 propagator_step_size_ * Acts::UnitConstants::mm;
217 propagator_options.maxSteps = propagator_max_steps_;
218
219 // #######################//
220 // Kalman Filter algorithm//
221 // #######################//
222
223 // Step 1 - Form the source links
224
225 // a) Loop over the sim Hits
226
227 auto setup = std::chrono::high_resolution_clock::now();
228 profiling_map_["setup"] +=
229 std::chrono::duration<double, std::milli>(setup - start).count();
230
231 const std::vector<ldmx::Measurement> measurements =
232 event.getCollection<ldmx::Measurement>(measurement_collection_,
233 input_pass_name_);
234
235 // check if SimParticleMap is available for truth matching
236 std::shared_ptr<tracking::sim::TruthMatchingTool> truth_matching_tool =
237 nullptr;
238 std::map<int, ldmx::SimParticle> particle_map;
239
240 if (event.exists(sim_particles_coll_name_, sim_particles_event_passname_)) {
241 ldmx_log(debug) << "Setting up track truth matching tool";
242 particle_map = event.getMap<int, ldmx::SimParticle>(
243 sim_particles_coll_name_, sim_particles_event_passname_);
244 truth_matching_tool = std::make_shared<tracking::sim::TruthMatchingTool>(
245 particle_map, measurements);
246 }
247
248 // The mapping between the geometry identifier
249 // and the IndexsourceLink that points to the hit
250 const auto geo_id_sl_map = makeGeoIdSourceLinkMap(tg, measurements);
251
252 auto hits = std::chrono::high_resolution_clock::now();
253 profiling_map_["hits"] +=
254 std::chrono::duration<double, std::milli>(hits - setup).count();
255
256 // ============ Setup the CKF ============
257
258 // Retrieve the seeds
259 const std::vector<ldmx::Track> seed_tracks =
260 event.getCollection<ldmx::Track>(seed_coll_name_, input_pass_name_);
261
262 ldmx_log(info) << "Number of " << seed_coll_name_
263 << " seed tracks = " << seed_tracks.size();
264
265 if (seed_tracks.empty()) {
266 std::vector<ldmx::Track> empty;
267 ldmx_log(warn) << "No seed tracks, returning...";
268 event.add(out_trk_collection_, empty);
269 return;
270 }
271
272 // Run the CKF on each seed and produce a track candidate
273 std::vector<Acts::BoundTrackParameters> start_parameters;
274
275 ldmx_log(debug) << "Transform the seed track to bound parameters";
276 int seed_track_index{0};
277 for (auto& seed : seed_tracks) {
278 // Transform the seed track to bound parameters.
279 // Perigee is stored in LDMX global frame; convert to ACTS frame for
280 // surface.
281 Acts::Vector3 perigee_acts = tracking::sim::utils::ldmx2Acts(Acts::Vector3(
282 seed.getPerigeeX(), seed.getPerigeeY(), seed.getPerigeeZ()));
283 std::shared_ptr<Acts::PerigeeSurface> perigee_surface =
284 Acts::Surface::makeShared<Acts::PerigeeSurface>(perigee_acts);
285
286 Acts::BoundVector param_vec;
287 param_vec << seed.getD0(), seed.getZ0(), seed.getPhi(), seed.getTheta(),
288 seed.getQoP(), seed.getT();
289
290 Acts::BoundSquareMatrix cov_mat =
291 tracking::sim::utils::unpackCov(seed.getPerigeeCov());
292
293 ldmx_log(debug) << " For seed index_ = " << seed_track_index
294 << ": Perigee X / Y / Z = " << seed.getPerigeeX() << " / "
295 << seed.getPerigeeY() << " / " << seed.getPerigeeZ()
296 << ", D0 = " << param_vec[0] << ", Z0 = " << param_vec[1]
297 << ", Phi = " << param_vec[2]
298 << ", Theta = " << param_vec[3]
299 << ", QoP = " << param_vec[4]
300 << ", Time = " << param_vec[5];
301
302 ldmx_log(debug) << " Cov matrix diagonal (" << cov_mat(0, 0) << ", "
303 << cov_mat(1, 1) << ", " << cov_mat(2, 2) << ")";
304
305 // need to set particle hypothesis...set to electron for now...
306 auto part_hypo{Acts::SinglyChargedParticleHypothesis::electron()};
307 start_parameters.push_back(Acts::BoundTrackParameters(
308 perigee_surface, param_vec, cov_mat, part_hypo));
309
310 // This is a global variable for performance checks
311 nseeds_++;
312 // This is just to index_ the seed we are looking at
313 seed_track_index++;
314 } // loop on seeds
315
316 auto seeds = std::chrono::high_resolution_clock::now();
317 profiling_map_["seeds"] +=
318 std::chrono::duration<double, std::milli>(seeds - hits).count();
319
320 Acts::GainMatrixUpdater kf_updater;
321
322 // configuration for the measurement selector. Empty geometry identifier means
323 // applicable to all the detector elements
324
325 Acts::MeasurementSelector::Config measurement_selector_cfg = {
326 // global default: no chi2 cut, only one measurement per surface
327 {Acts::GeometryIdentifier(), {{}, {outlier_pval_}, {1u}}},
328 };
329
330 Acts::MeasurementSelector meas_sel{measurement_selector_cfg};
331
332 tracking::sim::LdmxMeasurementCalibrator calibrator{measurements};
333
334 Acts::CombinatorialKalmanFilterExtensions<TrackContainer> ckf_extensions;
335
336 if (use1_dmeasurements_) {
337 ckf_extensions.calibrator
339 Acts::VectorMultiTrajectory>>(&calibrator);
340 } else {
341 ckf_extensions.calibrator
343 Acts::VectorMultiTrajectory>>(&calibrator);
344 }
345
346 ckf_extensions.updater.connect<
347 &Acts::GainMatrixUpdater::operator()<Acts::VectorMultiTrajectory>>(
348 &kf_updater);
349
350 ckf_extensions.measurementSelector
351 .connect<&Acts::MeasurementSelector::select<Acts::VectorMultiTrajectory>>(
352 &meas_sel);
353
354 ldmx_log(debug) << "SourceLinkAccessor...";
355
356 // Create source link accessor and connect delegate
357 struct SourceLinkAccIt {
358 using BaseIt = decltype(geo_id_sl_map.begin());
359 BaseIt it_;
360
361#pragma GCC diagnostic push
362#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
363
364 using difference_type = typename BaseIt::difference_type;
365 using iterator_category = typename BaseIt::iterator_category;
366 // using value_type = typename BaseIt::value_type::second_type;
367 using value_type = Acts::SourceLink;
368 using pointer = typename BaseIt::pointer;
369 using reference = value_type&;
370#pragma GCC diagnostic pop
371
372 SourceLinkAccIt& operator++() {
373 ++it_;
374 return *this;
375 }
376 bool operator==(const SourceLinkAccIt& other) const {
377 return it_ == other.it_;
378 }
379 bool operator!=(const SourceLinkAccIt& other) const {
380 return !(*this == other);
381 }
382 // const value_type& operator*() const { return it->second; }
383
384 // by value
385 value_type operator*() const { return value_type{it_->second}; }
386 };
387
388 auto source_link_accessor = [&](const Acts::Surface& surface)
389 -> std::pair<SourceLinkAccIt, SourceLinkAccIt> {
390 auto [begin, end] = geo_id_sl_map.equal_range(surface.geometryId());
391 return {SourceLinkAccIt{begin}, SourceLinkAccIt{end}};
392 };
393
394 Acts::SourceLinkAccessorDelegate<SourceLinkAccIt>
395 source_link_accessor_delegate;
396 source_link_accessor_delegate
397 .connect<&decltype(source_link_accessor)::operator(),
398 decltype(source_link_accessor)>(&source_link_accessor);
399
400 ldmx_log(debug) << "Setting up surfaces...";
401
402 std::shared_ptr<const Acts::PerigeeSurface> origin_surface =
403 Acts::Surface::makeShared<Acts::PerigeeSurface>(
404 Acts::Vector3(0., 0., 0.));
405
406 ldmx_log(debug) << "About to run CKF...";
407
408 // run the CKF for all initial track states
409 auto ckf_setup = std::chrono::high_resolution_clock::now();
410 profiling_map_["ckf_setup"] +=
411 std::chrono::duration<double, std::milli>(ckf_setup - seeds).count();
412
413 Acts::VectorTrackContainer vtc;
414 Acts::VectorMultiTrajectory mtj;
415 Acts::TrackContainer tc{vtc, mtj};
416
417 // The number of track candidates (i.e. startParameters.size()) is always
418 // the same as the number of seed tracks
419 ldmx_log(debug) << "Loop on the track candidates";
420 for (size_t track_id = 0u; track_id < start_parameters.size(); ++track_id) {
421 ldmx_log(debug) << "---------------------------";
422 ldmx_log(debug) << "Candidate Track ID = " << track_id;
423 // Define the CKF options here:
424 const Acts::CombinatorialKalmanFilterOptions<SourceLinkAccIt,
425 TrackContainer>
426 ckf_options(TrackingGeometryUser::geometryContext(),
427 TrackingGeometryUser::magneticFieldContext(),
428 TrackingGeometryUser::calibrationContext(),
429 source_link_accessor_delegate, ckf_extensions,
430 propagator_options, true /* multiple scattering */,
431 false /* energy loss */);
432
433 ldmx_log(debug) << " Checking options: multiple scattering = "
434 << ckf_options.multipleScattering
435 << " energy loss = " << ckf_options.energyLoss;
436
437 // Try field-map CKF first
438 auto results =
439 ckf_->findTracks(start_parameters.at(track_id), ckf_options, tc);
440
441 auto start_params = start_parameters.at(track_id).parameters().transpose();
442
443 // If field-map CKF fails, try appropriate fallback based on tracking system
444 if (!results.ok()) {
445 if (!tagger_tracking_) {
446 // Recoil tracking: try zero-B CKF as fallback
447 n_fieldmap_ckf_failed_recoil_++;
448 ldmx_log(debug)
449 << " Field-map CKF failed, trying zero-B CKF fallback";
450 results = ckf_zero_b_->findTracks(start_parameters.at(track_id),
451 ckf_options, tc);
452 if (results.ok()) {
453 n_zerob_ckf_recovered_recoil_++;
454 ldmx_log(debug) << " Yay! Zero-B CKF succeeded as fallback!";
455 } else {
456 ldmx_log(debug) << " Zero-B CKF also failed!";
457 }
458 } else {
459 // Tagger tracking: try const-B (1.5T) CKF as fallback
460 n_fieldmap_ckf_failed_tagger_++;
461 ldmx_log(debug)
462 << " Field-map CKF failed, trying const-B (1.5T) CKF fallback";
463 results = ckf_const_b_->findTracks(start_parameters.at(track_id),
464 ckf_options, tc);
465 if (results.ok()) {
466 n_constb_ckf_recovered_tagger_++;
467 ldmx_log(debug) << " Yay! Const-B CKF succeeded as fallback!";
468 } else {
469 ldmx_log(debug) << " Const-B CKF also failed!";
470 }
471 }
472 }
473
474 ldmx_log(debug)
475 << " Checking CKF success for track candidate with params: "
476 << " D0 = " << start_params[0] << " Z0 = " << start_params[1]
477 << ", Phi = " << start_params[2] << " Theta = " << start_params[3]
478 << ", QoP = " << start_params[4] << " Time = " << start_params[5];
479 if (not results.ok()) {
480 ldmx_log(debug) << " CKF failed!";
481 continue;
482 } else {
483 ldmx_log(debug) << " CKF succeded!";
484 }
485
486 auto& tracks_from_seed = results.value();
487 if (tracks_from_seed.size() != 1) {
488 ldmx_log(info) << " tracksFromSeed.size = " << tracks_from_seed.size();
489 }
490 // For now it seems this loop is only looping on a single element
491 for (auto& track : tracks_from_seed) {
492 // do the track smoothing...this is not done in the CKF code anymore
493 Acts::smoothTrack(geometryContext(), track); // from TrackHelpers
494 // Build the output Track
495 ldmx::Track trk;
496
497 // Extrapolate to the target surface
498 auto opt_target = trk_extrap_->extrapolate(track, target_surface_);
499
500 if (!opt_target) {
501 if (tagger_tracking_) {
502 n_fieldmap_target_extrap_failed_tagger_++;
503 ldmx_log(debug) << " Field-map target extrapolation failed, "
504 "trying const-B (1.5T) fallback";
505 opt_target = trk_extrap_const_b_->extrapolate(track, target_surface_);
506 if (opt_target)
507 n_constb_target_extrap_recovered_tagger_++;
508 else
509 ldmx_log(debug) << " Both field-map and Const-B target "
510 "extrapolation failed!";
511 } else {
512 n_fieldmap_target_extrap_failed_recoil_++;
513 ldmx_log(debug) << " Field-map target extrapolation failed, "
514 "trying zero-B fallback";
515 opt_target = trk_extrap_zero_b_->extrapolate(track, target_surface_);
516 if (opt_target)
517 n_zerob_target_extrap_recovered_recoil_++;
518 else
519 ldmx_log(debug)
520 << " Both field-map and Zero-B target extrapolation failed!";
521 }
522 }
523
524 if (!opt_target) {
525 ldmx_log(debug) << " Could not extrapolate to target! nhits = "
526 << track.nMeasurements() << " Printing track states:";
527 for (const auto ts : track.trackStatesReversed()) {
528 if (ts.hasSmoothed())
529 ldmx_log(debug) << " Parameters: " << ts.smoothed().transpose();
530 else
531 ldmx_log(debug) << " Track state not smoothed!";
532 }
533 ldmx_log(debug) << " ...skipping this track candidate...";
534 continue;
535 }
536
537 ldmx_log(debug) << " Successfully obtained TrackState at target";
538
539 // Build TrackState in LDMX coordinates and add to track
540 auto ts_at_target = tracking::sim::utils::makeTrackState(
541 geometryContext(), *opt_target, ldmx::AtTarget);
542 trk.addTrackState(ts_at_target);
543
544 ldmx_log(debug) << " Position at target (LDMX): ("
545 << ts_at_target.pos_[0] << ", " << ts_at_target.pos_[1]
546 << ", " << ts_at_target.pos_[2] << ") mm"
547 << " Momentum: (" << ts_at_target.mom_[0] << ", "
548 << ts_at_target.mom_[1] << ", " << ts_at_target.mom_[2]
549 << ") GeV";
550
551 // Update ACTS track reference surface (needed for downstream ACTS usage)
552 track.setReferenceSurface(target_surface_);
553 track.parameters() = opt_target->parameters();
554
555 // Store perigee (bound) parameters at the target for convenience
556 trk.setPerigeeParameters(tracking::sim::utils::convertActsToLdmxPars(
557 opt_target->parameters()));
558 if (opt_target->covariance()) {
559 std::vector<double> cov_vec;
560 tracking::sim::utils::flatCov(*(opt_target->covariance()), cov_vec);
561 trk.setPerigeeCov(cov_vec);
562 }
563 // Perigee location: target surface origin rotated to LDMX frame
564 Acts::Vector3 target_loc_ldmx = tracking::sim::utils::acts2Ldmx(
565 target_surface_->transform(geometryContext()).translation());
566 trk.setPerigeeLocation(target_loc_ldmx[0], target_loc_ldmx[1],
567 target_loc_ldmx[2]);
568
569 trk.setChi2(track.chi2());
570 trk.setNhits(track.nMeasurements());
571 trk.setNdf(track.nMeasurements() - 5);
572 trk.setNsharedHits(track.nSharedHits());
573 trk.setCharge(opt_target->parameters()[Acts::eBoundQOverP] > 0 ? 1 : -1);
574
575 // At least min_hits hits and p > 50 MeV
576 if ((trk.getNhits() <= min_hits_) ||
577 (std::abs(1. / trk.getQoP()) <= 0.05)) {
578 ldmx_log(debug)
579 << " > Track candidate did NOT meet the requirements: Nhits = "
580 << trk.getNhits() << " and p = " << std::abs(1. / trk.getQoP())
581 << " GeV";
582 continue;
583 }
584
585 // Add measurements to the final track
586 ldmx_log(debug) << " Add measurements to the final track from "
587 << track.nTrackStates() << " TrackStates with "
588 << track.nMeasurements() << " measurements";
589
590 int trk_state_index{0};
591 for (const auto ts : track.trackStatesReversed()) {
592 // Check TrackStates Quality
593 ldmx_log(debug) << " Checking Track State index_ = "
594 << trk_state_index << " at location "
595 << ts.referenceSurface()
596 .transform(geometryContext())
597 .translation()
598 .transpose();
599
600 if (ts.hasSmoothed()) {
601 ldmx_log(debug) << " Smoothed track parameters: "
602 << ts.smoothed().transpose();
603 // ldmx_log(debug) << " Smoothed covariance mtx:\n" <<
604 // ts.smoothedCovariance();
605 }
606
607 // Check if the track state is a measurement
608 auto type_flags = ts.typeFlags();
609
610 if (type_flags.test(Acts::TrackStateFlag::MeasurementFlag) &&
611 ts.hasUncalibratedSourceLink()) {
613 ts.getUncalibratedSourceLink()
614 .template get<acts_examples::IndexSourceLink>();
615
616 ldmx::Measurement ldmx_meas = measurements.at(sl.index());
617 ldmx_log(debug) << " Adding measurement to ldmx::track with "
618 "source link index_ = "
619 << sl.index();
620 ldmx_log(trace) << " Measurement:\n" << ldmx_meas;
621 trk.addMeasurementIndex(sl.index());
622
623 // Extract path length from the track state based on the angle
624 if (ts.hasSmoothed()) {
625 const auto& meas_surface = ts.referenceSurface();
626 const auto& smoothed_params = ts.smoothed();
627
628 // Get the momentum from the track parameters
629 // momentum = p * direction where direction = (sin(theta)*cos(phi),
630 // sin(theta)*sin(phi), cos(theta))
631 float p_inv = smoothed_params[Acts::eBoundQOverP];
632 float p = 1.0f / std::abs(p_inv);
633 float theta = smoothed_params[Acts::eBoundTheta];
634 float phi = smoothed_params[Acts::eBoundPhi];
635
636 Acts::Vector3 global_momentum(p * std::sin(theta) * std::cos(phi),
637 p * std::sin(theta) * std::sin(phi),
638 p * std::cos(theta));
639
640 // Get the local frame (transform from global to local)
641 auto local_frame_transform =
642 meas_surface.transform(geometryContext());
643 Acts::Vector3 local_momentum =
644 local_frame_transform.rotation().transpose() * global_momentum;
645
646 // Calculate local angle components (tangent of angles)
647 float phi_u = (local_momentum.z() != 0)
648 ? local_momentum.x() / local_momentum.z()
649 : 0.;
650 float phi_v = (local_momentum.z() != 0)
651 ? local_momentum.y() / local_momentum.z()
652 : 0.;
653
654 // Calculate the total angle from the local angle components
655 // tan(angle) = sqrt(phi_u^2 + phi_v^2)
656 // cos(angle) = 1 / sqrt(1 + tan(angle)^2)
657 // path_length = thickness / cos(angle)
658 const float sensor_thickness = 0.320f; // mm
659 float tan_angle_sq = phi_u * phi_u + phi_v * phi_v;
660 float cos_angle = 1.0f / std::sqrt(1.0f + tan_angle_sq);
661 float path_length = sensor_thickness / cos_angle;
662
663 ldmx_log(debug) << " Local angles: phi_u = " << phi_u
664 << ", phi_v = " << phi_v
665 << "; Path length = " << path_length << " mm";
666
667 // Calculate dE/dx and add to track (in MeV/mm)
668 float edep = ldmx_meas.getEdep();
669 float dedx = edep / path_length;
670 trk.addDedxMeasurement(dedx);
671
672 ldmx_log(debug) << " Edep = " << edep
673 << " MeV, dE/dx = " << dedx << " MeV/mm";
674 }
675 } else {
676 ldmx_log(debug) << " This TrackState is not a measurement";
677 }
678 trk_state_index++;
679 }
680
681 ldmx_log(debug) << " Starting extrapolations";
682 // Extrapolations
683 // To ECAL
684 const double ecal_scoring_plane = 240.5;
685 Acts::Vector3 pos(ecal_scoring_plane, 0., 0.);
686 Acts::Translation3 surf_translation(pos);
687 Acts::Transform3 surf_transform(surf_translation * surf_rotation_);
688 const std::shared_ptr<Acts::PlaneSurface> ecal_surface =
689 Acts::Surface::makeShared<Acts::PlaneSurface>(surf_transform);
690
691 // Beam Origin unbounded surface
692 const std::shared_ptr<Acts::Surface> beam_origin_surface =
693 tracking::sim::utils::unboundSurface(-700);
694
695 if (tagger_tracking_) {
696 ldmx_log(debug) << " Beam Origin Extrapolation";
697 auto opt_beam_origin =
698 trk_extrap_->extrapolate(track, beam_origin_surface);
699 if (opt_beam_origin) {
700 trk.addTrackState(tracking::sim::utils::makeTrackState(
701 geometryContext(), *opt_beam_origin, ldmx::AtBeamOrigin));
702 ldmx_log(debug)
703 << " Successfully obtained TrackState at beam origin";
704 }
705 }
706
707 // Recoil Extrapolation to ECAL only
708 if (!tagger_tracking_) {
709 ldmx_log(debug) << " Ecal Extrapolation";
710 auto opt_ecal = trk_extrap_->extrapolate(track, ecal_surface);
711
712 if (!opt_ecal) {
713 n_fieldmap_ecal_extrap_failed_recoil_++;
714 ldmx_log(debug) << " Field-map ECAL extrapolation failed, trying "
715 "zero-B fallback";
716 opt_ecal = trk_extrap_zero_b_->extrapolate(track, ecal_surface);
717 if (opt_ecal)
718 n_zerob_ecal_extrap_recovered_recoil_++;
719 else
720 ldmx_log(debug)
721 << " Both field-map and Zero-B ECAL extrapolation failed!";
722 }
723
724 if (opt_ecal) {
725 auto ts_at_ecal = tracking::sim::utils::makeTrackState(
726 geometryContext(), *opt_ecal, ldmx::AtECAL);
727 trk.addTrackState(ts_at_ecal);
728 ldmx_log(debug) << " Successfully obtained TrackState at ECAL";
729 ldmx_log(debug) << " Position at ECAL (LDMX): ("
730 << ts_at_ecal.pos_[0] << ", " << ts_at_ecal.pos_[1]
731 << ", " << ts_at_ecal.pos_[2] << ") mm";
732 }
733 }
734
735 // Truth matching
736 if (truth_matching_tool) {
737 auto truth_info = truth_matching_tool->truthMatch(trk);
738 trk.setTrackID(truth_info.track_id_);
739 trk.setPdgID(truth_info.pdg_id_);
740 trk.setTruthProb(truth_info.truth_prob_);
741 }
742
743 // Adding the track candidate to the track collection
744 ldmx_log(debug)
745 << " > Adding the track candidate to the track collection";
746 tracks.push_back(trk);
747 ntracks_++;
748 } // // loop on tracksFromSeed (which usually has 1 element)
749 } // loop seed track parameters (i.e. track candidates)
750
751 ldmx_log(info) << "Number of CKF tracks " << tracks.size();
752
753 auto ckf_run = std::chrono::high_resolution_clock::now();
754 profiling_map_["ckf_run"] +=
755 std::chrono::duration<double, std::milli>(ckf_run - ckf_setup).count();
756
757 // Calculating Shared Hits
758 auto shared_hits = computeSharedHits(
759 tracks, measurements, tg, tracking::sim::utils::sourceLinkHash,
760 tracking::sim::utils::sourceLinkEquality);
761 for (std::size_t i_track = 0; i_track < shared_hits.size(); ++i_track) {
762 tracks[i_track].setNsharedHits(shared_hits[i_track].size());
763 for (auto idx : shared_hits[i_track]) {
764 tracks[i_track].addSharedIndex(idx);
765 }
766 }
767
768 auto result_loop = std::chrono::high_resolution_clock::now();
769 profiling_map_["result_loop"] +=
770 std::chrono::duration<double, std::milli>(result_loop - ckf_run).count();
771
772 // Add the tracks to the event
773 event.add(out_trk_collection_, tracks);
774
775 auto end = std::chrono::high_resolution_clock::now();
776 // long long microseconds =
777 // std::chrono::duration_cast<std::chrono::microseconds>(end-start).count();
778 auto diff = end - start;
779 processing_time_ += std::chrono::duration<double, std::milli>(diff).count();
780} // end of produce()
781
783 if (use1_dmeasurements_)
784 ldmx_log(debug) << "Use1Dmeasurements = " << std::boolalpha
785 << use1_dmeasurements_;
786 if (remove_stereo_)
787 ldmx_log(debug) << "Remove_stereo = " << std::boolalpha << remove_stereo_;
788}
789
791 ldmx_log(info) << "--------------------------------- ";
792 ldmx_log(info) << "Found " << ntracks_ << " tracks / " << nseeds_
793 << " nseeds";
794 ldmx_log(info) << "AVG Time/Event: " << std::fixed << std::setprecision(1)
795 << processing_time_ / nevents_ << " ms";
796 ldmx_log(info) << "Breakdown::";
797 ldmx_log(info) << " setup Avg Time/Event = " << std::fixed
798 << std::setprecision(3) << profiling_map_["setup"] / nevents_
799 << " ms";
800 ldmx_log(info) << " hits Avg Time/Event = " << std::fixed
801 << std::setprecision(2) << profiling_map_["hits"] / nevents_
802 << " ms";
803 ldmx_log(info) << " seeds Avg Time/Event = " << std::fixed
804 << std::setprecision(3) << profiling_map_["seeds"] / nevents_
805 << " ms";
806 ldmx_log(info) << " ckf_setup Avg Time/Event = " << std::fixed
807 << std::setprecision(3)
808 << profiling_map_["ckf_setup"] / nevents_ << " ms";
809 ldmx_log(info) << " ckf_run Avg Time/Event = " << std::fixed
810 << std::setprecision(3) << profiling_map_["ckf_run"] / nevents_
811 << " ms";
812 ldmx_log(info) << " result_loop Avg Time/Event = " << std::fixed
813 << std::setprecision(1)
814 << profiling_map_["result_loop"] / nevents_ << " ms";
815
816 // CKF fallback statistics
817 ldmx_log(info) << "CKF Fallback Statistics::";
818 if (tagger_tracking_) {
819 ldmx_log(info) << " Tagger: Field-map CKF failed "
820 << n_fieldmap_ckf_failed_tagger_
821 << " times, const-B CKF recovered "
822 << n_constb_ckf_recovered_tagger_ << " ("
823 << (n_fieldmap_ckf_failed_tagger_ > 0
824 ? 100.0 * n_constb_ckf_recovered_tagger_ /
825 n_fieldmap_ckf_failed_tagger_
826 : 0.0)
827 << "%)";
828
829 // Extrapolation fallback statistics for tagger
830 ldmx_log(info) << "Extrapolation Fallback Statistics::";
831 ldmx_log(info) << " Tagger Target: Field-map extrap failed "
832 << n_fieldmap_target_extrap_failed_tagger_
833 << " times, const-B extrap recovered "
834 << n_constb_target_extrap_recovered_tagger_ << " ("
835 << (n_fieldmap_target_extrap_failed_tagger_ > 0
836 ? 100.0 * n_constb_target_extrap_recovered_tagger_ /
837 n_fieldmap_target_extrap_failed_tagger_
838 : 0.0)
839 << "%)";
840 }
841
842 if (!tagger_tracking_) {
843 ldmx_log(info) << " Recoil: Field-map CKF failed "
844 << n_fieldmap_ckf_failed_recoil_
845 << " times, zero-B CKF recovered "
846 << n_zerob_ckf_recovered_recoil_ << " ("
847 << (n_fieldmap_ckf_failed_recoil_ > 0
848 ? 100.0 * n_zerob_ckf_recovered_recoil_ /
849 n_fieldmap_ckf_failed_recoil_
850 : 0.0)
851 << "%)";
852
853 // Extrapolation fallback statistics
854 ldmx_log(info) << "Extrapolation Fallback Statistics::";
855 ldmx_log(info) << " Recoil Target: Field-map extrap failed "
856 << n_fieldmap_target_extrap_failed_recoil_
857 << " times, zero-B extrap recovered "
858 << n_zerob_target_extrap_recovered_recoil_ << " ("
859 << (n_fieldmap_target_extrap_failed_recoil_ > 0
860 ? 100.0 * n_zerob_target_extrap_recovered_recoil_ /
861 n_fieldmap_target_extrap_failed_recoil_
862 : 0.0)
863 << "%)";
864 ldmx_log(info) << " Recoil ECAL: Field-map extrap failed "
865 << n_fieldmap_ecal_extrap_failed_recoil_
866 << " times, zero-B extrap recovered "
867 << n_zerob_ecal_extrap_recovered_recoil_ << " ("
868 << (n_fieldmap_ecal_extrap_failed_recoil_ > 0
869 ? 100.0 * n_zerob_ecal_extrap_recovered_recoil_ /
870 n_fieldmap_ecal_extrap_failed_recoil_
871 : 0.0)
872 << "%)";
873 }
874}
875
877 dumpobj_ = parameters.get<bool>("dumpobj", 0);
878 pionstates_ = parameters.get<int>("pionstates", 0);
879
880 bfield_ = parameters.get<double>("bfield", -1.5);
881 const_b_field_ = parameters.get<bool>("const_b_field", false);
882 field_map_ = parameters.get<std::string>("field_map");
883 propagator_step_size_ = parameters.get<double>("propagator_step_size", 200.);
884 propagator_max_steps_ = parameters.get<int>("propagator_max_steps", 10000);
885 measurement_collection_ = parameters.get<std::string>(
886 "measurement_collection", "TaggerMeasurements");
887 outlier_pval_ = parameters.get<double>("outlier_pval_", 3.84);
888
889 debug_acts_ = parameters.get<bool>("debug_acts", false);
890
891 remove_stereo_ = parameters.get<bool>("remove_stereo", false);
892 use1_dmeasurements_ = parameters.get<bool>("use1Dmeasurements", true);
893 min_hits_ = parameters.get<int>("min_hits", 7);
894
895 // Ckf specific options
896 use_extrapolate_location_ =
897 parameters.get<bool>("use_extrapolate_location", true);
898 extrapolate_location_ =
899 parameters.get<std::vector<double>>("extrapolate_location", {0., 0., 0.});
900 use_seed_perigee_ = parameters.get<bool>("use_seed_perigee", false);
901
902 // seeds from the event
903 seed_coll_name_ = parameters.get<std::string>("seed_coll_name", "seedTracks");
904
905 sim_particles_coll_name_ =
906 parameters.get<std::string>("sim_particles_coll_name");
907 sim_particles_event_passname_ =
908 parameters.get<std::string>("sim_particles_event_passname");
909
910 // output track collection
911 out_trk_collection_ =
912 parameters.get<std::string>("out_trk_collection", "Tracks");
913
914 // keep track on which system tracking is running
915 tagger_tracking_ = parameters.get<bool>("tagger_tracking", true);
916
917 // BField Systematics
918 map_offset_ =
919 parameters.get<std::vector<double>>("map_offset_", {0., 0., 0.});
920
921 input_pass_name_ = parameters.get<std::string>("input_pass_name");
922} // end of configure()
923
924auto CKFProcessor::makeGeoIdSourceLinkMap(
926 const std::vector<ldmx::Measurement>& measurements)
927 -> std::unordered_multimap<Acts::GeometryIdentifier,
929 std::unordered_multimap<Acts::GeometryIdentifier,
931 geo_id_sl_map;
932
933 ldmx_log(debug) << "The makeGeoIdSourceLinkMap has " << measurements.size()
934 << " measurements";
935
936 // Check the hits associated to the surfaces
937 for (unsigned int i_meas = 0; i_meas < measurements.size(); i_meas++) {
938 ldmx::Measurement meas = measurements.at(i_meas);
939 unsigned int layerid = meas.getLayerID();
940
941 const Acts::Surface* hit_surface = tg.getSurface(layerid);
942
943 if (hit_surface) {
944 // Transform the ldmx space point from global to local and store the
945 // information
946
947 acts_examples::IndexSourceLink idx_sl(hit_surface->geometryId(), i_meas);
948 // mg aug 2024 ... these don't print statements
949 // don't compile using v36 in Acts...figure out later
950 /*
951 ldmx_log(debug)
952 << "Insert measurement on surface located at::"
953 << hit_surface->transform(geometry_context()).translation();
954 ldmx_log(debug) << "and geoId::" << hit_surface->geometryId();
955
956 ldmx_log(debug) << "Surface info::"
957 << std::tie(*hit_surface, geometry_context());
958 */
959 geo_id_sl_map.insert(std::make_pair(hit_surface->geometryId(), idx_sl));
960
961 } else
962 ldmx_log(debug) << getName() << "::HIT " << i_meas << " at layer_"
963 << (measurements.at(i_meas)).getLayerID()
964 << " is not associated to any surface?!";
965 }
966
967 return geo_id_sl_map;
968}
969
970template <typename geometry_t, typename source_link_hash_t,
971 typename source_link_equality_t>
972std::vector<std::vector<std::size_t>> CKFProcessor::computeSharedHits(
973 std::vector<ldmx::Track> tracks, std::vector<ldmx::Measurement> meas_coll,
974 geometry_t& tg, source_link_hash_t&& sourceLinkHash,
975 source_link_equality_t&& sourceLinkEquality) const {
976 auto measurement_index_map =
977 std::unordered_map<Acts::SourceLink, std::size_t, source_link_hash_t,
978 source_link_equality_t>(0, sourceLinkHash,
979 sourceLinkEquality);
980
981 std::vector<std::vector<std::size_t>> measurements_per_track;
982 boost::container::flat_map<std::size_t,
983 boost::container::flat_set<std::size_t>>
984 tracks_per_measurement;
985 std::vector<std::size_t> shared_measurements_per_track;
986 auto number_of_tracks = 0;
987
988 // Iterate through all input tracks, collect their properties like measurement
989 // count and chi2 and fill the measurement map in order to relate tracks to
990 // each other if they have shared hits.
991 for (const auto& track : tracks) {
992 // Kick out tracks that do not fulfill our initial requirements
993 // if (track.getNhits() < n_measurements_min_) {
994 // continue;
995 // }
996
997 std::vector<std::size_t> measurements;
998 for (auto imeas : track.getMeasurementsIdxs()) {
999 auto meas = meas_coll.at(imeas);
1000 const Acts::Surface* hit_surface = tg.getSurface(meas.getLayerID());
1001 // Store the index_ source link
1002 acts_examples::IndexSourceLink idx_sl(hit_surface->geometryId(), imeas);
1003 Acts::SourceLink source_link = Acts::SourceLink(idx_sl);
1004
1005 auto emplace = measurement_index_map.try_emplace(
1006 source_link, measurement_index_map.size());
1007 measurements.push_back(emplace.first->second);
1008 }
1009
1010 measurements_per_track.push_back(std::move(measurements));
1011
1012 ++number_of_tracks;
1013 }
1014
1015 // Now we relate measurements to tracks
1016 for (std::size_t i_track = 0; i_track < number_of_tracks; ++i_track) {
1017 for (auto i_measurement : measurements_per_track[i_track]) {
1018 tracks_per_measurement[i_measurement].insert(i_track);
1019 }
1020 }
1021
1022 // Finally, we can accumulate the number of shared measurements per track
1023 shared_measurements_per_track = std::vector<std::size_t>(number_of_tracks, 0);
1024
1025 std::vector<std::vector<std::size_t>> shared_measurement_idxs_per_track;
1026 for (std::size_t i_track = 0; i_track < number_of_tracks; ++i_track) {
1027 std::vector<std::size_t> shared_measurement_idxs;
1028 for (auto i_measurement : measurements_per_track[i_track]) {
1029 if (tracks_per_measurement[i_measurement].size() > 1) {
1030 ++shared_measurements_per_track[i_track];
1031 shared_measurement_idxs.push_back(i_measurement);
1032 }
1033 }
1034 shared_measurement_idxs_per_track.push_back(shared_measurement_idxs);
1035 }
1036 return shared_measurement_idxs_per_track;
1037}
1038
1039} // namespace reco
1040} // namespace tracking
1041
#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
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:105
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
int getLayerID() const
float getEdep() const
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
Implementation of a track object.
Definition Track.h:53
void produce(framework::Event &event) override
Run the processor.
void configure(framework::config::Parameters &parameters) override
Configure the processor using the given user specified parameters.
void onNewRun(const ldmx::RunHeader &rh) override
onNewRun is the first function called for each processor after the conditions are fully configured an...
CKFProcessor(const std::string &name, framework::Process &process)
Constructor.
int nseeds_
n seeds and n tracks
void onProcessStart() override
Callback for the EventProcessor to take any necessary action when the processing of events starts,...
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
a helper base class providing some methods to shorten access to common conditions used within the tra...
void calibrate1d(const Acts::GeometryContext &, const Acts::CalibrationContext &, const Acts::SourceLink &genericSourceLink, typename traj_t::TrackStateProxy trackState) const
Find the measurement corresponding to the source link.
void calibrate(const Acts::GeometryContext &, const Acts::CalibrationContext &, const Acts::SourceLink &genericSourceLink, typename traj_t::TrackStateProxy trackState) const
Find the measurement corresponding to the source link.
The measurement calibrator can be a function or a class/struct able to retrieve the sim hits containe...