File indexing completed on 2025-07-01 08:08:04
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012 #include "Acts/Utilities/detail/ReferenceWrapperAnyCompat.hpp"
0013
0014 #include "Acts/Definitions/Algebra.hpp"
0015 #include "Acts/EventData/MeasurementHelpers.hpp"
0016 #include "Acts/EventData/MultiTrajectory.hpp"
0017 #include "Acts/EventData/MultiTrajectoryHelpers.hpp"
0018 #include "Acts/EventData/SourceLink.hpp"
0019 #include "Acts/EventData/TrackParameters.hpp"
0020 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0021 #include "Acts/EventData/VectorTrackContainer.hpp"
0022 #include "Acts/Geometry/GeometryContext.hpp"
0023 #include "Acts/Geometry/TrackingVolume.hpp"
0024 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0025 #include "Acts/Material/Interactions.hpp"
0026 #include "Acts/Material/MaterialSlab.hpp"
0027 #include "Acts/Propagator/AbortList.hpp"
0028 #include "Acts/Propagator/ActionList.hpp"
0029 #include "Acts/Propagator/ConstrainedStep.hpp"
0030 #include "Acts/Propagator/DirectNavigator.hpp"
0031 #include "Acts/Propagator/Navigator.hpp"
0032 #include "Acts/Propagator/Propagator.hpp"
0033 #include "Acts/Propagator/StandardAborters.hpp"
0034 #include "Acts/Propagator/StraightLineStepper.hpp"
0035 #include "Acts/Propagator/detail/PointwiseMaterialInteraction.hpp"
0036 #include "Acts/TrackFitting/GlobalChiSquareFitterError.hpp"
0037 #include "Acts/TrackFitting/detail/VoidFitterComponents.hpp"
0038 #include "Acts/Utilities/CalibrationContext.hpp"
0039 #include "Acts/Utilities/Delegate.hpp"
0040 #include "Acts/Utilities/Logger.hpp"
0041 #include "Acts/Utilities/Result.hpp"
0042 #include "Acts/Utilities/TrackHelpers.hpp"
0043
0044 #include <functional>
0045 #include <limits>
0046 #include <map>
0047 #include <memory>
0048 #include <unordered_map>
0049
0050 namespace Acts::Experimental {
0051
0052 namespace Gx2fConstants {
0053 constexpr std::string_view gx2fnUpdateColumn = "Gx2fnUpdateColumn";
0054
0055
0056 constexpr TrackStatePropMask trackStateMask = TrackStatePropMask::Predicted |
0057 TrackStatePropMask::Jacobian |
0058 TrackStatePropMask::Calibrated;
0059
0060
0061
0062 const Eigen::Matrix<double, eBoundSize, 2> phiThetaProjector = [] {
0063 Eigen::Matrix<double, eBoundSize, 2> m =
0064 Eigen::Matrix<double, eBoundSize, 2>::Zero();
0065 m(eBoundPhi, 0) = 1.0;
0066 m(eBoundTheta, 1) = 1.0;
0067 return m;
0068 }();
0069 }
0070
0071
0072 template <typename traj_t>
0073 struct Gx2FitterExtensions {
0074 using TrackStateProxy = typename MultiTrajectory<traj_t>::TrackStateProxy;
0075 using ConstTrackStateProxy =
0076 typename MultiTrajectory<traj_t>::ConstTrackStateProxy;
0077 using Parameters = typename TrackStateProxy::Parameters;
0078
0079 using Calibrator =
0080 Delegate<void(const GeometryContext&, const CalibrationContext&,
0081 const SourceLink&, TrackStateProxy)>;
0082
0083 using Updater = Delegate<Result<void>(const GeometryContext&, TrackStateProxy,
0084 const Logger&)>;
0085
0086 using OutlierFinder = Delegate<bool(ConstTrackStateProxy)>;
0087
0088
0089
0090
0091 Calibrator calibrator;
0092
0093
0094 Updater updater;
0095
0096
0097
0098 OutlierFinder outlierFinder;
0099
0100
0101 SourceLinkSurfaceAccessor surfaceAccessor;
0102
0103
0104 Gx2FitterExtensions() {
0105 calibrator.template connect<&detail::voidFitterCalibrator<traj_t>>();
0106 updater.template connect<&detail::voidFitterUpdater<traj_t>>();
0107 outlierFinder.template connect<&detail::voidOutlierFinder<traj_t>>();
0108 surfaceAccessor.connect<&detail::voidSurfaceAccessor>();
0109 }
0110 };
0111
0112
0113
0114
0115 template <typename traj_t>
0116 struct Gx2FitterOptions {
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126
0127
0128
0129
0130 Gx2FitterOptions(const GeometryContext& gctx,
0131 const MagneticFieldContext& mctx,
0132 std::reference_wrapper<const CalibrationContext> cctx,
0133 Gx2FitterExtensions<traj_t> extensions_,
0134 const PropagatorPlainOptions& pOptions,
0135 const Surface* rSurface = nullptr, bool mScattering = false,
0136 bool eLoss = false,
0137 const FreeToBoundCorrection& freeToBoundCorrection_ =
0138 FreeToBoundCorrection(false),
0139 const std::size_t nUpdateMax_ = 5,
0140 double relChi2changeCutOff_ = 1e-5)
0141 : geoContext(gctx),
0142 magFieldContext(mctx),
0143 calibrationContext(cctx),
0144 extensions(extensions_),
0145 propagatorPlainOptions(pOptions),
0146 referenceSurface(rSurface),
0147 multipleScattering(mScattering),
0148 energyLoss(eLoss),
0149 freeToBoundCorrection(freeToBoundCorrection_),
0150 nUpdateMax(nUpdateMax_),
0151 relChi2changeCutOff(relChi2changeCutOff_) {}
0152
0153
0154 Gx2FitterOptions() = delete;
0155
0156
0157 std::reference_wrapper<const GeometryContext> geoContext;
0158
0159 std::reference_wrapper<const MagneticFieldContext> magFieldContext;
0160
0161 std::reference_wrapper<const CalibrationContext> calibrationContext;
0162
0163 Gx2FitterExtensions<traj_t> extensions;
0164
0165
0166 PropagatorPlainOptions propagatorPlainOptions;
0167
0168
0169 const Surface* referenceSurface = nullptr;
0170
0171
0172 bool multipleScattering = false;
0173
0174
0175 bool energyLoss = false;
0176
0177
0178
0179 FreeToBoundCorrection freeToBoundCorrection;
0180
0181
0182 std::size_t nUpdateMax = 5;
0183
0184
0185 double relChi2changeCutOff = 1e-7;
0186 };
0187
0188 template <typename traj_t>
0189 struct Gx2FitterResult {
0190
0191 traj_t* fittedStates{nullptr};
0192
0193
0194
0195
0196
0197 std::size_t lastMeasurementIndex = Acts::MultiTrajectoryTraits::kInvalid;
0198
0199
0200
0201
0202
0203 std::size_t lastTrackIndex = Acts::MultiTrajectoryTraits::kInvalid;
0204
0205
0206 std::optional<BoundTrackParameters> fittedParameters;
0207
0208
0209 std::size_t measurementStates = 0;
0210
0211
0212
0213
0214
0215 std::size_t measurementHoles = 0;
0216
0217
0218 std::size_t processedStates = 0;
0219
0220
0221 std::size_t processedMeasurements = 0;
0222
0223
0224 bool finished = false;
0225
0226
0227 std::vector<const Surface*> missedActiveSurfaces;
0228
0229
0230
0231 std::vector<const Surface*> passedAgainSurfaces;
0232
0233 Result<void> result{Result<void>::success()};
0234
0235
0236 std::size_t surfaceCount = 0;
0237 };
0238
0239
0240
0241
0242
0243
0244 struct ScatteringProperties {
0245 public:
0246
0247
0248
0249
0250
0251 ScatteringProperties(const BoundVector& scatteringAngles_,
0252 const ActsScalar invCovarianceMaterial_,
0253 const bool materialIsValid_)
0254 : m_scatteringAngles(scatteringAngles_),
0255 m_invCovarianceMaterial(invCovarianceMaterial_),
0256 m_materialIsValid(materialIsValid_) {}
0257
0258
0259 const BoundVector& scatteringAngles() const { return m_scatteringAngles; }
0260
0261
0262 BoundVector& scatteringAngles() { return m_scatteringAngles; }
0263
0264
0265 ActsScalar invCovarianceMaterial() const { return m_invCovarianceMaterial; }
0266
0267
0268 bool materialIsValid() const { return m_materialIsValid; }
0269
0270 private:
0271
0272
0273 BoundVector m_scatteringAngles;
0274
0275
0276
0277 ActsScalar m_invCovarianceMaterial;
0278
0279
0280
0281 bool m_materialIsValid;
0282 };
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
0293
0294
0295
0296
0297
0298
0299 template <std::size_t kMeasDim, typename track_state_t>
0300 void addMeasurementToGx2fSums(Eigen::MatrixXd& aMatrixExtended,
0301 Eigen::VectorXd& bVectorExtended, double& chi2sum,
0302 const std::vector<BoundMatrix>& jacobianFromStart,
0303 const track_state_t& trackState,
0304 const Logger& logger) {
0305
0306
0307 const ActsSquareMatrix<kMeasDim> covarianceMeasurement =
0308 trackState.template calibratedCovariance<kMeasDim>();
0309
0310 const auto safeInvCovMeasurement = safeInverse(covarianceMeasurement);
0311 if (!safeInvCovMeasurement) {
0312 ACTS_WARNING("addMeasurementToGx2fSums: safeInvCovMeasurement failed.");
0313 ACTS_VERBOSE(" covarianceMeasurement:\n" << covarianceMeasurement);
0314 return;
0315 }
0316
0317
0318
0319
0320 const std::size_t dimsExtendedParams = aMatrixExtended.rows();
0321
0322
0323 Eigen::MatrixXd extendedJacobian =
0324 Eigen::MatrixXd::Zero(eBoundSize, dimsExtendedParams);
0325
0326
0327 extendedJacobian.topLeftCorner<eBoundSize, eBoundSize>() =
0328 jacobianFromStart[0];
0329
0330
0331
0332
0333 for (std::size_t matSurface = 1; matSurface < jacobianFromStart.size();
0334 matSurface++) {
0335 const BoundMatrix jac = jacobianFromStart[matSurface];
0336
0337 const ActsMatrix<eBoundSize, 2> jacPhiTheta =
0338 jac * Gx2fConstants::phiThetaProjector;
0339
0340
0341 const std::size_t deltaPosition = eBoundSize + 2 * (matSurface - 1);
0342
0343 extendedJacobian.block<eBoundSize, 2>(0, deltaPosition) = jacPhiTheta;
0344 }
0345
0346 const BoundVector predicted = trackState.predicted();
0347
0348 const ActsVector<kMeasDim> measurement =
0349 trackState.template calibrated<kMeasDim>();
0350
0351 const ActsMatrix<kMeasDim, eBoundSize> projector =
0352 trackState.projector().template topLeftCorner<kMeasDim, eBoundSize>();
0353
0354 const Eigen::MatrixXd projJacobian = projector * extendedJacobian;
0355
0356 const ActsMatrix<kMeasDim, 1> projPredicted = projector * predicted;
0357
0358 const ActsVector<kMeasDim> residual = measurement - projPredicted;
0359
0360
0361 chi2sum += (residual.transpose() * (*safeInvCovMeasurement) * residual)(0, 0);
0362
0363 aMatrixExtended +=
0364 (projJacobian.transpose() * (*safeInvCovMeasurement) * projJacobian)
0365 .eval();
0366
0367 bVectorExtended +=
0368 (residual.transpose() * (*safeInvCovMeasurement) * projJacobian)
0369 .eval()
0370 .transpose();
0371
0372 ACTS_VERBOSE(
0373 "Contributions in addMeasurementToGx2fSums:\n"
0374 << "kMeasDim: " << kMeasDim << "\n"
0375 << "predicted" << predicted.transpose() << "\n"
0376 << "measurement: " << measurement.transpose() << "\n"
0377 << "covarianceMeasurement:\n"
0378 << covarianceMeasurement << "\n"
0379 << "projector:\n"
0380 << projector.eval() << "\n"
0381 << "projJacobian:\n"
0382 << projJacobian.eval() << "\n"
0383 << "projPredicted: " << (projPredicted.transpose()).eval() << "\n"
0384 << "residual: " << (residual.transpose()).eval() << "\n"
0385 << "extendedJacobian:\n"
0386 << extendedJacobian << "\n"
0387 << "aMatrixMeas:\n"
0388 << (projJacobian.transpose() * (*safeInvCovMeasurement) * projJacobian)
0389 .eval()
0390 << "\n"
0391 << "bVectorMeas: "
0392 << (residual.transpose() * (*safeInvCovMeasurement) * projJacobian).eval()
0393 << "\n"
0394 << "chi2sumMeas: "
0395 << (residual.transpose() * (*safeInvCovMeasurement) * residual)(0, 0)
0396 << "\n"
0397 << "safeInvCovMeasurement:\n"
0398 << (*safeInvCovMeasurement));
0399
0400 return;
0401 }
0402
0403
0404
0405
0406
0407
0408
0409
0410
0411
0412
0413
0414
0415
0416
0417
0418 template <typename track_state_t>
0419 void addMaterialToGx2fSums(
0420 Eigen::MatrixXd& aMatrixExtended, Eigen::VectorXd& bVectorExtended,
0421 double& chi2sum, const std::size_t nMaterialsHandled,
0422 const std::unordered_map<GeometryIdentifier, ScatteringProperties>&
0423 scatteringMap,
0424 const track_state_t& trackState, const Logger& logger) {
0425
0426 const GeometryIdentifier geoId = trackState.referenceSurface().geometryId();
0427 const auto scatteringMapId = scatteringMap.find(geoId);
0428 if (scatteringMapId == scatteringMap.end()) {
0429 ACTS_ERROR("No scattering angles found for material surface " << geoId);
0430 throw std::runtime_error(
0431 "No scattering angles found for material surface.");
0432 }
0433
0434 const ActsScalar sinThetaLoc = std::sin(trackState.predicted()[eBoundTheta]);
0435
0436
0437 const std::size_t deltaPosition = eBoundSize + 2 * nMaterialsHandled;
0438
0439 const BoundVector& scatteringAngles =
0440 scatteringMapId->second.scatteringAngles();
0441
0442 const ActsScalar invCov = scatteringMapId->second.invCovarianceMaterial();
0443
0444
0445 aMatrixExtended(deltaPosition, deltaPosition) +=
0446 invCov * sinThetaLoc * sinThetaLoc;
0447 bVectorExtended(deltaPosition, 0) -=
0448 invCov * scatteringAngles[eBoundPhi] * sinThetaLoc;
0449 chi2sum += invCov * scatteringAngles[eBoundPhi] * sinThetaLoc *
0450 scatteringAngles[eBoundPhi] * sinThetaLoc;
0451
0452
0453 aMatrixExtended(deltaPosition + 1, deltaPosition + 1) += invCov;
0454 bVectorExtended(deltaPosition + 1, 0) -=
0455 invCov * scatteringAngles[eBoundTheta];
0456 chi2sum +=
0457 invCov * scatteringAngles[eBoundTheta] * scatteringAngles[eBoundTheta];
0458
0459 ACTS_VERBOSE(
0460 "Contributions in addMaterialToGx2fSums:\n"
0461 << " invCov: " << invCov << "\n"
0462 << " sinThetaLoc: " << sinThetaLoc << "\n"
0463 << " deltaPosition: " << deltaPosition << "\n"
0464 << " Phi:\n"
0465 << " scattering angle: " << scatteringAngles[eBoundPhi] << "\n"
0466 << " aMatrix contribution: " << invCov * sinThetaLoc * sinThetaLoc
0467 << "\n"
0468 << " bVector contribution: "
0469 << invCov * scatteringAngles[eBoundPhi] * sinThetaLoc << "\n"
0470 << " chi2sum contribution: "
0471 << invCov * scatteringAngles[eBoundPhi] * sinThetaLoc *
0472 scatteringAngles[eBoundPhi] * sinThetaLoc
0473 << "\n"
0474 << " Theta:\n"
0475 << " scattering angle: " << scatteringAngles[eBoundTheta]
0476 << "\n"
0477 << " aMatrix contribution: " << invCov << "\n"
0478 << " bVector contribution: "
0479 << invCov * scatteringAngles[eBoundTheta] << "\n"
0480 << " chi2sum contribution: "
0481 << invCov * scatteringAngles[eBoundTheta] * scatteringAngles[eBoundTheta]
0482 << "\n");
0483
0484 return;
0485 }
0486
0487
0488
0489
0490
0491
0492
0493
0494
0495
0496
0497
0498
0499
0500 void updateCovariancePredicted(BoundMatrix& fullCovariancePredicted,
0501 Eigen::MatrixXd& aMatrixExtended,
0502 const std::size_t ndfSystem);
0503
0504
0505
0506
0507
0508
0509 template <typename propagator_t, typename traj_t>
0510 class Gx2Fitter {
0511
0512 using Gx2fNavigator = typename propagator_t::Navigator;
0513
0514
0515 static constexpr bool isDirectNavigator =
0516 std::is_same<Gx2fNavigator, DirectNavigator>::value;
0517
0518 public:
0519 Gx2Fitter(propagator_t pPropagator,
0520 std::unique_ptr<const Logger> _logger =
0521 getDefaultLogger("Gx2Fitter", Logging::INFO))
0522 : m_propagator(std::move(pPropagator)),
0523 m_logger{std::move(_logger)},
0524 m_actorLogger{m_logger->cloneWithSuffix("Actor")},
0525 m_addToSumLogger{m_logger->cloneWithSuffix("AddToSum")} {}
0526
0527 private:
0528
0529 propagator_t m_propagator;
0530
0531
0532 std::unique_ptr<const Logger> m_logger;
0533 std::unique_ptr<const Logger> m_actorLogger;
0534 std::unique_ptr<const Logger> m_addToSumLogger;
0535
0536 const Logger& logger() const { return *m_logger; }
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546 template <typename parameters_t>
0547 class Actor {
0548 public:
0549
0550 using result_type = Gx2FitterResult<traj_t>;
0551
0552
0553 const Surface* targetSurface = nullptr;
0554
0555
0556 const std::map<GeometryIdentifier, SourceLink>* inputMeasurements = nullptr;
0557
0558
0559 bool multipleScattering = false;
0560
0561
0562 bool energyLoss = false;
0563
0564
0565
0566 FreeToBoundCorrection freeToBoundCorrection;
0567
0568
0569 std::shared_ptr<MultiTrajectory<traj_t>> outputStates;
0570
0571
0572 const Logger* actorLogger{nullptr};
0573
0574
0575 const Logger& logger() const { return *actorLogger; }
0576
0577 Gx2FitterExtensions<traj_t> extensions;
0578
0579
0580 SurfaceReached targetReached;
0581
0582
0583 const CalibrationContext* calibrationContext{nullptr};
0584
0585
0586 const parameters_t* parametersWithHypothesis = nullptr;
0587
0588
0589
0590 std::unordered_map<GeometryIdentifier, ScatteringProperties>*
0591 scatteringMap = nullptr;
0592
0593
0594
0595
0596
0597
0598
0599
0600
0601
0602
0603 template <typename propagator_state_t, typename stepper_t,
0604 typename navigator_t>
0605 void operator()(propagator_state_t& state, const stepper_t& stepper,
0606 const navigator_t& navigator, result_type& result,
0607 const Logger& ) const {
0608 assert(result.fittedStates && "No MultiTrajectory set");
0609
0610
0611 if (result.measurementStates == inputMeasurements->size()) {
0612 ACTS_INFO("Actor: finish: All measurements have been found.");
0613 result.finished = true;
0614 } else if (state.navigation.navigationBreak) {
0615 ACTS_INFO("Actor: finish: navigationBreak.");
0616 result.finished = true;
0617 }
0618
0619
0620 if (result.finished || !result.result.ok()) {
0621
0622 if (result.measurementStates > 0) {
0623 result.missedActiveSurfaces.resize(result.measurementHoles);
0624 }
0625
0626 return;
0627 }
0628
0629
0630
0631 auto surface = navigator.currentSurface(state.navigation);
0632 if (surface == nullptr) {
0633 return;
0634 }
0635
0636 ++result.surfaceCount;
0637 const GeometryIdentifier geoId = surface->geometryId();
0638 ACTS_DEBUG("Surface " << geoId << " detected.");
0639
0640 const bool surfaceIsSensitive =
0641 (surface->associatedDetectorElement() != nullptr);
0642 const bool surfaceHasMaterial = (surface->surfaceMaterial() != nullptr);
0643
0644
0645
0646 bool doMaterial = multipleScattering && surfaceHasMaterial;
0647
0648
0649
0650 if (doMaterial) {
0651 ACTS_DEBUG(" The surface contains material, ...");
0652
0653 auto scatteringMapId = scatteringMap->find(geoId);
0654 if (scatteringMapId == scatteringMap->end()) {
0655 ACTS_DEBUG(" ... create entry in scattering map.");
0656
0657 detail::PointwiseMaterialInteraction interaction(surface, state,
0658 stepper);
0659
0660 const bool slabIsValid = interaction.evaluateMaterialSlab(
0661 state, navigator, MaterialUpdateStage::FullUpdate);
0662 double invSigma2 = 0.;
0663 if (slabIsValid) {
0664 const auto& particle =
0665 parametersWithHypothesis->particleHypothesis();
0666
0667 const double sigma =
0668 static_cast<double>(Acts::computeMultipleScatteringTheta0(
0669 interaction.slab, particle.absolutePdg(), particle.mass(),
0670 static_cast<float>(
0671 parametersWithHypothesis->parameters()[eBoundQOverP]),
0672 particle.absoluteCharge()));
0673 ACTS_VERBOSE(
0674 " The Highland formula gives sigma = " << sigma);
0675 invSigma2 = 1. / std::pow(sigma, 2);
0676 } else {
0677 ACTS_VERBOSE(" Material slab is not valid.");
0678 }
0679
0680 scatteringMap->emplace(
0681 geoId, ScatteringProperties{BoundVector::Zero(), invSigma2,
0682 slabIsValid});
0683 scatteringMapId = scatteringMap->find(geoId);
0684 } else {
0685 ACTS_DEBUG(" ... found entry in scattering map.");
0686 }
0687
0688 doMaterial = doMaterial && scatteringMapId->second.materialIsValid();
0689 }
0690
0691
0692 if (auto sourcelink_it = inputMeasurements->find(geoId);
0693 sourcelink_it != inputMeasurements->end()) {
0694 ACTS_DEBUG(" The surface contains a measurement.");
0695
0696
0697 stepper.transportCovarianceToBound(state.stepping, *surface,
0698 freeToBoundCorrection);
0699
0700
0701 auto& fittedStates = *result.fittedStates;
0702
0703
0704
0705 typename traj_t::TrackStateProxy trackStateProxy =
0706 fittedStates.makeTrackState(Gx2fConstants::trackStateMask,
0707 result.lastTrackIndex);
0708 const std::size_t currentTrackIndex = trackStateProxy.index();
0709
0710
0711
0712 {
0713 trackStateProxy.setReferenceSurface(surface->getSharedPtr());
0714
0715 auto res = stepper.boundState(state.stepping, *surface, false,
0716 freeToBoundCorrection);
0717 if (!res.ok()) {
0718 result.result = res.error();
0719 return;
0720 }
0721
0722 auto& [boundParams, jacobian, pathLength] = *res;
0723
0724
0725
0726 if (doMaterial) {
0727 ACTS_DEBUG(" Update parameters with scattering angles.");
0728 const auto scatteringMapId = scatteringMap->find(geoId);
0729 ACTS_VERBOSE(" scatteringAngles:\n"
0730 << scatteringMapId->second.scatteringAngles()
0731 << "\n boundParams before the update:\n"
0732 << boundParams);
0733 boundParams.parameters() +=
0734 scatteringMapId->second.scatteringAngles();
0735 ACTS_VERBOSE(" boundParams after the update:\n" << boundParams);
0736 }
0737
0738
0739 trackStateProxy.predicted() = boundParams.parameters();
0740 trackStateProxy.predictedCovariance() = state.stepping.cov;
0741
0742 trackStateProxy.jacobian() = jacobian;
0743 trackStateProxy.pathLength() = pathLength;
0744
0745 if (doMaterial) {
0746 stepper.update(state.stepping,
0747 transformBoundToFreeParameters(
0748 trackStateProxy.referenceSurface(),
0749 state.geoContext, trackStateProxy.predicted()),
0750 trackStateProxy.predicted(),
0751 trackStateProxy.predictedCovariance(), *surface);
0752 }
0753 }
0754
0755
0756
0757 extensions.calibrator(state.geoContext, *calibrationContext,
0758 sourcelink_it->second, trackStateProxy);
0759
0760
0761 auto typeFlags = trackStateProxy.typeFlags();
0762 typeFlags.set(TrackStateFlag::ParameterFlag);
0763 if (surfaceHasMaterial) {
0764 typeFlags.set(TrackStateFlag::MaterialFlag);
0765 }
0766
0767
0768 typeFlags.set(TrackStateFlag::MeasurementFlag);
0769
0770 ++result.processedMeasurements;
0771
0772 result.lastMeasurementIndex = currentTrackIndex;
0773 result.lastTrackIndex = currentTrackIndex;
0774
0775
0776
0777 ++result.measurementStates;
0778
0779
0780 ++result.processedStates;
0781
0782
0783
0784 result.measurementHoles = result.missedActiveSurfaces.size();
0785
0786 return;
0787 }
0788
0789 if (doMaterial) {
0790
0791
0792
0793 ACTS_DEBUG(
0794 " The surface contains no measurement, but material and maybe "
0795 "a hole.");
0796
0797
0798 stepper.transportCovarianceToBound(state.stepping, *surface,
0799 freeToBoundCorrection);
0800
0801
0802 auto& fittedStates = *result.fittedStates;
0803
0804
0805
0806 typename traj_t::TrackStateProxy trackStateProxy =
0807 fittedStates.makeTrackState(Gx2fConstants::trackStateMask,
0808 result.lastTrackIndex);
0809 const std::size_t currentTrackIndex = trackStateProxy.index();
0810
0811
0812
0813 {
0814 trackStateProxy.setReferenceSurface(surface->getSharedPtr());
0815
0816 auto res = stepper.boundState(state.stepping, *surface, false,
0817 freeToBoundCorrection);
0818 if (!res.ok()) {
0819 result.result = res.error();
0820 return;
0821 }
0822
0823 auto& [boundParams, jacobian, pathLength] = *res;
0824
0825
0826
0827
0828
0829 ACTS_DEBUG(" Update parameters with scattering angles.");
0830 const auto scatteringMapId = scatteringMap->find(geoId);
0831 ACTS_VERBOSE(" scatteringAngles:\n"
0832 << scatteringMapId->second.scatteringAngles()
0833 << "\n boundParams before the update:\n"
0834 << boundParams);
0835 boundParams.parameters() +=
0836 scatteringMapId->second.scatteringAngles();
0837 ACTS_VERBOSE(" boundParams after the update:\n" << boundParams);
0838
0839
0840 trackStateProxy.predicted() = boundParams.parameters();
0841 trackStateProxy.predictedCovariance() = state.stepping.cov;
0842
0843 trackStateProxy.jacobian() = jacobian;
0844 trackStateProxy.pathLength() = pathLength;
0845
0846 stepper.update(state.stepping,
0847 transformBoundToFreeParameters(
0848 trackStateProxy.referenceSurface(),
0849 state.geoContext, trackStateProxy.predicted()),
0850 trackStateProxy.predicted(),
0851 trackStateProxy.predictedCovariance(), *surface);
0852 }
0853
0854
0855 auto typeFlags = trackStateProxy.typeFlags();
0856 typeFlags.set(TrackStateFlag::ParameterFlag);
0857 typeFlags.set(TrackStateFlag::MaterialFlag);
0858
0859
0860
0861 const bool precedingMeasurementExists = (result.measurementStates > 0);
0862 if (surfaceIsSensitive && precedingMeasurementExists) {
0863 ACTS_DEBUG(" Surface is also sensitive. Marked as hole.");
0864 typeFlags.set(TrackStateFlag::HoleFlag);
0865
0866
0867 result.missedActiveSurfaces.push_back(surface);
0868 }
0869
0870 result.lastTrackIndex = currentTrackIndex;
0871
0872 ++result.processedStates;
0873
0874 return;
0875 }
0876
0877 if (surfaceIsSensitive || surfaceHasMaterial) {
0878
0879
0880
0881 if (multipleScattering) {
0882 ACTS_DEBUG(
0883 " The surface contains no measurement, but maybe a hole.");
0884 } else {
0885 ACTS_DEBUG(
0886 " The surface contains no measurement, but maybe a hole "
0887 "and/or material.");
0888 }
0889
0890
0891
0892
0893 const bool precedingMeasurementExists = (result.measurementStates > 0);
0894 if (!precedingMeasurementExists && !surfaceHasMaterial) {
0895 ACTS_DEBUG(
0896 " Ignoring hole, because there are no preceding "
0897 "measurements.");
0898 return;
0899 }
0900
0901 auto& fittedStates = *result.fittedStates;
0902
0903
0904
0905 typename traj_t::TrackStateProxy trackStateProxy =
0906 fittedStates.makeTrackState(Gx2fConstants::trackStateMask,
0907 result.lastTrackIndex);
0908 const std::size_t currentTrackIndex = trackStateProxy.index();
0909
0910
0911
0912 {
0913 trackStateProxy.setReferenceSurface(surface->getSharedPtr());
0914
0915 auto res = stepper.boundState(state.stepping, *surface, false,
0916 freeToBoundCorrection);
0917 if (!res.ok()) {
0918 result.result = res.error();
0919 return;
0920 }
0921 const auto& [boundParams, jacobian, pathLength] = *res;
0922
0923
0924 trackStateProxy.predicted() = boundParams.parameters();
0925 trackStateProxy.predictedCovariance() = state.stepping.cov;
0926
0927 trackStateProxy.jacobian() = jacobian;
0928 trackStateProxy.pathLength() = pathLength;
0929 }
0930
0931
0932 auto typeFlags = trackStateProxy.typeFlags();
0933 typeFlags.set(TrackStateFlag::ParameterFlag);
0934 if (surfaceHasMaterial) {
0935 ACTS_DEBUG(" It is material.");
0936 typeFlags.set(TrackStateFlag::MaterialFlag);
0937 }
0938
0939
0940 if (surfaceIsSensitive && precedingMeasurementExists) {
0941 ACTS_DEBUG(" It is a hole.");
0942 typeFlags.set(TrackStateFlag::HoleFlag);
0943
0944 result.missedActiveSurfaces.push_back(surface);
0945 }
0946
0947 result.lastTrackIndex = currentTrackIndex;
0948
0949 ++result.processedStates;
0950
0951 return;
0952 }
0953
0954 ACTS_DEBUG(" The surface contains no measurement/material/hole.");
0955 return;
0956 }
0957 };
0958
0959
0960 template <typename parameters_t>
0961 class Aborter {
0962 public:
0963
0964 using action_type = Actor<parameters_t>;
0965
0966 template <typename propagator_state_t, typename stepper_t,
0967 typename navigator_t, typename result_t>
0968 bool operator()(propagator_state_t& , const stepper_t& ,
0969 const navigator_t& , const result_t& result,
0970 const Logger& ) const {
0971 if (!result.result.ok() || result.finished) {
0972 return true;
0973 }
0974 return false;
0975 }
0976 };
0977
0978 public:
0979
0980
0981
0982
0983
0984
0985
0986
0987
0988
0989
0990
0991
0992
0993
0994
0995
0996
0997 template <typename source_link_iterator_t, typename start_parameters_t,
0998 typename parameters_t = BoundTrackParameters,
0999 typename track_container_t, template <typename> class holder_t>
1000 auto fit(source_link_iterator_t it, source_link_iterator_t end,
1001 const start_parameters_t& sParameters,
1002 const Gx2FitterOptions<traj_t>& gx2fOptions,
1003 TrackContainer<track_container_t, traj_t, holder_t>& trackContainer)
1004 const -> Result<typename TrackContainer<track_container_t, traj_t,
1005 holder_t>::TrackProxy>
1006 requires(!isDirectNavigator)
1007 {
1008
1009
1010
1011 ACTS_VERBOSE("Preparing " << std::distance(it, end)
1012 << " input measurements");
1013 std::map<GeometryIdentifier, SourceLink> inputMeasurements;
1014
1015 for (; it != end; ++it) {
1016 SourceLink sl = *it;
1017 auto geoId = gx2fOptions.extensions.surfaceAccessor(sl)->geometryId();
1018 inputMeasurements.emplace(geoId, std::move(sl));
1019 }
1020 ACTS_VERBOSE("inputMeasurements.size() = " << inputMeasurements.size());
1021
1022
1023
1024 const bool multipleScattering = gx2fOptions.multipleScattering;
1025
1026
1027
1028 using GX2FAborter = Aborter<parameters_t>;
1029 using GX2FActor = Actor<parameters_t>;
1030
1031 using GX2FResult = typename GX2FActor::result_type;
1032 using Actors = Acts::ActionList<GX2FActor>;
1033 using Aborters = Acts::AbortList<GX2FAborter>;
1034
1035 using PropagatorOptions =
1036 typename propagator_t::template Options<Actors, Aborters>;
1037
1038 start_parameters_t params = sParameters;
1039 BoundVector deltaParams = BoundVector::Zero();
1040 double chi2sum = 0;
1041 double oldChi2sum = std::numeric_limits<double>::max();
1042 BoundMatrix aMatrix = BoundMatrix::Zero();
1043 BoundVector bVector = BoundVector::Zero();
1044
1045
1046
1047
1048
1049 track_container_t trackContainerTempBackend;
1050 traj_t trajectoryTempBackend;
1051 TrackContainer trackContainerTemp{trackContainerTempBackend,
1052 trajectoryTempBackend};
1053
1054
1055
1056
1057 std::size_t tipIndex = Acts::MultiTrajectoryTraits::kInvalid;
1058
1059
1060
1061 std::size_t ndfSystem = std::numeric_limits<std::size_t>::max();
1062
1063
1064
1065 std::unordered_map<GeometryIdentifier, ScatteringProperties> scatteringMap;
1066
1067
1068
1069 BoundMatrix fullCovariancePredicted = BoundMatrix::Identity();
1070
1071 ACTS_VERBOSE("params:\n" << params);
1072
1073
1074 ACTS_DEBUG("Start to iterate");
1075
1076
1077
1078
1079 std::size_t nUpdate = 0;
1080 for (nUpdate = 0; nUpdate < gx2fOptions.nUpdateMax; nUpdate++) {
1081 ACTS_DEBUG("nUpdate = " << nUpdate + 1 << "/" << gx2fOptions.nUpdateMax);
1082
1083
1084 params.parameters() += deltaParams;
1085 ACTS_VERBOSE("updated params:\n" << params);
1086
1087
1088 Acts::GeometryContext geoCtx = gx2fOptions.geoContext;
1089 Acts::MagneticFieldContext magCtx = gx2fOptions.magFieldContext;
1090
1091 PropagatorOptions propagatorOptions(geoCtx, magCtx);
1092
1093
1094
1095 for (const auto& [surfaceId, _] : inputMeasurements) {
1096 propagatorOptions.navigation.insertExternalSurface(surfaceId);
1097 }
1098
1099 auto& gx2fActor = propagatorOptions.actionList.template get<GX2FActor>();
1100 gx2fActor.inputMeasurements = &inputMeasurements;
1101 gx2fActor.multipleScattering = multipleScattering;
1102 gx2fActor.extensions = gx2fOptions.extensions;
1103 gx2fActor.calibrationContext = &gx2fOptions.calibrationContext.get();
1104 gx2fActor.actorLogger = m_actorLogger.get();
1105 gx2fActor.scatteringMap = &scatteringMap;
1106 gx2fActor.parametersWithHypothesis = ¶ms;
1107
1108 auto propagatorState = m_propagator.makeState(params, propagatorOptions);
1109
1110 auto& r = propagatorState.template get<Gx2FitterResult<traj_t>>();
1111 r.fittedStates = &trajectoryTempBackend;
1112
1113
1114
1115 trackContainerTemp.clear();
1116
1117 auto propagationResult = m_propagator.template propagate(propagatorState);
1118
1119
1120 auto result = m_propagator.template makeResult(std::move(propagatorState),
1121 propagationResult,
1122 propagatorOptions, false);
1123
1124 if (!result.ok()) {
1125 ACTS_ERROR("Propagation failed: " << result.error());
1126 return result.error();
1127 }
1128
1129
1130
1131 auto& propRes = *result;
1132 GX2FResult gx2fResult = std::move(propRes.template get<GX2FResult>());
1133
1134 if (!gx2fResult.result.ok()) {
1135 ACTS_INFO("GlobalChiSquareFitter failed in actor: "
1136 << gx2fResult.result.error() << ", "
1137 << gx2fResult.result.error().message());
1138 return gx2fResult.result.error();
1139 }
1140
1141 auto track = trackContainerTemp.makeTrack();
1142 tipIndex = gx2fResult.lastMeasurementIndex;
1143
1144
1145
1146
1147
1148 if (tipIndex == Acts::MultiTrajectoryTraits::kInvalid) {
1149 ACTS_INFO("Did not find any measurements in nUpdate "
1150 << nUpdate + 1 << "/" << gx2fOptions.nUpdateMax);
1151 return Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements;
1152 }
1153
1154 track.tipIndex() = tipIndex;
1155 track.linkForward();
1156
1157
1158 std::size_t countNdf = 0;
1159
1160
1161
1162 std::size_t nMaterialSurfaces = 0;
1163 if (multipleScattering) {
1164 ACTS_DEBUG("Count the valid material surfaces.");
1165 for (const auto& trackState : track.trackStates()) {
1166 const auto typeFlags = trackState.typeFlags();
1167 const bool stateHasMaterial =
1168 typeFlags.test(TrackStateFlag::MaterialFlag);
1169
1170 if (!stateHasMaterial) {
1171 continue;
1172 }
1173
1174
1175 const GeometryIdentifier geoId =
1176 trackState.referenceSurface().geometryId();
1177
1178 const auto scatteringMapId = scatteringMap.find(geoId);
1179 assert(scatteringMapId != scatteringMap.end() &&
1180 "No scattering angles found for material surface.");
1181 if (!scatteringMapId->second.materialIsValid()) {
1182 continue;
1183 }
1184
1185 nMaterialSurfaces++;
1186 }
1187 }
1188
1189
1190
1191 const std::size_t dimsExtendedParams = eBoundSize + 2 * nMaterialSurfaces;
1192
1193
1194 chi2sum = 0;
1195 Eigen::MatrixXd aMatrixExtended =
1196 Eigen::MatrixXd::Zero(dimsExtendedParams, dimsExtendedParams);
1197 Eigen::VectorXd bVectorExtended =
1198 Eigen::VectorXd::Zero(dimsExtendedParams);
1199
1200 std::vector<BoundMatrix> jacobianFromStart;
1201 jacobianFromStart.emplace_back(BoundMatrix::Identity());
1202
1203
1204
1205
1206
1207 std::vector<GeometryIdentifier> geoIdVector;
1208
1209 for (const auto& trackState : track.trackStates()) {
1210
1211 const GeometryIdentifier geoId =
1212 trackState.referenceSurface().geometryId();
1213 ACTS_DEBUG("Start to investigate trackState on surface " << geoId);
1214 const auto typeFlags = trackState.typeFlags();
1215 const bool stateHasMeasurement =
1216 typeFlags.test(TrackStateFlag::MeasurementFlag);
1217 const bool stateHasMaterial =
1218 typeFlags.test(TrackStateFlag::MaterialFlag);
1219
1220
1221
1222
1223 bool doMaterial = multipleScattering && stateHasMaterial;
1224 if (doMaterial) {
1225 const auto scatteringMapId = scatteringMap.find(geoId);
1226 assert(scatteringMapId != scatteringMap.end() &&
1227 "No scattering angles found for material surface.");
1228 doMaterial = doMaterial && scatteringMapId->second.materialIsValid();
1229 }
1230
1231
1232 if (!stateHasMeasurement && !doMaterial) {
1233 ACTS_INFO(" Skip state.");
1234 continue;
1235 }
1236
1237
1238 for (auto& jac : jacobianFromStart) {
1239 jac = trackState.jacobian() * jac;
1240 }
1241
1242
1243 if (stateHasMeasurement) {
1244 ACTS_DEBUG(" Handle measurement.");
1245
1246 const auto measDim = trackState.calibratedSize();
1247
1248 if (measDim < 1 || 6 < measDim) {
1249 ACTS_ERROR("Can not process state with measurement with "
1250 << measDim << " dimensions.");
1251 throw std::domain_error(
1252 "Found measurement with less than 1 or more than 6 "
1253 "dimension(s).");
1254 }
1255
1256 countNdf += measDim;
1257
1258 visit_measurement(measDim, [&](auto N) {
1259 addMeasurementToGx2fSums<N>(aMatrixExtended, bVectorExtended,
1260 chi2sum, jacobianFromStart, trackState,
1261 *m_addToSumLogger);
1262 });
1263 }
1264
1265
1266 if (doMaterial) {
1267 ACTS_DEBUG(" Handle material");
1268
1269 jacobianFromStart.emplace_back(BoundMatrix::Identity());
1270
1271
1272 addMaterialToGx2fSums(aMatrixExtended, bVectorExtended, chi2sum,
1273 geoIdVector.size(), scatteringMap, trackState,
1274 *m_addToSumLogger);
1275
1276 geoIdVector.emplace_back(geoId);
1277 }
1278 }
1279
1280
1281
1282
1283
1284
1285 if (aMatrixExtended(4, 4) == 0) {
1286 ndfSystem = 4;
1287 } else if (aMatrixExtended(5, 5) == 0) {
1288 ndfSystem = 5;
1289 } else {
1290 ndfSystem = 6;
1291 }
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303 if ((nUpdate > 0) && (ndfSystem + 1 > countNdf)) {
1304 ACTS_INFO("Not enough measurements. Require "
1305 << ndfSystem + 1 << ", but only " << countNdf
1306 << " could be used.");
1307 return Experimental::GlobalChiSquareFitterError::NotEnoughMeasurements;
1308 }
1309
1310
1311 aMatrix = aMatrixExtended.topLeftCorner<eBoundSize, eBoundSize>().eval();
1312 bVector = bVectorExtended.topLeftCorner<eBoundSize, 1>().eval();
1313
1314
1315 Eigen::VectorXd deltaParamsExtended =
1316 aMatrixExtended.colPivHouseholderQr().solve(bVectorExtended);
1317
1318 deltaParams = deltaParamsExtended.topLeftCorner<eBoundSize, 1>().eval();
1319
1320 ACTS_VERBOSE("aMatrix:\n"
1321 << aMatrix << "\n"
1322 << "bVector:\n"
1323 << bVector << "\n"
1324 << "deltaParams:\n"
1325 << deltaParams << "\n"
1326 << "deltaParamsExtended:\n"
1327 << deltaParamsExtended << "\n"
1328 << "oldChi2sum = " << oldChi2sum << "\n"
1329 << "chi2sum = " << chi2sum);
1330
1331 if ((gx2fOptions.relChi2changeCutOff != 0) && (nUpdate > 0) &&
1332 (std::abs(chi2sum / oldChi2sum - 1) <
1333 gx2fOptions.relChi2changeCutOff)) {
1334 ACTS_INFO("Abort with relChi2changeCutOff after "
1335 << nUpdate + 1 << "/" << gx2fOptions.nUpdateMax
1336 << " iterations.");
1337 updateCovariancePredicted(fullCovariancePredicted, aMatrixExtended,
1338 ndfSystem);
1339 break;
1340 }
1341
1342 if (chi2sum > oldChi2sum + 1e-5) {
1343 ACTS_DEBUG("chi2 not converging monotonically");
1344
1345 updateCovariancePredicted(fullCovariancePredicted, aMatrixExtended,
1346 ndfSystem);
1347 break;
1348 }
1349
1350
1351
1352 if (nUpdate == gx2fOptions.nUpdateMax - 1) {
1353
1354
1355
1356
1357
1358 if (gx2fOptions.nUpdateMax > 5) {
1359 ACTS_INFO("Did not converge in " << gx2fOptions.nUpdateMax
1360 << " updates.");
1361 return Experimental::GlobalChiSquareFitterError::DidNotConverge;
1362 }
1363
1364 updateCovariancePredicted(fullCovariancePredicted, aMatrixExtended,
1365 ndfSystem);
1366 break;
1367 }
1368
1369 if (multipleScattering) {
1370
1371 for (std::size_t matSurface = 0; matSurface < nMaterialSurfaces;
1372 matSurface++) {
1373 const std::size_t deltaPosition = eBoundSize + 2 * matSurface;
1374 const GeometryIdentifier geoId = geoIdVector[matSurface];
1375 const auto scatteringMapId = scatteringMap.find(geoId);
1376 assert(scatteringMapId != scatteringMap.end() &&
1377 "No scattering angles found for material surface.");
1378 scatteringMapId->second.scatteringAngles().block<2, 1>(2, 0) +=
1379 deltaParamsExtended.block<2, 1>(deltaPosition, 0).eval();
1380 }
1381 }
1382
1383 oldChi2sum = chi2sum;
1384 }
1385 ACTS_DEBUG("Finished to iterate");
1386 ACTS_VERBOSE("final params:\n" << params);
1387
1388
1389 ACTS_VERBOSE("Final scattering angles:");
1390 for (const auto& [key, value] : scatteringMap) {
1391 if (!value.materialIsValid()) {
1392 continue;
1393 }
1394 const auto& angles = value.scatteringAngles();
1395 ACTS_VERBOSE(" ( " << angles[eBoundTheta] << " | " << angles[eBoundPhi]
1396 << " )");
1397 }
1398
1399 ACTS_VERBOSE("final covariance:\n" << fullCovariancePredicted);
1400
1401
1402
1403 if (gx2fOptions.nUpdateMax > 0) {
1404 ACTS_VERBOSE("final deltaParams:\n" << deltaParams);
1405 ACTS_VERBOSE("Propagate with the final covariance.");
1406
1407 params.covariance() = fullCovariancePredicted;
1408
1409
1410 Acts::GeometryContext geoCtx = gx2fOptions.geoContext;
1411 Acts::MagneticFieldContext magCtx = gx2fOptions.magFieldContext;
1412
1413 PropagatorOptions propagatorOptions(geoCtx, magCtx);
1414 auto& gx2fActor = propagatorOptions.actionList.template get<GX2FActor>();
1415 gx2fActor.inputMeasurements = &inputMeasurements;
1416 gx2fActor.multipleScattering = multipleScattering;
1417 gx2fActor.extensions = gx2fOptions.extensions;
1418 gx2fActor.calibrationContext = &gx2fOptions.calibrationContext.get();
1419 gx2fActor.actorLogger = m_actorLogger.get();
1420 gx2fActor.scatteringMap = &scatteringMap;
1421 gx2fActor.parametersWithHypothesis = ¶ms;
1422
1423 auto propagatorState = m_propagator.makeState(params, propagatorOptions);
1424
1425 auto& r = propagatorState.template get<Gx2FitterResult<traj_t>>();
1426 r.fittedStates = &trackContainer.trackStateContainer();
1427
1428 m_propagator.template propagate(propagatorState);
1429 }
1430
1431 if (!trackContainer.hasColumn(
1432 Acts::hashString(Gx2fConstants::gx2fnUpdateColumn))) {
1433 trackContainer.template addColumn<std::uint32_t>("Gx2fnUpdateColumn");
1434 }
1435
1436
1437 auto track = trackContainer.makeTrack();
1438 track.tipIndex() = tipIndex;
1439 track.parameters() = params.parameters();
1440 track.covariance() = fullCovariancePredicted;
1441 track.setReferenceSurface(params.referenceSurface().getSharedPtr());
1442
1443 if (trackContainer.hasColumn(
1444 Acts::hashString(Gx2fConstants::gx2fnUpdateColumn))) {
1445 ACTS_DEBUG("Add nUpdate to track");
1446 track.template component<std::uint32_t>("Gx2fnUpdateColumn") =
1447 static_cast<std::uint32_t>(nUpdate);
1448 }
1449
1450
1451 calculateTrackQuantities(track);
1452
1453
1454
1455 track.chi2() = chi2sum;
1456
1457
1458 return track;
1459 }
1460 };
1461
1462 }