LDMX Software
GSFProcessor.cxx
1#include "Tracking/Reco/GSFProcessor.h"
2
3#include <algorithm>
4#include <chrono>
5#include <iomanip>
6
7#include "Acts/EventData/SourceLink.hpp"
8#include "Tracking/Event/Track.h"
9
10namespace tracking {
11namespace reco {
12
13GSFProcessor::GSFProcessor(const std::string& name, framework::Process& process)
14 : TrackingGeometryUser(name, process) {}
15
17 beam_origin_surface_ = tracking::sim::utils::unboundSurface(-700);
18 // 1mm inside tagger ACTS volume outer boundary (~-618mm), 1.5mm upstream of
19 // L1 at x=-615.5mm
20 tagger_start_surface_ = tracking::sim::utils::unboundSurface(-617.);
21 target_surface_ = tracking::sim::utils::unboundSurface(0.);
22 ecal_surface_ = tracking::sim::utils::unboundSurface(240.5);
23
24 // Setup a interpolated bfield map
25 if (field_map_.empty())
26 loadBField();
27 else
29 const auto map =
30 std::static_pointer_cast<InterpolatedMagneticField3>(bField());
31
32 auto acts_logging_level = Acts::Logging::FATAL;
33
34 if (debug_) acts_logging_level = Acts::Logging::VERBOSE;
35
36 // Setup the GSF Fitter
37
38 // Stepper
39 // Acts::MixtureReductionMethod finalReductionMethod;
40 // const auto multi_stepper = Acts::MultiEigenStepperLoop{map};
41
42 // Acts::ComponentMergeMethod reductionMethod =
43 // Acts::ComponentMergeMethod::eMaxWeight;
44 // Acts::MultiEigenStepperLoop multi_stepper(
45 // map, reductionMethod,
46 // Acts::getDefaultLogger("GSF_STEP", acts_loggingLevel));
47
48 Acts::MultiEigenStepperLoop multi_stepper(map);
49 // Detailed Stepper
50
51 // Acts::MultiEigenStepperLoop multi_stepper(map, finalReductionMethod);
52
53 // Navigator
54 Acts::Navigator::Config nav_cfg{geometry().getTG()};
55 nav_cfg.resolveMaterial = true;
56 nav_cfg.resolvePassive = false;
57 nav_cfg.resolveSensitive = true;
58 const Acts::Navigator navigator(nav_cfg);
59
60 auto gsf_propagator =
61 GsfPropagator(std::move(multi_stepper), std::move(navigator),
62 Acts::getDefaultLogger("GSF_PROP", acts_logging_level));
63
64 auto bethe_heitler = std::make_shared<Acts::PolynomialBetheHeitlerApprox>(
65 Acts::makeDefaultBetheHeitlerApprox());
66
67 gsf_ = std::make_unique<std::decay_t<decltype(*gsf_)>>(
68 std::move(gsf_propagator), std::move(bethe_heitler),
69 Acts::getDefaultLogger("GSF", acts_logging_level));
70
71 const auto stepper = Acts::EigenStepper<>{map};
72 propagator_ = std::make_unique<Propagator>(
73 stepper, navigator,
74 Acts::getDefaultLogger("GSF_EXTRAP", acts_logging_level));
75
76 propagator_extrap_ = std::make_unique<GsfExtrapPropagator>(
77 Acts::EigenStepper<>{map}, Acts::VoidNavigator{});
78 trk_extrap_ = std::make_shared<std::decay_t<decltype(*trk_extrap_)>>(
79 *propagator_extrap_, geometryContext(), magneticFieldContext());
80}
81
84 parameters.get<std::string>("out_trk_collection", "GSFTracks");
85
87 parameters.get<std::string>("track_collection", "TaggerTracks");
89 parameters.get<std::string>("meas_collection", "DigiTaggerSimHits");
90
91 track_passname_ = parameters.get<std::string>("track_passname");
92 meas_passname_ = parameters.get<std::string>("meas_passname");
94 parameters.get<std::string>("track_collection_event_passname");
96 parameters.get<std::string>("meas_collection_event_passname");
97
98 max_components_ = parameters.get<int>("max_components", 4);
99 abort_on_error_ = parameters.get<bool>("abort_on_error", false);
101 parameters.get<bool>("disable_all_material_handling", false);
102 weight_cutoff_ = parameters.get<double>("weight_cutoff_", 1.0e-4);
103
104 propagator_max_steps_ = parameters.get<int>("propagator_max_steps", 10000);
105 propagator_step_size_ = parameters.get<double>("propagator_step_size", 200.);
106 field_map_ = parameters.get<std::string>("field_map");
107 use_perigee_ = parameters.get<bool>("usePerigee", false);
108
109 debug_ = parameters.get<bool>("debug", false);
110 tagger_tracking_ = parameters.get<bool>("tagger_tracking", true);
111
112 // final_reduction_method_ =
113 // parameters.get<double>("finalReductionMethod",);
114} // end of configure()
115
117 auto t_start = std::chrono::high_resolution_clock::now();
118
119 // General Setup
120
121 auto tg{geometry()};
122
123 // Retrieve the tracks
125 return;
126 const auto& tracks =
127 event.getCollection<ldmx::Track>(track_collection_, track_passname_);
128
129 // Retrieve the measurements
131 const auto& measurements =
133
134 tracking::sim::LdmxMeasurementCalibrator calibrator{measurements};
135
136 // GSF Setup
137 Acts::GainMatrixUpdater updater;
138 Acts::GsfExtensions<Acts::VectorMultiTrajectory> gsf_extensions;
139 gsf_extensions.updater.connect<
140 &Acts::GainMatrixUpdater::operator()<Acts::VectorMultiTrajectory>>(
141 &updater);
142 gsf_extensions.calibrator
144 Acts::VectorMultiTrajectory>>(&calibrator);
145
146 // Surface Accessor
147 struct SurfaceAccessor {
148 const Acts::TrackingGeometry* tracking_geometry_;
149
150 const Acts::Surface* operator()(const Acts::SourceLink& sourceLink) const {
151 const auto& index_source_link =
152 sourceLink.get<acts_examples::IndexSourceLink>();
153 return tracking_geometry_->findSurface(index_source_link.geometryId());
154 }
155 };
156
157 SurfaceAccessor m_sl_surface_accessor{tg.getTG().get()};
158 // m_slSurfaceAccessor.trackingGeometry = tg.getTG();
159 gsf_extensions.surfaceAccessor.connect<&SurfaceAccessor::operator()>(
160 &m_sl_surface_accessor);
161 gsf_extensions.mixtureReducer.connect<&Acts::reduceMixtureLargestWeights>();
162
163 // Propagator Options
164
165 // Move this at the start of the producer
166 Acts::PropagatorOptions<Acts::StepperPlainOptions,
167 Acts::NavigatorPlainOptions, ActionList>
168 propagator_options(geometryContext(), magneticFieldContext());
169
170 propagator_options.pathLimit = std::numeric_limits<double>::max();
171
172 // Activate loop protection at some pt value
173 propagator_options.loopProtection = false;
174 //(startParameters.transverseMomentum() < cfg.ptLoopers);
175
176 // Switch the material interaction on/off & eventually into logging mode
177 auto& m_interactor =
178 propagator_options.actorList.get<Acts::MaterialInteractor>();
179 m_interactor.multipleScattering = true;
180 m_interactor.energyLoss = true;
181 m_interactor.recordInteractions = false;
182
183 // The logger can be switched to sterile, e.g. for timing logging
184 auto& s_logger =
185 propagator_options.actorList.get<Acts::detail::SteppingLogger>();
186 s_logger.sterile = true;
187 // Set a maximum step size
188 propagator_options.stepping.maxStepSize =
189 propagator_step_size_ * Acts::UnitConstants::mm;
190 propagator_options.maxSteps = propagator_max_steps_;
191
192 // Electron hypothesis
193 // propagator_options.mass = 0.511 * Acts::UnitConstants::MeV;
194
195 // GSF options will be configured per-track
196 std::shared_ptr<const Acts::Surface> gsf_ref_surface;
197 Acts::GsfOptions<Acts::VectorMultiTrajectory> gsf_options{
198 geometryContext(), magneticFieldContext(), calibrationContext()};
199 gsf_options.extensions = gsf_extensions;
200 gsf_options.propagatorPlainOptions =
201 static_cast<Acts::PropagatorPlainOptions>(propagator_options);
202 gsf_options.maxComponents = max_components_;
203 gsf_options.weightCutoff = weight_cutoff_;
204 gsf_options.abortOnError = abort_on_error_;
205 gsf_options.disableAllMaterialHandling = disable_all_material_handling_;
206
207 // Output track container
208 std::vector<ldmx::Track> out_tracks;
209
210 Acts::VectorTrackContainer vtc;
211 Acts::VectorMultiTrajectory mtj;
212 Acts::TrackContainer tc{vtc, mtj};
213
214 // Loop on tracks
215 n_input_tracks_ += static_cast<int>(tracks.size());
216
217 unsigned int itrk = 0;
218 int n_gsf_ok_evt = 0;
219 int n_tgt_fail_evt = 0;
220 ldmx_log(debug) << "Starting GSF processing of " << tracks.size()
221 << " tracks";
222
223 for (auto& track : tracks) {
224 ldmx_log(debug) << "Processing track " << itrk << " with "
225 << track.getMeasurementsIdxs().size() << " measurements";
226 // Retrieve measurements on track
227 std::vector<ldmx::Measurement> meas_on_track;
228
229 // std::vector<ActsExamples::IndexSourceLink> fit_trackSourceLinks;
230 std::vector<Acts::SourceLink> fit_track_source_links;
231
232 for (auto imeas : track.getMeasurementsIdxs()) {
233 auto meas = measurements.at(imeas);
234 meas_on_track.push_back(meas);
235
236 // Retrieve the surface
237
238 const Acts::Surface* hit_surface =
239 tg.geo::TrackingGeometry::getSurface(meas.getLayerID());
240
241 // Store the index source link
242 acts_examples::IndexSourceLink idx_sl(hit_surface->geometryId(), imeas);
243 fit_track_source_links.push_back(Acts::SourceLink(idx_sl));
244 }
245
246 // Reverse the order of the vectors
247 std::reverse(meas_on_track.begin(), meas_on_track.end());
248 std::reverse(fit_track_source_links.begin(), fit_track_source_links.end());
249
250 for (auto m : meas_on_track) {
251 ldmx_log(trace) << " Measurement:\n" << m << "\n";
252 }
253
254 ldmx_log(debug) << " Track bound track parameters preparation:";
255
256 // Reconstruct BoundTrackParameters at perigee (target) from stored params.
257 // perigee_ is stored in LDMX frame; rotate to ACTS frame for surface
258 // creation.
259 Acts::Vector3 perigee_acts = tracking::sim::utils::ldmx2Acts(Acts::Vector3(
260 track.getPerigeeX(), track.getPerigeeY(), track.getPerigeeZ()));
261 std::shared_ptr<Acts::PerigeeSurface> perigee =
262 Acts::Surface::makeShared<Acts::PerigeeSurface>(perigee_acts);
263
264 Acts::BoundTrackParameters trk_btp =
265 tracking::sim::utils::boundTrackParameters(track, perigee);
266
267 Acts::BoundTrackParameters trk_btp_fit_start = trk_btp;
268
269 // For tagger: backward-extrapolate (via VoidNavigator) from the target
270 // perigee (x=0mm) to just inside the tagger outer boundary (x≈-650mm), then
271 // run the GSF forward (+x) through L1→L7. The CKF stores tagger track
272 // perigees at the target, so we must back-propagate before handing off to
273 // the GSF.
274 if (tagger_tracking_) {
275 auto opt_tagger_start =
276 trk_extrap_->extrapolate(trk_btp, tagger_start_surface_);
277 if (!opt_tagger_start) {
278 ldmx_log(debug)
279 << " Failed pre-fit extrapolation to tagger start surface (itrk="
280 << itrk << ")";
281 ++n_gsf_failed_;
282 continue;
283 }
284 trk_btp_fit_start = *opt_tagger_start;
285 }
286
287 ldmx_log(debug) << " Perigee surface (acts): (" << track.getPerigeeX()
288 << ", " << track.getPerigeeY() << ", "
289 << track.getPerigeeZ() << ")";
290
291 const Acts::BoundVector& trkpars = trk_btp.parameters();
292 ldmx_log(debug) << " Perigee parameters (d0, z0, phi, theta, q/p)= ("
293 << trkpars[Acts::eBoundLoc0] << ", "
294 << trkpars[Acts::eBoundLoc1] << ", "
295 << trkpars[Acts::eBoundPhi] << ", "
296 << trkpars[Acts::eBoundTheta] << ", "
297 << trkpars[Acts::eBoundQOverP] << ")";
298
299 const Acts::BoundVector& fit_start_pars = trk_btp_fit_start.parameters();
300 ldmx_log(debug) << " GSF start parameters (d0, z0, phi, theta, q/p)= ("
301 << fit_start_pars[Acts::eBoundLoc0] << ", "
302 << fit_start_pars[Acts::eBoundLoc1] << ", "
303 << fit_start_pars[Acts::eBoundPhi] << ", "
304 << fit_start_pars[Acts::eBoundTheta] << ", "
305 << fit_start_pars[Acts::eBoundQOverP] << ")";
306
307 ldmx_log(debug) << " About to run GSF fit with "
308 << fit_track_source_links.size() << " source links";
309
310 // GSF reference surface: for tagger use the start surface (x=-648mm, inside
311 // geometry), for recoil use the target (x=0mm).
312 if (tagger_tracking_) {
313 gsf_ref_surface = tagger_start_surface_;
314 } else {
315 gsf_ref_surface = target_surface_;
316 }
317 gsf_options.referenceSurface = &(*gsf_ref_surface);
318
319 auto gsf_refit_result =
320 gsf_->fit(fit_track_source_links.begin(), fit_track_source_links.end(),
321 trk_btp_fit_start, gsf_options, tc);
322
323 if (!gsf_refit_result.ok()) {
324 ldmx_log(debug) << " GSF re-fit failed (itrk=" << itrk
325 << "): " << gsf_refit_result.error().message();
326 if (n_gsf_failed_ < 5)
327 ldmx_log(info) << " [GSF dbg] fit failed (first few): "
328 << gsf_refit_result.error().message();
329 ++n_gsf_failed_;
330 continue;
331 }
332
333 ++n_gsf_ok_evt;
334 ldmx_log(debug) << " GSF fit succeeded (itrk=" << itrk
335 << "), tc.size()=" << tc.size();
336
337 auto gsftrk = gsf_refit_result.value();
338 // calculateTrackQuantities(gsftrk);
339
340 const Acts::BoundVector& perigee_pars = gsftrk.parameters();
341 const Acts::BoundMatrix& trk_cov = gsftrk.covariance();
342 const Acts::Surface& perigee_surface = gsftrk.referenceSurface();
343
344 ldmx_log(debug) << " Reference Surface (acts-x, acts-y, acts-z) = ("
345 << perigee_surface.localToGlobalTransform(geometryContext())
346 .translation()(0)
347 << ", "
348 << perigee_surface.localToGlobalTransform(geometryContext())
349 .translation()(1)
350 << ", "
351 << perigee_surface.localToGlobalTransform(geometryContext())
352 .translation()(2)
353 << ")";
354
355 ldmx_log(debug) << " nTrackStates=" << gsftrk.nTrackStates()
356 << " nMeasurements=" << gsftrk.nMeasurements()
357 << " chi2=" << gsftrk.chi2();
358 if (gsftrk.nTrackStates() == 0)
359 ldmx_log(info) << " [GSF dbg] track has 0 states after fit (itrk="
360 << itrk << ");";
361
362 ldmx_log(debug) << " Track parameters (d0, z0, phi, theta, q/p)= ("
363 << perigee_pars[Acts::eBoundLoc0] << ", "
364 << perigee_pars[Acts::eBoundLoc1] << ", "
365 << perigee_pars[Acts::eBoundPhi] << ", "
366 << perigee_pars[Acts::eBoundTheta] << ", "
367 << perigee_pars[Acts::eBoundQOverP] << ") ";
368
369 ldmx::Track trk;
370
371 // Extrapolate GSF track to target surface to get perigee parameters
372 auto opt_target = trk_extrap_->extrapolate(gsftrk, target_surface_);
373
374 ldmx_log(debug) << " Extrapolating to target (itrk=" << itrk << ")";
375 if (opt_target) {
376 ldmx_log(debug) << " GSF target extrapolation succeeded";
377 auto ts_at_target = tracking::sim::utils::makeTrackState(
378 geometryContext(), *opt_target, ldmx::AtTarget);
379 trk.addTrackState(ts_at_target);
380
381 trk.setPerigeeParameters(tracking::sim::utils::convertActsToLdmxPars(
382 opt_target->parameters()));
383 if (opt_target->covariance()) {
384 std::vector<double> cov_vec;
385 tracking::sim::utils::flatCov(*(opt_target->covariance()), cov_vec);
386 trk.setPerigeeCov(cov_vec);
387 }
388 Acts::Vector3 target_loc_ldmx = tracking::sim::utils::acts2Ldmx(
389 target_surface_->localToGlobalTransform(geometryContext())
390 .translation());
391 trk.setPerigeeLocation(target_loc_ldmx[0], target_loc_ldmx[1],
392 target_loc_ldmx[2]);
393
394 ldmx_log(debug)
395 << " GSF target parameters (d0, z0, phi, theta, q/p)= ("
396 << opt_target->parameters()[Acts::eBoundLoc0] << ", "
397 << opt_target->parameters()[Acts::eBoundLoc1] << ", "
398 << opt_target->parameters()[Acts::eBoundPhi] << ", "
399 << opt_target->parameters()[Acts::eBoundTheta] << ", "
400 << opt_target->parameters()[Acts::eBoundQOverP] << ")";
401 } else {
402 ++n_target_extrap_failed_;
403 ++n_tgt_fail_evt;
404 ldmx_log(debug) << " GSF target extrapolation failed (itrk=" << itrk
405 << "), using GSF fit parameters at reference surface";
406 trk.setPerigeeParameters(
407 tracking::sim::utils::convertActsToLdmxPars(perigee_pars));
408 std::vector<double> v_trk_cov;
409 tracking::sim::utils::flatCov(trk_cov, v_trk_cov);
410 trk.setPerigeeCov(v_trk_cov);
411 }
412
413 // Tagger: also add beam-origin state; Recoil: add ECAL state
414 if (tagger_tracking_) {
415 auto opt_beam_origin =
416 trk_extrap_->extrapolate(gsftrk, beam_origin_surface_);
417 if (opt_beam_origin)
418 trk.addTrackState(tracking::sim::utils::makeTrackState(
419 geometryContext(), *opt_beam_origin, ldmx::AtBeamOrigin));
420 } else {
421 ldmx_log(debug) << " ECAL extrapolation";
422 auto opt_ecal = trk_extrap_->extrapolate(gsftrk, ecal_surface_);
423 if (opt_ecal)
424 trk.addTrackState(tracking::sim::utils::makeTrackState(
425 geometryContext(), *opt_ecal, ldmx::AtECAL));
426 else
427 ++n_ecal_extrap_failed_;
428 }
429
430 trk.setChi2(gsftrk.chi2());
431 trk.setNhits(gsftrk.nMeasurements());
432 trk.setNdf(gsftrk.nMeasurements() - 5);
433 trk.setCharge(perigee_pars[Acts::eBoundQOverP] > 0 ? 1 : -1);
434
435 // Truth information carried over from input track
436 trk.setTrackID(track.getTrackID());
437 trk.setPdgID(track.getPdgID());
438 trk.setTruthProb(track.getTruthProb());
439
440 itrk++;
441
442 ldmx_log(debug) << " Added track to output, total tracks = "
443 << (out_tracks.size() + 1);
444
445 out_tracks.push_back(trk);
446
447 } // loop on tracks
448
449 ldmx_log(debug) << "[GSF evt " << nevents_ << "] in=" << tracks.size()
450 << " gsf_ok=" << n_gsf_ok_evt
451 << " tgt_fail=" << n_tgt_fail_evt
452 << " out=" << out_tracks.size();
453
454 n_output_tracks_ += static_cast<int>(out_tracks.size());
455 event.add(out_trk_collection_, out_tracks);
456
457 auto t_end = std::chrono::high_resolution_clock::now();
458 processing_time_ +=
459 std::chrono::duration<double, std::milli>(t_end - t_start).count();
460 ++nevents_;
461} // end of produce()
462
464
466 ldmx_log(info) << "--------------------------------- ";
467 ldmx_log(info) << "GSF: " << n_output_tracks_ << " output tracks / "
468 << n_input_tracks_ << " input tracks";
469 ldmx_log(info) << "AVG Time/Event: " << std::fixed << std::setprecision(1)
470 << processing_time_ / nevents_ << " ms";
471 ldmx_log(info) << "GSF Fit Failures: " << n_gsf_failed_;
472 ldmx_log(info) << "Extrapolation Failures::";
473 ldmx_log(info) << " Target: " << n_target_extrap_failed_ << " times";
474 if (!tagger_tracking_)
475 ldmx_log(info) << " ECAL: " << n_ecal_extrap_failed_ << " times";
476}
477
478} // namespace reco
479} // namespace tracking
480
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Implements an event buffer system for storing event data.
Definition Event.h:42
bool exists(const std::string &name, const std::string &passName, bool unique=true) const
Check for the existence of an object or collection with the given name and pass name in the event.
Definition Event.cxx:105
Class which represents the process under execution.
Definition Process.h:37
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:29
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:78
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:57
Implementation of a track object.
Definition Track.h:53
bool disable_all_material_handling_
Disable all material interactions during propagation.
std::shared_ptr< Acts::Surface > target_surface_
Target surface at z=0 mm (recoil track initialization, perigee output)
std::unique_ptr< const Acts::GaussianSumFitter< GsfPropagator, Acts::VectorMultiTrajectory > > gsf_
Gaussian Sum Fitter instance for track refitting.
void produce(framework::Event &event) override
Run the processor.
bool debug_
Enable verbose debug output logging.
std::string track_collection_
Collection name for input tracks to be refit.
size_t max_components_
Maximum number of mixture components in GSF fit.
std::string meas_collection_
Collection name for measurements associated with tracks.
bool abort_on_error_
Abort fit if any error occurs (strict error handling)
std::string meas_passname_
Pass name for measurement collection in event.
void onProcessEnd() override
Callback for the EventProcessor to take any necessary action when the processing of events finishes,...
double weight_cutoff_
Weight threshold below which mixture components are dropped.
std::string field_map_
Path to magnetic field map file.
std::shared_ptr< Acts::Surface > tagger_start_surface_
Tagger GSF start surface at x≈-617mm in ACTS (1mm inside tagger volume outer boundary ~-618mm,...
void configure(framework::config::Parameters &parameters) override
Configure the processor using the given user specified parameters.
int propagator_max_steps_
Maximum number of propagation steps before aborting.
bool use_perigee_
Use perigee parameterization for tracks.
GSFProcessor(const std::string &name, framework::Process &process)
Constructor.
void onProcessStart() override
Callback for the EventProcessor to take any necessary action when the processing of events starts,...
void onNewRun(const ldmx::RunHeader &rh) override
onNewRun is the first function called for each processor after the conditions are fully configured an...
std::string track_collection_event_passname_
Pass name qualifier for track collection event key.
std::unique_ptr< const Propagator > propagator_
Propagator for track extrapolation using eigen stepper.
double propagator_step_size_
Step size for track propagation in mm.
std::shared_ptr< Acts::Surface > beam_origin_surface_
Beam origin surface at z=-700 mm (tagger post-fit extrapolation via VoidNavigator)
std::string track_passname_
Pass name for track collection in event.
std::shared_ptr< Acts::Surface > ecal_surface_
ECAL surface at z=240.5 mm (ECAL scoring plane for recoil tracking)
std::string meas_collection_event_passname_
Pass name qualifier for measurement collection event key.
std::string out_trk_collection_
Collection name for output GSF-refitted tracks.
a helper base class providing some methods to shorten access to common conditions used within the tra...
std::shared_ptr< Acts::MagneticFieldProvider > bField() const
Return the loaded B-field provider.
void loadBField(const std::string &path, const std::vector< double > &map_offset={0., 0., 0.})
Load the interpolated B-field map from path and cache it.
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.
The measurement calibrator can be a function or a class/struct able to retrieve the sim hits containe...