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