ECCE @ 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 
34 
35 #include <functional>
36 #include <map>
37 #include <memory>
38 
39 namespace Acts {
40 
49 template <typename outlier_finder_t = VoidOutlierFinder>
51  // Broadcast the outlier finder type
52  using OutlierFinder = outlier_finder_t;
53 
55  KalmanFitterOptions() = delete;
56 
67  KalmanFitterOptions(std::reference_wrapper<const GeometryContext> gctx,
68  std::reference_wrapper<const MagneticFieldContext> mctx,
69  std::reference_wrapper<const CalibrationContext> cctx,
71  const Surface* rSurface = nullptr,
72  bool mScattering = true, bool eLoss = true,
73  bool bwdFiltering = false)
74  : geoContext(gctx),
75  magFieldContext(mctx),
76  calibrationContext(cctx),
77  outlierFinder(outlierFinder),
78  referenceSurface(rSurface),
79  multipleScattering(mScattering),
80  energyLoss(eLoss),
81  backwardFiltering(bwdFiltering) {}
82 
84  std::reference_wrapper<const GeometryContext> geoContext;
86  std::reference_wrapper<const MagneticFieldContext> magFieldContext;
88  std::reference_wrapper<const CalibrationContext> calibrationContext;
89 
92 
94  const Surface* referenceSurface = nullptr;
95 
97  bool multipleScattering = true;
98 
100  bool energyLoss = true;
101 
103  bool backwardFiltering = false;
104 };
105 
106 template <typename source_link_t>
108  // Fitted states that the actor has handled.
110 
111  // This is the index of the 'tip' of the track stored in multitrajectory.
112  // Since this KF only stores one trajectory, it is unambiguous.
113  // SIZE_MAX is the start of a trajectory.
114  size_t trackTip = SIZE_MAX;
115 
116  // The optional Parameters at the provided surface
117  std::optional<BoundParameters> fittedParameters;
118 
119  // Counter for states with measurements
120  size_t measurementStates = 0;
121 
122  // Counter for handled states
123  size_t processedStates = 0;
124 
125  // Indicator if smoothing has been done.
126  bool smoothed = false;
127 
128  // Indicator if initialization has been performed.
129  bool initialized = false;
130 
131  // Measurement surfaces without hits
132  std::vector<const Surface*> missedActiveSurfaces;
133 
134  // Indicator if forward filtering has been done
135  bool forwardFiltered = false;
136 
137  // Measurement surfaces handled in both forward and backward filtering
138  std::vector<const Surface*> passedAgainSurfaces;
139 
141 };
142 
182 template <typename propagator_t, typename updater_t = VoidKalmanUpdater,
183  typename smoother_t = VoidKalmanSmoother,
184  typename outlier_finder_t = VoidOutlierFinder,
185  typename calibrator_t = VoidMeasurementCalibrator,
186  typename input_converter_t = VoidKalmanComponents,
187  typename output_converter_t = VoidKalmanComponents>
189  public:
191  using MeasurementSurfaces = std::multimap<const Layer*, const Surface*>;
193  using KalmanNavigator = typename propagator_t::Navigator;
194 
196  static constexpr bool isDirectNavigator =
198 
200  KalmanFitter() = delete;
201 
204  std::unique_ptr<const Logger> logger =
205  getDefaultLogger("KalmanFilter", Logging::INFO),
206  input_converter_t pInputCnv = input_converter_t(),
207  output_converter_t pOutputCnv = output_converter_t())
208  : m_propagator(std::move(pPropagator)),
209  m_inputConverter(std::move(pInputCnv)),
210  m_outputConverter(std::move(pOutputCnv)),
211  m_logger(logger.release()) {}
212 
213  private:
216 
218  input_converter_t m_inputConverter;
219 
221  output_converter_t m_outputConverter;
222 
224  const Logger& logger() const { return *m_logger; }
225 
227  std::shared_ptr<const Logger> m_logger;
228 
236  template <typename source_link_t, typename parameters_t>
237  class Actor {
238  public:
240 
242  Actor(updater_t pUpdater = updater_t(), smoother_t pSmoother = smoother_t(),
243  outlier_finder_t pOutlierFinder = outlier_finder_t(),
244  calibrator_t pCalibrator = calibrator_t())
245  : m_updater(std::move(pUpdater)),
246  m_smoother(std::move(pSmoother)),
247  m_outlierFinder(std::move(pOutlierFinder)),
248  m_calibrator(std::move(pCalibrator)) {}
249 
252 
254  const Surface* targetSurface = nullptr;
255 
257  std::map<const Surface*, source_link_t> inputMeasurements;
258 
260  bool multipleScattering = true;
261 
263  bool energyLoss = true;
264 
266  bool backwardFiltering = false;
267 
276  template <typename propagator_state_t, typename stepper_t>
277  void operator()(propagator_state_t& state, const stepper_t& stepper,
278  result_type& result) const {
279  ACTS_VERBOSE("KalmanFitter step");
280 
281  // Initialization:
282  // - Only when track states are not set
283  if (!result.initialized) {
284  // -> Move the TrackState vector
285  // -> Feed the KalmanSequencer with the measurements to be fitted
286  ACTS_VERBOSE("Initializing");
287  initialize(state, stepper, result);
288  result.initialized = true;
289  }
290 
291  // Update:
292  // - Waiting for a current surface
293  auto surface = state.navigation.currentSurface;
294  if (surface != nullptr) {
295  // Check if the surface is in the measurement map
296  // -> Get the measurement / calibrate
297  // -> Create the predicted state
298  // -> Perform the kalman update
299  // -> Check outlier behavior
300  // -> Fill strack state information & update stepper information if
301  // non-outlier
302  if (state.stepping.navDir == forward and not result.smoothed and
303  not result.forwardFiltered) {
304  ACTS_VERBOSE("Perform forward filter step");
305  auto res = filter(surface, state, stepper, result);
306  if (!res.ok()) {
307  ACTS_ERROR("Error in forward filter: " << res.error());
308  result.result = res.error();
309  }
310  } else if (state.stepping.navDir == backward) {
311  ACTS_VERBOSE("Perform backward filter step");
312  auto res = backwardFilter(surface, state, stepper, result);
313  if (!res.ok()) {
314  ACTS_ERROR("Error in backward filter: " << res.error());
315  result.result = res.error();
316  }
317  }
318  }
319 
320  // Finalization:
321  // when all track states have been handled or the navigation is breaked,
322  // reset navigation&stepping before run backward filtering or
323  // proceed to run smoothing
324  if (state.stepping.navDir == forward) {
325  if (result.measurementStates == inputMeasurements.size() or
326  (result.measurementStates > 0 and
327  state.navigation.navigationBreak)) {
328  if (backwardFiltering and not result.forwardFiltered) {
329  ACTS_VERBOSE("Forward filtering done");
330  result.forwardFiltered = true;
331  // Start to run backward filtering:
332  // Reverse navigation direction and reset navigation and stepping
333  // state to last measurement
334  ACTS_VERBOSE("Reverse navigation direction.");
335  reverse(state, stepper, result);
336  } else if (not result.smoothed) {
337  // --> Search the starting state to run the smoothing
338  // --> Call the smoothing
339  // --> Set a stop condition when all track states have been handled
340  ACTS_VERBOSE("Finalize/run smoothing");
341  auto res = finalize(state, stepper, result);
342  if (!res.ok()) {
343  ACTS_ERROR("Error in finalize: " << res.error());
344  result.result = res.error();
345  }
346  }
347  }
348  }
349 
350  // Post-finalization:
351  // - Progress to target/reference surface and built the final track
352  // parameters
353  if ((result.smoothed or state.stepping.navDir == backward) and
354  targetReached(state, stepper, *targetSurface)) {
355  ACTS_VERBOSE("Completing");
356  // Transport & bind the parameter to the final surface
357  auto fittedState = stepper.boundState(state.stepping, *targetSurface);
358  // Assign the fitted parameters
359  result.fittedParameters = std::get<BoundParameters>(fittedState);
360  // Break the navigation for stopping the Propagation
361  state.navigation.navigationBreak = true;
362 
363  // Reset smoothed status of states missed in backward filtering
364  if (backwardFiltering) {
365  result.fittedStates.applyBackwards(result.trackTip, [&](auto state) {
366  auto fSurface = &state.referenceSurface();
367  auto surface_it = std::find_if(
368  result.passedAgainSurfaces.begin(),
369  result.passedAgainSurfaces.end(),
370  [=](const Surface* surface) { return surface == fSurface; });
371  if (surface_it == result.passedAgainSurfaces.end()) {
372  // If backward filtering missed this surface, then there is no
373  // smoothed parameter
374  state.data().ismoothed = detail_lt::IndexData::kInvalid;
375  }
376  });
377  }
378  }
379  }
380 
389  template <typename propagator_state_t, typename stepper_t>
390  void initialize(propagator_state_t& /*state*/, const stepper_t& /*stepper*/,
391  result_type& /*result*/) const {}
392 
401  template <typename propagator_state_t, typename stepper_t>
402  void reverse(propagator_state_t& state, stepper_t& stepper,
403  result_type& result) const {
404  // Reset propagator options
405  state.options.direction = backward;
406  state.options.maxStepSize = -1.0 * state.options.maxStepSize;
407  // Not sure if reset of pathLimit during propagation makes any sense
408  state.options.pathLimit = -1.0 * state.options.pathLimit;
409 
410  // Reset stepping&navigation state using last measurement track state on
411  // sensitive surface
412  state.navigation = typename propagator_t::NavigatorState();
413  result.fittedStates.applyBackwards(result.trackTip, [&](auto st) {
414  if (st.hasUncalibrated()) {
415  // Set the navigation state
416  state.navigation.startSurface = &st.referenceSurface();
417  state.navigation.startLayer =
418  state.navigation.startSurface->associatedLayer();
419  state.navigation.startVolume =
420  state.navigation.startLayer->trackingVolume();
421  state.navigation.targetSurface = targetSurface;
422  state.navigation.currentSurface = state.navigation.startSurface;
423  state.navigation.currentVolume = state.navigation.startVolume;
424 
425  // Update the stepping state
426  stepper.update(state.stepping,
427  st.filteredParameters(state.options.geoContext));
428  // Reverse stepping direction
429  state.stepping.navDir = backward;
430  state.stepping.stepSize = ConstrainedStep(state.options.maxStepSize);
431  state.stepping.pathAccumulated = 0.;
432  // Reinitialize the stepping jacobian
433  st.referenceSurface().initJacobianToGlobal(
434  state.options.geoContext, state.stepping.jacToGlobal,
435  state.stepping.pos, state.stepping.dir,
436  st.filteredParameters(state.options.geoContext).parameters());
437  state.stepping.jacobian = BoundMatrix::Identity();
438  state.stepping.jacTransport = FreeMatrix::Identity();
439  state.stepping.derivative = FreeVector::Zero();
440 
441  // For the last measurement state, smoothed is filtered
442  st.smoothed() = st.filtered();
443  st.smoothedCovariance() = st.filteredCovariance();
444  result.passedAgainSurfaces.push_back(&st.referenceSurface());
445 
446  // Update material effects for last measurement state in backward
447  // direction
448  materialInteractor(state.navigation.currentSurface, state, stepper);
449 
450  return false; // abort execution
451  }
452  return true; // continue execution
453  });
454  }
455 
465  template <typename propagator_state_t, typename stepper_t>
466  Result<void> filter(const Surface* surface, propagator_state_t& state,
467  const stepper_t& stepper, result_type& result) const {
468  // Try to find the surface in the measurement surfaces
469  auto sourcelink_it = inputMeasurements.find(surface);
470  if (sourcelink_it != inputMeasurements.end()) {
471  // Screen output message
472  ACTS_VERBOSE("Measurement surface " << surface->geoID()
473  << " detected.");
474 
475  // Update state and stepper with pre material effects
476  materialInteractor(surface, state, stepper, preUpdate);
477 
478  // Transport & bind the state to the current surface
479  auto [boundParams, jacobian, pathLength] =
480  stepper.boundState(state.stepping, *surface);
481 
482  // add a full TrackState entry multi trajectory
483  // (this allocates storage for all components, we will set them later)
484  result.trackTip = result.fittedStates.addTrackState(
486 
487  // now get track state proxy back
488  auto trackStateProxy =
489  result.fittedStates.getTrackState(result.trackTip);
490 
491  // assign the source link to the track state
492  trackStateProxy.uncalibrated() = sourcelink_it->second;
493 
494  // Fill the track state
495  trackStateProxy.predicted() = boundParams.parameters();
496  trackStateProxy.predictedCovariance() = *boundParams.covariance();
497  trackStateProxy.jacobian() = jacobian;
498  trackStateProxy.pathLength() = pathLength;
499 
500  // We have predicted parameters, so calibrate the uncalibrated input
501  // measuerement
502  std::visit(
503  [&](const auto& calibrated) {
504  trackStateProxy.setCalibrated(calibrated);
505  },
506  m_calibrator(trackStateProxy.uncalibrated(),
507  trackStateProxy.predicted()));
508 
509  // Get and set the type flags
510  auto& typeFlags = trackStateProxy.typeFlags();
511  typeFlags.set(TrackStateFlag::MaterialFlag);
512  typeFlags.set(TrackStateFlag::ParameterFlag);
513 
514  // If the update is successful, set covariance and
515  auto updateRes = m_updater(state.geoContext, trackStateProxy, forward);
516  if (!updateRes.ok()) {
517  ACTS_ERROR("Update step failed: " << updateRes.error());
518  return updateRes.error();
519  } else {
520  if (not m_outlierFinder(trackStateProxy)) {
521  // Set the measurement type flag
522  typeFlags.set(TrackStateFlag::MeasurementFlag);
523  // Update the stepping state with filtered parameters
524  ACTS_VERBOSE(
525  "Filtering step successful, updated parameters are : \n"
526  << trackStateProxy.filtered().transpose());
527  // update stepping state using filtered parameters after kalman
528  // update We need to (re-)construct a BoundParameters instance here,
529  // which is a bit awkward.
530  stepper.update(state.stepping, trackStateProxy.filteredParameters(
531  state.options.geoContext));
532  // We count the state with measurement
533  ++result.measurementStates;
534  } else {
535  ACTS_VERBOSE(
536  "Filtering step successful. But measurement is deterimined to "
537  "be an outlier. Stepping state is not updated.")
538  // Set the outlier type flag
539  typeFlags.set(TrackStateFlag::OutlierFlag);
540  }
541 
542  // Update state and stepper with post material effects
543  materialInteractor(surface, state, stepper, postUpdate);
544  }
545  // We count the processed state
546  ++result.processedStates;
547  } else if (surface->surfaceMaterial() != nullptr) {
548  // We only create track states here if there is already measurement
549  // detected
550  if (result.measurementStates > 0) {
551  // No source links on surface, add either hole or passive material
552  // TrackState entry multi trajectory. No storage allocation for
553  // uncalibrated/calibrated measurement and filtered parameter
554  result.trackTip = result.fittedStates.addTrackState(
557  result.trackTip);
558 
559  // now get track state proxy back
560  auto trackStateProxy =
561  result.fittedStates.getTrackState(result.trackTip);
562 
563  // Set the surface
564  trackStateProxy.setReferenceSurface(surface->getSharedPtr());
565 
566  // Set the track state flags
567  auto& typeFlags = trackStateProxy.typeFlags();
568  typeFlags.set(TrackStateFlag::MaterialFlag);
569  typeFlags.set(TrackStateFlag::ParameterFlag);
570 
571  if (surface->associatedDetectorElement() != nullptr) {
572  ACTS_VERBOSE("Detected hole on " << surface->geoID());
573  // If the surface is sensitive, set the hole type flag
574  typeFlags.set(TrackStateFlag::HoleFlag);
575 
576  // Count the missed surface
577  result.missedActiveSurfaces.push_back(surface);
578 
579  // Transport & bind the state to the current surface
580  auto [boundParams, jacobian, pathLength] =
581  stepper.boundState(state.stepping, *surface);
582 
583  // Fill the track state
584  trackStateProxy.predicted() = boundParams.parameters();
585  trackStateProxy.predictedCovariance() = *boundParams.covariance();
586  trackStateProxy.jacobian() = jacobian;
587  trackStateProxy.pathLength() = pathLength;
588  } else {
589  ACTS_VERBOSE("Detected in-sensitive surface " << surface->geoID());
590 
591  // Transport & get curvilinear state instead of bound state
592  auto [curvilinearParams, jacobian, pathLength] =
593  stepper.curvilinearState(state.stepping);
594 
595  // Fill the track state
596  trackStateProxy.predicted() = curvilinearParams.parameters();
597  trackStateProxy.predictedCovariance() =
598  *curvilinearParams.covariance();
599  trackStateProxy.jacobian() = jacobian;
600  trackStateProxy.pathLength() = pathLength;
601  }
602 
603  // Set the filtered parameter index to be the same with predicted
604  // parameter
605  trackStateProxy.data().ifiltered = trackStateProxy.data().ipredicted;
606 
607  // We count the processed state
608  ++result.processedStates;
609  }
610 
611  // Update state and stepper with material effects
612  materialInteractor(surface, state, stepper, fullUpdate);
613  }
614  return Result<void>::success();
615  }
616 
626  template <typename propagator_state_t, typename stepper_t>
627  Result<void> backwardFilter(const Surface* surface,
628  propagator_state_t& state,
629  const stepper_t& stepper,
630  result_type& result) const {
631  // Try to find the surface in the measurement surfaces
632  auto sourcelink_it = inputMeasurements.find(surface);
633  if (sourcelink_it != inputMeasurements.end()) {
634  // Screen output message
635  ACTS_VERBOSE("Measurement surface "
636  << surface->geoID()
637  << " detected in backward propagation.");
638 
639  // No backward filtering for last measurement state, but still update
640  // with material effects
641  if (state.stepping.navDir == backward and
642  surface == state.navigation.startSurface) {
643  materialInteractor(surface, state, stepper);
644  return Result<void>::success();
645  }
646 
647  // Update state and stepper with pre material effects
648  materialInteractor(surface, state, stepper, preUpdate);
649 
650  // Transport & bind the state to the current surface
651  auto [boundParams, jacobian, pathLength] =
652  stepper.boundState(state.stepping, *surface);
653 
654  // Create a detached track state proxy
655  auto tempTrackTip =
656  result.fittedStates.addTrackState(TrackStatePropMask::All);
657 
658  // Get the detached track state proxy back
659  auto trackStateProxy = result.fittedStates.getTrackState(tempTrackTip);
660 
661  // Assign the source link to the detached track state
662  trackStateProxy.uncalibrated() = sourcelink_it->second;
663 
664  // Fill the track state
665  trackStateProxy.predicted() = boundParams.parameters();
666  trackStateProxy.predictedCovariance() = *boundParams.covariance();
667  trackStateProxy.jacobian() = jacobian;
668  trackStateProxy.pathLength() = pathLength;
669 
670  // We have predicted parameters, so calibrate the uncalibrated input
671  // measuerement
672  std::visit(
673  [&](const auto& calibrated) {
674  trackStateProxy.setCalibrated(calibrated);
675  },
676  m_calibrator(trackStateProxy.uncalibrated(),
677  trackStateProxy.predicted()));
678 
679  // If the update is successful, set covariance and
680  auto updateRes = m_updater(state.geoContext, trackStateProxy, backward);
681  if (!updateRes.ok()) {
682  ACTS_ERROR("Backward update step failed: " << updateRes.error());
683  return updateRes.error();
684  } else {
685  // Update the stepping state with filtered parameters
686  ACTS_VERBOSE(
687  "Backward Filtering step successful, updated parameters are : \n"
688  << trackStateProxy.filtered().transpose());
689 
690  // Fill the smoothed parameter for the existing track state
691  result.fittedStates.applyBackwards(result.trackTip, [&](auto state) {
692  auto fSurface = &state.referenceSurface();
693  if (fSurface == surface) {
694  result.passedAgainSurfaces.push_back(surface);
695  state.smoothed() = trackStateProxy.filtered();
696  state.smoothedCovariance() = trackStateProxy.filteredCovariance();
697  return false;
698  }
699  return true;
700  });
701 
702  // update stepping state using filtered parameters after kalman update
703  // We need to (re-)construct a BoundParameters instance here, which is
704  // a bit awkward.
705  stepper.update(state.stepping, trackStateProxy.filteredParameters(
706  state.options.geoContext));
707 
708  // Update state and stepper with post material effects
709  materialInteractor(surface, state, stepper, postUpdate);
710  }
711  } else if (surface->surfaceMaterial() != nullptr) {
712  // Transport covariance
713  if (surface->associatedDetectorElement() != nullptr) {
714  ACTS_VERBOSE("Detected hole on " << surface->geoID()
715  << " in backward filtering");
716  if (state.stepping.covTransport) {
717  stepper.covarianceTransport(state.stepping, *surface);
718  }
719  } else {
720  ACTS_VERBOSE("Detected in-sensitive surface "
721  << surface->geoID() << " in backward filtering");
722  if (state.stepping.covTransport) {
723  stepper.covarianceTransport(state.stepping);
724  }
725  }
726 
727  // Not creating bound state here, so need manually reinitialize jacobian
728  state.stepping.jacobian = BoundMatrix::Identity();
729 
730  // Update state and stepper with material effects
731  materialInteractor(surface, state, stepper);
732  }
733 
734  return Result<void>::success();
735  }
736 
747  template <typename propagator_state_t, typename stepper_t>
748  void materialInteractor(
749  const Surface* surface, propagator_state_t& state, stepper_t& stepper,
750  const MaterialUpdateStage& updateStage = fullUpdate) const {
751  // Indicator if having material
752  bool hasMaterial = false;
753 
754  if (surface and surface->surfaceMaterial()) {
755  // Prepare relevant input particle properties
756  detail::PointwiseMaterialInteraction interaction(surface, state,
757  stepper);
758  // Evaluate the material properties
759  if (interaction.evaluateMaterialProperties(state, updateStage)) {
760  // Surface has material at this stage
761  hasMaterial = true;
762 
763  // Evaluate the material effects
764  interaction.evaluatePointwiseMaterialInteraction(multipleScattering,
765  energyLoss);
766 
767  // Screen out material effects info
768  ACTS_VERBOSE("Material effects on surface: "
769  << surface->geoID()
770  << " at update stage: " << updateStage << " are :");
771  ACTS_VERBOSE("eLoss = "
772  << interaction.Eloss << ", "
773  << "variancePhi = " << interaction.variancePhi << ", "
774  << "varianceTheta = " << interaction.varianceTheta
775  << ", "
776  << "varianceQoverP = " << interaction.varianceQoverP);
777 
778  // Update the state and stepper with material effects
779  interaction.updateState(state, stepper);
780  }
781  }
782 
783  if (not hasMaterial) {
784  // Screen out message
785  ACTS_VERBOSE("No material effects on surface: " << surface->geoID()
786  << " at update stage: "
787  << updateStage);
788  }
789  }
790 
799  template <typename propagator_state_t, typename stepper_t>
800  Result<void> finalize(propagator_state_t& state, const stepper_t& stepper,
801  result_type& result) const {
802  // Remember you smoothed the track states
803  result.smoothed = true;
804 
805  // Get the indices of measurement states;
806  std::vector<size_t> measurementIndices;
807  measurementIndices.reserve(result.measurementStates);
808  // Count track states to be smoothed
809  size_t nStates = 0;
810  result.fittedStates.applyBackwards(result.trackTip, [&](auto st) {
811  bool isMeasurement =
812  st.typeFlags().test(TrackStateFlag::MeasurementFlag);
813  if (isMeasurement) {
814  measurementIndices.emplace_back(st.index());
815  } else if (measurementIndices.empty()) {
816  // No smoothed parameters if the last measurement state has not been
817  // found yet
818  st.data().ismoothed = detail_lt::IndexData::kInvalid;
819  }
820  // Start count when the last measurement state is found
821  if (not measurementIndices.empty()) {
822  nStates++;
823  }
824  });
825  // Return error if the track has no measurement states (but this should
826  // not happen)
827  if (measurementIndices.empty()) {
828  ACTS_ERROR("Smoothing for a track without measurements.");
829  return KalmanFitterError::SmoothFailed;
830  }
831  // Screen output for debugging
832  if (logger().doPrint(Logging::VERBOSE)) {
833  ACTS_VERBOSE("Apply smoothing on " << nStates
834  << " filtered track states.");
835  }
836  // Smooth the track states
837  auto smoothRes = m_smoother(state.geoContext, result.fittedStates,
838  measurementIndices.front());
839 
840  if (!smoothRes.ok()) {
841  ACTS_ERROR("Smoothing step failed: " << smoothRes.error());
842  return smoothRes.error();
843  }
844  // Obtain the smoothed parameters at first measurement state
845  auto firstMeasurement =
846  result.fittedStates.getTrackState(measurementIndices.back());
847  parameters_t smoothedPars =
848  firstMeasurement.smoothedParameters(state.options.geoContext);
849 
850  // Update the stepping parameters - in order to progress to destination
851  ACTS_VERBOSE(
852  "Smoothing successful, updating stepping state, "
853  "set target surface.");
854  stepper.update(state.stepping, smoothedPars);
855  // Reverse the propagation direction
856  state.stepping.stepSize =
857  ConstrainedStep(-1. * state.options.maxStepSize);
858  state.stepping.navDir = backward;
859  // Set accumulatd path to zero before targeting surface
860  state.stepping.pathAccumulated = 0.;
861  // Not sure if the following line helps anything
862  state.options.direction = backward;
863 
864  return Result<void>::success();
865  }
866 
868  const Logger* m_logger;
869 
871  const Logger& logger() const { return *m_logger; }
872 
874  updater_t m_updater;
875 
877  smoother_t m_smoother;
878 
880  outlier_finder_t m_outlierFinder;
881 
883  calibrator_t m_calibrator;
884 
887  };
888 
889  template <typename source_link_t, typename parameters_t>
890  class Aborter {
891  public:
894 
895  template <typename propagator_state_t, typename stepper_t,
896  typename result_t>
897  bool operator()(propagator_state_t& /*state*/, const stepper_t& /*stepper*/,
898  const result_t& result) const {
899  if (!result.result.ok()) {
900  return true;
901  }
902  return false;
903  }
904  };
905 
906  public:
924  template <typename source_link_t, typename start_parameters_t,
925  typename kalman_fitter_options_t,
926  typename parameters_t = BoundParameters,
927  typename result_t = Result<KalmanFitterResult<source_link_t>>>
928  auto fit(const std::vector<source_link_t>& sourcelinks,
929  const start_parameters_t& sParameters,
930  const kalman_fitter_options_t& kfOptions) const
931  -> std::enable_if_t<!isDirectNavigator, result_t> {
932  static_assert(SourceLinkConcept<source_link_t>,
933  "Source link does not fulfill SourceLinkConcept");
934 
935  static_assert(
936  std::is_same<outlier_finder_t,
937  typename kalman_fitter_options_t::OutlierFinder>::value,
938  "Inconsistent type of outlier finder between kalman fitter and "
939  "kalman fitter options");
940 
941  // To be able to find measurements later, we put them into a map
942  // We need to copy input SourceLinks anyways, so the map can own them.
943  ACTS_VERBOSE("Preparing " << sourcelinks.size() << " input measurements");
944  std::map<const Surface*, source_link_t> inputMeasurements;
945  for (const auto& sl : sourcelinks) {
946  const Surface* srf = &sl.referenceSurface();
947  inputMeasurements.emplace(srf, sl);
948  }
949 
950  // Create the ActionList and AbortList
951  using KalmanAborter = Aborter<source_link_t, parameters_t>;
952  using KalmanActor = Actor<source_link_t, parameters_t>;
953  using KalmanResult = typename KalmanActor::result_type;
954  using Actors = ActionList<KalmanActor>;
955  using Aborters = AbortList<KalmanAborter>;
956 
957  // Create relevant options for the propagation options
959  kfOptions.geoContext, kfOptions.magFieldContext);
960 
961  // Catch the actor and set the measurements
962  auto& kalmanActor = kalmanOptions.actionList.template get<KalmanActor>();
963  kalmanActor.m_logger = m_logger.get();
964  kalmanActor.inputMeasurements = std::move(inputMeasurements);
965  kalmanActor.targetSurface = kfOptions.referenceSurface;
966  kalmanActor.multipleScattering = kfOptions.multipleScattering;
967  kalmanActor.energyLoss = kfOptions.energyLoss;
968  kalmanActor.backwardFiltering = kfOptions.backwardFiltering;
969 
970  // Set config for outlier finder
971  kalmanActor.m_outlierFinder = kfOptions.outlierFinder;
972 
973  // also set logger on updater and smoother
974  kalmanActor.m_updater.m_logger = m_logger;
975  kalmanActor.m_smoother.m_logger = m_logger;
976 
977  // Run the fitter
978  auto result = m_propagator.template propagate(sParameters, kalmanOptions);
979 
980  if (!result.ok()) {
981  ACTS_ERROR("Propapation failed: " << result.error());
982  return result.error();
983  }
984 
985  const auto& propRes = *result;
986 
988  auto kalmanResult = propRes.template get<KalmanResult>();
989 
992  if (kalmanResult.result.ok() and not kalmanResult.processedStates) {
993  kalmanResult.result = Result<void>(KalmanFitterError::PropagationInVain);
994  }
995 
996  if (!kalmanResult.result.ok()) {
997  ACTS_ERROR("KalmanFilter failed: " << kalmanResult.result.error());
998  return kalmanResult.result.error();
999  }
1000 
1001  // Return the converted Track
1002  return m_outputConverter(std::move(kalmanResult));
1003  }
1004 
1023  template <typename source_link_t, typename start_parameters_t,
1024  typename kalman_fitter_options_t,
1025  typename parameters_t = BoundParameters,
1026  typename result_t = Result<KalmanFitterResult<source_link_t>>>
1027  auto fit(const std::vector<source_link_t>& sourcelinks,
1028  const start_parameters_t& sParameters,
1029  const kalman_fitter_options_t& kfOptions,
1030  const std::vector<const Surface*>& sSequence) const
1031  -> std::enable_if_t<isDirectNavigator, result_t> {
1032  static_assert(SourceLinkConcept<source_link_t>,
1033  "Source link does not fulfill SourceLinkConcept");
1034 
1035  static_assert(
1036  std::is_same<outlier_finder_t,
1037  typename kalman_fitter_options_t::OutlierFinder>::value,
1038  "Inconsistent type of outlier finder between kalman fitter and "
1039  "kalman fitter options");
1040 
1041  // To be able to find measurements later, we put them into a map
1042  // We need to copy input SourceLinks anyways, so the map can own them.
1043  ACTS_VERBOSE("Preparing " << sourcelinks.size() << " input measurements");
1044  std::map<const Surface*, source_link_t> inputMeasurements;
1045  for (const auto& sl : sourcelinks) {
1046  const Surface* srf = &sl.referenceSurface();
1047  inputMeasurements.emplace(srf, sl);
1048  }
1049 
1050  // Create the ActionList and AbortList
1051  using KalmanAborter = Aborter<source_link_t, parameters_t>;
1052  using KalmanActor = Actor<source_link_t, parameters_t>;
1053  using KalmanResult = typename KalmanActor::result_type;
1055  using Aborters = AbortList<KalmanAborter>;
1056 
1057  // Create relevant options for the propagation options
1059  kfOptions.geoContext, kfOptions.magFieldContext);
1060 
1061  // Catch the actor and set the measurements
1062  auto& kalmanActor = kalmanOptions.actionList.template get<KalmanActor>();
1063  kalmanActor.m_logger = m_logger.get();
1064  kalmanActor.inputMeasurements = std::move(inputMeasurements);
1065  kalmanActor.targetSurface = kfOptions.referenceSurface;
1066  kalmanActor.multipleScattering = kfOptions.multipleScattering;
1067  kalmanActor.energyLoss = kfOptions.energyLoss;
1068  kalmanActor.backwardFiltering = kfOptions.backwardFiltering;
1069 
1070  // Set config for outlier finder
1071  kalmanActor.m_outlierFinder.m_config = kfOptions.outlierFinderConfig;
1072 
1073  // also set logger on updater and smoother
1074  kalmanActor.m_updater.m_logger = m_logger;
1075  kalmanActor.m_smoother.m_logger = m_logger;
1076 
1077  // Set the surface sequence
1078  auto& dInitializer =
1079  kalmanOptions.actionList.template get<DirectNavigator::Initializer>();
1080  dInitializer.surfaceSequence = sSequence;
1081 
1082  // Run the fitter
1083  auto result = m_propagator.template propagate(sParameters, kalmanOptions);
1084 
1085  if (!result.ok()) {
1086  ACTS_ERROR("Propapation failed: " << result.error());
1087  return result.error();
1088  }
1089 
1090  const auto& propRes = *result;
1091 
1093  auto kalmanResult = propRes.template get<KalmanResult>();
1094 
1097  if (kalmanResult.result.ok() and not kalmanResult.processedStates) {
1098  kalmanResult.result = Result<void>(KalmanFitterError::PropagationInVain);
1099  }
1100 
1101  if (!kalmanResult.result.ok()) {
1102  ACTS_ERROR("KalmanFilter failed: " << kalmanResult.result.error());
1103  return kalmanResult.result.error();
1104  }
1105 
1106  // Return the converted Track
1107  return m_outputConverter(std::move(kalmanResult));
1108  }
1109 };
1110 
1111 } // namespace Acts