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