EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CombinatorialKalmanFilter.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file CombinatorialKalmanFilter.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2016-2020 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 
37 
38 #include <functional>
39 #include <map>
40 #include <memory>
41 #include <unordered_map>
42 
43 namespace Acts {
44 
51  // Number of passed sensitive surfaces
52  size_t nSensitiveSurfaces = 0;
53  // Number of track states
54  size_t nStates = 0;
55  // Number of (non-outlier) measurements
56  size_t nMeasurements = 0;
57  // Number of outliers
58  size_t nOutliers = 0;
59  // Number of holes
60  size_t nHoles = 0;
61 };
62 
73 template <typename source_link_selector_t>
75  // Broadcast the source link selector type
76  using SourceLinkSelector = source_link_selector_t;
77 
78  // Broadcast the source link selector config type
80 
83 
99  std::reference_wrapper<const GeometryContext> gctx,
100  std::reference_wrapper<const MagneticFieldContext> mctx,
101  std::reference_wrapper<const CalibrationContext> cctx,
102  const SourceLinkSelectorConfig& slsCfg, LoggerWrapper logger_,
103  const PropagatorPlainOptions& pOptions, const Surface* rSurface = nullptr,
104  bool mScattering = true, bool eLoss = true, bool rSmoothing = true)
105  : geoContext(gctx),
106  magFieldContext(mctx),
107  calibrationContext(cctx),
108  sourcelinkSelectorConfig(slsCfg),
109  propagatorPlainOptions(pOptions),
110  referenceSurface(rSurface),
111  multipleScattering(mScattering),
112  energyLoss(eLoss),
113  smoothing(rSmoothing),
114  logger(logger_) {}
115 
117  std::reference_wrapper<const GeometryContext> geoContext;
119  std::reference_wrapper<const MagneticFieldContext> magFieldContext;
121  std::reference_wrapper<const CalibrationContext> calibrationContext;
122 
125 
128 
130  const Surface* referenceSurface = nullptr;
131 
133  bool multipleScattering = true;
134 
136  bool energyLoss = true;
137 
139  bool smoothing = true;
140 
143 };
144 
145 template <typename source_link_t>
147  // Fitted states that the actor has handled.
149 
150  // The indices of the 'tip' of the tracks stored in multitrajectory.
151  std::vector<size_t> trackTips;
152 
153  // The Parameters at the provided surface for separate tracks
154  std::unordered_map<size_t, BoundTrackParameters> fittedParameters;
155 
156  // The indices of the 'tip' of the unfinished tracks
157  std::vector<std::pair<size_t, CombinatorialKalmanFilterTipState>> activeTips;
158 
159  // The indices of source links in multitrajectory
160  std::unordered_map<const Surface*, std::unordered_map<size_t, size_t>>
162 
163  // Indicator if forward filtering has been done
164  bool forwardFiltered = false;
165 
166  // Indicator if smoothing has been done.
167  bool smoothed = false;
168 
169  // The index for the current smoothing track
170  size_t iSmoothed = 0;
171 
172  // Indicator if the propagation state has been reset
173  bool reset = false;
174 
175  // Indicator if track finding has been done
176  bool finished = false;
177 
178  // Temporary container for index and chi2 of intermediate source link
179  // candidates
180  std::vector<std::pair<size_t, double>> sourcelinkChi2;
181 
182  // Temporary container for index of final source link candidates
183  std::vector<size_t> sourcelinkCandidateIndices;
184 
186 };
187 
220 template <typename propagator_t, typename updater_t = VoidKalmanUpdater,
221  typename smoother_t = VoidKalmanSmoother,
222  typename source_link_selector_t = CKFSourceLinkSelector,
223  typename branch_stopper_t = VoidBranchStopper,
224  typename calibrator_t = VoidMeasurementCalibrator>
226  public:
228  using MeasurementSurfaces = std::multimap<const Layer*, const Surface*>;
230  using KalmanNavigator = typename propagator_t::Navigator;
231 
233  CombinatorialKalmanFilter() = delete;
234 
237  : m_propagator(std::move(pPropagator)) {}
238 
239  private:
242 
250  template <typename source_link_t, typename parameters_t>
251  class Actor {
252  public:
254  using BoundState = std::tuple<BoundTrackParameters, BoundMatrix, double>;
255  using CurvilinearState =
256  std::tuple<CurvilinearTrackParameters, BoundMatrix, double>;
259 
261  const Surface* targetSurface = nullptr;
262 
264  std::unordered_map<const Surface*, std::vector<source_link_t>>
266 
268  bool multipleScattering = true;
269 
271  bool energyLoss = true;
272 
274  bool smoothing = true;
275 
284  template <typename propagator_state_t, typename stepper_t>
285  void operator()(propagator_state_t& state, const stepper_t& stepper,
286  result_type& result) const {
287  const auto& logger = state.options.logger;
288 
289  if (result.finished) {
290  return;
291  }
292 
293  ACTS_VERBOSE("CombinatorialKalmanFilter step");
294 
295  // This following is added due to the fact that the navigation
296  // reinitialization in reset call cannot guarantee the navigator to target
297  // for extra layers in the reset volume.
298  // Currently, manually set navigation stage to allow for targeting layers
299  // after all the surfaces on the reset layer has been processed.
300  // Otherwise, the navigation stage will be Stage::boundaryTarget after
301  // navigator status call which means the extra layers on the reset volume
302  // won't be targeted.
303  // @Todo: Let the navigator do all the re-initialization
304  if (result.reset and state.navigation.navSurfaceIter ==
305  state.navigation.navSurfaces.end()) {
306  // So the navigator target call will target layers
307  state.navigation.navigationStage = KalmanNavigator::Stage::layerTarget;
308  // We only do this after the reset layer has been processed
309  result.reset = false;
310  }
311 
312  // Update:
313  // - Waiting for a current surface
314  auto surface = state.navigation.currentSurface;
315  if (surface != nullptr and not result.forwardFiltered) {
316  // There are three scenarios:
317  // 1) The surface is in the measurement map
318  // -> Select source links
319  // -> Perform the kalman update for selected non-outlier source links
320  // -> Add track states in multitrajectory. Multiple states mean branch
321  // splitting.
322  // -> Call branch stopper to justify each branch
323  // -> If there is non-outlier state, update stepper information
324  // 2) The surface is not in the measurement map but with material
325  // -> Add a hole or passive material state in multitrajectory
326  // -> Call branch stopper to justify the branch
327  // 3) The surface is neither in the measurement map nor with material
328  // -> Do nothing
329  ACTS_VERBOSE("Perform filter step");
330  auto res = filter(surface, state, stepper, result);
331  if (!res.ok()) {
332  ACTS_ERROR("Error in filter: " << res.error());
333  result.result = res.error();
334  }
335  }
336 
337  // Reset propagation state:
338  // - When navigation breaks and there is stil active tip present after
339  // recording&removing track tips on current surface
340  if (state.navigation.navigationBreak and not result.forwardFiltered) {
341  // Record the tips on current surface as trajectory entry indices
342  // (taking advantage of fact that those tips are consecutive in list of
343  // active tips) and remove those tips from active tips
344  if (not result.activeTips.empty()) {
345  // The last active tip
346  const auto& lastActiveTip = result.activeTips.back().first;
347  // Get the index of previous state
348  const auto& iprevious =
349  result.fittedStates.getTrackState(lastActiveTip).previous();
350  // Find the track states which have the same previous state and remove
351  // them from active tips
352  while (not result.activeTips.empty()) {
353  const auto& [currentTip, tipState] = result.activeTips.back();
354  if (result.fittedStates.getTrackState(currentTip).previous() !=
355  iprevious) {
356  break;
357  }
358  // Record the tips if there are measurements on the track
359  if (tipState.nMeasurements > 0) {
360  ACTS_VERBOSE("Find track with entry index = "
361  << currentTip << " and there are nMeasurements = "
362  << tipState.nMeasurements
363  << ", nOutliers = " << tipState.nOutliers
364  << ", nHoles = " << tipState.nHoles << " on track");
365  result.trackTips.emplace_back(currentTip);
366  }
367  // Remove the tip from list of active tips
368  result.activeTips.erase(result.activeTips.end() - 1);
369  }
370  }
371  // If no more active tip, done with forward filtering; Otherwise, reset
372  // propagation state to track state at last tip of active tips
373  if (result.activeTips.empty()) {
374  ACTS_VERBOSE("Forward Kalman filtering finds "
375  << result.trackTips.size() << " tracks");
376  result.forwardFiltered = true;
377  } else {
378  ACTS_VERBOSE("Propagation jumps to branch with tip = "
379  << result.activeTips.back().first);
380  reset(state, stepper, result);
381  }
382  }
383 
384  // Post-processing after forward filtering
385  if (result.forwardFiltered) {
386  // Return error if forward filtering finds no tracks
387  if (result.trackTips.empty()) {
388  result.result =
389  Result<void>(CombinatorialKalmanFilterError::NoTrackFound);
390  } else {
391  if (not smoothing) {
392  ACTS_VERBOSE("Finish forward Kalman filtering");
393  // Remember that track finding is done
394  result.finished = true;
395  } else {
396  // Iterate over the found tracks for smoothing and getting the
397  // fitted parameter. This needs to be accomplished in different
398  // propagation steps:
399  // -> first run smoothing for found track indexed with iSmoothed
400  if (not result.smoothed) {
401  ACTS_VERBOSE(
402  "Finalize/run smoothing for track with entry index = "
403  << result.trackTips.at(result.iSmoothed));
404  // --> Search the starting state to run the smoothing
405  // --> Call the smoothing
406  // --> Set a stop condition when all track states have been
407  // handled
408  auto res = finalize(state, stepper, result);
409  if (!res.ok()) {
410  ACTS_ERROR("Error in finalize: " << res.error());
411  result.result = res.error();
412  }
413  result.smoothed = true;
414  }
415  // -> then progress to target/reference surface and built the final
416  // track parameters for found track indexed with iSmoothed
417  if (result.smoothed and
418  targetReached(state, stepper, *targetSurface)) {
419  ACTS_VERBOSE("Completing the track with entry index = "
420  << result.trackTips.at(result.iSmoothed));
421  // Transport & bind the parameter to the final surface
422  auto fittedState =
423  stepper.boundState(state.stepping, *targetSurface);
424  // Assign the fitted parameters
425  result.fittedParameters.emplace(
426  result.trackTips.at(result.iSmoothed),
427  std::get<BoundTrackParameters>(fittedState));
428  // If there are more trajectories to handle:
429  // -> set the targetReached status to false
430  // -> set the smoothed status to false
431  // -> update the index of track to be smoothed
432  if (result.iSmoothed < result.trackTips.size() - 1) {
433  state.navigation.targetReached = false;
434  result.smoothed = false;
435  result.iSmoothed++;
436  // To avoid meaningless navigation target call
437  state.stepping.stepSize =
438  ConstrainedStep(state.options.maxStepSize);
439  // Need to go back to start targeting for the rest tracks
440  state.stepping.navDir = forward;
441  } else {
442  ACTS_VERBOSE(
443  "Finish forward Kalman filtering and backward smoothing");
444  // Remember that track finding is done
445  result.finished = true;
446  }
447  }
448  } // if run smoothing
449  } // if there are found tracks
450  } // if forward filtering is done
451  }
452 
461  template <typename propagator_state_t, typename stepper_t>
462  void reset(propagator_state_t& state, stepper_t& stepper,
463  result_type& result) const {
464  // Remember the propagation state has been reset
465  result.reset = true;
466  auto currentState =
467  result.fittedStates.getTrackState(result.activeTips.back().first);
468 
469  // Reset the navigation state
470  state.navigation = typename propagator_t::NavigatorState();
471  state.navigation.startSurface = &currentState.referenceSurface();
472  if (state.navigation.startSurface->associatedLayer() != nullptr) {
473  state.navigation.startLayer =
474  state.navigation.startSurface->associatedLayer();
475  }
476  state.navigation.startVolume =
477  state.navigation.startLayer->trackingVolume();
478  state.navigation.targetSurface = targetSurface;
479  state.navigation.currentSurface = state.navigation.startSurface;
480  state.navigation.currentVolume = state.navigation.startVolume;
481 
482  // Update the stepping state
483  stepper.resetState(state.stepping, currentState.filtered(),
484  currentState.filteredCovariance(),
485  currentState.referenceSurface(), state.stepping.navDir,
486  state.options.maxStepSize);
487 
488  // No Kalman filtering for the starting surface, but still need
489  // to consider the material effects here
490  materialInteractor(state.navigation.startSurface, state, stepper);
491  }
492 
505  template <typename propagator_state_t, typename stepper_t>
506  Result<void> filter(const Surface* surface, propagator_state_t& state,
507  const stepper_t& stepper, result_type& result) const {
508  const auto& logger = state.options.logger;
509  // Initialize the number of branches on current surface
510  size_t nBranchesOnSurface = 0;
511 
512  // Try to find the surface in the measurement surfaces
513  auto sourcelink_it = inputMeasurements.find(surface);
514  ACTS_VERBOSE("Searching on surface : ");
515  ACTS_VERBOSE(surface->geometryId());
516  if(sourcelink_it != inputMeasurements.end()){
517  }
518  else
519  ACTS_VERBOSE("No source link on surface. Skipping filtering");
520 
521  if (sourcelink_it != inputMeasurements.end()) {
522  // Screen output message
523  ACTS_VERBOSE("Measurement surface " << surface->geometryId()
524  << " detected.");
525 
526  // Transport & bind the state to the current surface
527  auto boundState = stepper.boundState(state.stepping, *surface);
528  auto boundParams = std::get<BoundTrackParameters>(boundState);
529 
530  // Update state and stepper with pre material effects
531  materialInteractor(surface, state, stepper, preUpdate);
532 
533  // Get all source links on surface
534  auto& sourcelinks = sourcelink_it->second;
535 
536  // Invoke the source link selector to select source links for either
537  // measurements or outlier.
538  // Calibrator is passed to the selector because
539  // selection has to be done based on calibrated measurement
540  bool isOutlier = false;
541  auto sourcelinkSelectionRes = m_sourcelinkSelector(
542  m_calibrator, boundParams, sourcelinks, result.sourcelinkChi2,
543  result.sourcelinkCandidateIndices, isOutlier, logger);
544  if (!sourcelinkSelectionRes.ok()) {
545  ACTS_ERROR("Selection of source links failed: "
546  << sourcelinkSelectionRes.error());
547  return sourcelinkSelectionRes.error();
548  }
549 
550  // Retrieve the previous tip and its state
551  // The states created on this surface will have the common previous tip
552  size_t prevTip = SIZE_MAX;
553  TipState prevTipState;
554  if (not result.activeTips.empty()) {
555  prevTip = result.activeTips.back().first;
556  prevTipState = result.activeTips.back().second;
557  // New state is to be added. Remove the last tip from active tips
558  result.activeTips.erase(result.activeTips.end() - 1);
559  }
560 
561  // Remember the tip of the neighbor state on this surface
562  size_t neighborTip = SIZE_MAX;
563  // Loop over the selected source links
564  for (const auto& index : result.sourcelinkCandidateIndices) {
565  // Determine if predicted parameter is already contained in
566  // neighboring state
567  bool isPredictedShared = (neighborTip != SIZE_MAX);
568 
569  // Determine if uncalibrated measurement are already
570  // contained in other track state
571  bool isSourcelinkShared = false;
572  size_t sharedTip = SIZE_MAX;
573  auto sourcelinkTips_it = result.sourcelinkTips.find(surface);
574  if (sourcelinkTips_it != result.sourcelinkTips.end()) {
575  auto& sourcelinkTipsOnSurface = sourcelinkTips_it->second;
576  auto index_it = sourcelinkTipsOnSurface.find(index);
577  if (index_it != sourcelinkTipsOnSurface.end()) {
578  isSourcelinkShared = true;
579  sharedTip = index_it->second;
580  }
581  }
582 
583  // The mask for adding a state in the multitrajectory
584  // No storage allocation for:
585  // -> predicted parameter and uncalibrated measurement if
586  // already stored
587  // -> filtered parameter for outlier
588  auto stateMask =
589  (isPredictedShared ? ~TrackStatePropMask::Predicted
590  : TrackStatePropMask::All) &
591  (isSourcelinkShared ? ~TrackStatePropMask::Uncalibrated
592  : TrackStatePropMask::All) &
593  (isOutlier ? ~TrackStatePropMask::Filtered
594  : TrackStatePropMask::All);
595 
596  // Add measurement/outlier track state to the multitrajectory
597  auto addStateRes =
598  addSourcelinkState(stateMask, boundState, sourcelinks.at(index),
599  isOutlier, result, state.geoContext, prevTip,
600  prevTipState, neighborTip, sharedTip, logger);
601  if (addStateRes.ok()) {
602  const auto& [currentTip, tipState] = addStateRes.value();
603  // Remember the track state tip for this stored source link
604  if (not isSourcelinkShared) {
605  auto& sourcelinkTipsOnSurface = result.sourcelinkTips[surface];
606  sourcelinkTipsOnSurface.emplace(index, currentTip);
607  }
608  // Remember the tip of neighbor state on this surface
609  neighborTip = currentTip;
610 
611  // Check if need to stop this branch
612  if (not m_branchStopper(tipState)) {
613  // Remember the active tip and its state
614  result.activeTips.emplace_back(std::move(currentTip),
615  std::move(tipState));
616  // Record the number of branches on surface
617  nBranchesOnSurface++;
618  }
619  }
620  } // end of loop for all selected source links on this surface
621 
622  if (nBranchesOnSurface > 0 and not isOutlier) {
623  // If there are measurement track states on this surface
624  ACTS_VERBOSE("Filtering step successful with " << nBranchesOnSurface
625  << " branches");
626  // Update stepping state using filtered parameters of last track
627  // state on this surface
628  auto ts =
629  result.fittedStates.getTrackState(result.activeTips.back().first);
630  stepper.update(state.stepping,
632  state.options.geoContext, ts),
633  ts.filteredCovariance());
634  ACTS_VERBOSE("Stepping state is updated with filtered parameter: \n"
635  << ts.filtered().transpose()
636  << " of track state with tip = "
637  << result.activeTips.back().first);
638  }
639  // Update state and stepper with post material effects
640  materialInteractor(surface, state, stepper, postUpdate);
641  } else if (surface->surfaceMaterial() != nullptr) {
642  // No splitting on the surface without source links. Set it to one
643  // first, but could be changed later
644  nBranchesOnSurface = 1;
645 
646  // Retrieve the previous tip and its state
647  size_t prevTip = SIZE_MAX;
648  TipState tipState;
649  if (not result.activeTips.empty()) {
650  prevTip = result.activeTips.back().first;
651  tipState = result.activeTips.back().second;
652  }
653 
654  // The surface could be either sensitive or passive
655  bool isSensitive = (surface->associatedDetectorElement() != nullptr);
656  std::string type = isSensitive ? "sensitive" : "passive";
657  ACTS_VERBOSE("Detected " << type
658  << " surface: " << surface->geometryId());
659  if (isSensitive) {
660  // Increment of number of passed sensitive surfaces
661  tipState.nSensitiveSurfaces++;
662  }
663  // Add state if there is already measurement detected on this branch
664  // For in-sensitive surface, only add state when smoothing is
665  // required
666  if (tipState.nMeasurements > 0 and
667  (isSensitive or (not isSensitive and smoothing))) {
668  // New state is to be added. Remove the last tip from active tips now
669  result.activeTips.erase(result.activeTips.end() - 1);
670 
671  // No source links on surface, add either hole or passive material
672  // TrackState. No storage allocation for uncalibrated/calibrated
673  // measurement and filtered parameter
674  auto stateMask =
675  ~(TrackStatePropMask::Uncalibrated |
676  TrackStatePropMask::Calibrated | TrackStatePropMask::Filtered);
677 
678  // Increment of number of processed states
679  tipState.nStates++;
680  size_t currentTip = SIZE_MAX;
681  if (isSensitive) {
682  // Transport & bind the state to the current surface
683  auto boundState = stepper.boundState(state.stepping, *surface);
684  // Add a hole track state to the multitrajectory
685  currentTip =
686  addHoleState(stateMask, boundState, result, prevTip, logger);
687  // Incremet of number of holes
688  tipState.nHoles++;
689  } else {
690  // Transport & get curvilinear state instead of bound state
691  auto curvilinearState = stepper.curvilinearState(state.stepping);
692  // Add a passive material track state to the multitrajectory
693  currentTip = addPassiveState(stateMask, curvilinearState, result,
694  prevTip, logger);
695  }
696 
697  // Check the branch
698  if (not m_branchStopper(tipState)) {
699  // Remember the active tip and its state
700  result.activeTips.emplace_back(std::move(currentTip),
701  std::move(tipState));
702  } else {
703  // No branch on this surface
704  nBranchesOnSurface = 0;
705  }
706  }
707  // Update state and stepper with material effects
708  materialInteractor(surface, state, stepper, fullUpdate);
709  } else {
710  // Neither measurement nor material on surface, this branch is still
711  // valid. Count the branch on current surface
712  nBranchesOnSurface = 1;
713  }
714 
715  // Reset current tip if there is no branch on current surface
716  if (nBranchesOnSurface == 0) {
717  ACTS_DEBUG("Branch on surface " << surface->geometryId()
718  << " is stopped");
719  if (not result.activeTips.empty()) {
720  ACTS_VERBOSE("Propagation jumps to branch with tip = "
721  << result.activeTips.back().first);
722  reset(state, stepper, result);
723  } else {
724  ACTS_VERBOSE("Stop forward Kalman filtering with "
725  << result.trackTips.size() << " found tracks");
726  result.forwardFiltered = true;
727  }
728  }
729 
730  return Result<void>::success();
731  }
732 
749  const TrackStatePropMask& stateMask, const BoundState& boundState,
750  const source_link_t& sourcelink, bool isOutlier, result_type& result,
751  std::reference_wrapper<const GeometryContext> geoContext,
752  const size_t& prevTip, const TipState& prevTipState,
753  size_t neighborTip = SIZE_MAX, size_t sharedTip = SIZE_MAX,
754  LoggerWrapper logger = getDummyLogger()) const {
755  // Inherit the tip state from the previous and will be updated later
756  TipState tipState = prevTipState;
757 
758  // Add a track state
759  auto currentTip = result.fittedStates.addTrackState(stateMask, prevTip);
760 
761  // Get the track state proxy
762  auto trackStateProxy = result.fittedStates.getTrackState(currentTip);
763 
764  auto [boundParams, jacobian, pathLength] = boundState;
765 
766  // Fill the parametric part of the track state proxy
767  if ((not ACTS_CHECK_BIT(stateMask, TrackStatePropMask::Predicted)) and
768  neighborTip != SIZE_MAX) {
769  // The predicted parameter is already stored, just set the index
770  auto neighborState = result.fittedStates.getTrackState(neighborTip);
771  trackStateProxy.data().ipredicted = neighborState.data().ipredicted;
772  } else {
773  trackStateProxy.predicted() = boundParams.parameters();
774  trackStateProxy.predictedCovariance() = *boundParams.covariance();
775  }
776  trackStateProxy.jacobian() = jacobian;
777  trackStateProxy.pathLength() = pathLength;
778 
779  // Assign the uncalibrated&calibrated measurement to the track
780  // state (the uncalibrated could be already stored in other states)
781  if ((not ACTS_CHECK_BIT(stateMask, TrackStatePropMask::Uncalibrated)) and
782  sharedTip != SIZE_MAX) {
783  // The uncalibrated are already stored, just set the
784  // index
785  auto shared = result.fittedStates.getTrackState(sharedTip);
786  trackStateProxy.data().iuncalibrated = shared.data().iuncalibrated;
787  } else {
788  trackStateProxy.uncalibrated() = sourcelink;
789  }
790  std::visit(
791  [&](const auto& calibrated) {
792  trackStateProxy.setCalibrated(calibrated);
793  },
794  m_calibrator(trackStateProxy.uncalibrated(),
795  trackStateProxy.predicted()));
796 
797  // Get and set the type flags
798  auto& typeFlags = trackStateProxy.typeFlags();
799  typeFlags.set(TrackStateFlag::MaterialFlag);
800  typeFlags.set(TrackStateFlag::ParameterFlag);
801 
802  // Increment of number of processedState and passed sensitive surfaces
803  tipState.nSensitiveSurfaces++;
804  tipState.nStates++;
805 
806  if (isOutlier) {
807  ACTS_VERBOSE("Creating outlier track state with tip = " << currentTip);
808  // Set the outlier flag
809  typeFlags.set(TrackStateFlag::OutlierFlag);
810  // Increment number of outliers
811  tipState.nOutliers++;
812  // No Kalman update for outlier
813  // Set the filtered parameter index to be the same with predicted
814  // parameter
815  trackStateProxy.data().ifiltered = trackStateProxy.data().ipredicted;
816  } else {
817  // Kalman update
818  auto updateRes = m_updater(geoContext, trackStateProxy);
819  if (!updateRes.ok()) {
820  ACTS_ERROR("Update step failed: " << updateRes.error());
821  return updateRes.error();
822  }
823  ACTS_VERBOSE(
824  "Creating measurement track state with tip = " << currentTip);
825  // Set the measurement flag
826  typeFlags.set(TrackStateFlag::MeasurementFlag);
827  // Increment number of measurements
828  tipState.nMeasurements++;
829  }
830  return std::make_pair(std::move(currentTip), std::move(tipState));
831  }
832 
842  size_t addHoleState(const TrackStatePropMask& stateMask,
843  const BoundState& boundState, result_type& result,
844  size_t prevTip = SIZE_MAX,
845  LoggerWrapper logger = getDummyLogger()) const {
846  // Add a track state
847  auto currentTip = result.fittedStates.addTrackState(stateMask, prevTip);
848  ACTS_VERBOSE("Creating Hole track state with tip = " << currentTip);
849 
850  // now get track state proxy back
851  auto trackStateProxy = result.fittedStates.getTrackState(currentTip);
852 
853  // Set the track state flags
854  auto& typeFlags = trackStateProxy.typeFlags();
855  typeFlags.set(TrackStateFlag::MaterialFlag);
856  typeFlags.set(TrackStateFlag::ParameterFlag);
857  typeFlags.set(TrackStateFlag::HoleFlag);
858 
859  auto [boundParams, jacobian, pathLength] = boundState;
860  // Fill the track state
861  trackStateProxy.predicted() = boundParams.parameters();
862  trackStateProxy.predictedCovariance() = *boundParams.covariance();
863  trackStateProxy.jacobian() = jacobian;
864  trackStateProxy.pathLength() = pathLength;
865  // Set the surface
866  trackStateProxy.setReferenceSurface(
867  boundParams.referenceSurface().getSharedPtr());
868  // Set the filtered parameter index to be the same with predicted
869  // parameter
870  trackStateProxy.data().ifiltered = trackStateProxy.data().ipredicted;
871 
872  return currentTip;
873  }
874 
886  size_t addPassiveState(const TrackStatePropMask& stateMask,
888  result_type& result, size_t prevTip = SIZE_MAX,
889  LoggerWrapper logger = getDummyLogger()) const {
890  // Add a track state
891  auto currentTip = result.fittedStates.addTrackState(stateMask, prevTip);
892  ACTS_VERBOSE(
893  "Creating track state on in-sensitive material surface with tip = "
894  << currentTip);
895 
896  // now get track state proxy back
897  auto trackStateProxy = result.fittedStates.getTrackState(currentTip);
898 
899  // Set the track state flags
900  auto& typeFlags = trackStateProxy.typeFlags();
901  typeFlags.set(TrackStateFlag::MaterialFlag);
902  typeFlags.set(TrackStateFlag::ParameterFlag);
903 
904  auto [curvilinearParams, jacobian, pathLength] = curvilinearState;
905  // Fill the track state
906  trackStateProxy.predicted() = curvilinearParams.parameters();
907  trackStateProxy.predictedCovariance() = *curvilinearParams.covariance();
908  trackStateProxy.jacobian() = jacobian;
909  trackStateProxy.pathLength() = pathLength;
910  // Set the surface; reuse the existing curvilinear surface
911  trackStateProxy.setReferenceSurface(
912  curvilinearParams.referenceSurface().getSharedPtr());
913  // Set the filtered parameter index to be the same with predicted
914  // parameter
915  trackStateProxy.data().ifiltered = trackStateProxy.data().ipredicted;
916 
917  return currentTip;
918  }
919 
930  template <typename propagator_state_t, typename stepper_t>
932  const Surface* surface, propagator_state_t& state, stepper_t& stepper,
933  const MaterialUpdateStage& updateStage = fullUpdate) const {
934  const auto& logger = state.options.logger;
935  // Indicator if having material
936  bool hasMaterial = false;
937 
938  if (surface and surface->surfaceMaterial()) {
939  // Prepare relevant input particle properties
940  detail::PointwiseMaterialInteraction interaction(surface, state,
941  stepper);
942  // Evaluate the material properties
943  if (interaction.evaluateMaterialSlab(state, updateStage)) {
944  // Surface has material at this stage
945  hasMaterial = true;
946 
947  // Evaluate the material effects
949  energyLoss);
950 
951  // Screen out material effects info
952  ACTS_VERBOSE("Material effects on surface: "
953  << surface->geometryId()
954  << " at update stage: " << updateStage << " are :");
955  ACTS_VERBOSE("eLoss = "
956  << interaction.Eloss << ", "
957  << "variancePhi = " << interaction.variancePhi << ", "
958  << "varianceTheta = " << interaction.varianceTheta
959  << ", "
960  << "varianceQoverP = " << interaction.varianceQoverP);
961 
962  // Update the state and stepper with material effects
963  interaction.updateState(state, stepper);
964  }
965  }
966 
967  if (not hasMaterial) {
968  // Screen out message
969  ACTS_VERBOSE("No material effects on surface: " << surface->geometryId()
970  << " at update stage: "
971  << updateStage);
972  }
973  }
974 
983  template <typename propagator_state_t, typename stepper_t>
984  Result<void> finalize(propagator_state_t& state, const stepper_t& stepper,
985  result_type& result) const {
986  const auto& logger = state.options.logger;
987  // The tip of the track being smoothed
988  const auto& currentTip = result.trackTips.at(result.iSmoothed);
989 
990  // Get the indices of measurement states;
991  std::vector<size_t> measurementIndices;
992  // Count track states to be smoothed
993  size_t nStates = 0;
994  result.fittedStates.applyBackwards(currentTip, [&](auto st) {
995  bool isMeasurement =
996  st.typeFlags().test(TrackStateFlag::MeasurementFlag);
997  if (isMeasurement) {
998  measurementIndices.emplace_back(st.index());
999  } else if (measurementIndices.empty()) {
1000  // No smoothed parameter if the last measurment state has not been
1001  // found yet
1002  st.data().ismoothed = detail_lt::IndexData::kInvalid;
1003  }
1004  // Start count when the last measurement state is found
1005  if (not measurementIndices.empty()) {
1006  nStates++;
1007  }
1008  });
1009  // Return error if the track has no measurement states (but this should
1010  // not happen)
1011  if (measurementIndices.empty()) {
1012  ACTS_ERROR("Smoothing for a track without measurements.");
1013  return CombinatorialKalmanFilterError::SmoothFailed;
1014  }
1015  // Screen output for debugging
1016  ACTS_VERBOSE("Apply smoothing on " << nStates
1017  << " filtered track states.");
1018  // Smooth the track states
1019  auto smoothRes = m_smoother(state.geoContext, result.fittedStates,
1020  measurementIndices.front());
1021  if (!smoothRes.ok()) {
1022  ACTS_ERROR("Smoothing step failed: " << smoothRes.error());
1023  return smoothRes.error();
1024  }
1025  // Obtain the smoothed parameters at first measurement state
1026  auto firstMeasurement =
1027  result.fittedStates.getTrackState(measurementIndices.back());
1028 
1029  // Update the stepping parameters - in order to progress to destination
1030  ACTS_VERBOSE(
1031  "Smoothing successful, updating stepping state, "
1032  "set target surface.");
1033  stepper.update(state.stepping,
1035  state.options.geoContext, firstMeasurement),
1036  firstMeasurement.smoothedCovariance());
1037  // Reverse the propagation direction
1038  state.stepping.stepSize =
1039  ConstrainedStep(-1. * state.options.maxStepSize);
1040  state.stepping.navDir = backward;
1041  // Set accumulatd path to zero before targeting surface
1042  state.stepping.pathAccumulated = 0.;
1043 
1044  return Result<void>::success();
1045  }
1046 
1048  updater_t m_updater;
1049 
1051  smoother_t m_smoother;
1052 
1054  source_link_selector_t m_sourcelinkSelector;
1055 
1057  branch_stopper_t m_branchStopper;
1058 
1060  calibrator_t m_calibrator;
1061 
1064  };
1065 
1066  template <typename source_link_t, typename parameters_t>
1067  class Aborter {
1068  public:
1071 
1072  template <typename propagator_state_t, typename stepper_t,
1073  typename result_t>
1074  bool operator()(propagator_state_t& /*state*/, const stepper_t& /*stepper*/,
1075  const result_t& result) const {
1076  if (!result.result.ok() or result.finished) {
1077  return true;
1078  }
1079  return false;
1080  }
1081  };
1082 
1083  public:
1101  template <typename source_link_container_t, typename start_parameters_t,
1102  typename parameters_t = BoundTrackParameters>
1105  findTracks(const source_link_container_t& sourcelinks,
1106  const start_parameters_t& sParameters,
1108  tfOptions) const {
1109  const auto& logger = tfOptions.logger;
1111  static_assert(SourceLinkConcept<SourceLink>,
1112  "Source link does not fulfill SourceLinkConcept");
1113 
1114  // To be able to find measurements later, we put them into a map
1115  // We need to copy input SourceLinks anyways, so the map can own them.
1116  ACTS_VERBOSE("Preparing " << sourcelinks.size() << " input measurements");
1117  std::unordered_map<const Surface*, std::vector<SourceLink>>
1118  inputMeasurements;
1119  for (const auto& sl : sourcelinks) {
1120  const Surface* srf = &sl.referenceSurface();
1121  ACTS_VERBOSE("Adding measurement on surface: " << srf->geometryId());
1122  inputMeasurements[srf].emplace_back(sl);
1123  }
1124 
1125  // Create the ActionList and AbortList
1126  using CombinatorialKalmanFilterAborter = Aborter<SourceLink, parameters_t>;
1127  using CombinatorialKalmanFilterActor = Actor<SourceLink, parameters_t>;
1129  typename CombinatorialKalmanFilterActor::result_type;
1132 
1133  // Create relevant options for the propagation options
1135  tfOptions.geoContext, tfOptions.magFieldContext, tfOptions.logger);
1136 
1137  // Set the trivial propagator options
1138  propOptions.setPlainOptions(tfOptions.propagatorPlainOptions);
1139 
1140  // Catch the actor and set the measurements
1141  auto& combKalmanActor =
1142  propOptions.actionList.template get<CombinatorialKalmanFilterActor>();
1143  combKalmanActor.inputMeasurements = std::move(inputMeasurements);
1144  combKalmanActor.targetSurface = tfOptions.referenceSurface;
1145  combKalmanActor.multipleScattering = tfOptions.multipleScattering;
1146  combKalmanActor.energyLoss = tfOptions.energyLoss;
1147  combKalmanActor.smoothing = tfOptions.smoothing;
1148 
1149  // Set config for source link selector
1150  combKalmanActor.m_sourcelinkSelector.m_config =
1151  tfOptions.sourcelinkSelectorConfig;
1152 
1153  // Run the CombinatorialKalmanFilter
1154  auto result = m_propagator.template propagate(sParameters, propOptions);
1155 
1156  if (!result.ok()) {
1157  ACTS_ERROR("Propapation failed: " << result.error());
1158  return result.error();
1159  }
1160 
1161  const auto& propRes = *result;
1162 
1164  auto combKalmanResult =
1165  propRes.template get<CombinatorialKalmanFilterResult>();
1166 
1169  // -> forward filtering for track finding
1170  // -> surface targeting to get fitted parameters at target surface
1171  // @TODO: Implement distinguishment between the above two cases if necessary
1172  if (combKalmanResult.result.ok() and not combKalmanResult.finished) {
1173  combKalmanResult.result = Result<void>(
1174  CombinatorialKalmanFilterError::PropagationReachesMaxSteps);
1175  }
1176 
1177  if (!combKalmanResult.result.ok()) {
1178  ACTS_ERROR("CombinatorialKalmanFilter failed: "
1179  << combKalmanResult.result.error());
1180  return combKalmanResult.result.error();
1181  }
1182 
1183  // Return the converted Track
1184  return combKalmanResult;
1185  }
1186 
1187 }; // namespace Acts
1188 
1189 } // namespace Acts