EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
KalmanFitter.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file KalmanFitter.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2016-2019 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
11 // Workaround for building on clang+libstdc++
13 
36 
37 #include <functional>
38 #include <map>
39 #include <memory>
40 
41 namespace Acts {
42 
51 template <typename outlier_finder_t = VoidOutlierFinder>
53  // Broadcast the outlier finder type
54  using OutlierFinder = outlier_finder_t;
55 
57  KalmanFitterOptions() = delete;
58 
71  KalmanFitterOptions(std::reference_wrapper<const GeometryContext> gctx,
72  std::reference_wrapper<const MagneticFieldContext> mctx,
73  std::reference_wrapper<const CalibrationContext> cctx,
74  const OutlierFinder& outlierFinder_,
75  LoggerWrapper logger_,
76  const PropagatorPlainOptions& pOptions,
77  const Surface* rSurface = nullptr,
78  bool mScattering = true, bool eLoss = true,
79  bool bwdFiltering = false)
80  : geoContext(gctx),
81  magFieldContext(mctx),
82  calibrationContext(cctx),
83  outlierFinder(outlierFinder_),
84  propagatorPlainOptions(pOptions),
85  referenceSurface(rSurface),
86  multipleScattering(mScattering),
87  energyLoss(eLoss),
88  backwardFiltering(bwdFiltering),
89  logger(logger_) {}
90 
92  std::reference_wrapper<const GeometryContext> geoContext;
94  std::reference_wrapper<const MagneticFieldContext> magFieldContext;
96  std::reference_wrapper<const CalibrationContext> calibrationContext;
97 
100 
103 
105  const Surface* referenceSurface = nullptr;
106 
108  bool multipleScattering = true;
109 
111  bool energyLoss = true;
112 
114  bool backwardFiltering = false;
115 
118 };
119 
120 template <typename source_link_t>
122  // Fitted states that the actor has handled.
124 
125  // This is the index of the 'tip' of the track stored in multitrajectory.
126  // Since this KF only stores one trajectory, it is unambiguous.
127  // SIZE_MAX is the start of a trajectory.
128  size_t trackTip = SIZE_MAX;
129 
130  // The optional Parameters at the provided surface
131  std::optional<BoundTrackParameters> fittedParameters;
132 
133  // Counter for states with measurements
134  size_t measurementStates = 0;
135 
136  // Counter for handled states
137  size_t processedStates = 0;
138 
139  // Indicator if smoothing has been done.
140  bool smoothed = false;
141 
142  // Indicator if the propagation state has been reset
143  bool reset = false;
144 
145  // Indicator if track fitting has been done
146  bool finished = false;
147 
148  // Measurement surfaces without hits
149  std::vector<const Surface*> missedActiveSurfaces;
150 
151  // Indicator if forward filtering has been done
152  bool forwardFiltered = false;
153 
154  // Measurement surfaces handled in both forward and backward filtering
155  std::vector<const Surface*> passedAgainSurfaces;
156 
158 };
159 
191 template <typename propagator_t, typename updater_t = VoidKalmanUpdater,
192  typename smoother_t = VoidKalmanSmoother,
193  typename outlier_finder_t = VoidOutlierFinder,
194  typename calibrator_t = VoidMeasurementCalibrator>
196  public:
198  using MeasurementSurfaces = std::multimap<const Layer*, const Surface*>;
200  using KalmanNavigator = typename propagator_t::Navigator;
201 
203  static constexpr bool isDirectNavigator =
205 
207  KalmanFitter() = delete;
208 
211  : m_propagator(std::move(pPropagator)) {}
212 
213  private:
216 
224  template <typename source_link_t, typename parameters_t>
225  class Actor {
226  public:
229 
231  const Surface* targetSurface = nullptr;
232 
234  std::map<const Surface*, source_link_t> inputMeasurements;
235 
237  bool multipleScattering = true;
238 
240  bool energyLoss = true;
241 
243  bool backwardFiltering = false;
244 
253  template <typename propagator_state_t, typename stepper_t>
254  void operator()(propagator_state_t& state, const stepper_t& stepper,
255  result_type& result) const {
256  const auto& logger = state.options.logger;
257 
258  if (result.finished) {
259  return;
260  }
261 
262  ACTS_VERBOSE("KalmanFitter step");
263 
264  // This following is added due to the fact that the navigation
265  // reinitialization in reverse call cannot guarantee the navigator to
266  // target for extra layers in the backward-propagation starting volume.
267  // Currently, manually set navigation stage to allow for targeting layers
268  // after all the surfaces on the backward-propagation starting layer has
269  // been processed. Otherwise, the navigation stage will be
270  // Stage::boundaryTarget after navigator status call which means the extra
271  // layers on the backward-propagation starting volume won't be targeted.
272  // @Todo: Let the navigator do all the re-initialization
273  if constexpr (not isDirectNavigator) {
274  if (result.reset and state.navigation.navSurfaceIter ==
275  state.navigation.navSurfaces.end()) {
276  // So the navigator target call will target layers
277 
278  state.navigation.navigationStage =
279  KalmanNavigator::Stage::layerTarget;
280  // We only do this after the backward-propagation
281  // starting layer has been processed
282  result.reset = false;
283  }
284  }
285  // Update:
286  // - Waiting for a current surface
287  auto surface = state.navigation.currentSurface;
288  if (surface != nullptr) {
289  // Check if the surface is in the measurement map
290  // -> Get the measurement / calibrate
291  // -> Create the predicted state
292  // -> Perform the kalman update
293  // -> Check outlier behavior
294  // -> Fill strack state information & update stepper information if
295  // non-outlier
296  if (state.stepping.navDir == forward and not result.smoothed and
297  not result.forwardFiltered) {
298  ACTS_VERBOSE("Perform forward filter step with surface ");
299  ACTS_VERBOSE(surface->geometryId());
300  auto res = filter(surface, state, stepper, result);
301  if (!res.ok()) {
302  ACTS_ERROR("Error in forward filter: " << res.error());
303  result.result = res.error();
304  }
305  } else if (state.stepping.navDir == backward and
306  result.forwardFiltered) {
307  ACTS_VERBOSE("Perform backward filter step");
308  auto res = backwardFilter(surface, state, stepper, result);
309  if (!res.ok()) {
310  ACTS_ERROR("Error in backward filter: " << res.error());
311  result.result = res.error();
312  }
313  }
314  }
315 
316  // Finalization:
317  // when all track states have been handled or the navigation is breaked,
318  // reset navigation&stepping before run backward filtering or
319  // proceed to run smoothing
320  if (state.stepping.navDir == forward) {
321  if (result.measurementStates == inputMeasurements.size() or
322  (result.measurementStates > 0 and
323  state.navigation.navigationBreak)) {
324  if (backwardFiltering and not result.forwardFiltered) {
325  ACTS_VERBOSE("Forward filtering done");
326  result.forwardFiltered = true;
327  // Start to run backward filtering:
328  // Reverse navigation direction and reset navigation and stepping
329  // state to last measurement
330  ACTS_VERBOSE("Reverse navigation direction.");
331  reverse(state, stepper, result);
332  } else if (not result.smoothed) {
333  // --> Search the starting state to run the smoothing
334  // --> Call the smoothing
335  // --> Set a stop condition when all track states have been
336  // handled
337  ACTS_VERBOSE("Finalize/run smoothing");
338  auto res = finalize(state, stepper, result);
339  if (!res.ok()) {
340  ACTS_ERROR("Error in finalize: " << res.error());
341  result.result = res.error();
342  }
343  }
344  }
345  }
346 
347  // Post-finalization:
348  // - Progress to target/reference surface and built the final track
349  // parameters
350  if (result.smoothed or state.stepping.navDir == backward) {
351  if (targetSurface == nullptr) {
352  // If no target surface provided:
353  // -> Return an error when using backward filtering mode
354  // -> Fitting is finished here
355  if (backwardFiltering) {
356  ACTS_ERROR(
357  "The target surface needed for aborting backward propagation "
358  "is not provided");
359  result.result =
360  Result<void>(KalmanFitterError::BackwardUpdateFailed);
361  } else {
362  ACTS_VERBOSE(
363  "No target surface set. Completing without fitted track "
364  "parameter");
365  // Remember the track fitting is done
366  result.finished = true;
367  }
368  } else if (targetReached(state, stepper, *targetSurface)) {
369  ACTS_VERBOSE("Completing with fitted track parameter");
370  // Transport & bind the parameter to the final surface
371  auto fittedState = stepper.boundState(state.stepping, *targetSurface);
372  // Assign the fitted parameters
373  result.fittedParameters = std::get<BoundTrackParameters>(fittedState);
374 
375  // Reset smoothed status of states missed in backward filtering
376  if (backwardFiltering) {
377  result.fittedStates.applyBackwards(
378  result.trackTip, [&](auto trackState) {
379  auto fSurface = &trackState.referenceSurface();
380  auto surface_it = std::find_if(
381  result.passedAgainSurfaces.begin(),
382  result.passedAgainSurfaces.end(),
383  [=](const Surface* s) { return s == fSurface; });
384  if (surface_it == result.passedAgainSurfaces.end()) {
385  // If backward filtering missed this surface, then there is
386  // no smoothed parameter
387  trackState.data().ismoothed =
389  }
390  });
391  }
392  // Remember the track fitting is done
393  result.finished = true;
394  }
395  }
396  }
397 
406  template <typename propagator_state_t, typename stepper_t>
407  void reverse(propagator_state_t& state, stepper_t& stepper,
408  result_type& result) const {
409  // Remember the navigation direciton has been reserved
410  result.reset = true;
411 
412  // Reset propagator options
413  state.options.maxStepSize = -1.0 * state.options.maxStepSize;
414  // Not sure if reset of pathLimit during propagation makes any sense
415  state.options.pathLimit = -1.0 * state.options.pathLimit;
416 
417  // Reset stepping&navigation state using last measurement track state on
418  // sensitive surface
419  state.navigation = typename propagator_t::NavigatorState();
420  result.fittedStates.applyBackwards(result.trackTip, [&](auto st) {
421  if (st.typeFlags().test(Acts::TrackStateFlag::MeasurementFlag)) {
422  // Set the navigation state
423  state.navigation.startSurface = &st.referenceSurface();
424  if (state.navigation.startSurface->associatedLayer() != nullptr) {
425  state.navigation.startLayer =
426  state.navigation.startSurface->associatedLayer();
427 
428  }
429  state.navigation.startVolume =
430  state.navigation.startLayer->trackingVolume();
431  state.navigation.targetSurface = targetSurface;
432  state.navigation.currentSurface = state.navigation.startSurface;
433  state.navigation.currentVolume = state.navigation.startVolume;
434 
435  // Update the stepping state
436  stepper.resetState(state.stepping, st.filtered(),
437  st.filteredCovariance(), st.referenceSurface(),
438  backward, state.options.maxStepSize);
439 
440  // For the last measurement state, smoothed is filtered
441  st.smoothed() = st.filtered();
442  st.smoothedCovariance() = st.filteredCovariance();
443  result.passedAgainSurfaces.push_back(&st.referenceSurface());
444 
445  // Update material effects for last measurement state in backward
446  // direction
447  materialInteractor(state.navigation.currentSurface, state, stepper);
448 
449  return false; // abort execution
450  }
451  return true; // continue execution
452  });
453  }
454 
464  template <typename propagator_state_t, typename stepper_t>
465  Result<void> filter(const Surface* surface, propagator_state_t& state,
466  const stepper_t& stepper, result_type& result) const {
467  const auto& logger = state.options.logger;
468  // Try to find the surface in the measurement surfaces
469  auto sourcelink_it = inputMeasurements.find(surface);
470  if(sourcelink_it != inputMeasurements.end()){
471  }
472  else
473  ACTS_VERBOSE("Couldn't find source link in measurement list");
474 
475  if (sourcelink_it != inputMeasurements.end()) {
476  // Screen output message
477  ACTS_VERBOSE("Measurement surface " << surface->geometryId()
478  << " detected.");
479 
480  // Transport & bind the state to the current surface
481  auto [boundParams, jacobian, pathLength] =
482  stepper.boundState(state.stepping, *surface);
483 
484  // Update state and stepper with pre material effects
485  materialInteractor(surface, state, stepper, preUpdate);
486 
487  // add a full TrackState entry multi trajectory
488  // (this allocates storage for all components, we will set them later)
489  result.trackTip = result.fittedStates.addTrackState(
490  TrackStatePropMask::All, result.trackTip);
491 
492  // now get track state proxy back
493  auto trackStateProxy =
494  result.fittedStates.getTrackState(result.trackTip);
495 
496  // assign the source link to the track state
497  trackStateProxy.uncalibrated() = sourcelink_it->second;
498 
499  // Fill the track state
500  trackStateProxy.predicted() = std::move(boundParams.parameters());
501  trackStateProxy.predictedCovariance() =
502  std::move(*boundParams.covariance());
503  trackStateProxy.jacobian() = std::move(jacobian);
504  trackStateProxy.pathLength() = std::move(pathLength);
505 
506  // We have predicted parameters, so calibrate the uncalibrated input
507  // measuerement
508  std::visit(
509  [&](const auto& calibrated) {
510  trackStateProxy.setCalibrated(calibrated);
511  },
512  m_calibrator(trackStateProxy.uncalibrated(),
513  trackStateProxy.predicted()));
514 
515  // Get and set the type flags
516  auto& typeFlags = trackStateProxy.typeFlags();
517  typeFlags.set(TrackStateFlag::MaterialFlag);
518  typeFlags.set(TrackStateFlag::ParameterFlag);
519 
520  // Check if the state is an outlier.
521  // If not, run Kalman update, tag it as a
522  // measurement and update the stepping state. Otherwise, just tag it as
523  // an outlier
524  if (not m_outlierFinder(trackStateProxy)) {
525  // Run Kalman update
526  auto updateRes =
527  m_updater(state.geoContext, trackStateProxy, forward);
528  if (!updateRes.ok()) {
529  ACTS_ERROR("Update step failed: " << updateRes.error());
530  return updateRes.error();
531  }
532  // Set the measurement type flag
533  typeFlags.set(TrackStateFlag::MeasurementFlag);
534  // Update the stepping state with filtered parameters
535  ACTS_VERBOSE("Filtering step successful, updated parameters are : \n"
536  << trackStateProxy.filtered().transpose());
537  // update stepping state using filtered parameters after kalman
538  stepper.update(state.stepping,
540  state.options.geoContext, trackStateProxy),
541  trackStateProxy.filteredCovariance());
542  // We count the state with measurement
543  ++result.measurementStates;
544  } else {
545  ACTS_VERBOSE(
546  "Filtering step successful. But measurement is deterimined "
547  "to "
548  "be an outlier. Stepping state is not updated.")
549  // Set the outlier type flag
550  typeFlags.set(TrackStateFlag::OutlierFlag);
551  }
552 
553  // Update state and stepper with post material effects
554  materialInteractor(surface, state, stepper, postUpdate);
555  // We count the processed state
556  ++result.processedStates;
557  } else if (surface->surfaceMaterial() != nullptr) {
558  // We only create track states here if there is already measurement
559  // detected
560  if (result.measurementStates > 0) {
561  // No source links on surface, add either hole or passive material
562  // TrackState entry multi trajectory. No storage allocation for
563  // uncalibrated/calibrated measurement and filtered parameter
564  result.trackTip = result.fittedStates.addTrackState(
565  ~(TrackStatePropMask::Uncalibrated |
566  TrackStatePropMask::Calibrated | TrackStatePropMask::Filtered),
567  result.trackTip);
568 
569  // now get track state proxy back
570  auto trackStateProxy =
571  result.fittedStates.getTrackState(result.trackTip);
572 
573  // Set the surface
574  trackStateProxy.setReferenceSurface(surface->getSharedPtr());
575 
576  // Set the track state flags
577  auto& typeFlags = trackStateProxy.typeFlags();
578  typeFlags.set(TrackStateFlag::MaterialFlag);
579  typeFlags.set(TrackStateFlag::ParameterFlag);
580 
581  if (surface->associatedDetectorElement() != nullptr) {
582  ACTS_VERBOSE("Detected hole on " << surface->geometryId());
583  // If the surface is sensitive, set the hole type flag
584  typeFlags.set(TrackStateFlag::HoleFlag);
585 
586  // Count the missed surface
587  result.missedActiveSurfaces.push_back(surface);
588 
589  // Transport & bind the state to the current surface
590  auto [boundParams, jacobian, pathLength] =
591  stepper.boundState(state.stepping, *surface);
592 
593  // Fill the track state
594  trackStateProxy.predicted() = std::move(boundParams.parameters());
595  trackStateProxy.predictedCovariance() =
596  std::move(*boundParams.covariance());
597  trackStateProxy.jacobian() = std::move(jacobian);
598  trackStateProxy.pathLength() = std::move(pathLength);
599  } else {
600  ACTS_VERBOSE("Detected in-sensitive surface "
601  << surface->geometryId());
602 
603  // Transport & get curvilinear state instead of bound state
604  auto [curvilinearParams, jacobian, pathLength] =
605  stepper.curvilinearState(state.stepping);
606 
607  // Fill the track state
608  trackStateProxy.predicted() =
609  std::move(curvilinearParams.parameters());
610  trackStateProxy.predictedCovariance() =
611  std::move(*curvilinearParams.covariance());
612  trackStateProxy.jacobian() = std::move(jacobian);
613  trackStateProxy.pathLength() = std::move(pathLength);
614  }
615 
616  // Set the filtered parameter index to be the same with predicted
617  // parameter
618  trackStateProxy.data().ifiltered = trackStateProxy.data().ipredicted;
619 
620  // We count the processed state
621  ++result.processedStates;
622  }
623 
624  // Update state and stepper with material effects
625  materialInteractor(surface, state, stepper, fullUpdate);
626  }
627  return Result<void>::success();
628  }
629 
639  template <typename propagator_state_t, typename stepper_t>
640  Result<void> backwardFilter(const Surface* surface,
641  propagator_state_t& state,
642  const stepper_t& stepper,
643  result_type& result) const {
644  const auto& logger = state.options.logger;
645  // Try to find the surface in the measurement surfaces
646  auto sourcelink_it = inputMeasurements.find(surface);
647  if (sourcelink_it != inputMeasurements.end()) {
648  // Screen output message
649  ACTS_VERBOSE("Measurement surface "
650  << surface->geometryId()
651  << " detected in backward propagation.");
652 
653  // No backward filtering for last measurement state, but still update
654  // with material effects
655  if (state.stepping.navDir == backward and
656  surface == state.navigation.startSurface) {
657  materialInteractor(surface, state, stepper);
658  return Result<void>::success();
659  }
660 
661  // Transport & bind the state to the current surface
662  auto [boundParams, jacobian, pathLength] =
663  stepper.boundState(state.stepping, *surface);
664 
665  // Update state and stepper with pre material effects
666  materialInteractor(surface, state, stepper, preUpdate);
667 
668  // Create a detached track state proxy
669  auto tempTrackTip =
670  result.fittedStates.addTrackState(TrackStatePropMask::All);
671 
672  // Get the detached track state proxy back
673  auto trackStateProxy = result.fittedStates.getTrackState(tempTrackTip);
674 
675  // Assign the source link to the detached track state
676  trackStateProxy.uncalibrated() = sourcelink_it->second;
677 
678  // Fill the track state
679  trackStateProxy.predicted() = std::move(boundParams.parameters());
680  trackStateProxy.predictedCovariance() =
681  std::move(*boundParams.covariance());
682  trackStateProxy.jacobian() = std::move(jacobian);
683  trackStateProxy.pathLength() = std::move(pathLength);
684 
685  // We have predicted parameters, so calibrate the uncalibrated input
686  // measuerement
687  std::visit(
688  [&](const auto& calibrated) {
689  trackStateProxy.setCalibrated(calibrated);
690  },
691  m_calibrator(trackStateProxy.uncalibrated(),
692  trackStateProxy.predicted()));
693 
694  // If the update is successful, set covariance and
695  auto updateRes =
696  m_updater(state.geoContext, trackStateProxy, backward, logger);
697  if (!updateRes.ok()) {
698  ACTS_ERROR("Backward update step failed: " << updateRes.error());
699  return updateRes.error();
700  } else {
701  // Update the stepping state with filtered parameters
702  ACTS_VERBOSE(
703  "Backward Filtering step successful, updated parameters are : "
704  "\n"
705  << trackStateProxy.filtered().transpose());
706 
707  // Fill the smoothed parameter for the existing track state
708  result.fittedStates.applyBackwards(
709  result.trackTip, [&](auto trackState) {
710  auto fSurface = &trackState.referenceSurface();
711  if (fSurface == surface) {
712  result.passedAgainSurfaces.push_back(surface);
713  trackState.smoothed() = trackStateProxy.filtered();
714  trackState.smoothedCovariance() =
715  trackStateProxy.filteredCovariance();
716  return false;
717  }
718  return true;
719  });
720 
721  // update stepping state using filtered parameters after kalman
722  // update We need to (re-)construct a BoundTrackParameters instance
723  // here, which is a bit awkward.
724  stepper.update(state.stepping,
726  state.options.geoContext, trackStateProxy),
727  trackStateProxy.filteredCovariance());
728 
729  // Update state and stepper with post material effects
730  materialInteractor(surface, state, stepper, postUpdate);
731  }
732  } else if (surface->surfaceMaterial() != nullptr) {
733  // Transport covariance
734  if (surface->associatedDetectorElement() != nullptr) {
735  ACTS_VERBOSE("Detected hole on " << surface->geometryId()
736  << " in backward filtering");
737  if (state.stepping.covTransport) {
738  stepper.covarianceTransport(state.stepping, *surface);
739  }
740  } else {
741  ACTS_VERBOSE("Detected in-sensitive surface "
742  << surface->geometryId() << " in backward filtering");
743  if (state.stepping.covTransport) {
744  stepper.covarianceTransport(state.stepping);
745  }
746  }
747 
748  // Not creating bound state here, so need manually reinitialize
749  // jacobian
750  state.stepping.jacobian = BoundMatrix::Identity();
751 
752  // Update state and stepper with material effects
753  materialInteractor(surface, state, stepper);
754  }
755 
756  return Result<void>::success();
757  }
758 
769  template <typename propagator_state_t, typename stepper_t>
770  void materialInteractor(
771  const Surface* surface, propagator_state_t& state, stepper_t& stepper,
772  const MaterialUpdateStage& updateStage = fullUpdate) const {
773  const auto& logger = state.options.logger;
774  // Indicator if having material
775  bool hasMaterial = false;
776 
777  if (surface and surface->surfaceMaterial()) {
778  // Prepare relevant input particle properties
779  detail::PointwiseMaterialInteraction interaction(surface, state,
780  stepper);
781  // Evaluate the material properties
782  if (interaction.evaluateMaterialSlab(state, updateStage)) {
783  // Surface has material at this stage
784  hasMaterial = true;
785 
786  // Evaluate the material effects
787  interaction.evaluatePointwiseMaterialInteraction(multipleScattering,
788  energyLoss);
789 
790  // Screen out material effects info
791  ACTS_VERBOSE("Material effects on surface: "
792  << surface->geometryId()
793  << " at update stage: " << updateStage << " are :");
794  ACTS_VERBOSE("eLoss = "
795  << interaction.Eloss << ", "
796  << "variancePhi = " << interaction.variancePhi << ", "
797  << "varianceTheta = " << interaction.varianceTheta
798  << ", "
799  << "varianceQoverP = " << interaction.varianceQoverP);
800 
801  // Update the state and stepper with material effects
802  interaction.updateState(state, stepper);
803  }
804  }
805 
806  if (not hasMaterial) {
807  // Screen out message
808  ACTS_VERBOSE("No material effects on surface: " << surface->geometryId()
809  << " at update stage: "
810  << updateStage);
811  }
812  }
813 
822  template <typename propagator_state_t, typename stepper_t>
823  Result<void> finalize(propagator_state_t& state, const stepper_t& stepper,
824  result_type& result) const {
825  const auto& logger = state.options.logger;
826  // Remember you smoothed the track states
827  result.smoothed = true;
828 
829  // Get the indices of measurement states;
830  std::vector<size_t> measurementIndices;
831  measurementIndices.reserve(result.measurementStates);
832  // Count track states to be smoothed
833  size_t nStates = 0;
834  result.fittedStates.applyBackwards(result.trackTip, [&](auto st) {
835  bool isMeasurement =
836  st.typeFlags().test(TrackStateFlag::MeasurementFlag);
837  if (isMeasurement) {
838  measurementIndices.emplace_back(st.index());
839  } else if (measurementIndices.empty()) {
840  // No smoothed parameters if the last measurement state has not been
841  // found yet
842  st.data().ismoothed = detail_lt::IndexData::kInvalid;
843  }
844  // Start count when the last measurement state is found
845  if (not measurementIndices.empty()) {
846  nStates++;
847  }
848  });
849  // Return error if the track has no measurement states (but this should
850  // not happen)
851  if (measurementIndices.empty()) {
852  ACTS_ERROR("Smoothing for a track without measurements.");
853  return KalmanFitterError::SmoothFailed;
854  }
855  // Screen output for debugging
856  if (logger().doPrint(Logging::VERBOSE)) {
857  ACTS_VERBOSE("Apply smoothing on " << nStates
858  << " filtered track states.");
859  }
860 
861  // Smooth the track states
862  auto smoothRes = m_smoother(state.geoContext, result.fittedStates,
863  measurementIndices.front(), logger);
864  if (!smoothRes.ok()) {
865  ACTS_ERROR("Smoothing step failed: " << smoothRes.error());
866  return smoothRes.error();
867  }
868  // Obtain the smoothed parameters at first measurement state
869  auto firstMeasurement =
870  result.fittedStates.getTrackState(measurementIndices.back());
871 
872  // Update the stepping parameters - in order to progress to destination
873  ACTS_VERBOSE(
874  "Smoothing successful, updating stepping state, "
875  "set target surface.");
876  stepper.update(state.stepping,
878  state.options.geoContext, firstMeasurement),
879  firstMeasurement.smoothedCovariance());
880  // Reverse the propagation direction
881  state.stepping.stepSize =
882  ConstrainedStep(-1. * state.options.maxStepSize);
883  state.stepping.navDir = backward;
884  // Set accumulatd path to zero before targeting surface
885  state.stepping.pathAccumulated = 0.;
886 
887  return Result<void>::success();
888  }
889 
891  updater_t m_updater;
892 
894  smoother_t m_smoother;
895 
897  outlier_finder_t m_outlierFinder;
898 
900  calibrator_t m_calibrator;
901 
904  };
905 
906  template <typename source_link_t, typename parameters_t>
907  class Aborter {
908  public:
911 
912  template <typename propagator_state_t, typename stepper_t,
913  typename result_t>
914  bool operator()(propagator_state_t& /*state*/, const stepper_t& /*stepper*/,
915  const result_t& result) const {
916  if (!result.result.ok() or result.finished) {
917  return true;
918  }
919  return false;
920  }
921  };
922 
923  public:
941  template <typename source_link_t, typename start_parameters_t,
942  typename parameters_t = BoundTrackParameters>
943  auto fit(const std::vector<source_link_t>& sourcelinks,
944  const start_parameters_t& sParameters,
945  const KalmanFitterOptions<outlier_finder_t>& kfOptions) const
946  -> std::enable_if_t<!isDirectNavigator,
948  const auto& logger = kfOptions.logger;
949 
950  static_assert(SourceLinkConcept<source_link_t>,
951  "Source link does not fulfill SourceLinkConcept");
952 
953  // To be able to find measurements later, we put them into a map
954  // We need to copy input SourceLinks anyways, so the map can own them.
955  ACTS_VERBOSE("Preparing " << sourcelinks.size() << " input measurements");
956  std::map<const Surface*, source_link_t> inputMeasurements;
957  for (const auto& sl : sourcelinks) {
958  const Surface* srf = &sl.referenceSurface();
959  ACTS_VERBOSE("Adding surface with geoID ");
960  ACTS_VERBOSE(srf->geometryId());
961  inputMeasurements.emplace(srf, sl);
962  }
963 
964  // Create the ActionList and AbortList
965  using KalmanAborter = Aborter<source_link_t, parameters_t>;
966  using KalmanActor = Actor<source_link_t, parameters_t>;
967  using KalmanResult = typename KalmanActor::result_type;
968  using Actors = ActionList<KalmanActor>;
969  using Aborters = AbortList<KalmanAborter>;
970 
971  // Create relevant options for the propagation options
973  kfOptions.geoContext, kfOptions.magFieldContext, logger);
974 
975  // Set the trivial propagator options
976  kalmanOptions.setPlainOptions(kfOptions.propagatorPlainOptions);
977 
978  // Catch the actor and set the measurements
979  auto& kalmanActor = kalmanOptions.actionList.template get<KalmanActor>();
980  kalmanActor.inputMeasurements = std::move(inputMeasurements);
981  kalmanActor.targetSurface = kfOptions.referenceSurface;
982  kalmanActor.multipleScattering = kfOptions.multipleScattering;
983  kalmanActor.energyLoss = kfOptions.energyLoss;
984  kalmanActor.backwardFiltering = kfOptions.backwardFiltering;
985 
986  // Set config for outlier finder
987  kalmanActor.m_outlierFinder = kfOptions.outlierFinder;
988 
989  // Run the fitter
990  auto result = m_propagator.template propagate(sParameters, kalmanOptions);
991 
992  if (!result.ok()) {
993  ACTS_VERBOSE("Propapation failed: " << result.error());
994  return result.error();
995  }
996 
997  const auto& propRes = *result;
998 
1000  auto kalmanResult = propRes.template get<KalmanResult>();
1001 
1004  if (kalmanResult.result.ok() and not kalmanResult.processedStates) {
1005  kalmanResult.result = Result<void>(KalmanFitterError::NoMeasurementFound);
1006  }
1007 
1008  if (!kalmanResult.result.ok()) {
1009  ACTS_VERBOSE("KalmanFilter failed: " << kalmanResult.result.error());
1010  return kalmanResult.result.error();
1011  }
1012 
1013  // Return the converted Track
1014  return kalmanResult;
1015  }
1016 
1035  template <typename source_link_t, typename start_parameters_t,
1036  typename parameters_t = BoundTrackParameters>
1037  auto fit(const std::vector<source_link_t>& sourcelinks,
1038  const start_parameters_t& sParameters,
1039  const KalmanFitterOptions<outlier_finder_t>& kfOptions,
1040  const std::vector<const Surface*>& sSequence) const
1041  -> std::enable_if_t<isDirectNavigator,
1043  const auto& logger = kfOptions.logger;
1044  static_assert(SourceLinkConcept<source_link_t>,
1045  "Source link does not fulfill SourceLinkConcept");
1046 
1047  // To be able to find measurements later, we put them into a map
1048  // We need to copy input SourceLinks anyways, so the map can own them.
1049  ACTS_VERBOSE("Preparing " << sourcelinks.size() << " input measurements");
1050  std::map<const Surface*, source_link_t> inputMeasurements;
1051  for (const auto& sl : sourcelinks) {
1052  const Surface* srf = &sl.referenceSurface();
1053  inputMeasurements.emplace(srf, sl);
1054  }
1055 
1056  // Create the ActionList and AbortList
1057  using KalmanAborter = Aborter<source_link_t, parameters_t>;
1058  using KalmanActor = Actor<source_link_t, parameters_t>;
1059  using KalmanResult = typename KalmanActor::result_type;
1061  using Aborters = AbortList<KalmanAborter>;
1062 
1063  // Create relevant options for the propagation options
1065  kfOptions.geoContext, kfOptions.magFieldContext, logger);
1066 
1067  // Set the trivial propagator options
1068  kalmanOptions.setPlainOptions(kfOptions.propagatorPlainOptions);
1069 
1070  // Catch the actor and set the measurements
1071  auto& kalmanActor = kalmanOptions.actionList.template get<KalmanActor>();
1072  kalmanActor.inputMeasurements = std::move(inputMeasurements);
1073  kalmanActor.targetSurface = kfOptions.referenceSurface;
1074  kalmanActor.multipleScattering = kfOptions.multipleScattering;
1075  kalmanActor.energyLoss = kfOptions.energyLoss;
1076  kalmanActor.backwardFiltering = kfOptions.backwardFiltering;
1077  // Set config for outlier finder
1078  kalmanActor.m_outlierFinder = kfOptions.outlierFinder;
1079 
1080  // Set the surface sequence
1081  auto& dInitializer =
1082  kalmanOptions.actionList.template get<DirectNavigator::Initializer>();
1083  dInitializer.navSurfaces = sSequence;
1084 
1085  // Run the fitter
1086  auto result = m_propagator.template propagate(sParameters, kalmanOptions);
1087 
1088  if (!result.ok()) {
1089  ACTS_ERROR("Propapation failed: " << result.error());
1090  return result.error();
1091  }
1092 
1093  const auto& propRes = *result;
1094 
1096  auto kalmanResult = propRes.template get<KalmanResult>();
1097 
1100  if (kalmanResult.result.ok() and not kalmanResult.processedStates) {
1101  kalmanResult.result = Result<void>(KalmanFitterError::NoMeasurementFound);
1102  }
1103 
1104  if (!kalmanResult.result.ok()) {
1105  ACTS_ERROR("KalmanFilter failed: " << kalmanResult.result.error());
1106  return kalmanResult.result.error();
1107  }
1108 
1109  // Return the converted Track
1110  return kalmanResult;
1111  }
1112 };
1113 
1114 } // namespace Acts