ECCE @ 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-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 #include <unordered_map>
39 
40 namespace Acts {
41 
48  // Number of passed sensitive surfaces
49  size_t nSensitiveSurfaces = 0;
50  // Number of track states
51  size_t nStates = 0;
52  // Number of (non-outlier) measurements
53  size_t nMeasurements = 0;
54  // Number of outliers
55  size_t nOutliers = 0;
56  // Number of holes
57  size_t nHoles = 0;
58 };
59 
70 template <typename source_link_selector_t>
72  // Broadcast the source link selector type
73  using SourceLinkSelector = source_link_selector_t;
74 
75  // Broadcast the source link selector config type
77 
80 
94  std::reference_wrapper<const GeometryContext> gctx,
95  std::reference_wrapper<const MagneticFieldContext> mctx,
96  std::reference_wrapper<const CalibrationContext> cctx,
97  const SourceLinkSelectorConfig& slsCfg, const Surface* rSurface = nullptr,
98  bool mScattering = true, bool eLoss = true, bool rSmoothing = true)
99  : geoContext(gctx),
100  magFieldContext(mctx),
101  calibrationContext(cctx),
102  sourcelinkSelectorConfig(slsCfg),
103  referenceSurface(rSurface),
104  multipleScattering(mScattering),
105  energyLoss(eLoss),
106  smoothing(rSmoothing) {}
107 
109  std::reference_wrapper<const GeometryContext> geoContext;
111  std::reference_wrapper<const MagneticFieldContext> magFieldContext;
113  std::reference_wrapper<const CalibrationContext> calibrationContext;
114 
117 
119  const Surface* referenceSurface = nullptr;
120 
122  bool multipleScattering = true;
123 
125  bool energyLoss = true;
126 
128  bool smoothing = true;
129 };
130 
131 template <typename source_link_t>
133  // Fitted states that the actor has handled.
135 
136  // The indices of the 'tip' of the tracks stored in multitrajectory.
137  std::vector<size_t> trackTips;
138 
139  // The Parameters at the provided surface for separate tracks
140  std::unordered_map<size_t, BoundParameters> fittedParameters;
141 
142  // The indices of the 'tip' of the unfinished tracks
143  std::vector<std::pair<size_t, CombinatorialKalmanFilterTipState>> activeTips;
144 
145  // The indices of source links in multitrajectory
146  std::unordered_map<const Surface*, std::unordered_map<size_t, size_t>>
148 
149  // Indicator if forward filtering has been done
150  bool forwardFiltered = false;
151 
152  // Indicator if smoothing has been done.
153  bool smoothed = false;
154 
155  // The index for the current smoothing track
156  size_t iSmoothed = 0;
157 
158  // Indicator if initialization has been performed.
159  bool initialized = false;
160 
161  // Temporary container for index and chi2 of intermediate source link
162  // candidates
163  std::vector<std::pair<size_t, double>> sourcelinkChi2;
164 
165  // Temporary container for index of final source link candidates
166  std::vector<size_t> sourcelinkCandidateIndices;
167 
169 };
170 
211 template <typename propagator_t, typename updater_t = VoidKalmanUpdater,
212  typename smoother_t = VoidKalmanSmoother,
213  typename source_link_selector_t = CKFSourceLinkSelector,
214  typename branch_stopper_t = VoidBranchStopper,
215  typename calibrator_t = VoidMeasurementCalibrator,
216  typename input_converter_t = VoidKalmanComponents,
217  typename output_converter_t = VoidKalmanComponents>
219  public:
221  using MeasurementSurfaces = std::multimap<const Layer*, const Surface*>;
222 
224  CombinatorialKalmanFilter() = delete;
225 
228  propagator_t pPropagator,
229  std::unique_ptr<const Logger> logger =
230  getDefaultLogger("CombinatorialKalmanFilter", Logging::INFO),
231  input_converter_t pInputCnv = input_converter_t(),
232  output_converter_t pOutputCnv = output_converter_t())
233  : m_propagator(std::move(pPropagator)),
234  m_inputConverter(std::move(pInputCnv)),
235  m_outputConverter(std::move(pOutputCnv)),
236  m_logger(logger.release()) {}
237 
238  private:
241 
243  input_converter_t m_inputConverter;
244 
246  output_converter_t m_outputConverter;
247 
249  const Logger& logger() const { return *m_logger; }
250 
252  std::shared_ptr<const Logger> m_logger;
253 
261  template <typename source_link_t, typename parameters_t>
262  class Actor {
263  public:
266  using BoundState = std::tuple<BoundParameters, BoundMatrix, double>;
267  using CurvilinearState =
268  std::tuple<CurvilinearParameters, BoundMatrix, double>;
271 
273  Actor(updater_t pUpdater = updater_t(), smoother_t pSmoother = smoother_t(),
274  source_link_selector_t pSourceLinkSelector = source_link_selector_t(),
275  branch_stopper_t pBranchStopper = branch_stopper_t(),
276  calibrator_t pCalibrator = calibrator_t())
277  : m_updater(std::move(pUpdater)),
278  m_smoother(std::move(pSmoother)),
279  m_calibrator(std::move(pCalibrator)) {}
280 
282  const Surface* targetSurface = nullptr;
283 
285  std::unordered_map<const Surface*, std::vector<source_link_t>>
287 
289  bool multipleScattering = true;
290 
292  bool energyLoss = true;
293 
295  bool smoothing = true;
296 
305  template <typename propagator_state_t, typename stepper_t>
306  void operator()(propagator_state_t& state, const stepper_t& stepper,
307  result_type& result) const {
308  ACTS_VERBOSE("CombinatorialKalmanFilter step");
309 
310  // Initialization:
311  // - Only when track states are not set
312  if (!result.initialized) {
313  ACTS_VERBOSE("Initializing");
314  result.initialized = true;
315  }
316 
317  // Update:
318  // - Waiting for a current surface
319  auto surface = state.navigation.currentSurface;
320  if (surface != nullptr and not result.forwardFiltered) {
321  // There are three scenarios:
322  // 1) The surface is in the measurement map
323  // -> Select source links
324  // -> Perform the kalman update for selected non-outlier source links
325  // -> Add track states in multitrajectory. Multiple states mean branch
326  // splitting.
327  // -> Call branch stopper to justify each branch
328  // -> If there is non-outlier state, update stepper information
329  // 2) The surface is not in the measurement map but with material
330  // -> Add a hole or passive material state in multitrajectory
331  // -> Call branch stopper to justify the branch
332  // 3) The surface is neither in the measurement map nor with material
333  // -> Do nothing
334  ACTS_VERBOSE("Perform filter step");
335  auto res = filter(surface, state, stepper, result);
336  if (!res.ok()) {
337  ACTS_ERROR("Error in filter: " << res.error());
338  result.result = res.error();
339  }
340  }
341 
342  // Reset propagation state:
343  // - When navigation breaks and there is stil active tip present after
344  // recording&removing track tips on current surface
345  if (state.navigation.navigationBreak and not result.forwardFiltered) {
346  // Record the tips on current surface as trajectory entry indices
347  // (taking advantage of fact that those tips are consecutive in list of
348  // active tips) and remove those tips from active tips
349  if (not result.activeTips.empty()) {
350  // The last active tip
351  const auto& lastActiveTip = result.activeTips.back().first;
352  // Get the index of previous state
353  const auto& iprevious =
354  result.fittedStates.getTrackState(lastActiveTip).previous();
355  // Find the track states which have the same previous state and remove
356  // them from active tips
357  while (not result.activeTips.empty()) {
358  const auto& [currentTip, tipState] = result.activeTips.back();
359  if (result.fittedStates.getTrackState(currentTip).previous() !=
360  iprevious) {
361  break;
362  }
363  // Record the tips if there are measurements on the track
364  if (tipState.nMeasurements > 0) {
365  ACTS_VERBOSE("Find track with entry index = "
366  << currentTip << " and there are nMeasurements = "
367  << tipState.nMeasurements
368  << ", nOutliers = " << tipState.nOutliers
369  << ", nHoles = " << tipState.nHoles << " on track");
370  result.trackTips.emplace_back(currentTip);
371  }
372  // Remove the tip from list of active tips
373  result.activeTips.erase(result.activeTips.end() - 1);
374  }
375  }
376  // If no more active tip, done with forward filtering; Otherwise, reset
377  // propagation state to track state at last tip of active tips
378  if (result.activeTips.empty()) {
379  ACTS_VERBOSE("Forward Kalman filtering finds "
380  << result.trackTips.size() << " tracks");
381  result.forwardFiltered = true;
382  } else {
383  ACTS_VERBOSE("Propagation jumps to branch with tip = "
384  << result.activeTips.back().first);
385  reset(state, stepper, result);
386  }
387  }
388 
389  // Post-processing after forward filtering
390  if (result.forwardFiltered) {
391  // Return error if forward filtering finds no tracks
392  if (result.trackTips.empty()) {
393  result.result =
394  Result<void>(CombinatorialKalmanFilterError::NoTracksFound);
395  } else {
396  if (not smoothing) {
397  // Manually set the targetReached to abort the propagation
398  ACTS_VERBOSE("Finish forward Kalman filtering");
399  state.navigation.targetReached = true;
400  } else {
401  // Iterate over the found tracks for smoothing and getting the
402  // fitted parameter. This needs to be accomplished in different
403  // propagation steps:
404  // -> first run smoothing for found track indexed with iSmoothed
405  if (not result.smoothed) {
406  ACTS_VERBOSE(
407  "Finalize/run smoothing for track with entry index = "
408  << result.trackTips.at(result.iSmoothed));
409  // --> Search the starting state to run the smoothing
410  // --> Call the smoothing
411  // --> Set a stop condition when all track states have been
412  // handled
413  auto res = finalize(state, stepper, result);
414  if (!res.ok()) {
415  ACTS_ERROR("Error in finalize: " << res.error());
416  result.result = res.error();
417  }
418  result.smoothed = true;
419  }
420  // -> then progress to target/reference surface and built the final
421  // track parameters for found track indexed with iSmoothed
422  if (result.smoothed and
423  targetReached(state, stepper, *targetSurface)) {
424  ACTS_VERBOSE("Completing the track with entry index = "
425  << result.trackTips.at(result.iSmoothed));
426  // Transport & bind the parameter to the final surface
427  auto fittedState =
428  stepper.boundState(state.stepping, *targetSurface);
429  // Assign the fitted parameters
430  result.fittedParameters.emplace(
431  result.trackTips.at(result.iSmoothed),
432  std::get<BoundParameters>(fittedState));
433  // If there are more trajectories to handle:
434  // -> set the targetReached status to false
435  // -> set the smoothed status to false
436  // -> update the index of track to be smoothed
437  if (result.iSmoothed < result.trackTips.size() - 1) {
438  state.navigation.targetReached = false;
439  result.smoothed = false;
440  result.iSmoothed++;
441  // To avoid meaningless navigation target call
442  state.stepping.stepSize =
443  ConstrainedStep(state.options.maxStepSize);
444  // Need to go back to start targeting for the rest tracks
445  state.stepping.navDir = forward;
446  } else {
447  ACTS_VERBOSE(
448  "Finish forward Kalman filtering and backward smoothing");
449  }
450  }
451  } // if run smoothing
452  } // if there are found tracks
453  } // if forward filtering is done
454  }
455 
464  template <typename propagator_state_t, typename stepper_t>
465  void reset(propagator_state_t& state, stepper_t& stepper,
466  result_type& result) const {
467  auto currentState =
468  result.fittedStates.getTrackState(result.activeTips.back().first);
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.update(state.stepping,
484  currentState.filteredParameters(state.options.geoContext));
485  // Reinitialize the stepping jacobian
486  currentState.referenceSurface().initJacobianToGlobal(
487  state.options.geoContext, state.stepping.jacToGlobal,
488  state.stepping.pos, state.stepping.dir,
489  currentState.filteredParameters(state.options.geoContext)
490  .parameters());
491  state.stepping.jacobian = BoundMatrix::Identity();
492  state.stepping.jacTransport = FreeMatrix::Identity();
493  state.stepping.derivative = FreeVector::Zero();
494  // Reset step size and accumulated path
495  state.stepping.stepSize = ConstrainedStep(state.options.maxStepSize);
496  state.stepping.pathAccumulated = currentState.pathLength();
497 
498  // No Kalman filtering for the starting surface, but still need
499  // to consider the material effects here
500  materialInteractor(state.navigation.startSurface, state, stepper);
501  }
502 
515  template <typename propagator_state_t, typename stepper_t>
516  Result<void> filter(const Surface* surface, propagator_state_t& state,
517  const stepper_t& stepper, result_type& result) const {
518  // Initialize the number of branches on current surface
519  size_t nBranchesOnSurface = 0;
520 
521  // Try to find the surface in the measurement surfaces
522  auto sourcelink_it = inputMeasurements.find(surface);
523  if (sourcelink_it != inputMeasurements.end()) {
524  // Screen output message
525  ACTS_VERBOSE("Measurement surface " << surface->geoID()
526  << " detected.");
527 
528  // Update state and stepper with pre material effects
529  materialInteractor(surface, state, stepper, preUpdate);
530 
531  // Transport & bind the state to the current surface
532  auto boundState = stepper.boundState(state.stepping, *surface);
533  auto boundParams = std::get<BoundParameters>(boundState);
534 
535  // Get all source links on surface
536  auto& sourcelinks = sourcelink_it->second;
537 
538  // Invoke the source link selector to select source links for either
539  // measurements or outlier.
540  // Calibrator is passed to the selector because
541  // selection has to be done based on calibrated measurement
542  bool isOutlier = false;
543  auto sourcelinkSelectionRes = m_sourcelinkSelector(
544  m_calibrator, boundParams, sourcelinks, result.sourcelinkChi2,
545  result.sourcelinkCandidateIndices, isOutlier);
546  if (!sourcelinkSelectionRes.ok()) {
547  ACTS_ERROR("Selection of source links failed: "
548  << sourcelinkSelectionRes.error());
549  return sourcelinkSelectionRes.error();
550  }
551 
552  // Retrieve the previous tip and its state
553  // The states created on this surface will have the common previous tip
554  size_t prevTip = SIZE_MAX;
555  TipState prevTipState;
556  if (not result.activeTips.empty()) {
557  prevTip = result.activeTips.back().first;
558  prevTipState = result.activeTips.back().second;
559  // New state is to be added. Remove the last tip from active tips
560  result.activeTips.erase(result.activeTips.end() - 1);
561  }
562 
563  // Remember the tip of the neighbor state on this surface
564  size_t neighborTip = SIZE_MAX;
565  // Loop over the selected source links
566  for (const auto& index : result.sourcelinkCandidateIndices) {
567  // Determine if predicted parameter is already contained in
568  // neighboring state
569  bool isPredictedShared = (neighborTip != SIZE_MAX);
570 
571  // Determine if uncalibrated measurement are already
572  // contained in other track state
573  bool isSourcelinkShared = false;
574  size_t sharedTip = SIZE_MAX;
575  auto sourcelinkTips_it = result.sourcelinkTips.find(surface);
576  if (sourcelinkTips_it != result.sourcelinkTips.end()) {
577  auto& sourcelinkTipsOnSurface = sourcelinkTips_it->second;
578  auto index_it = sourcelinkTipsOnSurface.find(index);
579  if (index_it != sourcelinkTipsOnSurface.end()) {
580  isSourcelinkShared = true;
581  sharedTip = index_it->second;
582  }
583  }
584 
585  // The mask for adding a state in the multitrajectory
586  // No storage allocation for:
587  // -> predicted parameter and uncalibrated measurement if
588  // already stored
589  // -> filtered parameter for outlier
590  auto stateMask =
591  (isPredictedShared ? ~TrackStatePropMask::Predicted
593  (isSourcelinkShared ? ~TrackStatePropMask::Uncalibrated
595  (isOutlier ? ~TrackStatePropMask::Filtered
597 
598  // Add measurement/outlier track state to the multitrajectory
599  auto addStateRes = addSourcelinkState(
600  stateMask, boundState, sourcelinks.at(index), isOutlier, result,
601  state.geoContext, prevTip, prevTipState, neighborTip, sharedTip);
602  if (addStateRes.ok()) {
603  const auto& [currentTip, tipState] = addStateRes.value();
604  // Remember the track state tip for this stored source link
605  if (not isSourcelinkShared) {
606  auto& sourcelinkTipsOnSurface = result.sourcelinkTips[surface];
607  sourcelinkTipsOnSurface.emplace(index, currentTip);
608  }
609  // Remember the tip of neighbor state on this surface
610  neighborTip = currentTip;
611 
612  // Check if need to stop this branch
613  if (not m_branchStopper(tipState)) {
614  // Remember the active tip and its state
615  result.activeTips.emplace_back(std::move(currentTip),
616  std::move(tipState));
617  // Record the number of branches on surface
618  nBranchesOnSurface++;
619  }
620  }
621  } // end of loop for all selected source links on this surface
622 
623  if (nBranchesOnSurface > 0 and not isOutlier) {
624  // If there are measurement track states on this surface
625  ACTS_VERBOSE("Filtering step successful with " << nBranchesOnSurface
626  << " branches");
627  // Update stepping state using filtered parameters of last track
628  // state on this surface
629  auto filteredParams =
630  result.fittedStates.getTrackState(result.activeTips.back().first)
631  .filteredParameters(state.options.geoContext);
632  stepper.update(state.stepping, filteredParams);
633  ACTS_VERBOSE("Stepping state is updated with filtered parameter: \n"
634  << filteredParams.parameters().transpose()
635  << " of track state with tip = "
636  << result.activeTips.back().first);
637  }
638  // Update state and stepper with post material effects
639  materialInteractor(surface, state, stepper, postUpdate);
640  } else if (surface->surfaceMaterial() != nullptr) {
641  // No splitting on the surface without source links. Set it to one
642  // first, but could be changed later
643  nBranchesOnSurface = 1;
644 
645  // Retrieve the previous tip and its state
646  size_t prevTip = SIZE_MAX;
647  TipState tipState;
648  if (not result.activeTips.empty()) {
649  prevTip = result.activeTips.back().first;
650  tipState = result.activeTips.back().second;
651  }
652 
653  // The surface could be either sensitive or passive
654  bool isSensitive = (surface->associatedDetectorElement() != nullptr);
655  std::string type = isSensitive ? "sensitive" : "passive";
656  ACTS_VERBOSE("Detected " << type << " surface: " << surface->geoID());
657  if (isSensitive) {
658  // Increment of number of passed sensitive surfaces
659  tipState.nSensitiveSurfaces++;
660  }
661  // Add state if there is already measurement detected on this branch
662  // For in-sensitive surface, only add state when smoothing is
663  // required
664  if (tipState.nMeasurements > 0 and
665  (isSensitive or (not isSensitive and smoothing))) {
666  // New state is to be added. Remove the last tip from active tips now
667  result.activeTips.erase(result.activeTips.end() - 1);
668 
669  // No source links on surface, add either hole or passive material
670  // TrackState. No storage allocation for uncalibrated/calibrated
671  // measurement and filtered parameter
672  auto stateMask =
675 
676  // Increment of number of processed states
677  tipState.nStates++;
678  size_t currentTip = SIZE_MAX;
679  if (isSensitive) {
680  // Transport & bind the state to the current surface
681  auto boundState = stepper.boundState(state.stepping, *surface);
682  // Add a hole track state to the multitrajectory
683  currentTip = addHoleState(stateMask, boundState, result, prevTip);
684  // Incremet of number of holes
685  tipState.nHoles++;
686  } else {
687  // Transport & get curvilinear state instead of bound state
688  auto curvilinearState = stepper.curvilinearState(state.stepping);
689  // Add a passive material track state to the multitrajectory
690  currentTip =
691  addPassiveState(stateMask, curvilinearState, result, prevTip);
692  }
693 
694  // Check the branch
695  if (not m_branchStopper(tipState)) {
696  // Remember the active tip and its state
697  result.activeTips.emplace_back(std::move(currentTip),
698  std::move(tipState));
699  } else {
700  // No branch on this surface
701  nBranchesOnSurface = 0;
702  }
703  }
704  // Update state and stepper with material effects
705  materialInteractor(surface, state, stepper, fullUpdate);
706  } else {
707  // Neither measurement nor material on surface, this branch is still
708  // valid. Count the branch on current surface
709  nBranchesOnSurface = 1;
710  }
711 
712  // Reset current tip if there is no branch on current surface
713  if (nBranchesOnSurface == 0) {
714  ACTS_DEBUG("Branch on surface " << surface->geoID() << " is stopped");
715  if (not result.activeTips.empty()) {
716  ACTS_VERBOSE("Propagation jumps to branch with tip = "
717  << result.activeTips.back().first);
718  reset(state, stepper, result);
719  } else {
720  ACTS_VERBOSE("Stop forward Kalman filtering with "
721  << result.trackTips.size() << " found tracks");
722  result.forwardFiltered = true;
723  }
724  }
725 
726  return Result<void>::success();
727  }
728 
745  const TrackStatePropMask::Type& stateMask, const BoundState& boundState,
746  const source_link_t& sourcelink, bool isOutlier, result_type& result,
747  std::reference_wrapper<const GeometryContext> geoContext,
748  const size_t& prevTip, const TipState& prevTipState,
749  size_t neighborTip = SIZE_MAX, size_t sharedTip = SIZE_MAX) const {
750  // Inherit the tip state from the previous and will be updated later
751  TipState tipState = prevTipState;
752 
753  // Add a track state
754  auto currentTip = result.fittedStates.addTrackState(stateMask, prevTip);
755 
756  // Get the track state proxy
757  auto trackStateProxy = result.fittedStates.getTrackState(currentTip);
758 
759  auto [boundParams, jacobian, pathLength] = boundState;
760 
761  // Fill the parametric part of the track state proxy
762  if ((not ACTS_CHECK_BIT(stateMask, TrackStatePropMask::Predicted)) and
763  neighborTip != SIZE_MAX) {
764  // The predicted parameter is already stored, just set the index
765  auto neighborState = result.fittedStates.getTrackState(neighborTip);
766  trackStateProxy.data().ipredicted = neighborState.data().ipredicted;
767  } else {
768  trackStateProxy.predicted() = boundParams.parameters();
769  trackStateProxy.predictedCovariance() = *boundParams.covariance();
770  }
771  trackStateProxy.jacobian() = jacobian;
772  trackStateProxy.pathLength() = pathLength;
773 
774  // Assign the uncalibrated&calibrated measurement to the track
775  // state (the uncalibrated could be already stored in other states)
776  if ((not ACTS_CHECK_BIT(stateMask, TrackStatePropMask::Uncalibrated)) and
777  sharedTip != SIZE_MAX) {
778  // The uncalibrated are already stored, just set the
779  // index
780  auto shared = result.fittedStates.getTrackState(sharedTip);
781  trackStateProxy.data().iuncalibrated = shared.data().iuncalibrated;
782  } else {
783  trackStateProxy.uncalibrated() = sourcelink;
784  }
785  std::visit(
786  [&](const auto& calibrated) {
787  trackStateProxy.setCalibrated(calibrated);
788  },
789  m_calibrator(trackStateProxy.uncalibrated(),
790  trackStateProxy.predicted()));
791 
792  // Get and set the type flags
793  auto& typeFlags = trackStateProxy.typeFlags();
794  typeFlags.set(TrackStateFlag::MaterialFlag);
795  typeFlags.set(TrackStateFlag::ParameterFlag);
796 
797  // Increment of number of processedState and passed sensitive surfaces
798  tipState.nSensitiveSurfaces++;
799  tipState.nStates++;
800 
801  if (isOutlier) {
802  ACTS_VERBOSE("Creating outlier track state with tip = " << currentTip);
803  // Set the outlier flag
804  typeFlags.set(TrackStateFlag::OutlierFlag);
805  // Increment number of outliers
806  tipState.nOutliers++;
807  // No Kalman update for outlier
808  // Set the filtered parameter index to be the same with predicted
809  // parameter
810  trackStateProxy.data().ifiltered = trackStateProxy.data().ipredicted;
811  } else {
812  // Kalman update
813  auto updateRes = m_updater(geoContext, trackStateProxy);
814  if (!updateRes.ok()) {
815  ACTS_ERROR("Update step failed: " << updateRes.error());
816  return updateRes.error();
817  }
818  ACTS_VERBOSE(
819  "Creating measurement track state with tip = " << currentTip);
820  // Set the measurement flag
821  typeFlags.set(TrackStateFlag::MeasurementFlag);
822  // Increment number of measurements
823  tipState.nMeasurements++;
824  }
825  return std::make_pair(std::move(currentTip), std::move(tipState));
826  }
827 
837  size_t addHoleState(const TrackStatePropMask::Type& stateMask,
838  const BoundState& boundState, result_type& result,
839  size_t prevTip = SIZE_MAX) const {
840  // Add a track state
841  auto currentTip = result.fittedStates.addTrackState(stateMask, prevTip);
842  ACTS_VERBOSE("Creating Hole track state with tip = " << currentTip);
843 
844  // now get track state proxy back
845  auto trackStateProxy = result.fittedStates.getTrackState(currentTip);
846 
847  // Set the track state flags
848  auto& typeFlags = trackStateProxy.typeFlags();
849  typeFlags.set(TrackStateFlag::MaterialFlag);
850  typeFlags.set(TrackStateFlag::ParameterFlag);
851  typeFlags.set(TrackStateFlag::HoleFlag);
852 
853  auto [boundParams, jacobian, pathLength] = boundState;
854  // Fill the track state
855  trackStateProxy.predicted() = boundParams.parameters();
856  trackStateProxy.predictedCovariance() = *boundParams.covariance();
857  trackStateProxy.jacobian() = jacobian;
858  trackStateProxy.pathLength() = pathLength;
859  // Set the surface
860  trackStateProxy.setReferenceSurface(
861  boundParams.referenceSurface().getSharedPtr());
862  // Set the filtered parameter index to be the same with predicted
863  // parameter
864  trackStateProxy.data().ifiltered = trackStateProxy.data().ipredicted;
865 
866  return currentTip;
867  }
868 
880  size_t addPassiveState(const TrackStatePropMask::Type& stateMask,
882  result_type& result,
883  size_t prevTip = SIZE_MAX) const {
884  // Add a track state
885  auto currentTip = result.fittedStates.addTrackState(stateMask, prevTip);
886  ACTS_VERBOSE(
887  "Creating track state on in-sensitive material surface with tip = "
888  << currentTip);
889 
890  // now get track state proxy back
891  auto trackStateProxy = result.fittedStates.getTrackState(currentTip);
892 
893  // Set the track state flags
894  auto& typeFlags = trackStateProxy.typeFlags();
895  typeFlags.set(TrackStateFlag::MaterialFlag);
896  typeFlags.set(TrackStateFlag::ParameterFlag);
897 
898  auto [curvilinearParams, jacobian, pathLength] = curvilinearState;
899  // Fill the track state
900  trackStateProxy.predicted() = curvilinearParams.parameters();
901  trackStateProxy.predictedCovariance() = *curvilinearParams.covariance();
902  trackStateProxy.jacobian() = jacobian;
903  trackStateProxy.pathLength() = pathLength;
904  // Set the surface
905  trackStateProxy.setReferenceSurface(Surface::makeShared<PlaneSurface>(
906  curvilinearParams.position(), curvilinearParams.momentum()));
907  // Set the filtered parameter index to be the same with predicted
908  // parameter
909  trackStateProxy.data().ifiltered = trackStateProxy.data().ipredicted;
910 
911  return currentTip;
912  }
913 
924  template <typename propagator_state_t, typename stepper_t>
926  const Surface* surface, propagator_state_t& state, stepper_t& stepper,
927  const MaterialUpdateStage& updateStage = fullUpdate) const {
928  // Indicator if having material
929  bool hasMaterial = false;
930 
931  if (surface and surface->surfaceMaterial()) {
932  // Prepare relevant input particle properties
933  detail::PointwiseMaterialInteraction interaction(surface, state,
934  stepper);
935  // Evaluate the material properties
936  if (interaction.evaluateMaterialProperties(state, updateStage)) {
937  // Surface has material at this stage
938  hasMaterial = true;
939 
940  // Evaluate the material effects
942  energyLoss);
943 
944  // Screen out material effects info
945  ACTS_VERBOSE("Material effects on surface: "
946  << surface->geoID()
947  << " at update stage: " << updateStage << " are :");
948  ACTS_VERBOSE("eLoss = "
949  << interaction.Eloss << ", "
950  << "variancePhi = " << interaction.variancePhi << ", "
951  << "varianceTheta = " << interaction.varianceTheta
952  << ", "
953  << "varianceQoverP = " << interaction.varianceQoverP);
954 
955  // Update the state and stepper with material effects
956  interaction.updateState(state, stepper);
957  }
958  }
959 
960  if (not hasMaterial) {
961  // Screen out message
962  ACTS_VERBOSE("No material effects on surface: " << surface->geoID()
963  << " at update stage: "
964  << updateStage);
965  }
966  }
967 
976  template <typename propagator_state_t, typename stepper_t>
977  Result<void> finalize(propagator_state_t& state, const stepper_t& stepper,
978  result_type& result) const {
979  // The tip of the track being smoothed
980  const auto& currentTip = result.trackTips.at(result.iSmoothed);
981 
982  // Get the indices of measurement states;
983  std::vector<size_t> measurementIndices;
984  // Count track states to be smoothed
985  size_t nStates = 0;
986  result.fittedStates.applyBackwards(currentTip, [&](auto st) {
987  bool isMeasurement =
988  st.typeFlags().test(TrackStateFlag::MeasurementFlag);
989  if (isMeasurement) {
990  measurementIndices.emplace_back(st.index());
991  } else if (measurementIndices.empty()) {
992  // No smoothed parameter if the last measurment state has not been
993  // found yet
994  st.data().ismoothed = detail_lt::IndexData::kInvalid;
995  }
996  // Start count when the last measurement state is found
997  if (not measurementIndices.empty()) {
998  nStates++;
999  }
1000  });
1001  // Return error if the track has no measurement states (but this should
1002  // not happen)
1003  if (measurementIndices.empty()) {
1004  ACTS_ERROR("Smoothing for a track without measurements.");
1005  return CombinatorialKalmanFilterError::SmoothFailed;
1006  }
1007  // Screen output for debugging
1008  ACTS_VERBOSE("Apply smoothing on " << nStates
1009  << " filtered track states.");
1010  // Smooth the track states
1011  auto smoothRes = m_smoother(state.geoContext, result.fittedStates,
1012  measurementIndices.front());
1013  if (!smoothRes.ok()) {
1014  ACTS_ERROR("Smoothing step failed: " << smoothRes.error());
1015  return smoothRes.error();
1016  }
1017  // Obtain the smoothed parameters at first measurement state
1018  auto firstMeasurement =
1019  result.fittedStates.getTrackState(measurementIndices.back());
1020  parameters_t smoothedPars =
1021  firstMeasurement.smoothedParameters(state.options.geoContext);
1022 
1023  // Update the stepping parameters - in order to progress to destination
1024  ACTS_VERBOSE(
1025  "Smoothing successful, updating stepping state, "
1026  "set target surface.");
1027  stepper.update(state.stepping, smoothedPars);
1028  // Reverse the propagation direction
1029  state.stepping.stepSize =
1030  ConstrainedStep(-1. * state.options.maxStepSize);
1031  state.stepping.navDir = backward;
1032  // Set accumulatd path to zero before targeting surface
1033  state.stepping.pathAccumulated = 0.;
1034  // Not sure if the following line helps anything
1035  state.options.direction = backward;
1036 
1037  return Result<void>::success();
1038  }
1039 
1043 
1045  const Logger& logger() const { return *m_logger; }
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()) {
1077  return true;
1078  }
1079  return false;
1080  }
1081  };
1082 
1083  public:
1104  template <typename source_link_t, typename start_parameters_t,
1105  typename comb_kalman_filter_options_t,
1106  typename parameters_t = BoundParameters>
1108  const std::vector<source_link_t>& sourcelinks,
1109  const start_parameters_t& sParameters,
1110  const comb_kalman_filter_options_t& tfOptions) const {
1111  static_assert(SourceLinkConcept<source_link_t>,
1112  "Source link does not fulfill SourceLinkConcept");
1113 
1114  static_assert(
1115  std::is_same<
1116  source_link_selector_t,
1117  typename comb_kalman_filter_options_t::SourceLinkSelector>::value,
1118  "Inconsistent type of source link selector between "
1119  "CombinatorialKalmanFilter and "
1120  " CombinatorialKalmanFilter options");
1121 
1122  // To be able to find measurements later, we put them into a map
1123  // We need to copy input SourceLinks anyways, so the map can own them.
1124  ACTS_VERBOSE("Preparing " << sourcelinks.size() << " input measurements");
1125  std::unordered_map<const Surface*, std::vector<source_link_t>>
1126  inputMeasurements;
1127  for (const auto& sl : sourcelinks) {
1128  const Surface* srf = &sl.referenceSurface();
1129  inputMeasurements[srf].emplace_back(sl);
1130  }
1131 
1132  // Create the ActionList and AbortList
1133  using CombinatorialKalmanFilterAborter =
1135  using CombinatorialKalmanFilterActor = Actor<source_link_t, parameters_t>;
1137  typename CombinatorialKalmanFilterActor::result_type;
1140 
1141  // Create relevant options for the propagation options
1142  PropagatorOptions<Actors, Aborters> propOptions(tfOptions.geoContext,
1143  tfOptions.magFieldContext);
1144  // Set max steps
1145  propOptions.maxSteps = 10000;
1146 
1147  // Catch the actor and set the measurements
1148  auto& combKalmanActor =
1149  propOptions.actionList.template get<CombinatorialKalmanFilterActor>();
1150  combKalmanActor.m_logger = m_logger.get();
1151  combKalmanActor.inputMeasurements = std::move(inputMeasurements);
1152  combKalmanActor.targetSurface = tfOptions.referenceSurface;
1153  combKalmanActor.multipleScattering = tfOptions.multipleScattering;
1154  combKalmanActor.energyLoss = tfOptions.energyLoss;
1155  combKalmanActor.smoothing = tfOptions.smoothing;
1156 
1157  // Set config and logger for source link selector
1158  combKalmanActor.m_sourcelinkSelector.m_config =
1159  tfOptions.sourcelinkSelectorConfig;
1160  combKalmanActor.m_sourcelinkSelector.m_logger = m_logger;
1161 
1162  // also set logger on updater and smoother
1163  combKalmanActor.m_updater.m_logger = m_logger;
1164  combKalmanActor.m_smoother.m_logger = m_logger;
1165 
1166  // Run the CombinatorialKalmanFilter
1167  auto result = m_propagator.template propagate(sParameters, propOptions);
1168 
1169  if (!result.ok()) {
1170  ACTS_ERROR("Propapation failed: " << result.error());
1171  return result.error();
1172  }
1173 
1174  const auto& propRes = *result;
1175 
1177  auto combKalmanResult =
1178  propRes.template get<CombinatorialKalmanFilterResult>();
1179 
1182  if (combKalmanResult.result.ok() and not combKalmanResult.forwardFiltered) {
1183  combKalmanResult.result = Result<void>(
1184  CombinatorialKalmanFilterError::PropagationReachesMaxSteps);
1185  }
1186 
1187  if (!combKalmanResult.result.ok()) {
1188  ACTS_ERROR("CombinatorialKalmanFilter failed: "
1189  << combKalmanResult.result.error());
1190  return combKalmanResult.result.error();
1191  }
1192 
1193  // Return the converted Track
1194  return m_outputConverter(std::move(combKalmanResult));
1195  }
1196 
1197 }; // namespace Acts
1198 
1199 } // namespace Acts