44 std::static_pointer_cast<InterpolatedMagneticField3>(
bField());
46 auto acts_logging_level = Acts::Logging::FATAL;
48 if (
debug_) acts_logging_level = Acts::Logging::VERBOSE;
62 Acts::MultiEigenStepperLoop multi_stepper(map);
68 Acts::Navigator::Config nav_cfg{geometry().getTG()};
69 nav_cfg.resolveMaterial =
true;
70 nav_cfg.resolvePassive =
false;
71 nav_cfg.resolveSensitive =
true;
72 const Acts::Navigator navigator(nav_cfg);
75 GsfPropagator(std::move(multi_stepper), std::move(navigator),
76 Acts::getDefaultLogger(
"GSF_PROP", acts_logging_level));
78 auto bethe_heitler = std::make_shared<Acts::PolynomialBetheHeitlerApprox>(
79 Acts::makeDefaultBetheHeitlerApprox());
81 gsf_ = std::make_unique<GsfFitter>(
82 std::move(gsf_propagator), bethe_heitler,
83 Acts::getDefaultLogger(
"GSF", acts_logging_level));
85 const auto stepper = Acts::EigenStepper<>{map};
88 Acts::getDefaultLogger(
"GSF_EXTRAP", acts_logging_level));
90 propagator_extrap_ = std::make_unique<GsfExtrapPropagator>(
91 Acts::EigenStepper<>{map}, Acts::VoidNavigator{});
92 trk_extrap_ = std::make_shared<std::decay_t<
decltype(*trk_extrap_)>>(
93 *propagator_extrap_, geometryContext(), magneticFieldContext());
96 const auto zero_b_field =
97 std::make_shared<Acts::ConstantBField>(Acts::Vector3(0., 0., 0.));
98 const auto const_b_field = std::make_shared<Acts::ConstantBField>(
99 Acts::Vector3(0., 0.,
bfield_ * Acts::UnitConstants::T));
101 MultiStepper zero_b_multi_stepper(zero_b_field);
104 std::move(zero_b_multi_stepper), navigator,
105 Acts::getDefaultLogger(
"GSF_PROP_ZERO_B", acts_logging_level)),
106 bethe_heitler, Acts::getDefaultLogger(
"GSF_ZERO_B", acts_logging_level));
108 MultiStepper const_b_multi_stepper(const_b_field);
111 std::move(const_b_multi_stepper), navigator,
112 Acts::getDefaultLogger(
"GSF_PROP_CONST_B", acts_logging_level)),
113 bethe_heitler, Acts::getDefaultLogger(
"GSF_CONST_B", acts_logging_level));
115 propagator_extrap_zero_b_ = std::make_unique<GsfExtrapPropagator>(
116 Acts::EigenStepper<>{zero_b_field}, Acts::VoidNavigator{});
118 std::make_shared<std::decay_t<
decltype(*trk_extrap_zero_b_)>>(
119 *propagator_extrap_zero_b_, geometryContext(),
120 magneticFieldContext());
122 propagator_extrap_const_b_ = std::make_unique<GsfExtrapPropagator>(
123 Acts::EigenStepper<>{const_b_field}, Acts::VoidNavigator{});
124 trk_extrap_const_b_ =
125 std::make_shared<std::decay_t<
decltype(*trk_extrap_const_b_)>>(
126 *propagator_extrap_const_b_, geometryContext(),
127 magneticFieldContext());
168 auto t_start = std::chrono::high_resolution_clock::now();
182 const auto& measurements =
188 Acts::GainMatrixUpdater updater;
189 Acts::GsfExtensions<Acts::VectorMultiTrajectory> gsf_extensions;
190 gsf_extensions.updater.connect<
191 &Acts::GainMatrixUpdater::operator()<Acts::VectorMultiTrajectory>>(
193 gsf_extensions.calibrator
195 Acts::VectorMultiTrajectory>>(&calibrator);
198 struct SurfaceAccessor {
199 const Acts::TrackingGeometry* tracking_geometry_;
201 const Acts::Surface* operator()(
const Acts::SourceLink& sourceLink)
const {
202 const auto& index_source_link =
204 return tracking_geometry_->findSurface(index_source_link.geometryId());
208 SurfaceAccessor m_sl_surface_accessor{tg.getTG().get()};
210 gsf_extensions.surfaceAccessor.connect<&SurfaceAccessor::operator()>(
211 &m_sl_surface_accessor);
212 gsf_extensions.mixtureReducer.connect<&Acts::reduceMixtureLargestWeights>();
217 Acts::PropagatorOptions<Acts::StepperPlainOptions,
218 Acts::NavigatorPlainOptions, ActionList>
219 propagator_options(geometryContext(), magneticFieldContext());
221 propagator_options.pathLimit = std::numeric_limits<double>::max();
224 propagator_options.loopProtection =
false;
229 propagator_options.actorList.get<Acts::MaterialInteractor>();
230 m_interactor.multipleScattering =
true;
231 m_interactor.energyLoss =
true;
232 m_interactor.recordInteractions =
false;
236 propagator_options.actorList.get<Acts::detail::SteppingLogger>();
237 s_logger.sterile =
true;
239 propagator_options.stepping.maxStepSize =
247 std::shared_ptr<const Acts::Surface> gsf_ref_surface;
248 Acts::GsfOptions<Acts::VectorMultiTrajectory> gsf_options{
249 geometryContext(), magneticFieldContext(), calibrationContext()};
250 gsf_options.extensions = gsf_extensions;
251 gsf_options.propagatorPlainOptions =
252 static_cast<Acts::PropagatorPlainOptions
>(propagator_options);
259 std::vector<ldmx::Track> out_tracks;
261 Acts::VectorTrackContainer vtc;
262 Acts::VectorMultiTrajectory mtj;
263 Acts::TrackContainer tc{vtc, mtj};
266 n_input_tracks_ +=
static_cast<int>(tracks.size());
268 unsigned int itrk = 0;
269 int n_gsf_ok_evt = 0;
270 int n_tgt_fail_evt = 0;
271 ldmx_log(debug) <<
"Starting GSF processing of " << tracks.size()
275 const GsfFitter& fallback_gsf =
277 auto& fallback_extrap =
278 tagger_tracking_ ? trk_extrap_const_b_ : trk_extrap_zero_b_;
279 const std::string fallback_name = tagger_tracking_ ?
"const-B" :
"zero-B";
281 for (
auto& track : tracks) {
282 ldmx_log(debug) <<
"Processing track " << itrk <<
" with "
283 << track.getMeasurementsIdxs().size() <<
" measurements";
285 std::vector<ldmx::Measurement> meas_on_track;
288 std::vector<Acts::SourceLink> fit_track_source_links;
290 for (
auto imeas : track.getMeasurementsIdxs()) {
291 auto meas = measurements.at(imeas);
292 meas_on_track.push_back(meas);
296 const Acts::Surface* hit_surface =
297 tg.geo::TrackingGeometry::getSurface(meas.getLayerID());
301 fit_track_source_links.push_back(Acts::SourceLink(idx_sl));
305 std::reverse(meas_on_track.begin(), meas_on_track.end());
306 std::reverse(fit_track_source_links.begin(), fit_track_source_links.end());
308 for (
auto m : meas_on_track) {
309 ldmx_log(trace) <<
" Measurement:\n" << m <<
"\n";
312 ldmx_log(debug) <<
" Track bound track parameters preparation:";
317 Acts::Vector3 perigee_acts = tracking::sim::utils::ldmx2Acts(Acts::Vector3(
318 track.getPerigeeX(), track.getPerigeeY(), track.getPerigeeZ()));
319 std::shared_ptr<Acts::PerigeeSurface> perigee =
320 Acts::Surface::makeShared<Acts::PerigeeSurface>(perigee_acts);
322 Acts::BoundTrackParameters trk_btp =
323 tracking::sim::utils::boundTrackParameters(track, perigee);
325 Acts::BoundTrackParameters trk_btp_fit_start = trk_btp;
332 if (tagger_tracking_) {
333 auto opt_tagger_start =
335 if (!opt_tagger_start) {
336 ++n_fieldmap_start_extrap_failed_;
337 ldmx_log(debug) <<
" Field-map pre-fit extrapolation to tagger start "
338 "surface failed, trying "
339 << fallback_name <<
" fallback (itrk=" << itrk <<
")";
342 if (opt_tagger_start) ++n_fallback_start_extrap_recovered_;
344 if (!opt_tagger_start) {
346 <<
" Failed pre-fit extrapolation to tagger start surface (itrk="
348 ++n_start_extrap_failed_;
351 trk_btp_fit_start = *opt_tagger_start;
354 ldmx_log(debug) <<
" Perigee surface (acts): (" << track.getPerigeeX()
355 <<
", " << track.getPerigeeY() <<
", "
356 << track.getPerigeeZ() <<
")";
358 const Acts::BoundVector& trkpars = trk_btp.parameters();
359 ldmx_log(debug) <<
" Perigee parameters (d0, z0, phi, theta, q/p)= ("
360 << trkpars[Acts::eBoundLoc0] <<
", "
361 << trkpars[Acts::eBoundLoc1] <<
", "
362 << trkpars[Acts::eBoundPhi] <<
", "
363 << trkpars[Acts::eBoundTheta] <<
", "
364 << trkpars[Acts::eBoundQOverP] <<
")";
366 const Acts::BoundVector& fit_start_pars = trk_btp_fit_start.parameters();
367 ldmx_log(debug) <<
" GSF start parameters (d0, z0, phi, theta, q/p)= ("
368 << fit_start_pars[Acts::eBoundLoc0] <<
", "
369 << fit_start_pars[Acts::eBoundLoc1] <<
", "
370 << fit_start_pars[Acts::eBoundPhi] <<
", "
371 << fit_start_pars[Acts::eBoundTheta] <<
", "
372 << fit_start_pars[Acts::eBoundQOverP] <<
")";
374 ldmx_log(debug) <<
" About to run GSF fit with "
375 << fit_track_source_links.size() <<
" source links";
379 if (tagger_tracking_) {
384 gsf_options.referenceSurface = &(*gsf_ref_surface);
387 std::optional<Acts::TrackIndexType> fitted_index;
390 auto gsf_refit_result =
gsf_->fit(fit_track_source_links.begin(),
391 fit_track_source_links.end(),
392 trk_btp_fit_start, gsf_options, tc);
394 if (gsf_refit_result.ok()) {
395 fitted_index = gsf_refit_result.value().index();
397 ++n_fieldmap_gsf_failed_;
398 ldmx_log(debug) <<
" Field-map GSF re-fit failed (itrk=" << itrk
399 <<
"): " << gsf_refit_result.error().message()
400 <<
", trying " << fallback_name <<
" fallback";
401 if (n_fieldmap_gsf_failed_ <= 5)
402 ldmx_log(info) <<
" [GSF dbg] fit failed (first few): "
403 << gsf_refit_result.error().message();
405 auto fallback_result = fallback_gsf.fit(
406 fit_track_source_links.begin(), fit_track_source_links.end(),
407 trk_btp_fit_start, gsf_options, tc);
409 if (fallback_result.ok()) {
410 ++n_fallback_gsf_recovered_;
411 fitted_index = fallback_result.value().index();
412 ldmx_log(debug) <<
" Yay! " << fallback_name
413 <<
" GSF succeeded as fallback!";
415 ldmx_log(debug) <<
" " << fallback_name
416 <<
" GSF also failed (itrk=" << itrk
417 <<
"): " << fallback_result.error().message();
428 ldmx_log(debug) <<
" GSF fit succeeded (itrk=" << itrk
429 <<
"), tc.size()=" << tc.size();
431 auto gsftrk = tc.getTrack(*fitted_index);
434 const Acts::BoundVector& perigee_pars = gsftrk.parameters();
435 const Acts::BoundMatrix& trk_cov = gsftrk.covariance();
436 const Acts::Surface& perigee_surface = gsftrk.referenceSurface();
438 ldmx_log(debug) <<
" Reference Surface (acts-x, acts-y, acts-z) = ("
439 << perigee_surface.localToGlobalTransform(geometryContext())
442 << perigee_surface.localToGlobalTransform(geometryContext())
445 << perigee_surface.localToGlobalTransform(geometryContext())
449 ldmx_log(debug) <<
" nTrackStates=" << gsftrk.nTrackStates()
450 <<
" nMeasurements=" << gsftrk.nMeasurements()
451 <<
" chi2=" << gsftrk.chi2();
452 if (gsftrk.nTrackStates() == 0)
453 ldmx_log(info) <<
" [GSF dbg] track has 0 states after fit (itrk="
456 ldmx_log(debug) <<
" Track parameters (d0, z0, phi, theta, q/p)= ("
457 << perigee_pars[Acts::eBoundLoc0] <<
", "
458 << perigee_pars[Acts::eBoundLoc1] <<
", "
459 << perigee_pars[Acts::eBoundPhi] <<
", "
460 << perigee_pars[Acts::eBoundTheta] <<
", "
461 << perigee_pars[Acts::eBoundQOverP] <<
") ";
466 ldmx_log(debug) <<
" Extrapolating to target (itrk=" << itrk <<
")";
470 ++n_fieldmap_target_extrap_failed_;
471 ldmx_log(debug) <<
" Field-map target extrapolation failed, trying "
472 << fallback_name <<
" fallback";
474 if (opt_target) ++n_fallback_target_extrap_recovered_;
478 ldmx_log(debug) <<
" GSF target extrapolation succeeded";
479 auto ts_at_target = tracking::sim::utils::makeTrackState(
480 geometryContext(), *opt_target, ldmx::AtTarget);
481 trk.addTrackState(ts_at_target);
483 trk.setPerigeeParameters(tracking::sim::utils::convertActsToLdmxPars(
484 opt_target->parameters()));
485 if (opt_target->covariance()) {
486 std::vector<double> cov_vec;
487 tracking::sim::utils::flatCov(*(opt_target->covariance()), cov_vec);
488 trk.setPerigeeCov(cov_vec);
490 Acts::Vector3 target_loc_ldmx = tracking::sim::utils::acts2Ldmx(
493 trk.setPerigeeLocation(target_loc_ldmx[0], target_loc_ldmx[1],
497 <<
" GSF target parameters (d0, z0, phi, theta, q/p)= ("
498 << opt_target->parameters()[Acts::eBoundLoc0] <<
", "
499 << opt_target->parameters()[Acts::eBoundLoc1] <<
", "
500 << opt_target->parameters()[Acts::eBoundPhi] <<
", "
501 << opt_target->parameters()[Acts::eBoundTheta] <<
", "
502 << opt_target->parameters()[Acts::eBoundQOverP] <<
")";
504 ++n_target_extrap_failed_;
506 ldmx_log(debug) <<
" GSF target extrapolation failed (itrk=" << itrk
507 <<
"), using GSF fit parameters at reference surface";
508 trk.setPerigeeParameters(
509 tracking::sim::utils::convertActsToLdmxPars(perigee_pars));
510 std::vector<double> v_trk_cov;
511 tracking::sim::utils::flatCov(trk_cov, v_trk_cov);
512 trk.setPerigeeCov(v_trk_cov);
516 if (tagger_tracking_) {
517 auto opt_beam_origin =
519 if (!opt_beam_origin)
523 trk.addTrackState(tracking::sim::utils::makeTrackState(
524 geometryContext(), *opt_beam_origin, ldmx::AtBeamOrigin));
526 ldmx_log(debug) <<
" ECAL extrapolation";
527 auto opt_ecal = trk_extrap_->extrapolate(gsftrk,
ecal_surface_);
530 ++n_fieldmap_ecal_extrap_failed_;
531 ldmx_log(debug) <<
" Field-map ECAL extrapolation failed, trying "
532 << fallback_name <<
" fallback";
533 opt_ecal = fallback_extrap->extrapolate(gsftrk,
ecal_surface_);
534 if (opt_ecal) ++n_fallback_ecal_extrap_recovered_;
538 trk.addTrackState(tracking::sim::utils::makeTrackState(
539 geometryContext(), *opt_ecal, ldmx::AtECAL));
541 ++n_ecal_extrap_failed_;
544 trk.setChi2(gsftrk.chi2());
545 trk.setNhits(gsftrk.nMeasurements());
546 trk.setNdf(gsftrk.nMeasurements() - 5);
547 trk.setCharge(perigee_pars[Acts::eBoundQOverP] > 0 ? 1 : -1);
550 trk.setTrackID(track.getTrackID());
551 trk.setPdgID(track.getPdgID());
552 trk.setTruthProb(track.getTruthProb());
556 ldmx_log(debug) <<
" Added track to output, total tracks = "
557 << (out_tracks.size() + 1);
559 out_tracks.push_back(trk);
563 ldmx_log(debug) <<
"[GSF evt " << nevents_ <<
"] in=" << tracks.size()
564 <<
" gsf_ok=" << n_gsf_ok_evt
565 <<
" tgt_fail=" << n_tgt_fail_evt
566 <<
" out=" << out_tracks.size();
568 n_output_tracks_ +=
static_cast<int>(out_tracks.size());
571 auto t_end = std::chrono::high_resolution_clock::now();
573 std::chrono::duration<double, std::milli>(t_end - t_start).count();