diff --git a/Core/include/Acts/EventData/TrackState.hpp b/Core/include/Acts/EventData/TrackState.hpp index d4d861e2eac9cb4abfdf38568e48f385d6c02e06..21fe35e4575c043844611907a0f463f6c54281e3 100644 --- a/Core/include/Acts/EventData/TrackState.hpp +++ b/Core/include/Acts/EventData/TrackState.hpp @@ -146,6 +146,9 @@ public: double pathLength = 0.; } parameter; + /// The increment of the chi^2 after the Kalman update (filtering). + double filteredChi2Increment = 0; + /// @brief Nested measurement part /// This is the uncalibrated and calibrated measurement /// (in case the latter is different) diff --git a/Core/include/Acts/Fitter/GainMatrixUpdator.hpp b/Core/include/Acts/Fitter/GainMatrixUpdator.hpp index bacfc24b4b7fc38fc078c2578a5da62c6c437142..a82742c241e047cd405a44fba9befa89f78d05cf 100644 --- a/Core/include/Acts/Fitter/GainMatrixUpdator.hpp +++ b/Core/include/Acts/Fitter/GainMatrixUpdator.hpp @@ -108,20 +108,26 @@ public: filtered_covariance = (CovMatrix_t::Identity() - K * H) * predicted_covariance; + // Create new filtered parameters and covariance + parameters_t filtered( + std::make_unique<const CovMatrix_t>(filtered_covariance), + filtered_parameters, + predicted.referenceSurface().getSharedPtr()); + + const auto R_filtered = (calibrated.covariance() + - H * filtered_covariance * H.transpose()); + const auto r_filtered = calibrated.residual(filtered); + const double chi2Increment + = (r_filtered.transpose() * R_filtered * r_filtered).value(); + // plug calibrated measurement back into track state trackState.measurement.calibrated = std::move(calibrated); + trackState.parameter.filtered = std::move(filtered); + trackState.filteredChi2Increment = chi2Increment; }, *trackState.measurement.uncalibrated); - // Create new filtered parameters and covariance - parameters_t filtered( - std::make_unique<const CovMatrix_t>(std::move(filtered_covariance)), - filtered_parameters, - predicted.referenceSurface().getSharedPtr()); - - trackState.parameter.filtered = std::move(filtered); - // always succeed, no outlier logic yet return true; } diff --git a/Core/include/Acts/Fitter/SequentialKalmanFitter.hpp b/Core/include/Acts/Fitter/SequentialKalmanFitter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..700da210f27dd4405f64fd296b261b4cb262df5b --- /dev/null +++ b/Core/include/Acts/Fitter/SequentialKalmanFitter.hpp @@ -0,0 +1,197 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2016-2018 Acts project team +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#pragma once + +#include "Acts/Fitter/detail/VoidKalmanComponents.hpp" +#include "Acts/Propagator/AbortList.hpp" +#include "Acts/Propagator/ActionList.hpp" +#include "Acts/Propagator/Propagator.hpp" + +#include <boost/variant.hpp> +#include <memory> + +namespace Acts { + +/** + * Sequential Kalman Fitter algorithm to traverse through a geometry with + * measurements and select a single best combination (= track). For this, a + * prepared measurement selector is asked to supply measurements for each + * visited surface in the geometry, which are then ranked by chi^2 increment. + * The one with the smalles chi^2 increment is taken, the track parameters + * updated and the algorithm is continued. + * + * @tparam propagator_t The type of the propagator to use. + * @tparam track_state_t The type of the track state to use. + * @tparam measurement_selector_t The type of the measurement selector + * algorithm. Should implement an operator returning a vector of track + * states to used for a given surface. + * @tparam updator_t The type of the updator (used for updating the track + * parameters with a measurement) + * @tparam calibrator_t The type of the calibrator (used for calibrating the + * measurements depending on a track state) + */ +template <typename propagator_t, + typename track_state_t, + typename measurement_selector_t, + typename updator_t = VoidKalmanUpdator, + typename calibrator_t = VoidKalmanComponents> +class SequentialKalmanFitter +{ +public: + /// Default constructor is deleted + SequentialKalmanFitter() = delete; + + /// Constructor taking a propagator as input + explicit SequentialKalmanFitter(propagator_t pPropagator) + : m_propagator(std::move(pPropagator)) + { + } + + /** + * Main function of the fitter, which takes a seeding start parameters as + * input and the prepared measurement selector and outputs a result object + * including the found track. + * @tparam parameters_t The type of the seed parameters + * @param sParameters The seed parameters to start with + * @param pMeasurementSelector The prefilled measurement selector, which will + * return the measurements for a given surface + * @return A result structure with the selected track states. + */ + template <typename parameters_t> + auto + fit(const parameters_t& sParameters, + std::shared_ptr<measurement_selector_t> pMeasurementSelector) const + { + // Create the ActionList and AbortList + using KalmanResult = typename KalmanActor::result_type; + using Actors = ActionList<KalmanActor>; + using Aborters = AbortList<>; + + // Create relevant options for the propagation options + PropagatorOptions<Actors, Aborters> kalmanOptions; + auto& kalmanActor = kalmanOptions.actionList.template get<KalmanActor>(); + kalmanActor.m_measurementSelectorPtr = std::move(pMeasurementSelector); + + // Run the fitter + const auto& result + = m_propagator.template propagate(sParameters, kalmanOptions); + + // Get the result of the fit + auto kalmanResult = result.template get<KalmanResult>(); + + // Return the converted Track + return kalmanResult; + } + +private: + /// Used propagator instance + propagator_t m_propagator; + + /** + * Actor used in the SequentialKalmanFitter propagation. + * On every call, it will look for compatible measurements on the surface + * and select the one with the lowest chi^2 increment. + */ + class KalmanActor + { + public: + /** + * Creates a new actor (is called by the propagator automatically). + * @param pUpdator The updator instance used during the step. + * @param pCalibrator The calibrator instance used during the step. + */ + KalmanActor(updator_t pUpdator = updator_t(), + calibrator_t pCalibrator = calibrator_t()) + : m_updator(std::move(pUpdator)), m_calibrator(std::move(pCalibrator)) + { + } + + /// The type of the result of this actor including the final track and the + /// number of holes. + struct result_type + { + /// The resulting track of the algorithm + std::vector<track_state_t> resultStateList = {}; + /// How many surfaces had no measurement on them + unsigned int numberOfHoles = 0; + }; + + /** + * Main function of this actor: + * * Is only executed if propagated to a surface + * * let the measurements selector give us a list of possible next + * measurements + * * call the update process on them + * * rank them by chi^2 and pick the best one. + * @tparam propagator_state_t The propagator state type + * @param state The state coming from the propagator + * @param result The result we use for storing e.g. the resulting track + */ + template <typename propagator_state_t> + void + operator()(propagator_state_t& state, result_type& result) const + { + // Do not go on if there is no surface + const Surface* surface = state.navigation.currentSurface; + if (not surface) { + return; + } + + // Request a list of possible next states by the measurement selector + assert(m_measurementSelectorPtr != nullptr); + std::vector<track_state_t> nextStates + = (*m_measurementSelectorPtr)(*surface, state); + if (nextStates.empty()) { + // Only count it as hole if a sensitive surface + if (surface->associatedDetectorElement() != nullptr) { + result.numberOfHoles++; + } + return; + } + + // Loop through all possible measurement + // perform an update and rank by chi^2. + auto boundStates = state.stepping.boundState(*surface, true); + track_state_t* chosenNextState = nullptr; + for (auto& nextState : nextStates) { + nextState.parameter.predicted = std::get<0>(boundStates); + nextState.parameter.jacobian = std::get<1>(boundStates); + nextState.parameter.pathLength = std::get<2>(boundStates); + + m_updator(nextState); + + if (chosenNextState == nullptr) { + chosenNextState = &nextState; + } else { + const double chosenChi2 = chosenNextState->filteredChi2Increment; + const double nextChi2 = nextState.filteredChi2Increment; + if (chosenChi2 > nextChi2) { + chosenNextState = &nextState; + } + } + } + + assert(chosenNextState != nullptr); + + // Update the fitted state with the chosen next state and also store it in + // the track list + state.stepping.update(*(chosenNextState->parameter.filtered)); + result.resultStateList.push_back(std::move(*chosenNextState)); + } + + /// The pointer to the measurement selector (needs to be filled by the user) + std::shared_ptr<measurement_selector_t> m_measurementSelectorPtr; + /// The updator instance + updator_t m_updator; + /// The calibrator instance + calibrator_t m_calibrator; + }; +}; + +} // namespace Acts diff --git a/Tests/Core/CommonHelpers/include/Acts/Tests/CommonHelpers/CubicTrackingGeometry.hpp b/Tests/Core/CommonHelpers/include/Acts/Tests/CommonHelpers/CubicTrackingGeometry.hpp index 594fe8d5ddb6f22e3ce0c0f094afc08af29efe47..195f8df5838d2293c303dcc5797a4a98817c2a3b 100644 --- a/Tests/Core/CommonHelpers/include/Acts/Tests/CommonHelpers/CubicTrackingGeometry.hpp +++ b/Tests/Core/CommonHelpers/include/Acts/Tests/CommonHelpers/CubicTrackingGeometry.hpp @@ -5,7 +5,7 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. - +#pragma once #include <vector> #include "Acts/Detector/TrackingGeometry.hpp" #include "Acts/Detector/TrackingVolume.hpp" diff --git a/Tests/Core/CommonHelpers/include/Acts/Tests/CommonHelpers/MeasurementCreatorHelper.hpp b/Tests/Core/CommonHelpers/include/Acts/Tests/CommonHelpers/MeasurementCreatorHelper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f3962125e385abdce379a48adba560589325dd8e --- /dev/null +++ b/Tests/Core/CommonHelpers/include/Acts/Tests/CommonHelpers/MeasurementCreatorHelper.hpp @@ -0,0 +1,264 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2016-2018 Acts project team +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#pragma once +#include <algorithm> +#include <math.h> +#include <random> +#include <vector> +#include "Acts/Detector/TrackingGeometry.hpp" +#include "Acts/EventData/Measurement.hpp" +#include "Acts/EventData/TrackParameters.hpp" +#include "Acts/EventData/TrackState.hpp" +#include "Acts/Extrapolator/Navigator.hpp" +#include "Acts/Fitter/KalmanFitter.hpp" +#include "Acts/Propagator/EigenStepper.hpp" +#include "Acts/Propagator/Propagator.hpp" +#include "Acts/Propagator/StraightLineStepper.hpp" +#include "Acts/Propagator/detail/DebugOutputActor.hpp" +#include "Acts/Propagator/detail/StandardAborters.hpp" +#include "Acts/Surfaces/Surface.hpp" +#include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Definitions.hpp" +#include "Acts/Utilities/GeometryID.hpp" + +namespace Acts { +namespace Test { + + // A few initialisations and definitionas + using Identifier = unsigned int; + + using TrackState = TrackState<Identifier, BoundParameters>; + using Resolution = std::pair<ParID_t, double>; + using ElementResolution = std::vector<Resolution>; + using VolumeResolution = std::map<geo_id_value, ElementResolution>; + using DetectorResolution = std::map<geo_id_value, VolumeResolution>; + + using DebugOutput = detail::DebugOutputActor; + + std::normal_distribution<double> gauss(0., 1.); + std::uniform_int_distribution<int> numberOfMeasurements(1, 5); + std::mt19937 generator(42); + + /// @brief This struct creates FittableMeasurements on the + /// detector surfaces, according to the given smearing xxparameters + /// + struct MeasurementCreator + { + /// @brief Constructor + MeasurementCreator() = default; + + /// The detector resolution + DetectorResolution detectorResolution; + + /// Randomize number of hits + bool randomizeNumberOfMeasurements = false; + + struct result_type + { + std::vector<TrackState> trackStates; + std::map<const Surface*, std::vector<FittableMeasurement<Identifier>>> + measurementMap; + }; + + /// @brief Operater that is callable by an ActionList. The function collects + /// the surfaces + /// + /// @tparam propagator_state_t Type of the propagator state + /// @param [in] state State of the propagator + /// @param [out] result Vector of matching surfaces + template <typename propagator_state_t> + void + operator()(propagator_state_t& state, result_type& result) const + { + // monitor the current surface + const Surface* surface = state.navigation.currentSurface; + if (surface == nullptr or not surface->associatedDetectorElement()) { + return; + } + + auto geoID = surface->geoID(); + geo_id_value volumeID = geoID.value(GeometryID::volume_mask); + geo_id_value layerID = geoID.value(GeometryID::layer_mask); + // find volume and layer information for this + auto vResolution = detectorResolution.find(volumeID); + if (vResolution == detectorResolution.end()) { + return; + } + // find layer resolutions + auto lResolution = vResolution->second.find(layerID); + if (lResolution == vResolution->second.end()) { + return; + } + + unsigned int nMeasurements = 1; + + if (randomizeNumberOfMeasurements) { + nMeasurements + = static_cast<unsigned int>(numberOfMeasurements(generator)); + } + + for (unsigned int identifier = 0; identifier < nMeasurements; + identifier++) { + addSingleMeasurement(identifier, state, result, surface, lResolution); + } + } + + template <typename propagator_state_t, typename resolution_t> + void + addSingleMeasurement(unsigned int identifier, + propagator_state_t& state, + result_type& result, + const Surface* surface, + resolution_t lResolution) const + { + // Apply global to local + Acts::Vector2D lPos; + surface->globalToLocal( + state.stepping.position(), state.stepping.direction(), lPos); + if (lResolution->second.size() == 1) { + double sp = lResolution->second[0].second; + ActsSymMatrixD<1> cov1D; + cov1D << sp * sp; + double dp = sp * gauss(generator); + + if (identifier != 0) { + dp *= 20; + } + + if (lResolution->second[0].first == eLOC_0) { + // push back & move a LOC_0 measurement + Measurement<Identifier, eLOC_0> m( + surface->getSharedPtr(), identifier, cov1D, lPos[eLOC_0] + dp); + result.measurementMap[surface].push_back(m); + result.trackStates.push_back(TrackState(std::move(m))); + } else { + // push back & move a LOC_1 measurement + Measurement<Identifier, eLOC_1> m( + surface->getSharedPtr(), identifier, cov1D, lPos[eLOC_1] + dp); + result.measurementMap[surface].push_back(m); + result.trackStates.push_back(TrackState(std::move(m))); + } + } else if (lResolution->second.size() == 2) { + // Create the measurement and move it + double sx = lResolution->second[eLOC_0].second; + double sy = lResolution->second[eLOC_1].second; + ActsSymMatrixD<2> cov2D; + cov2D << sx * sx, 0., 0., sy * sy; + double dx = sx * gauss(generator); + double dy = sy * gauss(generator); + + if (identifier != 0) { + dx *= 20; + dy *= 20; + } + + // push back & move a LOC_0, LOC_1 measurement + Measurement<Identifier, eLOC_0, eLOC_1> m(surface->getSharedPtr(), + identifier, + cov2D, + lPos[eLOC_0] + dx, + lPos[eLOC_1] + dy); + result.measurementMap[surface].push_back(m); + result.trackStates.push_back(TrackState(std::move(m))); + } + } + }; + + template <class AStepper> + MeasurementCreator::result_type + createMeasurements(std::shared_ptr<const TrackingGeometry> detector, + AStepper stepper, + const Vector3D& startPosition, + const Vector3D& startMomentum, + DetectorResolution detRes, + bool addNoiseMeasurements = false, + bool debugMode = false) + { + // Build navigator for the measurement creation + Navigator navigator(detector); + navigator.resolvePassive = false; + navigator.resolveMaterial = true; + navigator.resolveSensitive = true; + + // Define the measurement propagator + using MeasurementPropagator = Propagator<AStepper, Navigator>; + + // Build propagator for the measurement creation + MeasurementPropagator propagator(stepper, navigator); + + SingleCurvilinearTrackParameters<NeutralPolicy> startParameters( + nullptr, startPosition, startMomentum); + + // Create action list for the measurement creation + using MeasurementActions = ActionList<MeasurementCreator, DebugOutput>; + using MeasurementAborters = AbortList<detail::EndOfWorldReached>; + + // Set options for propagator + PropagatorOptions<MeasurementActions, MeasurementAborters> propOps; + propOps.debug = debugMode; + auto& mCreator = propOps.actionList.get<MeasurementCreator>(); + mCreator.detectorResolution = detRes; + mCreator.randomizeNumberOfMeasurements = addNoiseMeasurements; + + // Launch and collect - the measurements + auto result = propagator.propagate(startParameters, propOps); + if (debugMode) { + const auto debugString + = result.template get<DebugOutput::result_type>().debugString; + std::cout << ">>>> Measurement creation: " << std::endl; + std::cout << debugString; + } + + auto measurements = result.template get<MeasurementCreator::result_type>(); + return measurements; + } + + template <class AStepper> + MeasurementCreator::result_type + createMeasurementsOnCubicDetector( + std::shared_ptr<const TrackingGeometry> detector, + AStepper stepper, + const Vector3D& startPosition, + const Vector3D& startMomentum, + bool addNoiseMeasurements = false, + bool debugMode = false) + { + auto pixelResX = Resolution(eLOC_0, 25. * units::_um); + auto pixelResY = Resolution(eLOC_1, 50. * units::_um); + auto stripResX = Resolution(eLOC_0, 100. * units::_um); + auto stripResY = Resolution(eLOC_1, 150. * units::_um); + + ElementResolution pixelElementRes = {pixelResX, pixelResY}; + ElementResolution stripElementResI = {stripResX}; + ElementResolution stripElementResO = {stripResY}; + + VolumeResolution pixelVolumeRes; + pixelVolumeRes[2] = pixelElementRes; + pixelVolumeRes[4] = pixelElementRes; + + VolumeResolution stripVolumeRes; + stripVolumeRes[2] = stripElementResI; + stripVolumeRes[4] = stripElementResO; + stripVolumeRes[6] = stripElementResI; + stripVolumeRes[8] = stripElementResO; + + DetectorResolution detRes; + detRes[2] = pixelVolumeRes; + detRes[3] = stripVolumeRes; + + return createMeasurements(detector, + stepper, + startPosition, + startMomentum, + detRes, + addNoiseMeasurements, + debugMode); + } + +} // namespace Test +} // namespace Acts diff --git a/Tests/Core/Fitter/CMakeLists.txt b/Tests/Core/Fitter/CMakeLists.txt index 31df73253cbc42cbe6e1c8845a3ac6a35fb4c40f..059778e3994e88a8ea3c8ba03b4d04f09a1ec05f 100644 --- a/Tests/Core/Fitter/CMakeLists.txt +++ b/Tests/Core/Fitter/CMakeLists.txt @@ -4,6 +4,12 @@ target_link_libraries (KalmanFitterTests PRIVATE ActsCore ActsTestsCommonHelpers add_test (NAME KalmanFitterUnitTests COMMAND KalmanFitterTests) acts_add_test_to_cdash_project (PROJECT ACore TEST KalmanFitterUnitTests TARGETS KalmanFitterTests) +add_executable (SequentialKalmanFitterTests SequentialKalmanFitterTests.cpp) +target_include_directories (SequentialKalmanFitterTests PRIVATE ${Boost_INCLUDE_DIRS}) +target_link_libraries (SequentialKalmanFitterTests PRIVATE ActsCore ActsTestsCommonHelpers) +add_test (NAME SequentialKalmanFitterUnitTests COMMAND SequentialKalmanFitterTests) +acts_add_test_to_cdash_project (PROJECT ACore TEST SequentialKalmanFitterUnitTests TARGETS SequentialKalmanFitterTests) + add_executable (GainMatrixTests GainMatrixTests.cpp) target_include_directories (GainMatrixTests PRIVATE ${Boost_INCLUDE_DIRS}) target_link_libraries (GainMatrixTests PRIVATE ActsCore) diff --git a/Tests/Core/Fitter/KalmanFilterTestUtils.hpp b/Tests/Core/Fitter/KalmanFilterTestUtils.hpp deleted file mode 100644 index a7ffd9beeb568b9909a7fbf26b5ddc2fe50091df..0000000000000000000000000000000000000000 --- a/Tests/Core/Fitter/KalmanFilterTestUtils.hpp +++ /dev/null @@ -1,152 +0,0 @@ -// This file is part of the Acts project. -// -// Copyright (C) 2017-2018 Acts project team -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#pragma once -#include <cmath> -#include <fstream> -#include <iostream> -#include <memory> -#include <random> -#include <vector> -#include "Acts/Detector/TrackingGeometry.hpp" -#include "Acts/EventData/Measurement.hpp" -#include "Acts/Extrapolation/ExtrapolationCell.hpp" -#include "Acts/Extrapolation/ExtrapolationEngine.hpp" -#include "Acts/Extrapolation/IExtrapolationEngine.hpp" -#include "Acts/Extrapolation/MaterialEffectsEngine.hpp" -#include "Acts/Extrapolation/RungeKuttaEngine.hpp" -#include "Acts/Extrapolation/StaticEngine.hpp" -#include "Acts/Extrapolation/StaticNavigationEngine.hpp" -#include "Acts/Fitter/KalmanFitter.hpp" -#include "Acts/Fitter/KalmanUpdator.hpp" -#include "Acts/MagneticField/ConstantBField.hpp" -#include "Acts/Surfaces/PerigeeSurface.hpp" -#include "Acts/Utilities/Definitions.hpp" -#include "Acts/Utilities/Logger.hpp" -#include "Acts/Utilities/Units.hpp" - -// shorthands -using namespace Acts; -using FitMeas_t = FittableMeasurement<long int>; -template <ParID_t... pars> -using Meas_t = Measurement<long int, pars...>; - -/// fit cache -struct MyCache -{ - std::unique_ptr<const KF::Step<long int>::JacobianMatrix> jacobian; - std::unique_ptr<const BoundParameters> parameters; - - MyCache() = default; - MyCache(const MyCache&) = delete; - MyCache(MyCache&&) = default; -}; - -/// extrapolation wrapper -class MyExtrapolator -{ -public: - MyExtrapolator(std::shared_ptr<const IExtrapolationEngine> exEngine - = nullptr); - - MyCache - operator()(const FitMeas_t& m, const TrackParameters& tp) const; - -private: - std::shared_ptr<const IExtrapolationEngine> m_exEngine; -}; - -/// dummy class, returns measurement unchanged -class NoCalibration -{ -public: - std::unique_ptr<const FitMeas_t> - operator()(const FitMeas_t& m, const BoundParameters&) const; -}; - -class CacheGenerator -{ -public: - std::unique_ptr<KF::Step<long int>> - operator()(MyCache m) const; -}; - -MyExtrapolator::MyExtrapolator( - std::shared_ptr<const IExtrapolationEngine> exEngine) - : m_exEngine(std::move(exEngine)){}; - -/// wrapper around extrapolate call to exEngine, setting the right flags -MyCache -MyExtrapolator::operator()(const FitMeas_t& m, const TrackParameters& tp) const -{ - auto exCell = std::make_unique<ExtrapolationCell<TrackParameters>>(tp); - exCell->addConfigurationMode(ExtrapolationMode::CollectJacobians); - (*exCell).pathLimit = 500; - const Surface& sf = getSurface(m); - - m_exEngine->extrapolate(*exCell, &sf); - MyCache c; - auto j = exCell->extrapolationSteps.back().transportJacobian.release(); - c.jacobian.reset(new KF::Step<long int>::JacobianMatrix(*j)); - auto pars - = static_cast<const BoundParameters*>(exCell->leadParameters->clone()); - c.parameters.reset(pars); - - return c; -}; - -std::unique_ptr<const FitMeas_t> -NoCalibration::operator()(const FitMeas_t& m, const BoundParameters&) const -{ - return std::make_unique<const FitMeas_t>(m); -}; - -std::unique_ptr<KF::Step<long int>> -CacheGenerator::operator()(MyCache m) const -{ - auto step = std::make_unique<KF::Step<long int>>(); - step->setPredictedState(std::move(m.parameters)); - step->setJacobian(std::move(m.jacobian)); - - return step; -}; - -/// set up extrapolation -std::shared_ptr<IExtrapolationEngine> -initExtrapolator(const std::shared_ptr<const TrackingGeometry>& geo) -{ - auto propConfig = RungeKuttaEngine<>::Config(); - propConfig.fieldService - = std::make_shared<ConstantBField>(0, 0, 2 * Acts::units::_T); - auto propEngine = std::make_shared<RungeKuttaEngine<>>(propConfig); - - auto matConfig = MaterialEffectsEngine::Config(); - auto materialEngine = std::make_shared<MaterialEffectsEngine>(matConfig); - - auto navConfig = StaticNavigationEngine::Config(); - navConfig.propagationEngine = propEngine; - navConfig.materialEffectsEngine = materialEngine; - navConfig.trackingGeometry = geo; - auto navEngine = std::make_shared<StaticNavigationEngine>(navConfig); - - auto statConfig = StaticEngine::Config(); - statConfig.propagationEngine = propEngine; - statConfig.navigationEngine = navEngine; - statConfig.materialEffectsEngine = materialEngine; - auto statEngine = std::make_shared<StaticEngine>(statConfig); - - auto exEngineConfig = ExtrapolationEngine::Config(); - exEngineConfig.trackingGeometry = geo; - exEngineConfig.propagationEngine = propEngine; - exEngineConfig.navigationEngine = navEngine; - exEngineConfig.extrapolationEngines = {statEngine}; - auto exEngine = std::make_shared<ExtrapolationEngine>(exEngineConfig); - exEngine->setLogger(getDefaultLogger("ExtrapolationEngine", Logging::INFO)); - - return exEngine; -}; \ No newline at end of file diff --git a/Tests/Core/Fitter/KalmanFitterTests.cpp b/Tests/Core/Fitter/KalmanFitterTests.cpp index dd5655cfae3a2ae275021cb2f1a0c268dbd3f38d..8dcc2094e9932a971f196a69570294e1e6e84f32 100644 --- a/Tests/Core/Fitter/KalmanFitterTests.cpp +++ b/Tests/Core/Fitter/KalmanFitterTests.cpp @@ -30,250 +30,47 @@ #include "Acts/Propagator/detail/StandardAborters.hpp" #include "Acts/Surfaces/Surface.hpp" #include "Acts/Tests/CommonHelpers/CubicTrackingGeometry.hpp" +#include "Acts/Tests/CommonHelpers/MeasurementCreatorHelper.hpp" #include "Acts/Utilities/BinningType.hpp" #include "Acts/Utilities/Definitions.hpp" #include "Acts/Utilities/GeometryID.hpp" namespace Acts { namespace Test { - - // A few initialisations and definitionas - using Identifier = GeometryID; - using Jacobian = BoundParameters::CovMatrix_t; - - using TrackState = TrackState<Identifier, BoundParameters>; - using Resolution = std::pair<ParID_t, double>; - using ElementResolution = std::vector<Resolution>; - using VolumeResolution = std::map<geo_id_value, ElementResolution>; - using DetectorResolution = std::map<geo_id_value, VolumeResolution>; - - using DebugOutput = detail::DebugOutputActor; - - std::normal_distribution<double> gauss(0., 1.); - std::default_random_engine generator(42); - - ActsSymMatrixD<1> cov1D; - ActsSymMatrixD<2> cov2D; - - bool debugMode = false; - - /// @brief This struct creates FittableMeasurements on the - /// detector surfaces, according to the given smearing xxparameters - /// - struct MeasurementCreator - { - /// @brief Constructor - MeasurementCreator() = default; - - /// The detector resolution - DetectorResolution detectorResolution; - - using result_type = std::vector<TrackState>; - - /// @brief Operater that is callable by an ActionList. The function collects - /// the surfaces - /// - /// @tparam propagator_state_t Type of the propagator state - /// @param [in] state State of the propagator - /// @param [out] result Vector of matching surfaces - template <typename propagator_state_t> - void - operator()(propagator_state_t& state, result_type& result) const - { - // monitor the current surface - auto surface = state.navigation.currentSurface; - if (surface and surface->associatedDetectorElement()) { - auto geoID = surface->geoID(); - geo_id_value volumeID = geoID.value(GeometryID::volume_mask); - geo_id_value layerID = geoID.value(GeometryID::layer_mask); - // find volume and layer information for this - auto vResolution = detectorResolution.find(volumeID); - if (vResolution != detectorResolution.end()) { - // find layer resolutions - auto lResolution = vResolution->second.find(layerID); - if (lResolution != vResolution->second.end()) { - // Apply global to local - Acts::Vector2D lPos; - surface->globalToLocal( - state.stepping.position(), state.stepping.direction(), lPos); - if (lResolution->second.size() == 1) { - double sp = lResolution->second[0].second; - cov1D << sp * sp; - double dp = sp * gauss(generator); - if (lResolution->second[0].first == eLOC_0) { - // push back & move a LOC_0 measurement - Measurement<Identifier, eLOC_0> m0( - surface->getSharedPtr(), geoID, cov1D, lPos[eLOC_0] + dp); - result.push_back(TrackState(std::move(m0))); - } else { - // push back & move a LOC_1 measurement - Measurement<Identifier, eLOC_1> m1( - surface->getSharedPtr(), geoID, cov1D, lPos[eLOC_1] + dp); - result.push_back(TrackState(std::move(m1))); - } - } else if (lResolution->second.size() == 2) { - // Create the measurment and move it - double sx = lResolution->second[eLOC_0].second; - double sy = lResolution->second[eLOC_1].second; - cov2D << sx * sx, 0., 0., sy * sy; - double dx = sx * gauss(generator); - double dy = sy * gauss(generator); - // push back & move a LOC_0, LOC_1 measurement - Measurement<Identifier, eLOC_0, eLOC_1> m01( - surface->getSharedPtr(), - geoID, - cov2D, - lPos[eLOC_0] + dx, - lPos[eLOC_1] + dy); - result.push_back(TrackState(std::move(m01))); - } - } - } - } - } - }; - - double dX, dY; - Vector3D pos; - Surface const* sur; - - /// - /// @brief Simplified material interaction effect by pure gaussian deflection - /// - struct MaterialScattering - { - /// @brief Constructor - MaterialScattering() = default; - - /// @brief Main action list call operator for the scattering on material - /// - /// @todo deal momentum in a gaussian way properly - /// - /// @tparam propagator_state_t State of the propagator - /// @param [in] state State of the propagation - template <typename propagator_state_t> - void - operator()(propagator_state_t& state) const - { - // Check if there is a surface with material and a covariance is existing - if (state.navigation.currentSurface - && state.navigation.currentSurface->associatedMaterial() - && state.stepping.cov != ActsSymMatrixD<5>::Zero()) { - // Sample angles - std::normal_distribution<double> scatterAngle( - 0., 0.017); //< \approx 1 degree - double dPhi = scatterAngle(generator), dTheta = scatterAngle(generator); - - // Update the covariance - state.stepping.cov(ePHI, ePHI) += dPhi * dPhi; - state.stepping.cov(eTHETA, eTHETA) += dTheta * dTheta; - - // Update the angles - double theta = std::acos(state.stepping.direction().z()); - double phi = std::atan2(state.stepping.direction().y(), - state.stepping.direction().x()); - - state.stepping.update( - state.stepping.position(), - {std::sin(theta + dTheta) * std::cos(phi + dPhi), - std::sin(theta + dTheta) * std::sin(phi + dPhi), - std::cos(theta + dTheta)}, - std::max(state.stepping.p - - std::abs(gauss(generator)) * units::_MeV, - 0.)); - } - } - }; - /// /// @brief Unit test for Kalman fitter with measurements along the x-axis /// BOOST_AUTO_TEST_CASE(kalman_fitter_zero_field) { - // Build detector - CubicTrackingGeometry cGeometry; - auto detector = cGeometry(); + CubicTrackingGeometry geom; + auto detector = geom(); - // Build navigator for the measurement creatoin - Navigator mNavigator(detector); - mNavigator.resolvePassive = false; - mNavigator.resolveMaterial = true; - mNavigator.resolveSensitive = true; - - // Use straingt line stepper to create the measurements StraightLineStepper mStepper; + Vector3D startPosition(-3. * units::_m, 0., 0.); + Vector3D startMomentum(1. * units::_GeV, 0., 0); - // Define the measurement propagator - using MeasurementPropagator = Propagator<StraightLineStepper, Navigator>; - - // Build propagator for the measurement creation - MeasurementPropagator mPropagator(mStepper, mNavigator); - Vector3D mPos(-3. * units::_m, 0., 0.), mMom(1. * units::_GeV, 0., 0); - SingleCurvilinearTrackParameters<NeutralPolicy> mStart(nullptr, mPos, mMom); - - // Create action list for the measurement creation - using MeasurementActions = ActionList<MeasurementCreator, DebugOutput>; - using MeasurementAborters = AbortList<detail::EndOfWorldReached>; - - auto pixelResX = Resolution(eLOC_0, 25. * units::_um); - auto pixelResY = Resolution(eLOC_1, 50. * units::_um); - auto stripResX = Resolution(eLOC_0, 100. * units::_um); - auto stripResY = Resolution(eLOC_1, 150. * units::_um); - - ElementResolution pixelElementRes = {pixelResX, pixelResY}; - ElementResolution stripElementResI = {stripResX}; - ElementResolution stripElementResO = {stripResY}; - - VolumeResolution pixelVolumeRes; - pixelVolumeRes[2] = pixelElementRes; - pixelVolumeRes[4] = pixelElementRes; - - VolumeResolution stripVolumeRes; - stripVolumeRes[2] = stripElementResI; - stripVolumeRes[4] = stripElementResO; - stripVolumeRes[6] = stripElementResI; - stripVolumeRes[8] = stripElementResO; - - DetectorResolution detRes; - detRes[2] = pixelVolumeRes; - detRes[3] = stripVolumeRes; - - // Set options for propagator - PropagatorOptions<MeasurementActions, MeasurementAborters> mOptions; - mOptions.debug = debugMode; - auto& mCreator = mOptions.actionList.get<MeasurementCreator>(); - mCreator.detectorResolution = detRes; - - // Launch and collect - the measurements - auto mResult = mPropagator.propagate(mStart, mOptions); - if (debugMode) { - const auto debugString - = mResult.template get<DebugOutput::result_type>().debugString; - std::cout << ">>>> Measurement creation: " << std::endl; - std::cout << debugString; - } - - auto measurements = mResult.template get<MeasurementCreator::result_type>(); + auto measurements = createMeasurementsOnCubicDetector( + detector, mStepper, startPosition, startMomentum) + .trackStates; BOOST_TEST(measurements.size() == 6); // The KalmanFitter - we use the eigen stepper for covariance transport - // Build navigator for the measurement creatoin + Navigator rNavigator(detector); rNavigator.resolvePassive = false; rNavigator.resolveMaterial = true; rNavigator.resolveSensitive = true; // Configure propagation with deactivated B-field - ConstantBField bField(Vector3D(0., 0., 0.)); using RecoStepper = EigenStepper<ConstantBField>; - RecoStepper rStepper(bField); + RecoStepper rStepper; using RecoPropagator = Propagator<RecoStepper, Navigator>; RecoPropagator rPropagator(rStepper, rNavigator); // Set initial parameters for the particle track ActsSymMatrixD<5> cov; - cov << 1000. * units::_um, 0., 0., 0., 0., 0., 1000. * units::_um, 0., 0., - 0., 0., 0., 0.05, 0., 0., 0., 0., 0., 0.05, 0., 0., 0., 0., 0., 0.01; + cov << 1. * units::_um, 0., 0., 0., 0., 0., 1. * units::_um, 0., 0., 0., 0., + 0., 0.001, 0., 0., 0., 0., 0., 0.001, 0., 0., 0., 0., 0., 0.001; auto covPtr = std::make_unique<const ActsSymMatrixD<5>>(cov); diff --git a/Tests/Core/Fitter/SequentialKalmanFitterTests.cpp b/Tests/Core/Fitter/SequentialKalmanFitterTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0623d247b6ac2375a5afcffe74d87ea17c5eebc0 --- /dev/null +++ b/Tests/Core/Fitter/SequentialKalmanFitterTests.cpp @@ -0,0 +1,120 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2016-2018 Acts project team +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#define BOOST_TEST_MODULE SequentialKalmanFitter Tests + +#include <boost/test/included/unit_test.hpp> + +#include <algorithm> +#include <math.h> +#include <random> +#include <vector> +#include "Acts/Detector/TrackingGeometry.hpp" +#include "Acts/EventData/Measurement.hpp" +#include "Acts/EventData/TrackParameters.hpp" +#include "Acts/EventData/TrackState.hpp" +#include "Acts/Extrapolator/Navigator.hpp" +#include "Acts/Fitter/GainMatrixSmoother.hpp" +#include "Acts/Fitter/GainMatrixUpdator.hpp" +#include "Acts/Fitter/SequentialKalmanFitter.hpp" +#include "Acts/MagneticField/ConstantBField.hpp" +#include "Acts/Propagator/EigenStepper.hpp" +#include "Acts/Propagator/Propagator.hpp" +#include "Acts/Tests/CommonHelpers/CubicTrackingGeometry.hpp" +#include "Acts/Tests/CommonHelpers/MeasurementCreatorHelper.hpp" + +namespace Acts { +namespace Test { + + struct HitSelector + { + public: + HitSelector(std::map<const Surface*, + std::vector<FittableMeasurement<Identifier>>> map) + : m_measurementMap(std::move(map)) + { + } + + template <typename propagator_state_t> + std::vector<TrackState> + operator()(const Surface& surface, const propagator_state_t& /*state*/) + { + const auto& measurements = m_measurementMap[&surface]; + if (measurements.empty()) { + return {}; + } + + std::vector<TrackState> trackStates; + for (const auto& measurement : measurements) { + trackStates.emplace_back(measurement); + } + + return trackStates; + } + + private: + std::map<const Surface*, std::vector<FittableMeasurement<Identifier>>> + m_measurementMap; + }; + + /// + /// @brief Unit test for Kalman fitter with measurements along the x-axis + /// + BOOST_AUTO_TEST_CASE(sequential_kalman_fitter_zero_field) + { + CubicTrackingGeometry geom; + auto detector = geom(); + + StraightLineStepper mStepper; + Vector3D startPosition(-3. * units::_m, 0., 0.); + Vector3D startMomentum(1. * units::_GeV, 0., 0); + + auto measurementMap + = createMeasurementsOnCubicDetector( + detector, mStepper, startPosition, startMomentum, true) + .measurementMap; + + // The KalmanFitter - we use the eigen stepper for covariance transport + // Build navigator for the measurement creatoin + Navigator rNavigator(detector); + rNavigator.resolvePassive = false; + rNavigator.resolveMaterial = true; + rNavigator.resolveSensitive = true; + + // Configure propagation with deactivated B-field + using RecoStepper = EigenStepper<ConstantBField>; + RecoStepper rStepper; + using RecoPropagator = Propagator<RecoStepper, Navigator>; + RecoPropagator rPropagator(rStepper, rNavigator); + + // Set initial parameters for the particle track + ActsSymMatrixD<5> cov; + cov << 1. * units::_um, 0., 0., 0., 0., 0., 1. * units::_um, 0., 0., 0., 0., + 0., 0.001, 0., 0., 0., 0., 0., 0.001, 0., 0., 0., 0., 0., 0.001; + + auto covPtr = std::make_unique<const ActsSymMatrixD<5>>(cov); + + SingleCurvilinearTrackParameters<ChargedPolicy> rStart( + std::move(covPtr), startPosition, startMomentum, 1.); + + using Updator = GainMatrixUpdator<BoundParameters>; + using KalmanFitter = SequentialKalmanFitter<RecoPropagator, + TrackState, + HitSelector, + Updator>; + + KalmanFitter fitter(std::move(rPropagator)); + auto testFit = fitter.fit( + rStart, std::make_shared<HitSelector>(std::move(measurementMap))); + + BOOST_TEST(testFit.resultStateList.size() == 6); + BOOST_TEST(testFit.numberOfHoles == 0); + } + +} // namespace Test +} // namespace Acts