Run the processor.
176 {
177 eventnr_++;
178
179 auto tg{geometry()};
180
181
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
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
199 propagator_options.loopProtection = false;
200
201
202
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
210 auto& s_logger =
211 propagator_options.actionList.get<Acts::detail::SteppingLogger>();
212 s_logger.sterile = true;
213
214 propagator_options.stepping.maxStepSize =
215 propagator_step_size_ * Acts::UnitConstants::mm;
216 propagator_options.maxSteps = propagator_max_steps_;
217
218
219
220
221
222
223
224
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 =
232 input_pass_name_);
233
234
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(
"SimParticles", sim_particles_event_passname_)) {
240 ldmx_log(debug) << "Setting up track truth matching tool";
242 "SimParticles", sim_particles_event_passname_);
243 truth_matching_tool = std::make_shared<tracking::sim::TruthMatchingTool>(
244 particle_map, measurements);
245 }
246
247
248
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
256
257
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
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
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
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
308
309 seed_track_index++;
310 }
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
319
320
321 Acts::MeasurementSelector::Config measurement_selector_cfg = {
322
323 {Acts::GeometryIdentifier(), {{}, {outlier_pval_}, {1u}}},
324 };
325
326 Acts::MeasurementSelector meas_sel{measurement_selector_cfg};
327
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
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
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
379
380
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
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
414
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
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 ,
427 false );
428
429 ldmx_log(debug) << " Checking options: multiple scattering = "
430 << ckf_options.multipleScattering
431 << " energy loss = " << ckf_options.energyLoss;
432
433
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
440 if (!results.ok()) {
441 if (!tagger_tracking_) {
442
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
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
487 for (auto& track : tracks_from_seed) {
488
489 Acts::smoothTrack(geometryContext(), track);
490
493
494
495 bool success = trk_extrap_->trackStateAtSurface(
496 track, target_surface_, ts_at_target, ldmx::TrackStateType::AtTarget);
497
498
499
500 if (!success) {
501 if (tagger_tracking_) {
502
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
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
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 }
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
566
567
568
569
570
571
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
578
579 const Acts::BoundVector& track_pars = track.parameters();
580
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
591 trk.setPerigeeLocation(0, 0, 0);
593 trk.setPerigeeCov(ts_at_target.cov_);
594
595 trk.setChi2(track.chi2());
596 trk.setNhits(track.nMeasurements());
597
598
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
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
615
616 continue;
617 }
618
619
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
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
638
639 }
640
641
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
651 ldmx_log(debug) << " Adding measurement to ldmx::track with "
652 "source link index_ = "
654 ldmx_log(trace) << " Measurement:\n" << ldmx_meas;
655 trk.addMeasurementIndex(sl.
index());
656
657
658 if (ts.hasSmoothed()) {
659 const auto& meas_surface = ts.referenceSurface();
660 const auto& smoothed_params = ts.smoothed();
661
662
663
664
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
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
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
689
690
691
692 const float sensor_thickness = 0.320f;
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
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
717
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
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";
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
744 if (!tagger_tracking_) {
745 ldmx_log(debug) << " Ecal Extrapolation";
747 success = trk_extrap_->trackStateAtSurface(
748 track, ecal_surface, ts_at_ecal, ldmx::TrackStateType::AtECAL);
749
750
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
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
790 ldmx_log(debug)
791 << " > Adding the track candidate to the track collection";
792 tracks.push_back(trk);
793 ntracks_++;
794 }
795 }
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
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
819 event.add(out_trk_collection_, tracks);
820
821 auto end = std::chrono::high_resolution_clock::now();
822
823
824 auto diff = end - start;
825 processing_time_ += std::chrono::duration<double, std::milli>(diff).count();
826}
constexpr Index index() const
Access the index_.
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.
Class representing a simulated particle.
Implementation of a track object.
void setPerigeeParameters(const std::vector< double > &par)
d_0 z_0 phi_0 theta q/p t
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.