From f4c6413d0c8f13bb98e022e9b0e45554d6ad5773 Mon Sep 17 00:00:00 2001
From: Xiaocong Ai <xiaocong.ai@cern.ch>
Date: Tue, 3 Dec 2019 23:55:15 -0800
Subject: [PATCH] Include material interaction in KalmanActor

---
 Core/include/Acts/Fitter/KalmanFitter.hpp | 145 +++++++++++++++++++---
 1 file changed, 131 insertions(+), 14 deletions(-)

diff --git a/Core/include/Acts/Fitter/KalmanFitter.hpp b/Core/include/Acts/Fitter/KalmanFitter.hpp
index 7ecad6dff..98aa3b8b6 100644
--- a/Core/include/Acts/Fitter/KalmanFitter.hpp
+++ b/Core/include/Acts/Fitter/KalmanFitter.hpp
@@ -18,11 +18,14 @@
 #include "Acts/Fitter/detail/VoidKalmanComponents.hpp"
 #include "Acts/Geometry/GeometryContext.hpp"
 #include "Acts/MagneticField/MagneticFieldContext.hpp"
+#include "Acts/Material/MaterialProperties.hpp"
 #include "Acts/Propagator/AbortList.hpp"
 #include "Acts/Propagator/ActionList.hpp"
 #include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Propagator/DirectNavigator.hpp"
 #include "Acts/Propagator/Propagator.hpp"
+#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/detail/PointwiseMaterialInteraction.hpp"
 #include "Acts/Propagator/detail/StandardAborters.hpp"
 #include "Acts/Utilities/CalibrationContext.hpp"
 #include "Acts/Utilities/Definitions.hpp"
@@ -192,7 +195,7 @@ class KalmanFitter {
   template <typename source_link_t, typename parameters_t>
   class Actor {
    public:
-    using TrackState = TrackState<source_link_t, parameters_t>;
+    using TrackStateType = TrackState<source_link_t, parameters_t>;
 
     /// Explicit constructor with updater and calibrator
     Actor(updater_t pUpdater = updater_t(), smoother_t pSmoother = smoother_t(),
@@ -210,6 +213,12 @@ class KalmanFitter {
     /// Allows retrieving measurements for a surface
     std::map<const Surface*, source_link_t> inputMeasurements;
 
+    /// Whether to consider multiple scattering.
+    bool multipleScattering = true;
+
+    /// Whether to consider energy loss.
+    bool energyLoss = true;
+
     /// @brief Kalman actor operation
     ///
     /// @tparam propagator_state_t is the type of Propagagor state
@@ -306,16 +315,20 @@ class KalmanFitter {
     template <typename propagator_state_t, typename stepper_t>
     Result<void> filter(const Surface* surface, propagator_state_t& state,
                         const stepper_t& stepper, result_type& result) const {
-      // Transport & bind the state to the current surface
-      auto [boundParams, jacobian, pathLength] =
-          stepper.boundState(state.stepping, *surface, true);
-
       // Try to find the surface in the measurement surfaces
       auto sourcelink_it = inputMeasurements.find(surface);
       if (sourcelink_it != inputMeasurements.end()) {
         // Screen output message
         ACTS_VERBOSE("Measurement surface " << surface->geoID()
                                             << " detected.");
+
+        // Update state and stepper with pre material effects
+        materialInteractor(surface, state, stepper, preUpdate);
+
+        // Transport & bind the state to the current surface
+        auto [boundParams, jacobian, pathLength] =
+            stepper.boundState(state.stepping, *surface, true);
+
         // add a full TrackState entry multi trajectory
         // (this allocates storage for all components, we will set them later)
         result.trackTip = result.fittedStates.addTrackState(
@@ -344,9 +357,10 @@ class KalmanFitter {
                          trackStateProxy.predicted()));
 
         // Get and set the type flags
-        auto typeFlags = trackStateProxy.typeFlags();
+        auto& typeFlags = trackStateProxy.typeFlags();
         typeFlags.set(TrackStateFlag::MeasurementFlag);
         typeFlags.set(TrackStateFlag::ParameterFlag);
+        typeFlags.set(TrackStateFlag::MaterialFlag);
 
         // If the update is successful, set covariance and
         auto updateRes = m_updater(state.geoContext, trackStateProxy);
@@ -354,7 +368,7 @@ class KalmanFitter {
           ACTS_ERROR("Update step failed: " << updateRes.error());
           return updateRes.error();
         } else {
-          // Update the stepping state
+          // Update the stepping state with filtered parameters
           ACTS_VERBOSE("Filtering step successful, updated parameters are : \n"
                        << trackStateProxy.filtered().transpose());
           // update stepping state using filtered parameters after kalman update
@@ -362,21 +376,45 @@ class KalmanFitter {
           // a bit awkward.
           stepper.update(state.stepping, trackStateProxy.filteredParameters(
                                              state.options.geoContext));
+
+          // Update state and stepper with post material effects
+          materialInteractor(surface, state, stepper, postUpdate);
         }
         // We count the processed state
         ++result.processedStates;
       } else {
-        // When no measurement on this surface,
-        // create a track state from predicted parameter
-        TrackState trackState(boundParams);
+        // Transport & bind the state to the current surface
+        auto [boundParams, jacobian, pathLength] =
+            stepper.boundState(state.stepping, *surface, true);
+
+        // Create a track state from predicted parameter
+        TrackStateType trackState(boundParams);
 
         // Fill the track state
         trackState.parameter.jacobian = jacobian;
         trackState.parameter.pathLength = pathLength;
 
-        // @todo: set the filtered parameter by updating the predicted parameter
-        // with material effects
+        // Update state and stepper with material effects
+        auto interaction =
+            materialInteractor(surface, state, stepper, fullUpdate);
 
+        // Set the filtered parameter by updating the predicted parameter with
+        // material effects
+        auto updateRes =
+            materialUpdater(state.geoContext, trackState, interaction);
+        if (!updateRes.ok()) {
+          ACTS_ERROR(
+              "Update with material effects failed: " << updateRes.error());
+          return updateRes.error();
+        } else {
+          ACTS_VERBOSE(
+              "Update with material effects successful, updated parameters are "
+              ": \n"
+              << *trackState.parameter.filtered.parameters());
+        }
+
+        // Set the track state flags
+        trackState.setType(TrackStateFlag::MaterialFlag);
         if (surface->associatedDetectorElement() != nullptr) {
           // If the surface is sensitive, set the hole type flag
           trackState.setType(TrackStateFlag::HoleFlag);
@@ -385,8 +423,6 @@ class KalmanFitter {
           ACTS_VERBOSE("Detected hole on " << surface->geoID());
           result.missedActiveSurfaces.push_back(surface);
         } else {
-          // If the surface is in-sensitive, set the material type flag
-          trackState.setType(TrackStateFlag::MaterialFlag);
           ACTS_VERBOSE("Detected in-sensitive surface " << surface->geoID());
         }
 
@@ -397,6 +433,87 @@ class KalmanFitter {
       return Result<void>::success();
     }
 
+    /// @brief Kalman actor operation : material interaction
+    ///
+    /// @tparam propagator_state_t is the type of Propagagor state
+    /// @tparam stepper_t Type of the stepper
+    ///
+    /// @param surface The surface where the material interaction happens
+    /// @param state The mutable propagator state object
+    /// @param stepper The stepper in use
+    /// @param mStage The materal update stage
+    ///
+    /// @return The material interaction
+    template <typename propagator_state_t, typename stepper_t>
+    detail::PointwiseMaterialInteraction materialInteractor(
+        const Surface* surface, propagator_state_t& state, stepper_t& stepper,
+        const MaterialUpdateStage& mStage) const {
+      // Prepare relevant input particle properties
+      detail::PointwiseMaterialInteraction interaction(surface, state, stepper);
+
+      // Evaluate the material properties
+      if (interaction.evaluateMaterialProperties(state, mStage)) {
+        // Evaluate the material effects
+        interaction.evaluatePointwiseMaterialInteraction(multipleScattering,
+                                                         energyLoss);
+
+        // Transport the covariance to the current position in space
+        // the 'true' indicates re-initializaiton of the further transport
+        if (interaction.performCovarianceTransport) {
+          stepper.covarianceTransport(state.stepping, true);
+        }
+
+        // Update the state and stepper with material effects
+        interaction.updateState(state, stepper);
+      }
+
+      return std::move(interaction);
+    }
+
+    /// @brief Kalman actor operation : material effects updater
+    ///
+    /// @tparam track_state_t is the type of track state
+    ///
+    /// @param gctx The current geometry context object, e.g. alignment
+    /// @param trackState the track state
+    ///
+    /// @return Bool indicating whether this update was 'successful'
+    template <typename track_state_t>
+    Result<void> materialUpdater(
+        const GeometryContext& geoContext, track_state_t& trackState,
+        const detail::PointwiseMaterialInteraction& interaction) const {
+      // Predicted parameter and covariance
+      parameters_t predicted = *trackState.parameter.predicted;
+      auto predicted_parameter = predicted.parameters();
+      auto predicted_covariance = *predicted.covariance();
+
+      // Get the update for parameter
+      BoundParameters::ParVector_t deltaParamVec;
+      // The delta of q/p
+      double deltaQOP = interaction.q / interaction.nextP - interaction.qOverP;
+      deltaParamVec << 0, 0, 0, 0, deltaQOP, 0.;
+
+      // Get the update for covariance
+      Acts::BoundSymMatrix deltaParamCov;
+      deltaParamCov.setZero();
+      deltaParamCov.diagonal() << 0, 0, interaction.variances.x(),
+          interaction.variances.y(), interaction.variances.z(), 0;
+
+      // The updated parameter and covariance
+      auto filtered_parameters = predicted_parameter + deltaParamVec;
+      auto filtered_covariance = predicted_covariance + deltaParamCov;
+
+      // The filtered parameter
+      parameters_t filtered(geoContext, std::move(filtered_covariance),
+                            std::move(filtered_parameters),
+                            predicted.referenceSurface().getSharedPtr());
+
+      // Set the filtered parameter for the track state
+      trackState.parameter.filtered = std::move(filtered);
+
+      return Result<void>::success();
+    }
+
     /// @brief Kalman actor operation : finalize
     ///
     /// @tparam propagator_state_t is the type of Propagagor state
-- 
GitLab