31 std::static_pointer_cast<InterpolatedMagneticField3>(
bField());
33 auto acts_logging_level = Acts::Logging::FATAL;
35 if (
debug_) acts_logging_level = Acts::Logging::VERBOSE;
49 Acts::MultiEigenStepperLoop multi_stepper(map);
55 Acts::Navigator::Config nav_cfg{geometry().getTG()};
56 nav_cfg.resolveMaterial =
true;
57 nav_cfg.resolvePassive =
false;
58 nav_cfg.resolveSensitive =
true;
59 const Acts::Navigator navigator(nav_cfg);
62 GsfPropagator(std::move(multi_stepper), std::move(navigator),
63 Acts::getDefaultLogger(
"GSF_PROP", acts_logging_level));
65 auto bethe_heitler = std::make_shared<Acts::PolynomialBetheHeitlerApprox>(
66 Acts::makeDefaultBetheHeitlerApprox());
68 gsf_ = std::make_unique<GsfFitter>(
69 std::move(gsf_propagator), bethe_heitler,
70 Acts::getDefaultLogger(
"GSF", acts_logging_level));
72 const auto stepper = Acts::EigenStepper<>{map};
75 Acts::getDefaultLogger(
"GSF_EXTRAP", acts_logging_level));
77 propagator_extrap_ = std::make_unique<GsfExtrapPropagator>(
78 Acts::EigenStepper<>{map}, Acts::VoidNavigator{});
79 trk_extrap_ = std::make_shared<std::decay_t<
decltype(*trk_extrap_)>>(
80 *propagator_extrap_, geometryContext(), magneticFieldContext());
83 const auto zero_b_field =
84 std::make_shared<Acts::ConstantBField>(Acts::Vector3(0., 0., 0.));
85 const auto const_b_field = std::make_shared<Acts::ConstantBField>(
86 Acts::Vector3(0., 0.,
bfield_ * Acts::UnitConstants::T));
88 MultiStepper zero_b_multi_stepper(zero_b_field);
91 std::move(zero_b_multi_stepper), navigator,
92 Acts::getDefaultLogger(
"GSF_PROP_ZERO_B", acts_logging_level)),
93 bethe_heitler, Acts::getDefaultLogger(
"GSF_ZERO_B", acts_logging_level));
95 MultiStepper const_b_multi_stepper(const_b_field);
98 std::move(const_b_multi_stepper), navigator,
99 Acts::getDefaultLogger(
"GSF_PROP_CONST_B", acts_logging_level)),
100 bethe_heitler, Acts::getDefaultLogger(
"GSF_CONST_B", acts_logging_level));
102 propagator_extrap_zero_b_ = std::make_unique<GsfExtrapPropagator>(
103 Acts::EigenStepper<>{zero_b_field}, Acts::VoidNavigator{});
105 std::make_shared<std::decay_t<
decltype(*trk_extrap_zero_b_)>>(
106 *propagator_extrap_zero_b_, geometryContext(),
107 magneticFieldContext());
109 propagator_extrap_const_b_ = std::make_unique<GsfExtrapPropagator>(
110 Acts::EigenStepper<>{const_b_field}, Acts::VoidNavigator{});
111 trk_extrap_const_b_ =
112 std::make_shared<std::decay_t<
decltype(*trk_extrap_const_b_)>>(
113 *propagator_extrap_const_b_, geometryContext(),
114 magneticFieldContext());
155 auto t_start = std::chrono::high_resolution_clock::now();
169 const auto& measurements =
175 Acts::GainMatrixUpdater updater;
176 Acts::GsfExtensions<Acts::VectorMultiTrajectory> gsf_extensions;
177 gsf_extensions.updater.connect<
178 &Acts::GainMatrixUpdater::operator()<Acts::VectorMultiTrajectory>>(
180 gsf_extensions.calibrator
182 Acts::VectorMultiTrajectory>>(&calibrator);
185 struct SurfaceAccessor {
186 const Acts::TrackingGeometry* tracking_geometry_;
188 const Acts::Surface* operator()(
const Acts::SourceLink& sourceLink)
const {
189 const auto& index_source_link =
191 return tracking_geometry_->findSurface(index_source_link.geometryId());
195 SurfaceAccessor m_sl_surface_accessor{tg.getTG().get()};
197 gsf_extensions.surfaceAccessor.connect<&SurfaceAccessor::operator()>(
198 &m_sl_surface_accessor);
199 gsf_extensions.mixtureReducer.connect<&Acts::reduceMixtureLargestWeights>();
204 Acts::PropagatorOptions<Acts::StepperPlainOptions,
205 Acts::NavigatorPlainOptions, ActionList>
206 propagator_options(geometryContext(), magneticFieldContext());
208 propagator_options.pathLimit = std::numeric_limits<double>::max();
211 propagator_options.loopProtection =
false;
216 propagator_options.actorList.get<Acts::MaterialInteractor>();
217 m_interactor.multipleScattering =
true;
218 m_interactor.energyLoss =
true;
219 m_interactor.recordInteractions =
false;
223 propagator_options.actorList.get<Acts::detail::SteppingLogger>();
224 s_logger.sterile =
true;
226 propagator_options.stepping.maxStepSize =
234 std::shared_ptr<const Acts::Surface> gsf_ref_surface;
235 Acts::GsfOptions<Acts::VectorMultiTrajectory> gsf_options{
236 geometryContext(), magneticFieldContext(), calibrationContext()};
237 gsf_options.extensions = gsf_extensions;
238 gsf_options.propagatorPlainOptions =
239 static_cast<Acts::PropagatorPlainOptions
>(propagator_options);
246 std::vector<ldmx::Track> out_tracks;
248 Acts::VectorTrackContainer vtc;
249 Acts::VectorMultiTrajectory mtj;
250 Acts::TrackContainer tc{vtc, mtj};
253 n_input_tracks_ +=
static_cast<int>(tracks.size());
255 unsigned int itrk = 0;
256 int n_gsf_ok_evt = 0;
257 int n_tgt_fail_evt = 0;
258 ldmx_log(debug) <<
"Starting GSF processing of " << tracks.size()
262 const GsfFitter& fallback_gsf =
264 auto& fallback_extrap =
265 tagger_tracking_ ? trk_extrap_const_b_ : trk_extrap_zero_b_;
266 const std::string fallback_name = tagger_tracking_ ?
"const-B" :
"zero-B";
268 for (
auto& track : tracks) {
269 ldmx_log(debug) <<
"Processing track " << itrk <<
" with "
270 << track.getMeasurementsIdxs().size() <<
" measurements";
272 std::vector<ldmx::Measurement> meas_on_track;
275 std::vector<Acts::SourceLink> fit_track_source_links;
277 for (
auto imeas : track.getMeasurementsIdxs()) {
278 auto meas = measurements.at(imeas);
279 meas_on_track.push_back(meas);
283 const Acts::Surface* hit_surface =
284 tg.geo::TrackingGeometry::getSurface(meas.getLayerID());
288 fit_track_source_links.push_back(Acts::SourceLink(idx_sl));
292 std::reverse(meas_on_track.begin(), meas_on_track.end());
293 std::reverse(fit_track_source_links.begin(), fit_track_source_links.end());
295 for (
auto m : meas_on_track) {
296 ldmx_log(trace) <<
" Measurement:\n" << m <<
"\n";
299 ldmx_log(debug) <<
" Track bound track parameters preparation:";
304 Acts::Vector3 perigee_acts = tracking::sim::utils::ldmx2Acts(Acts::Vector3(
305 track.getPerigeeX(), track.getPerigeeY(), track.getPerigeeZ()));
306 std::shared_ptr<Acts::PerigeeSurface> perigee =
307 Acts::Surface::makeShared<Acts::PerigeeSurface>(perigee_acts);
309 Acts::BoundTrackParameters trk_btp =
310 tracking::sim::utils::boundTrackParameters(track, perigee);
312 Acts::BoundTrackParameters trk_btp_fit_start = trk_btp;
319 if (tagger_tracking_) {
320 auto opt_tagger_start =
322 if (!opt_tagger_start) {
323 ++n_fieldmap_start_extrap_failed_;
324 ldmx_log(debug) <<
" Field-map pre-fit extrapolation to tagger start "
325 "surface failed, trying "
326 << fallback_name <<
" fallback (itrk=" << itrk <<
")";
329 if (opt_tagger_start) ++n_fallback_start_extrap_recovered_;
331 if (!opt_tagger_start) {
333 <<
" Failed pre-fit extrapolation to tagger start surface (itrk="
335 ++n_start_extrap_failed_;
338 trk_btp_fit_start = *opt_tagger_start;
341 ldmx_log(debug) <<
" Perigee surface (acts): (" << track.getPerigeeX()
342 <<
", " << track.getPerigeeY() <<
", "
343 << track.getPerigeeZ() <<
")";
345 const Acts::BoundVector& trkpars = trk_btp.parameters();
346 ldmx_log(debug) <<
" Perigee parameters (d0, z0, phi, theta, q/p)= ("
347 << trkpars[Acts::eBoundLoc0] <<
", "
348 << trkpars[Acts::eBoundLoc1] <<
", "
349 << trkpars[Acts::eBoundPhi] <<
", "
350 << trkpars[Acts::eBoundTheta] <<
", "
351 << trkpars[Acts::eBoundQOverP] <<
")";
353 const Acts::BoundVector& fit_start_pars = trk_btp_fit_start.parameters();
354 ldmx_log(debug) <<
" GSF start parameters (d0, z0, phi, theta, q/p)= ("
355 << fit_start_pars[Acts::eBoundLoc0] <<
", "
356 << fit_start_pars[Acts::eBoundLoc1] <<
", "
357 << fit_start_pars[Acts::eBoundPhi] <<
", "
358 << fit_start_pars[Acts::eBoundTheta] <<
", "
359 << fit_start_pars[Acts::eBoundQOverP] <<
")";
361 ldmx_log(debug) <<
" About to run GSF fit with "
362 << fit_track_source_links.size() <<
" source links";
366 if (tagger_tracking_) {
371 gsf_options.referenceSurface = &(*gsf_ref_surface);
374 std::optional<Acts::TrackIndexType> fitted_index;
377 auto gsf_refit_result =
gsf_->fit(fit_track_source_links.begin(),
378 fit_track_source_links.end(),
379 trk_btp_fit_start, gsf_options, tc);
381 if (gsf_refit_result.ok()) {
382 fitted_index = gsf_refit_result.value().index();
384 ++n_fieldmap_gsf_failed_;
385 ldmx_log(debug) <<
" Field-map GSF re-fit failed (itrk=" << itrk
386 <<
"): " << gsf_refit_result.error().message()
387 <<
", trying " << fallback_name <<
" fallback";
388 if (n_fieldmap_gsf_failed_ <= 5)
389 ldmx_log(info) <<
" [GSF dbg] fit failed (first few): "
390 << gsf_refit_result.error().message();
392 auto fallback_result = fallback_gsf.fit(
393 fit_track_source_links.begin(), fit_track_source_links.end(),
394 trk_btp_fit_start, gsf_options, tc);
396 if (fallback_result.ok()) {
397 ++n_fallback_gsf_recovered_;
398 fitted_index = fallback_result.value().index();
399 ldmx_log(debug) <<
" Yay! " << fallback_name
400 <<
" GSF succeeded as fallback!";
402 ldmx_log(debug) <<
" " << fallback_name
403 <<
" GSF also failed (itrk=" << itrk
404 <<
"): " << fallback_result.error().message();
415 ldmx_log(debug) <<
" GSF fit succeeded (itrk=" << itrk
416 <<
"), tc.size()=" << tc.size();
418 auto gsftrk = tc.getTrack(*fitted_index);
421 const Acts::BoundVector& perigee_pars = gsftrk.parameters();
422 const Acts::BoundMatrix& trk_cov = gsftrk.covariance();
423 const Acts::Surface& perigee_surface = gsftrk.referenceSurface();
425 ldmx_log(debug) <<
" Reference Surface (acts-x, acts-y, acts-z) = ("
426 << perigee_surface.localToGlobalTransform(geometryContext())
429 << perigee_surface.localToGlobalTransform(geometryContext())
432 << perigee_surface.localToGlobalTransform(geometryContext())
436 ldmx_log(debug) <<
" nTrackStates=" << gsftrk.nTrackStates()
437 <<
" nMeasurements=" << gsftrk.nMeasurements()
438 <<
" chi2=" << gsftrk.chi2();
439 if (gsftrk.nTrackStates() == 0)
440 ldmx_log(info) <<
" [GSF dbg] track has 0 states after fit (itrk="
443 ldmx_log(debug) <<
" Track parameters (d0, z0, phi, theta, q/p)= ("
444 << perigee_pars[Acts::eBoundLoc0] <<
", "
445 << perigee_pars[Acts::eBoundLoc1] <<
", "
446 << perigee_pars[Acts::eBoundPhi] <<
", "
447 << perigee_pars[Acts::eBoundTheta] <<
", "
448 << perigee_pars[Acts::eBoundQOverP] <<
") ";
453 ldmx_log(debug) <<
" Extrapolating to target (itrk=" << itrk <<
")";
457 ++n_fieldmap_target_extrap_failed_;
458 ldmx_log(debug) <<
" Field-map target extrapolation failed, trying "
459 << fallback_name <<
" fallback";
461 if (opt_target) ++n_fallback_target_extrap_recovered_;
465 ldmx_log(debug) <<
" GSF target extrapolation succeeded";
466 auto ts_at_target = tracking::sim::utils::makeTrackState(
467 geometryContext(), *opt_target, ldmx::AtTarget);
468 trk.addTrackState(ts_at_target);
470 trk.setPerigeeParameters(tracking::sim::utils::convertActsToLdmxPars(
471 opt_target->parameters()));
472 if (opt_target->covariance()) {
473 std::vector<double> cov_vec;
474 tracking::sim::utils::flatCov(*(opt_target->covariance()), cov_vec);
475 trk.setPerigeeCov(cov_vec);
477 Acts::Vector3 target_loc_ldmx = tracking::sim::utils::acts2Ldmx(
480 trk.setPerigeeLocation(target_loc_ldmx[0], target_loc_ldmx[1],
484 <<
" GSF target parameters (d0, z0, phi, theta, q/p)= ("
485 << opt_target->parameters()[Acts::eBoundLoc0] <<
", "
486 << opt_target->parameters()[Acts::eBoundLoc1] <<
", "
487 << opt_target->parameters()[Acts::eBoundPhi] <<
", "
488 << opt_target->parameters()[Acts::eBoundTheta] <<
", "
489 << opt_target->parameters()[Acts::eBoundQOverP] <<
")";
491 ++n_target_extrap_failed_;
493 ldmx_log(debug) <<
" GSF target extrapolation failed (itrk=" << itrk
494 <<
"), using GSF fit parameters at reference surface";
495 trk.setPerigeeParameters(
496 tracking::sim::utils::convertActsToLdmxPars(perigee_pars));
497 std::vector<double> v_trk_cov;
498 tracking::sim::utils::flatCov(trk_cov, v_trk_cov);
499 trk.setPerigeeCov(v_trk_cov);
503 if (tagger_tracking_) {
504 auto opt_beam_origin =
506 if (!opt_beam_origin)
510 trk.addTrackState(tracking::sim::utils::makeTrackState(
511 geometryContext(), *opt_beam_origin, ldmx::AtBeamOrigin));
513 ldmx_log(debug) <<
" ECAL extrapolation";
514 auto opt_ecal = trk_extrap_->extrapolate(gsftrk,
ecal_surface_);
517 ++n_fieldmap_ecal_extrap_failed_;
518 ldmx_log(debug) <<
" Field-map ECAL extrapolation failed, trying "
519 << fallback_name <<
" fallback";
520 opt_ecal = fallback_extrap->extrapolate(gsftrk,
ecal_surface_);
521 if (opt_ecal) ++n_fallback_ecal_extrap_recovered_;
525 trk.addTrackState(tracking::sim::utils::makeTrackState(
526 geometryContext(), *opt_ecal, ldmx::AtECAL));
528 ++n_ecal_extrap_failed_;
531 trk.setChi2(gsftrk.chi2());
532 trk.setNhits(gsftrk.nMeasurements());
533 trk.setNdf(gsftrk.nMeasurements() - 5);
534 trk.setCharge(perigee_pars[Acts::eBoundQOverP] > 0 ? 1 : -1);
537 trk.setTrackID(track.getTrackID());
538 trk.setPdgID(track.getPdgID());
539 trk.setTruthProb(track.getTruthProb());
543 ldmx_log(debug) <<
" Added track to output, total tracks = "
544 << (out_tracks.size() + 1);
546 out_tracks.push_back(trk);
550 ldmx_log(debug) <<
"[GSF evt " << nevents_ <<
"] in=" << tracks.size()
551 <<
" gsf_ok=" << n_gsf_ok_evt
552 <<
" tgt_fail=" << n_tgt_fail_evt
553 <<
" out=" << out_tracks.size();
555 n_output_tracks_ +=
static_cast<int>(out_tracks.size());
558 auto t_end = std::chrono::high_resolution_clock::now();
560 std::chrono::duration<double, std::milli>(t_end - t_start).count();