ATLAS Offline Software
Loading...
Searching...
No Matches
Chi2Fitter.h
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4#ifndef MUONTRACKFINDINGTOOLS_FITTER_H
5#define MUONTRACKFINDINGTOOLS_FITTER_H
6
7#include "Acts/Definitions/Algebra.hpp"
8#include "Acts/Definitions/Common.hpp"
9#include "Acts/EventData/MeasurementHelpers.hpp"
10#include "Acts/EventData/MultiTrajectory.hpp"
11#include "Acts/EventData/SourceLink.hpp"
12#include "Acts/EventData/TrackProxyConcept.hpp"
13#include "Acts/EventData/Types.hpp"
14#include "Acts/EventData/VectorMultiTrajectory.hpp"
15#include "Acts/EventData/VectorTrackContainer.hpp"
16#include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp"
17#include "Acts/Geometry/GeometryContext.hpp"
18#include "Acts/Geometry/TrackingVolume.hpp"
19#include "Acts/MagneticField/MagneticFieldContext.hpp"
20#include "Acts/Material/Interactions.hpp"
21#include "Acts/Propagator/DirectNavigator.hpp"
22#include "Acts/Propagator/PropagatorOptions.hpp"
23#include "Acts/Propagator/StandardAborters.hpp"
24#include "Acts/Propagator/detail/PointwiseMaterialInteraction.hpp"
25#include "Acts/TrackFitting/GlobalChiSquareFitterError.hpp"
26#include "Acts/TrackFitting/detail/VoidFitterComponents.hpp"
27#include "Acts/Utilities/CalibrationContext.hpp"
28#include "Acts/Utilities/Delegate.hpp"
29#include "Acts/Utilities/Logger.hpp"
30#include "Acts/Utilities/Result.hpp"
31#include "Acts/Utilities/TrackHelpers.hpp"
32
33#include <functional>
34#include <limits>
35#include <memory>
36#include <type_traits>
37#include <unordered_map>
38
39using namespace Acts;
41
43struct Gx2fSystem;
46
47namespace Gx2fConstants {
48constexpr std::string_view gx2fnUpdateColumn = "Gx2fnUpdateColumn";
49
50// Mask for the track states. We don't need Predicted and Filtered
51constexpr TrackStatePropMask trackStateMask = TrackStatePropMask::Smoothed |
52 TrackStatePropMask::Jacobian |
53 TrackStatePropMask::Calibrated;
54
55} // namespace Gx2fConstants
56
72 constexpr bool isValid() const { return m_isValid; }
73
76 constexpr double lostEnergy() const { return m_lostEnergy; }
79 constexpr double lostSigma() const { return m_lossSigma; }
80
82 ELossAtSurface() = default;
83
87 explicit ELossAtSurface(const double energy, const double sigma) noexcept
88 : m_isValid{std::min(energy, sigma) > Acts::s_epsilon},
89 m_lostEnergy{energy},
90 m_lossSigma{sigma} {}
91
93 ELossAtSurface& operator+=(const ELossAtSurface& other) noexcept {
94 if (&other != this && other.isValid()) {
95 m_lostEnergy += other.m_lostEnergy;
96 m_lossSigma = Acts::fastHypot(m_lossSigma, other.lostSigma());
97 m_isValid = true;
98 }
99 return *this;
100 }
101
111 static ELossAtSurface invalidELoss(const GeometryContext& geoContext,
112 const CalibrationContext& calibContext,
113 const Vector3& pos, const Vector3& dir,
114 const Surface& matSurface);
115
116 private:
118 bool m_isValid{false};
120 double m_lostEnergy{0.};
122 double m_lossSigma{0.};
123 };
124
128 constexpr bool isValid() const { return m_isValid; }
130 constexpr double sigma() const { return m_sigma; }
135 explicit ScatteringAtSurface(const double sigma)
136 : m_isValid{sigma > Acts::s_epsilon}, m_sigma{sigma} {}
137
139 if (&other != this && other.isValid()) {
140 m_sigma += other.sigma();
141 m_isValid = true;
142 }
143
144 return *this;
145 }
146
147 bool m_isValid{false};
150 double m_sigma{0.};
151 };
152
163
166 constexpr bool materialIsValid() const {
167 return m_scatterer.isValid() || m_eloss.isValid();
168 }
169
172 constexpr std::size_t nDim() const {
173 return (m_scatterer.isValid() ? 2ul : 0ul) +
174 (m_eloss.isValid() ? 1ul : 0ul);
175 }
176
177 const ScatteringAtSurface& scatterer() const { return m_scatterer; }
178
179 const ELossAtSurface& energyLoss() const { return m_eloss; }
180
181 constexpr double deltaTheta() const { return m_scatTheta; }
182
183 constexpr double deltaPhi() const { return m_scatPhi; }
184
185 constexpr double lostEnergy() const { return m_lostEnergy; }
186
187 constexpr double qOverPderiv() const { return m_qOverPderiv; }
188
189 constexpr double qOverPratio() const { return m_qOverPratio; }
190
192 // subtract the energy loss from the q/p parameters
195 void updateTrackParameters(BoundTrackParameters& trackPars);
196
197 void contributionToGx2fSums(Gx2fSystem& extendedSystem, const double theta,
198 const std::size_t firstParamIdx,
199 const Logger& logger) const;
200
201 void updateParameters(const Eigen::VectorXd& deltaParamsExtended,
202 const std::size_t firstParamIdx);
203
204 private:
209
213
215 double m_lostEnergy{0.};
217 double m_scatTheta{0.};
219 double m_scatPhi{0.};
221 double m_qOverPderiv{0.};
222 // qoverP ratio after and before the energy loss
223 double m_qOverPratio{1.};
224};
225
227template <typename traj_t>
230 using TrackStateProxy = typename MultiTrajectory<traj_t>::TrackStateProxy;
233 typename MultiTrajectory<traj_t>::ConstTrackStateProxy;
235 using Parameters = typename TrackStateProxy::Parameters;
236
239 Delegate<void(const GeometryContext&, const CalibrationContext&,
240 const SourceLink&, TrackStateProxy)>;
241
243 using OutlierFinder = Delegate<bool(ConstTrackStateProxy)>;
244
254 const GeometryContext& geoContext, const CalibrationContext& calibContext,
255 const Vector3& pos, const Vector3& dir, const Surface& matSurf)>;
256
268
272
274 SourceLinkSurfaceAccessor surfaceAccessor;
275
278 calibrator.template connect<&Acts::detail::voidFitterCalibrator<traj_t>>();
279 outlierFinder.template connect<&Acts::detail::voidOutlierFinder<traj_t>>();
280 surfaceAccessor.connect<&Acts::detail::voidSurfaceAccessor>();
283 }
284};
285
289template <typename traj_t>
304 Gx2FitterOptions(const GeometryContext& gctx,
305 const MagneticFieldContext& mctx,
306 std::reference_wrapper<const CalibrationContext> cctx,
307 Gx2FitterExtensions<traj_t> extensions_,
308 const PropagatorPlainOptions& pOptions)
309 : geoContext(gctx),
310 magFieldContext(mctx),
311 calibrationContext(cctx),
312 extensions(std::move(extensions_)),
313 propagatorPlainOptions(pOptions){}
314
315
317 std::reference_wrapper<const GeometryContext> geoContext;
319 std::reference_wrapper<const MagneticFieldContext> magFieldContext;
321 std::reference_wrapper<const CalibrationContext> calibrationContext;
322
325
327 PropagatorPlainOptions propagatorPlainOptions;
328
331
333 bool doEnergyLoss = false;
334
339
345 double scatteringCutOff = 0.;
348 double elossCutOff = 0.;
349
352 FreeToBoundCorrection freeToBoundCorrection{false};
353
355 std::size_t nUpdateMax = 5;
356
358 double relChi2changeCutOff = 1e-5;
359};
360
362template <typename traj_t>
365 traj_t* fittedStates{nullptr};
366
369 std::unordered_map<GeometryIdentifier, Gx2fMaterialProperties> materialMap{};
370
376
381 std::size_t lastMeasurementIndex = Acts::kTrackIndexInvalid;
382
387 std::size_t lastTrackIndex = Acts::kTrackIndexInvalid;
388
390 std::size_t measurementStates{0ul};
391
396 std::size_t measurementHoles{0ul};
397
399 std::size_t processedStates{0ul};
400
402 std::size_t processedMeasurements{0ul};
403
405 std::vector<std::size_t> materialParamIndices{eBoundSize};
406
408 bool finished = false;
409
411 std::vector<const Surface*> missedActiveSurfaces;
412
414 std::size_t surfaceCount = 0;
415};
416
425 explicit Gx2fSystem(std::size_t nDims) : m_nDims{nDims} {}
426
429 std::size_t nDims() const { return m_nDims; }
430
433 double chi2() const { return m_chi2; }
434
437 double& chi2() { return m_chi2; }
438
441 const Eigen::MatrixXd& aMatrix() const { return m_aMatrix; }
442
445 Eigen::MatrixXd& aMatrix() { return m_aMatrix; }
446
449 const Eigen::VectorXd& bVector() const { return m_bVector; }
450
453 Eigen::VectorXd& bVector() { return m_bVector; }
454
457 std::size_t ndf() const { return m_ndf; }
458
461 std::size_t& ndf() { return m_ndf; }
462
472 std::size_t findRequiredNdf() {
473 std::size_t ndfSystem = 0;
474 if (m_aMatrix(4, 4) == 0) {
475 ndfSystem = 4;
476 } else if (m_aMatrix(5, 5) == 0) {
477 ndfSystem = 5;
478 } else {
479 ndfSystem = 6;
480 }
481
482 return ndfSystem;
483 }
484
487 bool isWellDefined() { return m_ndf > findRequiredNdf(); }
488
489 private:
491 std::size_t m_nDims;
492
494 double m_chi2 = 0.;
495
497 Eigen::MatrixXd m_aMatrix{Eigen::MatrixXd::Zero(m_nDims, m_nDims)};
498
500 Eigen::VectorXd m_bVector{Eigen::VectorXd::Zero(m_nDims)};
501
503 std::size_t m_ndf = 0u;
504};
505
523 Gx2fSystem& extendedSystem,
524 const std::vector<BoundMatrix>& jacobianFromStart,
525 const std::vector<std::size_t>& materialIndices,
526 const std::vector<double>& qOverPderiv,
527 const Eigen::MatrixXd& covarianceMeasurement, const BoundVector& predicted,
528 const Eigen::VectorXd& measurement, const Eigen::MatrixXd& projector,
529 const Logger& logger);
530
544template <std::size_t kMeasDim, typename track_state_t>
546 const std::vector<BoundMatrix>& jacobianFromStart,
547 const std::vector<std::size_t>& materialIndices,
548 const std::vector<double>& qOverPderiv,
549 const track_state_t& trackState,
550 const Logger& logger) {
551 const SquareMatrix<kMeasDim> covarianceMeasurement =
552 trackState.template calibratedCovariance<kMeasDim>();
553
554 const BoundVector predicted = trackState.smoothed();
555
556 const Vector<kMeasDim> measurement =
557 trackState.template calibrated<kMeasDim>();
558
559 const Matrix<kMeasDim, eBoundSize> projector =
560 trackState.template projectorSubspaceHelper<kMeasDim>().projector();
561
563 extendedSystem, jacobianFromStart, materialIndices, qOverPderiv,
564 covarianceMeasurement, predicted, measurement, projector, logger);
565}
566
581template <TrackProxyConcept track_proxy_t>
583 const track_proxy_t track, Gx2fSystem& extendedSystem,
584 const std::unordered_map<GeometryIdentifier, Gx2fMaterialProperties>&
585 materialMap,
586 const std::vector<std::size_t>& materialParamIndices,
587 std::vector<GeometryIdentifier>& geoIdVector, const Logger& logger) {
588 std::vector<BoundMatrix> jacobianFromStart;
589 jacobianFromStart.emplace_back(BoundMatrix::Identity());
590 geoIdVector.reserve(track.nTrackStates());
591 std::vector<double> qOverPderiv;
592
593 bool foundELoss{false};
594 for (const auto& trackState : track.trackStates()) {
595 // Get and store geoId for the current surface
596 const GeometryIdentifier geoId = trackState.referenceSurface().geometryId();
597 ACTS_DEBUG("Start to investigate trackState on surface " << geoId);
598 const auto typeFlags = trackState.typeFlags();
599
600 // update all Jacobians from start for every track state
601 for (auto& jac : jacobianFromStart) {
602 jac = trackState.jacobian() * jac;
603 }
604
605 // Skip the hole states
606 if (!typeFlags.hasMeasurement() && !typeFlags.hasMaterial()) {
607 ACTS_DEBUG(" Skip state.");
608 continue;
609 }
610
611 // Handle measurement
612 if (typeFlags.hasMeasurement()) {
613 ACTS_DEBUG(" Handle measurement. ");
614
615 const auto measDim = trackState.calibratedSize();
616
617 if (measDim < 1 || 6 < measDim) {
618 ACTS_ERROR("Can not process state with measurement with "
619 << measDim << " dimensions.");
620 throw std::domain_error(
621 "Found measurement with less than 1 or more than 6 dimension(s).");
622 }
623
624 extendedSystem.ndf() += measDim;
625
626 visit_measurement(measDim, [&](auto N) {
627 addMeasurementToGx2fSums<N>(extendedSystem, jacobianFromStart,
628 materialParamIndices, qOverPderiv,
629 trackState, logger);
630 });
631 }
632
633 // Handle material
634 if (typeFlags.hasMaterial()) {
635 ACTS_DEBUG(" Handle material");
636 const auto materialMapId = materialMap.find(geoId);
637 if (materialMapId == materialMap.end()) {
638 ACTS_ERROR("No material properties found for surface " << geoId);
639 throw std::runtime_error("No material properties found for surface.");
640 }
642 if (materialMapId->second.nDim() == 0ul) {
643 continue;
644 }
645 if (materialMapId->second.energyLoss().isValid()) {
646 ACTS_VERBOSE("The material surface carries energy loss");
647 foundELoss = true;
648 }
649 const std::size_t deltaPosition =
650 materialParamIndices.at(geoIdVector.size());
651 geoIdVector.push_back(geoId);
652
653 materialMapId->second.contributionToGx2fSums(
654 extendedSystem, trackState.smoothed()[eBoundTheta], deltaPosition,
655 logger);
656
657 // after material surfaces, the measurements depend on the updated qoverp
658 // we need to scale the jacobian term for the qoverp row
659 if (foundELoss) {
660 ACTS_DEBUG(" Update Jacobian for q/p after material surface "
661 << geoId << " with ratio " << materialMapId->second.qOverPratio());
662 jacobianFromStart.front().row(eBoundQOverP) *=
663 materialMapId->second.qOverPratio();
664 }
665 // Add for this material a new Jacobian, starting from this surface.
666 jacobianFromStart.emplace_back(BoundMatrix::Identity());
667 qOverPderiv.push_back(materialMapId->second.qOverPderiv());
668 }
669 }
670}
671
680Eigen::VectorXd computeGx2fDeltaParams(const Gx2fSystem& extendedSystem);
681
690 BoundTrackParameters& params, const Eigen::VectorXd& deltaParamsExtended,
691 std::unordered_map<GeometryIdentifier, Gx2fMaterialProperties>& materialMap,
692 const std::vector<GeometryIdentifier>& geoIdVector);
693
704void updateGx2fCovarianceParams(BoundMatrix& fullCovariancePredicted,
705 Gx2fSystem& extendedSystem);
706
712template <typename propagator_t, typename traj_t>
715 using Gx2fNavigator = typename propagator_t::Navigator;
716
717 static constexpr auto kInvalid = kTrackIndexInvalid;
718
719 public:
728 explicit Gx2Fitter(const propagator_t& pPropagator,
729 std::unique_ptr<const Logger> log =
730 getDefaultLogger("Gx2Fitter", Logging::INFO))
731 : m_propagator(pPropagator), m_logger{std::move(log)} {}
732
733 private:
735 propagator_t m_propagator;
736
738 std::unique_ptr<const Logger> m_logger;
739 std::unique_ptr<const Logger> m_actorLogger{
740 m_logger->cloneWithSuffix("Actor")};
741 std::unique_ptr<const Logger> m_addToSumLogger{
742 m_logger->cloneWithSuffix("AddToSum")};
743
744 const Logger& logger() const { return *m_logger; }
745
754 class GX2FActor {
755 public:
758
759 using TrackStateProxy_t = typename traj_t::TrackStateProxy;
760
762 const std::unordered_map<const Surface*, SourceLink>* inputMeasurements{};
763
766
768 bool doEnergyLoss = false;
769
770 double scatteringCutOff = 0.;
774 double elossCutOff = 0.;
775
778 FreeToBoundCorrection freeToBoundCorrection;
780 const Logger* actorLogger{nullptr};
781
783 const Logger& logger() const { return *actorLogger; }
784
786
788 const CalibrationContext* calibrationContext{nullptr};
789
792 const BoundTrackParameters* startParameters = nullptr;
793
804 template <typename propagator_state_t, typename stepper_t,
805 typename navigator_t>
806 Result<void> act(propagator_state_t& state, const stepper_t& stepper,
807 const navigator_t& navigator, result_type& result,
808 const Logger& /*logger*/) const {
809 assert(result.fittedStates && "No MultiTrajectory set");
810
811 // Check if we can stop to propagate
812 if (result.measurementStates == inputMeasurements->size()) {
813 ACTS_DEBUG("Actor: finish: All measurements have been found.");
814 result.finished = true;
815 } else if (state.navigation.navigationBreak) {
816 ACTS_DEBUG("Actor: finish: navigationBreak.");
817 result.finished = true;
818 }
819
820 // End the propagation and return to the fitter
821 if (result.finished) {
822 // Remove the missing surfaces that occur after the last measurement
823 if (result.measurementStates > 0) {
824 result.missedActiveSurfaces.resize(result.measurementHoles);
825 }
826
827 return Result<void>::success();
828 }
829
830 // We are only interested in surfaces. If we are not on a surface, we
831 // continue the navigation
832 const Surface* surface = navigator.currentSurface(state.navigation);
833 if (surface == nullptr) {
834 return Result<void>::success();
835 }
836
837 ++result.surfaceCount;
838 const GeometryIdentifier geoId = surface->geometryId();
839 ACTS_DEBUG("Surface "
840 << geoId << ", " << surface->bounds() << " detected. "
841 << "Sensitive: " << (surface->isSensitive() ? "yes" : "no")
842 << ", has material: "
843 << (surface->hasMaterial() ? "yes" : "no"));
844
845 auto matFillResult = fillMaterialMap(state, stepper, navigator, result);
846 if (!matFillResult.ok()) {
847 return matFillResult.error();
848 }
849
850 auto trackStateResult = makeTrackState(state, stepper, navigator, result);
851 if (!trackStateResult.ok()) {
852 return trackStateResult.error();
853 }
854 // Here we handle all measurements
855 if (auto sourceLinkIt = inputMeasurements->find(surface);
856 sourceLinkIt != inputMeasurements->end()) {
857 // We count the processed measurement
858 ++result.processedMeasurements;
859
860 result.lastMeasurementIndex = result.lastTrackIndex;
861
862 // TODO check for outlier first
863 // We count the state with measurement
864 ++result.measurementStates;
865
866 // We count the processed state
867 ++result.processedStates;
868
869 // Update the number of holes count only when encountering a
870 // measurement
871 result.measurementHoles = result.missedActiveSurfaces.size();
872
873 return Result<void>::success();
874 }
875
876 ACTS_DEBUG(" The surface contains no measurement/material/hole.");
877 return Result<void>::success();
878 }
879
880 template <typename propagator_state_t, typename stepper_t,
881 typename navigator_t>
882 Result<void> makeTrackState(propagator_state_t& state,
883 const stepper_t& stepper,
884 const navigator_t& navigator,
885 result_type& result) const {
886 const Surface* surface = navigator.currentSurface(state.navigation);
887
888 const GeometryIdentifier geoId = surface->geometryId();
889
890 auto sourceLinkIt = inputMeasurements->find(surface);
891 auto materialItr = result.materialMap.find(geoId);
892 const bool isSensitive =
893 surface->isSensitive() || sourceLinkIt != inputMeasurements->end();
894 const bool hasMaterial = surface->hasMaterial() &&
895 materialItr != result.materialMap.end() &&
896 materialItr->second.materialIsValid();
898 if (!isSensitive && !hasMaterial) {
899 return Result<void>::success();
900 }
901
902 auto& fittedStates = *result.fittedStates;
903
904 // Add a <trackStateMask> TrackState entry multi trajectory. This
905 // allocates storage for all components, which we will set later.
906 TrackStateProxy_t trackStateProxy = fittedStates.makeTrackState(
907 Gx2fConstants::trackStateMask, result.lastTrackIndex);
908
909 result.lastTrackIndex = trackStateProxy.index();
910
911 auto typeFlags = trackStateProxy.typeFlags();
912 typeFlags.setHasParameters();
913
914 // Transport the covariance to the surface
915 auto transportRes = stepper.transportToBound(
916 state.stepping, *surface, freeToBoundCorrection);
917 if (!transportRes.ok()) {
918 return transportRes.error();
919 }
920
921 // Set the trackStateProxy components with the state from the ongoing
922 // propagation
923 trackStateProxy.setReferenceSurface(surface->getSharedPtr());
924
925 // Not const since, we might need to update with scattering angles
926 auto res = stepper.boundParameters(state.stepping, *surface);
927 if (!res.ok()) {
928 return res.error();
929 }
930 // Not const since, we might need to update with scattering angles
931 auto& boundParams = *res;
932
933 trackStateProxy.smoothedCovariance() = stepper.covariance(state.stepping).value();
934 trackStateProxy.jacobian() = *transportRes;
935 trackStateProxy.pathLength() = stepper.pathLength(state.stepping);
936
938 if (hasMaterial) {
939 typeFlags.setHasMaterial();
940
941 // track
942 ACTS_DEBUG(" Update parameters with material effects.");
943 ACTS_VERBOSE(" boundParams before the update: "
944 << boundParams.parameters().transpose());
945
946 materialItr->second.updateTrackParameters(boundParams);
947 ACTS_VERBOSE(" boundParams after the update: "
948 << boundParams.parameters().transpose());
949
950 stepper.update(state.stepping,
951 transformBoundToFreeParameters(
952 trackStateProxy.referenceSurface(), state.geoContext,
953 boundParams.parameters()),
954 boundParams.parameters(),
955 trackStateProxy.smoothedCovariance(), *surface);
956 }
957
958 // Fill the track state
959 trackStateProxy.smoothed() = boundParams.parameters();
960 if (!isSensitive) {
961 return Result<void>::success();
962 }
963
964 if (sourceLinkIt == inputMeasurements->end()) {
965 typeFlags.setIsHole();
966 return Result<void>::success();
967 }
968 typeFlags.setIsMeasurement();
969
970 // We have smoothed parameters, so calibrate the uncalibrated input
971 // measurement
972 extensions.calibrator(state.geoContext, *calibrationContext,
973 sourceLinkIt->second, trackStateProxy);
974
975 return Result<void>::success();
976 }
977
981
986
987 template <typename propagator_state_t, typename stepper_t,
988 typename navigator_t>
989 Result<void> fillMaterialMap(propagator_state_t& state,
990 const stepper_t& stepper,
991 const navigator_t& navigator,
992 result_type& result) const {
994 ACTS_DEBUG(
995 __func__
996 << " | "
997 << "Multiple scattering and energy loss are disabled. Returning");
998 return Result<void>::success();
999 }
1000 const Surface* surface = navigator.currentSurface(state.navigation);
1001 if (!surface->hasMaterial()) {
1002 return Result<void>::success();
1003 }
1004
1005 // Found material - add a scatteringAngles entry if not done yet and
1006 // energy loss if enabled. Handling will happen later
1007 ACTS_DEBUG(" The surface contains material ...");
1008
1009 const GeometryIdentifier geoId = surface->geometryId();
1010 auto materialMapId = result.materialMap.find(geoId);
1012
1013 if (materialMapId != result.materialMap.end()) {
1014 ACTS_VERBOSE(" .... entry is already in the material map.");
1015 if (materialMapId->second.nDim() == 0ul) {
1016 ACTS_VERBOSE(" Material surface "
1017 << geoId
1018 << " has zero dimension - do not keep the index.");
1019 return Result<void>::success();
1020 }
1021 result.materialParamIndices.push_back(
1022 result.materialParamIndices.back() + materialMapId->second.nDim());
1023 return Result<void>::success();
1024 }
1025
1026 ACTS_DEBUG(" ... create entry in material map.");
1027
1028 const Result<MaterialSlab> slabResult =
1029 Acts::detail::evaluateMaterialSlab(
1030 state, stepper, *surface,
1031 Acts::detail::determineMaterialUpdateMode(
1032 state, navigator, MaterialUpdateMode::FullUpdate));
1033 if (!slabResult.ok()) {
1034 ACTS_DEBUG(__func__ << " | "
1035 << "Failed to evaluate material slab: "
1036 << slabResult.error());
1037 return Result<void>::failure(slabResult.error());
1038 }
1039 const MaterialSlab& slab = *slabResult;
1040 const bool goodSlab = !slab.isVacuum();
1041
1042 using ScatteringAtSurface = Gx2fMaterialProperties::ScatteringAtSurface;
1043 using ELossAtSurface = Gx2fMaterialProperties::ELossAtSurface;
1044
1045 ELossAtSurface eLoss{};
1046 ScatteringAtSurface scatterer{};
1047
1048 if (goodSlab) {
1049 const auto& particle = startParameters->particleHypothesis();
1050
1051 const double sigma =
1052 static_cast<double>(Acts::computeMultipleScatteringTheta0(
1053 slab, particle.absolutePdg(), particle.mass(),
1054 static_cast<float>(startParameters->parameters()[eBoundQOverP]),
1055 particle.absoluteCharge()));
1056 ACTS_VERBOSE(" The Highland formula gives sigma = " << sigma);
1057
1058 scatterer = ScatteringAtSurface{sigma};
1059
1060 } else {
1061 ACTS_VERBOSE(" Material slab is not valid.");
1062 }
1063
1064 if (doEnergyLoss) {
1065 eLoss = extensions.elossAccumulator(
1066 state.geoContext, *calibrationContext,
1067 stepper.position(state.stepping), stepper.direction(state.stepping),
1068 *surface);
1071 if (!eLoss.isValid() && goodSlab) {
1072 const auto& particle = startParameters->particleHypothesis();
1073
1074 const double qOverP = stepper.qOverP(state.stepping);
1075 ACTS_VERBOSE("The particle has momentum "
1076 << (1. / qOverP) * particle.absoluteCharge()
1077 << " and charge " << particle.absoluteCharge());
1078
1079 eLoss = ELossAtSurface{
1080 computeEnergyLossMean(slab, particle.absolutePdg(),
1081 particle.mass(), qOverP,
1082 particle.absoluteCharge()),
1084 static_cast<double>(Acts::computeEnergyLossLandauSigma(
1085 slab, particle.mass(), static_cast<float>(qOverP),
1086 particle.absoluteCharge()))};
1087
1088 ACTS_VERBOSE(" The Bethe-Bloch formula gives energy loss "
1089 << eLoss.lostEnergy() << " with uncertainty "
1090 << eLoss.lostSigma());
1091 }
1092 }
1093
1095 scatterer += result.accScatterer;
1096 eLoss += result.accEloss;
1097 if (scatterer.sigma() < scatteringCutOff) {
1098 result.accScatterer = std::move(scatterer);
1099 scatterer = ScatteringAtSurface{};
1100 } else {
1101 result.accScatterer = ScatteringAtSurface{};
1102 }
1103
1104 if (eLoss.lostEnergy() < elossCutOff) {
1105 result.accEloss = std::move(eLoss);
1106 eLoss = ELossAtSurface{};
1107 } else {
1108 result.accEloss = ELossAtSurface{};
1109 }
1110 ACTS_DEBUG(" ... add entry in material map with scattering "
1111 << scatterer.sigma() << " and energy loss "
1112 << eLoss.lostEnergy());
1113 materialMapId =
1114 result.materialMap
1115 .emplace(geoId, Gx2fMaterialProperties{std::move(scatterer),
1116 std::move(eLoss)})
1117 .first;
1118 if (materialMapId->second.nDim() == 0ul) {
1119 ACTS_VERBOSE(" Material surface "
1120 << geoId
1121 << " has zero dimension - do not keep the index.");
1122 return Result<void>::success();
1123 }
1124 result.materialParamIndices.push_back(result.materialParamIndices.back() +
1125 materialMapId->second.nDim());
1126
1127 return Result<void>::success();
1128 }
1129
1130 template <typename propagator_state_t, typename stepper_t,
1131 typename navigator_t, typename result_t>
1132 bool checkAbort(propagator_state_t& /*state*/, const stepper_t& /*stepper*/,
1133 const navigator_t& /*navigator*/, const result_t& result,
1134 const Logger& /*logger*/) const {
1135 return result.finished;
1136 }
1137 };
1138
1139 public:
1156 template <typename source_link_iterator_t,
1157 TrackContainerFrontend track_container_t>
1159 source_link_iterator_t it, source_link_iterator_t end,
1160 const BoundTrackParameters& sParameters,
1161 const Gx2FitterOptions<traj_t>& gx2fOptions,
1162 track_container_t& trackContainer) const
1163 requires(!std::is_same_v<Gx2fNavigator, DirectNavigator>)
1164 {
1165 // Preprocess Measurements (SourceLinks -> map)
1166 // To be able to find measurements later, we put them into a map.
1167 // We need to copy input SourceLinks anyway, so the map can own them.
1168 ACTS_VERBOSE("Preparing " << std::distance(it, end)
1169 << " input measurements");
1170 std::unordered_map<const Surface*, SourceLink> inputMeasurements{};
1171
1172 for (; it != end; ++it) {
1173 inputMeasurements.try_emplace(gx2fOptions.extensions.surfaceAccessor(*it),
1174 *it);
1175 }
1176
1177 using GX2FResult = typename GX2FActor::result_type;
1178 using Actors = Acts::ActorList<GX2FActor>;
1179
1180 using PropagatorOptions = typename propagator_t::template Options<Actors>;
1181
1182 BoundTrackParameters params = sParameters;
1183 params.covariance() = BoundMatrix::Identity();
1184 // params.
1185 double chi2sum = 0;
1186 double oldChi2sum = std::numeric_limits<double>::max();
1187
1188 // We need to create a temporary track container. We create several times a
1189 // new track and delete it after updating the parameters. However, if we
1190 // would work on the externally provided track container, it would be
1191 // difficult to remove the correct track, if it contains more than one.
1192 typename track_container_t::TrackContainerBackend trackContainerTempBackend;
1193 traj_t trajectoryTempBackend;
1194 TrackContainer trackContainerTemp{trackContainerTempBackend,
1195 trajectoryTempBackend};
1196
1197 // Create an index of the 'tip' of the track stored in multitrajectory. It
1198 // is needed outside the update loop. It will be updated with each iteration
1199 // and used for the final track
1200 std::size_t tipIndex = kInvalid;
1201
1202 // The materialMap stores for each visited surface their scattering
1203 // properties
1204 std::unordered_map<GeometryIdentifier, Gx2fMaterialProperties> materialMap;
1205
1206 // This will be filled during the updates with the final covariance of the
1207 // track parameters.
1208 BoundMatrix fullCovariancePredicted = BoundMatrix::Identity();
1209
1210 ACTS_VERBOSE("Initial parameters: " << params.parameters().transpose());
1211
1213 ACTS_DEBUG("Start to iterate");
1214
1215 // Iterate the fit and improve result. Abort after n steps or after
1216 // convergence.
1217 // nUpdate is initialized outside to save its state for the track.
1218 std::size_t nUpdate = 0;
1219 for (nUpdate = 0; nUpdate < gx2fOptions.nUpdateMax; ++nUpdate) {
1220 ACTS_DEBUG("nUpdate = " << nUpdate + 1 << "/" << gx2fOptions.nUpdateMax);
1221
1222 // set up propagator and co
1223 PropagatorOptions propagatorOptions{gx2fOptions.propagatorPlainOptions};
1224
1225 // Add the measurement surface as external surface to the navigator.
1226 // We will try to hit those surface by ignoring boundary checks.
1227 for (const auto& [surface, _] : inputMeasurements) {
1228 propagatorOptions.navigation.registerMeasurementSurface(*surface);
1229 }
1230
1231 auto& gx2fActor = propagatorOptions.actorList.template get<GX2FActor>();
1232 gx2fActor.inputMeasurements = &inputMeasurements;
1233 if (gx2fOptions.includeMaterialInIter) {
1234 gx2fActor.doMultipleScattering = gx2fOptions.doMultipleScattering;
1235 gx2fActor.doEnergyLoss = gx2fOptions.doEnergyLoss;
1236 gx2fActor.scatteringCutOff = gx2fOptions.scatteringCutOff;
1237 gx2fActor.elossCutOff = gx2fOptions.elossCutOff;
1238 }
1239 gx2fActor.extensions = gx2fOptions.extensions;
1240 gx2fActor.calibrationContext = &gx2fOptions.calibrationContext.get();
1241 gx2fActor.actorLogger = m_actorLogger.get();
1242 gx2fActor.startParameters = &params;
1243
1244 auto propagatorState = m_propagator.makeState(propagatorOptions);
1245
1246 auto propagatorInitResult =
1247 m_propagator.initialize(propagatorState, params, nullptr);
1248 if (!propagatorInitResult.ok()) {
1249 ACTS_DEBUG("Propagation initialization failed: "
1250 << propagatorInitResult.error());
1251 return propagatorInitResult.error();
1252 }
1253
1254 auto& r = propagatorState.template get<Gx2FitterResult<traj_t>>();
1255 r.fittedStates = &trajectoryTempBackend;
1256 r.materialMap = std::move(materialMap);
1257
1258 // Clear the track container. It could be more performant to update the
1259 // existing states, but this needs some more thinking.
1260 trackContainerTemp.clear();
1261
1262 // Run the fitter
1263 auto propagationResult = m_propagator.propagate(propagatorState);
1264
1265 auto result =
1266 m_propagator.makeResult(std::move(propagatorState), propagationResult,
1267 propagatorOptions, false);
1268
1269 if (!result.ok()) {
1270 ACTS_DEBUG("Propagation failed: " << result.error());
1271 return result.error();
1272 }
1273
1274 // TODO Improve Propagator + Actor [allocate before loop], rewrite
1275 // makeMeasurements
1276 auto& propRes = *result;
1277 GX2FResult gx2fResult = std::move(propRes.template get<GX2FResult>());
1279 materialMap = std::move(gx2fResult.materialMap);
1280
1281 auto track = trackContainerTemp.makeTrack();
1282 tipIndex = gx2fResult.lastMeasurementIndex;
1283
1284 // It could happen, that no measurements were found. Then the track would
1285 // be empty and the following operations would be invalid. Usually, this
1286 // only happens during the first iteration, due to bad initial parameters.
1287 if (tipIndex == kInvalid) {
1288 ACTS_INFO("Did not find any measurements in nUpdate "
1289 << nUpdate + 1 << "/" << gx2fOptions.nUpdateMax);
1290 return Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements;
1291 }
1292
1293 track.tipIndex() = tipIndex;
1294 track.linkForward();
1295
1296 // System that we fill with the information gathered by the actor and
1297 // evaluate later
1298 Gx2fSystem extendedSystem{gx2fResult.materialParamIndices.back()};
1299
1300 // This vector stores the IDs for each visited material in order. We use
1301 // it later for updating the scattering angles. We cannot use
1302 // materialMap directly, since we cannot guarantee, that we will visit
1303 // all stored material in each propagation.
1304 std::vector<GeometryIdentifier> geoIdVector;
1305
1306 fillGx2fSystem(track, extendedSystem, materialMap,
1307 gx2fResult.materialParamIndices, geoIdVector,
1309
1310 chi2sum = extendedSystem.chi2();
1311
1312 // This check takes into account the evaluated dimensions of the
1313 // measurements. To fit, we need at least NDF+1 measurements. However, we
1314 // count n-dimensional measurements for n measurements, reducing the
1315 // effective number of needed measurements. We might encounter the case,
1316 // where we cannot use some (parts of a) measurements, maybe if we do not
1317 // support that kind of measurement. This is also taken into account here.
1318 // We skip the check during the first iteration, since we cannot guarantee
1319 // to hit all/enough measurement surfaces with the initial parameter
1320 // guess.
1321 // We skip the check during the first iteration, since we cannot guarantee
1322 // to hit all/enough measurement surfaces with the initial parameter
1323 // guess.
1324 if ((nUpdate > 0) && !extendedSystem.isWellDefined()) {
1325 ACTS_INFO("Not enough measurements. Require "
1326 << extendedSystem.findRequiredNdf() + 1 << ", but only "
1327 << extendedSystem.ndf() << " could be used.");
1328 return Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements;
1329 }
1330
1331 Eigen::VectorXd deltaParamsExtended =
1332 computeGx2fDeltaParams(extendedSystem);
1333
1334 ACTS_VERBOSE("aMatrix:\n"
1335 << extendedSystem.aMatrix() << "\n"
1336 << "bVector:\n"
1337 << extendedSystem.bVector() << "\n"
1338 << "deltaParamsExtended:\n"
1339 << deltaParamsExtended << "\n"
1340 << "oldChi2sum = " << oldChi2sum << "\n"
1341 << "chi2sum = " << extendedSystem.chi2());
1342
1343 if ((gx2fOptions.relChi2changeCutOff != 0) && (nUpdate > 0) &&
1344 (std::abs(extendedSystem.chi2() / oldChi2sum - 1) <
1345 gx2fOptions.relChi2changeCutOff)) {
1346 ACTS_DEBUG("Abort with relChi2changeCutOff after "
1347 << nUpdate + 1 << "/" << gx2fOptions.nUpdateMax
1348 << " iterations.");
1349 updateGx2fCovarianceParams(fullCovariancePredicted, extendedSystem);
1350 break;
1351 }
1352
1353 if (extendedSystem.chi2() > oldChi2sum + 1e-5) {
1354 ACTS_DEBUG("chi2 not converging monotonically in update " << nUpdate);
1355 }
1356
1357 // If this is the final iteration, update the covariance and break.
1358 // Otherwise, we would update the scattering angles too much.
1359 if (nUpdate == gx2fOptions.nUpdateMax - 1) {
1360 // Since currently most of our tracks converge in 4-5 updates, we want
1361 // to set nUpdateMax higher than that to guarantee convergence for most
1362 // tracks. In cases, where we set a smaller nUpdateMax, it's because we
1363 // want to investigate the behaviour of the fitter before it converges,
1364 // like in some unit-tests.
1365 if (gx2fOptions.nUpdateMax > 5) {
1366 ACTS_INFO("Did not converge in " << gx2fOptions.nUpdateMax
1367 << " updates.");
1368 return Experimental::GlobalChiSquareFitterError::DidNotConverge;
1369 }
1370
1371 updateGx2fCovarianceParams(fullCovariancePredicted, extendedSystem);
1372 break;
1373 }
1374
1375 updateGx2fParams(params, deltaParamsExtended, materialMap, geoIdVector);
1376 ACTS_VERBOSE("Updated parameters: " << params.parameters().transpose());
1377
1378 oldChi2sum = extendedSystem.chi2();
1379 }
1380 ACTS_DEBUG("Iterations finished");
1381 ACTS_VERBOSE("Final parameters: " << params.parameters().transpose());
1382 ACTS_VERBOSE("Final scattering angles and energy loss:");
1383 for (const auto& [key, value] : materialMap) {
1384 if (!value.materialIsValid()) {
1385 continue;
1386 }
1387 ACTS_VERBOSE(" ( " << value.deltaTheta() << " | " << value.deltaPhi()
1388 << "|" << value.lostEnergy() << " )");
1389 }
1391
1393 if (!gx2fOptions.includeMaterialInIter &&
1394 (gx2fOptions.doEnergyLoss || gx2fOptions.doMultipleScattering)) {
1395 ACTS_DEBUG("Start to evaluate material");
1396
1397 // Set up the propagator
1398 PropagatorOptions propagatorOptions{gx2fOptions.propagatorPlainOptions};
1399
1400 // Add the measurement surface as external surface to the navigator.
1401 // We will try to hit those surface by ignoring boundary checks.
1402 for (const auto& [surface, _] : inputMeasurements) {
1403 propagatorOptions.navigation.registerMeasurementSurface(*surface);
1404 }
1405
1406 auto& gx2fActor = propagatorOptions.actorList.template get<GX2FActor>();
1407 gx2fActor.inputMeasurements = &inputMeasurements;
1408 gx2fActor.doMultipleScattering = gx2fOptions.doMultipleScattering;
1409 gx2fActor.doEnergyLoss = gx2fOptions.doEnergyLoss;
1410 gx2fActor.extensions = gx2fOptions.extensions;
1411 gx2fActor.calibrationContext = &gx2fOptions.calibrationContext.get();
1412 gx2fActor.actorLogger = m_actorLogger.get();
1413 gx2fActor.startParameters = &params;
1414 gx2fActor.scatteringCutOff = gx2fOptions.scatteringCutOff;
1415 gx2fActor.elossCutOff = gx2fOptions.elossCutOff;
1416
1417 auto propagatorState = m_propagator.makeState(propagatorOptions);
1418
1419 auto propagatorInitResult =
1420 m_propagator.initialize(propagatorState, params, nullptr);
1421 if (!propagatorInitResult.ok()) {
1422 ACTS_DEBUG("Propagation initialization failed: "
1423 << propagatorInitResult.error());
1424 return propagatorInitResult.error();
1425 }
1426
1427 auto& r = propagatorState.template get<Gx2FitterResult<traj_t>>();
1428 r.fittedStates = &trajectoryTempBackend;
1429 r.materialMap = std::move(materialMap);
1430
1431 // Clear the track container. It could be more performant to update the
1432 // existing states, but this needs some more thinking.
1433 trackContainerTemp.clear();
1434
1435 // Run the fitter
1436 auto propagationResult = m_propagator.propagate(propagatorState);
1437
1438 auto result =
1439 m_propagator.makeResult(std::move(propagatorState), propagationResult,
1440 propagatorOptions, false);
1441
1442 if (!result.ok()) {
1443 ACTS_DEBUG("Propagation failed: " << result.error());
1444 return result.error();
1445 }
1446
1447 // TODO Improve Propagator + Actor [allocate before loop], rewrite
1448 // makeMeasurements
1449 auto& propRes = *result;
1450 GX2FResult gx2fResult = std::move(propRes.template get<GX2FResult>());
1451 materialMap = std::move(gx2fResult.materialMap);
1452 auto track = trackContainerTemp.makeTrack();
1453 tipIndex = gx2fResult.lastMeasurementIndex;
1454
1455 // It could happen, that no measurements were found. Then the track would
1456 // be empty and the following operations would be invalid. Usually, this
1457 // only happens during the first iteration, due to bad initial parameters.
1458 if (tipIndex == kInvalid) {
1459 ACTS_INFO("Did not find any measurements in material fit.");
1460 return Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements;
1461 }
1462
1463 track.tipIndex() = tipIndex;
1464 track.linkForward();
1465
1466 // System that we fill with the information gathered by the actor and
1467 // evaluate later
1468 Gx2fSystem extendedSystem{gx2fResult.materialParamIndices.back()};
1469
1470 // This vector stores the IDs for each visited material in order. We use
1471 // it later for updating the scattering angles. We cannot use
1472 // materialMap directly, since we cannot guarantee, that we will visit
1473 // all stored material in each propagation.
1474 std::vector<GeometryIdentifier> geoIdVector;
1475
1476 fillGx2fSystem(track, extendedSystem, materialMap,
1477 gx2fResult.materialParamIndices, geoIdVector,
1479
1480 chi2sum = extendedSystem.chi2();
1481
1482 // This check takes into account the evaluated dimensions of the
1483 // measurements. To fit, we need at least NDF+1 measurements. However, we
1484 // count n-dimensional measurements for n measurements, reducing the
1485 // effective number of needed measurements. We might encounter the case,
1486 // where we cannot use some (parts of a) measurements, maybe if we do not
1487 // support that kind of measurement. This is also taken into account here.
1488 // We skip the check during the first iteration, since we cannot guarantee
1489 // to hit all/enough measurement surfaces with the initial parameter
1490 // guess.
1491 if ((nUpdate > 0) && !extendedSystem.isWellDefined()) {
1492 ACTS_INFO("Not enough measurements. Require "
1493 << extendedSystem.findRequiredNdf() + 1 << ", but only "
1494 << extendedSystem.ndf() << " could be used.");
1495 return Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements;
1496 }
1497
1498 Eigen::VectorXd deltaParamsExtended =
1499 computeGx2fDeltaParams(extendedSystem);
1500
1501 ACTS_VERBOSE("aMatrix:\n"
1502 << extendedSystem.aMatrix() << "\n"
1503 << "bVector:\n"
1504 << extendedSystem.bVector() << "\n"
1505 << "deltaParamsExtended:\n"
1506 << deltaParamsExtended << "\n"
1507 << "oldChi2sum = " << oldChi2sum << "\n"
1508 << "chi2sum = " << extendedSystem.chi2());
1509
1510 chi2sum = extendedSystem.chi2();
1511
1512 updateGx2fParams(params, deltaParamsExtended, materialMap, geoIdVector);
1513 ACTS_VERBOSE("Updated parameters: " << params.parameters().transpose());
1514
1515 updateGx2fCovarianceParams(fullCovariancePredicted, extendedSystem);
1516
1517 ACTS_DEBUG("Finished to evaluate material");
1518 ACTS_VERBOSE("Final parameters after material: "
1519 << params.parameters().transpose());
1522
1523 ACTS_VERBOSE("Final scattering angles and energy loss:");
1524 for (const auto& [key, value] : materialMap) {
1525 if (!value.materialIsValid()) {
1526 continue;
1527 }
1528 ACTS_VERBOSE(" ( " << value.deltaTheta() << " | " << value.deltaPhi()
1529 << "|" << value.lostEnergy() << " )");
1530 }
1531 }
1532 ACTS_VERBOSE("Final covariance:\n" << fullCovariancePredicted);
1533
1534 // Propagate again with the final covariance matrix. This is necessary to
1535 // obtain the propagated covariance for each state.
1536 // We also need to recheck the result and find the tipIndex, because at this
1537 // step, we will not ignore the boundary checks for measurement surfaces. We
1538 // want to create trackstates only on surfaces, that we actually hit.
1539 if (false && gx2fOptions.nUpdateMax > 0) {
1540 ACTS_VERBOSE("Propagate with the final covariance.");
1541 // update covariance
1542 params.covariance() = fullCovariancePredicted;
1543
1544 // set up the propagator
1545 PropagatorOptions propagatorOptions{gx2fOptions.propagatorPlainOptions};
1546 auto& gx2fActor = propagatorOptions.actorList.template get<GX2FActor>();
1547 gx2fActor.inputMeasurements = &inputMeasurements;
1548 gx2fActor.doMultipleScattering = gx2fOptions.doMultipleScattering;
1549 gx2fActor.doEnergyLoss = gx2fOptions.doEnergyLoss;
1550 gx2fActor.extensions = gx2fOptions.extensions;
1551 gx2fActor.calibrationContext = &gx2fOptions.calibrationContext.get();
1552 gx2fActor.actorLogger = m_actorLogger.get();
1553 gx2fActor.startParameters = &params;
1554
1555 auto propagatorState = m_propagator.makeState(propagatorOptions);
1556
1557 auto propagatorInitResult =
1558 m_propagator.initialize(propagatorState, params, nullptr);
1559 if (!propagatorInitResult.ok()) {
1560 ACTS_DEBUG("Propagation initialization failed: "
1561 << propagatorInitResult.error());
1562 return propagatorInitResult.error();
1563 }
1564
1565 auto& r = propagatorState.template get<Gx2FitterResult<traj_t>>();
1566 r.fittedStates = &trackContainer.trackStateContainer();
1567 r.materialMap = std::move(materialMap);
1568
1569 // Run the fitter
1570 auto propagationResult = m_propagator.propagate(propagatorState);
1571
1572 auto result =
1573 m_propagator.makeResult(std::move(propagatorState), propagationResult,
1574 propagatorOptions, false);
1575
1576 if (!result.ok()) {
1577 ACTS_DEBUG("Propagation failed: " << result.error());
1578 return result.error();
1579 }
1580
1581 auto& propRes = *result;
1582 GX2FResult gx2fResult = std::move(propRes.template get<GX2FResult>());
1583 materialMap = std::move(gx2fResult.materialMap);
1584
1585 if (tipIndex != gx2fResult.lastMeasurementIndex) {
1586 ACTS_INFO("Final fit used unreachable measurements.");
1587 tipIndex = gx2fResult.lastMeasurementIndex;
1588
1589 // It could happen, that no measurements were found. Then the track
1590 // would be empty and the following operations would be invalid.
1591 if (tipIndex == kInvalid) {
1592 ACTS_INFO("Did not find any measurements in final propagation.");
1593 return Experimental::GlobalChiSquareFitterError::
1594 NotEnoughMeasurements;
1595 }
1596 }
1597 }
1598
1599 if (!trackContainer.hasColumn(
1600 Acts::hashString(Gx2fConstants::gx2fnUpdateColumn))) {
1601 trackContainer.template addColumn<std::uint32_t>("Gx2fnUpdateColumn");
1602 }
1603
1604 // Prepare track for return
1605 auto track = trackContainer.makeTrack();
1606 track.tipIndex() = tipIndex;
1607 track.parameters() = params.parameters();
1608 track.covariance() = fullCovariancePredicted;
1609 track.setReferenceSurface(params.referenceSurface().getSharedPtr());
1610
1611 if (trackContainer.hasColumn(
1612 Acts::hashString(Gx2fConstants::gx2fnUpdateColumn))) {
1613 ACTS_DEBUG("Add nUpdate to track");
1614 track.template component<std::uint32_t>("Gx2fnUpdateColumn") =
1615 static_cast<std::uint32_t>(nUpdate);
1616 }
1617
1618 // TODO write test for calculateTrackQuantities
1619 calculateTrackQuantities(track);
1620
1621 // Set the chi2sum for the track summary manually, since we don't calculate
1622 // it for each state
1623 track.chi2() = chi2sum;
1624
1625 track.linkForward();
1626
1627 // Return the converted Track
1628 return track;
1629 }
1630};
1631
1633
1634}
1635#endif
Scalar theta() const
theta method
std::pair< std::vector< unsigned int >, bool > res
Propagator Actor plugin for the GX2F.
Definition Chi2Fitter.h:754
const CalibrationContext * calibrationContext
Calibration context for the fit.
Definition Chi2Fitter.h:788
double elossCutOff
Cutoff value on the energy loss to fine tune the number of energy loss parameters in the fit.
Definition Chi2Fitter.h:774
const BoundTrackParameters * startParameters
Start parameters of the track fit.
Definition Chi2Fitter.h:792
bool checkAbort(propagator_state_t &, const stepper_t &, const navigator_t &, const result_t &result, const Logger &) const
Result< void > act(propagator_state_t &state, const stepper_t &stepper, const navigator_t &navigator, result_type &result, const Logger &) const
Gx2f actor operation.
Definition Chi2Fitter.h:806
Result< void > fillMaterialMap(propagator_state_t &state, const stepper_t &stepper, const navigator_t &navigator, result_type &result) const
Definition Chi2Fitter.h:989
FreeToBoundCorrection freeToBoundCorrection
Whether to include non-linear correction during global to local transformation.
Definition Chi2Fitter.h:778
const Logger * actorLogger
The logger instance.
Definition Chi2Fitter.h:780
const Logger & logger() const
Logger helper.
Definition Chi2Fitter.h:783
Gx2FitterExtensions< traj_t > extensions
Definition Chi2Fitter.h:785
Gx2FitterResult< traj_t > result_type
Broadcast the result_type.
Definition Chi2Fitter.h:757
Result< void > makeTrackState(propagator_state_t &state, const stepper_t &stepper, const navigator_t &navigator, result_type &result) const
Definition Chi2Fitter.h:882
typename traj_t::TrackStateProxy TrackStateProxy_t
Definition Chi2Fitter.h:759
const std::unordered_map< const Surface *, SourceLink > * inputMeasurements
Allows retrieving measurements for a surface.
Definition Chi2Fitter.h:762
bool doMultipleScattering
Whether to consider multiple scattering.
Definition Chi2Fitter.h:765
bool doEnergyLoss
Whether to consider energy loss.
Definition Chi2Fitter.h:768
const Logger & logger() const
Definition Chi2Fitter.h:744
typename propagator_t::Navigator Gx2fNavigator
The navigator type.
Definition Chi2Fitter.h:715
Result< typename track_container_t::TrackProxy > fit(source_link_iterator_t it, source_link_iterator_t end, const BoundTrackParameters &sParameters, const Gx2FitterOptions< traj_t > &gx2fOptions, track_container_t &trackContainer) const
Fit implementation.
Gx2Fitter(const propagator_t &pPropagator, std::unique_ptr< const Logger > log=getDefaultLogger("Gx2Fitter", Logging::INFO))
Constructor for the Global Chi-Square Fitter.
Definition Chi2Fitter.h:728
int r
Definition globals.cxx:22
void updateGx2fParams(BoundTrackParameters &params, const Eigen::VectorXd &deltaParamsExtended, std::unordered_map< GeometryIdentifier, Gx2fMaterialProperties > &materialMap, const std::vector< GeometryIdentifier > &geoIdVector)
Update parameters (and scattering angles if applicable).
Eigen::VectorXd computeGx2fDeltaParams(const Gx2fSystem &extendedSystem)
Solve the gx2f system to get the delta parameters for the update.
void addMeasurementToGx2fSums(Gx2fSystem &extendedSystem, const std::vector< BoundMatrix > &jacobianFromStart, const std::vector< std::size_t > &materialIndices, const std::vector< double > &qOverPderiv, const track_state_t &trackState, const Logger &logger)
Process measurements and fill the aMatrix and bVector.
Definition Chi2Fitter.h:545
void addMeasurementToGx2fSumsBackend(Gx2fSystem &extendedSystem, const std::vector< BoundMatrix > &jacobianFromStart, const std::vector< std::size_t > &materialIndices, const std::vector< double > &qOverPderiv, const Eigen::MatrixXd &covarianceMeasurement, const BoundVector &predicted, const Eigen::VectorXd &measurement, const Eigen::MatrixXd &projector, const Logger &logger)
Adds a measurement to the GX2F equation system in a modular backend function.
void updateGx2fCovarianceParams(BoundMatrix &fullCovariancePredicted, Gx2fSystem &extendedSystem)
Calculate and update the covariance of the fitted parameters.
void fillGx2fSystem(const track_proxy_t track, Gx2fSystem &extendedSystem, const std::unordered_map< GeometryIdentifier, Gx2fMaterialProperties > &materialMap, const std::vector< std::size_t > &materialParamIndices, std::vector< GeometryIdentifier > &geoIdVector, const Logger &logger)
Fill the GX2F system with data from a track.
Definition Chi2Fitter.h:582
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:132
static Root::TMsgLogger logger("iLumiCalc")
constexpr std::string_view gx2fnUpdateColumn
Definition Chi2Fitter.h:48
constexpr TrackStatePropMask trackStateMask
Definition Chi2Fitter.h:51
STL namespace.
Extension struct which holds delegates to customise the GX2F behaviour.
Definition Chi2Fitter.h:228
typename MultiTrajectory< traj_t >::ConstTrackStateProxy ConstTrackStateProxy
Type alias for const track state proxy from multi-trajectory.
Definition Chi2Fitter.h:232
Delegate< void(const GeometryContext &, const CalibrationContext &, const SourceLink &, TrackStateProxy)> Calibrator
Type alias for calibrator delegate to process measurements.
Definition Chi2Fitter.h:238
typename TrackStateProxy::Parameters Parameters
Type alias for track parameters from track state proxy.
Definition Chi2Fitter.h:235
Delegate< Gx2fMaterialProperties::ELossAtSurface( const GeometryContext &geoContext, const CalibrationContext &calibContext, const Vector3 &pos, const Vector3 &dir, const Surface &matSurf)> ELossAccumulator
Type alias of a delegate to retrieve the measured energy loss at a given surface e....
Definition Chi2Fitter.h:253
Delegate< bool(ConstTrackStateProxy)> OutlierFinder
Type alias for outlier finder delegate to identify measurement outliers.
Definition Chi2Fitter.h:243
typename MultiTrajectory< traj_t >::TrackStateProxy TrackStateProxy
Type alias for mutable track state proxy from multi-trajectory.
Definition Chi2Fitter.h:230
Gx2FitterExtensions()
Default constructor which connects the default void components.
Definition Chi2Fitter.h:277
Combined options for the Global-Chi-Square fitter.
Definition Chi2Fitter.h:290
Gx2FitterOptions(const GeometryContext &gctx, const MagneticFieldContext &mctx, std::reference_wrapper< const CalibrationContext > cctx, Gx2FitterExtensions< traj_t > extensions_, const PropagatorPlainOptions &pOptions)
PropagatorOptions with context.
Definition Chi2Fitter.h:304
Gx2FitterExtensions< ActsTrk::MutableTrackStateBackend > extensions
Definition Chi2Fitter.h:324
std::reference_wrapper< const CalibrationContext > calibrationContext
Definition Chi2Fitter.h:321
std::reference_wrapper< const MagneticFieldContext > magFieldContext
Definition Chi2Fitter.h:319
std::reference_wrapper< const GeometryContext > geoContext
Definition Chi2Fitter.h:317
Result container for a global chi-square fit.
Definition Chi2Fitter.h:363
std::vector< std::size_t > materialParamIndices
Index of the next material parameter.
Definition Chi2Fitter.h:405
std::unordered_map< GeometryIdentifier, Gx2fMaterialProperties > materialMap
The materialMap stores for each visited surface their scattering properties.
Definition Chi2Fitter.h:369
std::size_t surfaceCount
Count how many surfaces have been hit.
Definition Chi2Fitter.h:414
Gx2fMaterialProperties::ScatteringAtSurface accScatterer
Accumulated scattering.
Definition Chi2Fitter.h:375
bool finished
Indicator if track fitting has been done.
Definition Chi2Fitter.h:408
Gx2fMaterialProperties::ELossAtSurface accEloss
Accumulated energy loss to cache the energy loss which is below the threshold.
Definition Chi2Fitter.h:373
std::size_t processedStates
Counter for handled states.
Definition Chi2Fitter.h:399
std::size_t lastTrackIndex
This is the index of the 'tip' of the states stored in multitrajectory.
Definition Chi2Fitter.h:387
std::size_t lastMeasurementIndex
This is the index of the 'tip' of the track stored in multitrajectory.
Definition Chi2Fitter.h:381
std::vector< const Surface * > missedActiveSurfaces
Measurement surfaces without hits.
Definition Chi2Fitter.h:411
traj_t * fittedStates
Fitted states that the actor has handled.
Definition Chi2Fitter.h:365
std::size_t measurementHoles
Counter for measurements holes A hole correspond to a surface with an associated detector element wit...
Definition Chi2Fitter.h:396
std::size_t processedMeasurements
Counter for handled measurements.
Definition Chi2Fitter.h:402
std::size_t measurementStates
Counter for states with non-outlier measurements.
Definition Chi2Fitter.h:390
Auxiliary struct to include the measured energy loss at a surface.
Definition Chi2Fitter.h:69
constexpr double lostSigma() const
The uncertainty on the energy loss.
Definition Chi2Fitter.h:79
ELossAtSurface & operator+=(const ELossAtSurface &other) noexcept
Absorb the energy loss from another object.
Definition Chi2Fitter.h:93
constexpr double lostEnergy() const
The amount of lost energy (Bethe bloch / measurement).
Definition Chi2Fitter.h:76
ELossAtSurface(const double energy, const double sigma) noexcept
Constructor taking the measured energy loss and its uncertainty.
Definition Chi2Fitter.h:87
double m_lostEnergy
The amount of lost energy at a given surface.
Definition Chi2Fitter.h:120
double m_lossSigma
The uncertainty on the lost energy.
Definition Chi2Fitter.h:122
constexpr bool isValid() const
Is the measured energy loss valid.
Definition Chi2Fitter.h:72
bool m_isValid
Flag toggling whether the parameters are valid.
Definition Chi2Fitter.h:118
ELossAtSurface()=default
Default constructor to construct an invalid energy loss.
static ELossAtSurface invalidELoss(const GeometryContext &geoContext, const CalibrationContext &calibContext, const Vector3 &pos, const Vector3 &dir, const Surface &matSurface)
Function that satisfies the eLossAccumulator delegate pattern and the produces an invalid energy loss...
Auxiliary struct to describe the material scattering at a surface.
Definition Chi2Fitter.h:126
bool m_isValid
Flag toggling whether the scatterer is valid.
Definition Chi2Fitter.h:147
ScatteringAtSurface()=default
Default constructor to construct an invalid scatterer.
constexpr double sigma() const
Uncertainty on the scattering angle.
Definition Chi2Fitter.h:130
double m_sigma
The inverse uncertainty on the scattering angles calculated using e.g.
Definition Chi2Fitter.h:150
ScatteringAtSurface & operator+=(const ScatteringAtSurface &other)
Definition Chi2Fitter.h:138
ScatteringAtSurface(const double sigma)
Constructor taking the width of the scatterer.
Definition Chi2Fitter.h:135
constexpr bool isValid() const
Is the scattering uncertainty valid.
Definition Chi2Fitter.h:128
A container to store material properties for each material surface.
Definition Chi2Fitter.h:62
double m_scatPhi
Fitted delta phi scattering.
Definition Chi2Fitter.h:219
constexpr double qOverPderiv() const
Definition Chi2Fitter.h:187
ELossAtSurface m_eloss
Description of the energy loss.
Definition Chi2Fitter.h:208
void updateParameters(const Eigen::VectorXd &deltaParamsExtended, const std::size_t firstParamIdx)
const ScatteringAtSurface & scatterer() const
Definition Chi2Fitter.h:177
constexpr std::size_t nDim() const
Number of dimensions from the material scatterer.
Definition Chi2Fitter.h:172
double m_lostEnergy
Fitted parameters.
Definition Chi2Fitter.h:215
constexpr bool materialIsValid() const
Accessor for the material validity flag.
Definition Chi2Fitter.h:166
double m_scatTheta
Fitted delta theta scattering.
Definition Chi2Fitter.h:217
constexpr double lostEnergy() const
Definition Chi2Fitter.h:185
double m_qOverPderiv
qOverP derivative with respect to eloss
Definition Chi2Fitter.h:221
void contributionToGx2fSums(Gx2fSystem &extendedSystem, const double theta, const std::size_t firstParamIdx, const Logger &logger) const
ScatteringAtSurface m_scatterer
Description of the scattering angles.
Definition Chi2Fitter.h:206
const ELossAtSurface & energyLoss() const
Definition Chi2Fitter.h:179
constexpr double deltaPhi() const
Definition Chi2Fitter.h:183
constexpr double deltaTheta() const
Definition Chi2Fitter.h:181
constexpr double qOverPratio() const
Definition Chi2Fitter.h:189
void updateTrackParameters(BoundTrackParameters &trackPars)
Update the track parameters with the scattering angles and.
Gx2fMaterialProperties(ScatteringAtSurface &&scatterer, ELossAtSurface &&eLoss)
Constructor to initialize material properties.
Definition Chi2Fitter.h:158
A container to manage all properties of a gx2f system.
Definition Chi2Fitter.h:421
std::size_t m_ndf
Number of degrees of freedom of the system.
Definition Chi2Fitter.h:503
bool isWellDefined()
Checks if the system has sufficient degrees of freedom for fitting.
Definition Chi2Fitter.h:487
double & chi2()
Accessor for the accumulated chi-squared value (mutable version).
Definition Chi2Fitter.h:437
std::size_t m_nDims
Number of dimensions of the (extended) system.
Definition Chi2Fitter.h:491
Eigen::VectorXd & bVector()
Accessor for the extended system vector (mutable version).
Definition Chi2Fitter.h:453
double chi2() const
Accessor for the accumulated chi-squared value (const version).
Definition Chi2Fitter.h:433
std::size_t findRequiredNdf()
Determines the minimum number of degrees of freedom required for the fit.
Definition Chi2Fitter.h:472
std::size_t & ndf()
Accessor for the number of degrees of freedom (mutable version).
Definition Chi2Fitter.h:461
Gx2fSystem(std::size_t nDims)
Constructor to initialize matrices and vectors to zero based on specified dimensions.
Definition Chi2Fitter.h:425
std::size_t nDims() const
Accessor for the number of dimensions of the extended system.
Definition Chi2Fitter.h:429
std::size_t ndf() const
Accessor for the number of degrees of freedom (const version).
Definition Chi2Fitter.h:457
const Eigen::VectorXd & bVector() const
Accessor for the extended system vector (const version).
Definition Chi2Fitter.h:449
const Eigen::MatrixXd & aMatrix() const
Accessor for the extended system matrix (const version).
Definition Chi2Fitter.h:441
Eigen::MatrixXd & aMatrix()
Accessor for the extended system matrix (mutable version).
Definition Chi2Fitter.h:445
Eigen::VectorXd m_bVector
Extended vector for accumulation.
Definition Chi2Fitter.h:500
double m_chi2
Sum of chi-squared values.
Definition Chi2Fitter.h:494
Eigen::MatrixXd m_aMatrix
Extended matrix for accumulation.
Definition Chi2Fitter.h:497