File indexing completed on 2026-05-27 07:23:36
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include "Acts/Definitions/Common.hpp"
0012 #include "Acts/Definitions/TrackParametrization.hpp"
0013 #include "Acts/EventData/BoundTrackParameters.hpp"
0014 #include "Acts/EventData/MultiTrajectory.hpp"
0015 #include "Acts/EventData/TrackContainerFrontendConcept.hpp"
0016 #include "Acts/EventData/VectorMultiTrajectory.hpp"
0017 #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp"
0018 #include "Acts/Geometry/GeometryContext.hpp"
0019 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0020 #include "Acts/Propagator/ActorList.hpp"
0021 #include "Acts/Propagator/DirectNavigator.hpp"
0022 #include "Acts/Propagator/PropagatorOptions.hpp"
0023 #include "Acts/Propagator/StandardAborters.hpp"
0024 #include "Acts/Propagator/detail/LoopProtection.hpp"
0025 #include "Acts/Propagator/detail/PointwiseMaterialInteraction.hpp"
0026 #include "Acts/Utilities/CalibrationContext.hpp"
0027 #include "Acts/Utilities/Logger.hpp"
0028 #include "Acts/Utilities/Result.hpp"
0029
0030 #include <memory>
0031 #include <vector>
0032
0033 namespace Acts::Experimental {
0034
0035
0036 template <typename traj_t>
0037 struct ReferenceTrajectoryBuilderOptions {
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047 ReferenceTrajectoryBuilderOptions(
0048 const GeometryContext& gctx, const MagneticFieldContext& mctx,
0049 const PropagatorPlainOptions& pOptions, const Surface* tSurface = nullptr,
0050 bool mScattering = true, bool eLoss = true,
0051 const FreeToBoundCorrection& freeToBoundCorrection_ =
0052 FreeToBoundCorrection(false))
0053 : geoContext(gctx),
0054 magFieldContext(mctx),
0055 propagatorPlainOptions(pOptions),
0056 referenceSurface(tSurface),
0057 multipleScattering(mScattering),
0058 energyLoss(eLoss),
0059 freeToBoundCorrection(freeToBoundCorrection_) {}
0060
0061
0062 std::reference_wrapper<const GeometryContext> geoContext;
0063
0064 std::reference_wrapper<const MagneticFieldContext> magFieldContext;
0065
0066
0067 PropagatorPlainOptions propagatorPlainOptions;
0068
0069
0070 const Surface* referenceSurface = nullptr;
0071
0072
0073 bool multipleScattering = true;
0074
0075
0076 bool energyLoss = true;
0077
0078
0079
0080 FreeToBoundCorrection freeToBoundCorrection;
0081 };
0082
0083
0084 template <typename traj_t>
0085 struct ReferenceTrajectoryBuilderResult {
0086
0087 traj_t* trajectory{nullptr};
0088
0089
0090 std::size_t lastTrackStateIndex = kTrackIndexInvalid;
0091
0092
0093
0094 std::optional<BoundTrackParameters> referenceParameters;
0095
0096
0097 bool finished = false;
0098
0099
0100 PathLimitReached pathLimitReached;
0101 };
0102
0103
0104 template <typename propagator_t, typename traj_t>
0105 class ReferenceTrajectoryBuilder {
0106
0107 using NavigatorType = typename propagator_t::Navigator;
0108
0109
0110 static constexpr bool isDirectNavigator =
0111 std::is_same_v<NavigatorType, DirectNavigator>;
0112
0113
0114 using ResultType = ReferenceTrajectoryBuilderResult<traj_t>;
0115
0116 public:
0117
0118 using Options = ReferenceTrajectoryBuilderOptions<traj_t>;
0119
0120
0121 using TrackStateProxy = typename traj_t::TrackStateProxy;
0122
0123
0124 using ConstTrackStateProxy = typename traj_t::ConstTrackStateProxy;
0125
0126
0127
0128
0129 explicit ReferenceTrajectoryBuilder(
0130 propagator_t pPropagator,
0131 std::unique_ptr<const Logger> _logger =
0132 getDefaultLogger("ReferenceTrajectoryBuilder", Logging::INFO))
0133 : m_propagator(std::move(pPropagator)),
0134 m_logger{std::move(_logger)},
0135 m_actorLogger{m_logger->cloneWithSuffix("Actor")} {}
0136
0137 private:
0138
0139 propagator_t m_propagator;
0140
0141
0142 std::unique_ptr<const Logger> m_logger;
0143
0144 std::unique_ptr<const Logger> m_actorLogger;
0145
0146
0147 const Logger& logger() const { return *m_logger; }
0148
0149 class Actor {
0150 public:
0151
0152 using result_type = ResultType;
0153
0154
0155 SurfaceReached targetReached{std::numeric_limits<double>::lowest()};
0156
0157
0158 bool multipleScattering = true;
0159
0160
0161 bool energyLoss = true;
0162
0163
0164
0165 FreeToBoundCorrection freeToBoundCorrection;
0166
0167
0168 EndOfWorldReached endOfWorldReached;
0169
0170
0171 VolumeConstraintAborter volumeConstraintAborter;
0172
0173
0174 const Logger* actorLogger{nullptr};
0175
0176
0177 const Logger& logger() const { return *actorLogger; }
0178
0179
0180
0181
0182
0183
0184
0185
0186
0187
0188
0189 template <typename propagator_state_t, typename stepper_t,
0190 typename navigator_t>
0191 Result<void> act(propagator_state_t& state, const stepper_t& stepper,
0192 const navigator_t& navigator, result_type& result,
0193 const Logger& ) const {
0194 assert(result.trajectory && "No MultiTrajectory set");
0195
0196 if (result.finished) {
0197 return Result<void>::success();
0198 }
0199
0200 ACTS_VERBOSE("ReferenceTrajectory step at pos: "
0201 << stepper.position(state.stepping).transpose()
0202 << " dir: " << stepper.direction(state.stepping).transpose()
0203 << " momentum: "
0204 << stepper.absoluteMomentum(state.stepping));
0205
0206 if (result.pathLimitReached.internalLimit ==
0207 std::numeric_limits<double>::max()) {
0208 detail::setupLoopProtection(state, stepper, result.pathLimitReached,
0209 true, logger());
0210 }
0211
0212 if (const Surface* surface = navigator.currentSurface(state.navigation);
0213 surface != nullptr) {
0214 ACTS_VERBOSE("Handle Surface " << surface->geometryId() << " "
0215 << state.options.direction);
0216 auto res = handleSurface(*surface, state, stepper, navigator, result);
0217 if (!res.ok()) {
0218 ACTS_DEBUG("Error in " << state.options.direction
0219 << " filter: " << res.error());
0220 return res.error();
0221 }
0222 }
0223
0224 const bool isEndOfWorldReached =
0225 endOfWorldReached.checkAbort(state, stepper, navigator, logger());
0226 const bool isVolumeConstraintReached = volumeConstraintAborter.checkAbort(
0227 state, stepper, navigator, logger());
0228 const bool isPathLimitReached = result.pathLimitReached.checkAbort(
0229 state, stepper, navigator, logger());
0230 const bool isTargetReached =
0231 targetReached.checkAbort(state, stepper, navigator, logger());
0232 if (isEndOfWorldReached || isVolumeConstraintReached ||
0233 isPathLimitReached || isTargetReached) {
0234 ACTS_VERBOSE(
0235 "Finalizing reference trajectory: "
0236 << (isEndOfWorldReached ? "end of world reached; " : "")
0237 << (isVolumeConstraintReached ? "volume constraint reached; " : "")
0238 << (isPathLimitReached ? "path limit reached; " : "")
0239 << (isTargetReached ? "target surface reached; " : ""));
0240
0241 if (isTargetReached) {
0242 ACTS_VERBOSE("Setting parameters at target surface");
0243
0244 auto res = stepper.boundState(state.stepping, *targetReached.surface);
0245 if (!res.ok()) {
0246 ACTS_DEBUG("Error while acquiring bound state for target surface: "
0247 << res.error() << " " << res.error().message());
0248 return res.error();
0249 } else {
0250 const auto& [boundParams, jacobian, pathLength] = *res;
0251 result.referenceParameters = boundParams;
0252 }
0253 }
0254
0255 result.finished = true;
0256 }
0257
0258 return Result<void>::success();
0259 }
0260
0261 template <typename propagator_state_t, typename stepper_t,
0262 typename navigator_t>
0263 bool checkAbort(propagator_state_t& , const stepper_t& ,
0264 const navigator_t& , const result_type& result,
0265 const Logger& ) const {
0266 return result.finished;
0267 }
0268
0269 template <typename propagator_state_t, typename stepper_t,
0270 typename navigator_t>
0271 Result<void> handleSurface(const Surface& surface,
0272 propagator_state_t& state,
0273 const stepper_t& stepper,
0274 const navigator_t& navigator,
0275 result_type& result) const {
0276 stepper.transportCovarianceToBound(state.stepping, surface,
0277 freeToBoundCorrection);
0278
0279 detail::performMaterialInteraction(
0280 state, stepper, surface,
0281 detail::determineMaterialUpdateMode(state, navigator,
0282 MaterialUpdateMode::PreUpdate),
0283 NoiseUpdateMode::addNoise, multipleScattering, energyLoss, logger());
0284
0285 TrackStatePropMask mask =
0286 TrackStatePropMask::Predicted | TrackStatePropMask::Jacobian;
0287 TrackStateProxy trackStateProxy =
0288 result.trajectory->makeTrackState(mask, result.lastTrackStateIndex);
0289
0290 ConstTrackStateProxy trackStateProxyConst{trackStateProxy};
0291
0292 trackStateProxy.setReferenceSurface(surface.getSharedPtr());
0293 auto res = stepper.boundState(state.stepping, surface, false,
0294 freeToBoundCorrection);
0295 if (!res.ok()) {
0296 ACTS_DEBUG("Propagate to surface " << surface.geometryId()
0297 << " failed: " << res.error());
0298 return res.error();
0299 }
0300 const auto& [boundParams, jacobian, pathLength] = *res;
0301
0302 trackStateProxy.predicted() = boundParams.parameters();
0303 trackStateProxy.predictedCovariance() = state.stepping.cov;
0304 trackStateProxy.jacobian() = jacobian;
0305 trackStateProxy.pathLength() = pathLength;
0306
0307 auto typeFlags = trackStateProxy.typeFlags();
0308 typeFlags.setHasParameters();
0309 if (surface.hasMaterial()) {
0310 typeFlags.setHasMaterial();
0311 }
0312
0313 result.lastTrackStateIndex = trackStateProxy.index();
0314
0315 detail::performMaterialInteraction(
0316 state, stepper, surface,
0317 detail::determineMaterialUpdateMode(state, navigator,
0318 MaterialUpdateMode::PostUpdate),
0319 NoiseUpdateMode::addNoise, multipleScattering, energyLoss, logger());
0320
0321 return Result<void>::success();
0322 }
0323 };
0324
0325 public:
0326
0327
0328
0329
0330
0331
0332
0333 template <TrackContainerFrontend track_container_t>
0334 Result<typename track_container_t::TrackProxy> build(
0335 const BoundTrackParameters& sParameters, const Options& actorOptions,
0336 track_container_t& trackContainer) const {
0337 auto propagatorOptions = makePropagatorOptions(
0338 actorOptions, nullptr, actorOptions.referenceSurface);
0339 return buildImpl(sParameters, propagatorOptions, trackContainer);
0340 }
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351 template <TrackContainerFrontend track_container_t>
0352 Result<typename track_container_t::TrackProxy> build(
0353 const BoundTrackParameters& sParameters, const Options& actorOptions,
0354 const std::vector<const Surface*>& sSequence,
0355 track_container_t& trackContainer) const
0356 requires isDirectNavigator
0357 {
0358 auto propagatorOptions = makePropagatorOptions(
0359 actorOptions, &sSequence, actorOptions.referenceSurface);
0360 return buildImpl(sParameters, propagatorOptions, trackContainer);
0361 }
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371 template <typename track_proxy_t, typename source_link_range_t>
0372 void attachSourceLinks(
0373 track_proxy_t trackProxy, const source_link_range_t& sourceLinkRange,
0374 const SourceLinkSurfaceAccessor& surfaceAccessor) const {
0375 const std::size_t nMeasurements = std::ranges::distance(sourceLinkRange);
0376
0377 ACTS_VERBOSE("Preparing " << nMeasurements << " input measurements");
0378 std::unordered_map<const Surface*, SourceLink> inputMeasurements;
0379 for (const SourceLink& sourceLink : sourceLinkRange) {
0380 const Surface* surface = surfaceAccessor(sourceLink);
0381 inputMeasurements.try_emplace(surface, sourceLink);
0382 }
0383
0384 for (auto trackState : trackProxy.trackStates()) {
0385 if (!trackState.hasReferenceSurface()) {
0386 continue;
0387 }
0388 const Surface& surface = trackState.referenceSurface();
0389
0390 if (!surface.isSensitive()) {
0391 continue;
0392 }
0393
0394 auto typeFlagsMap = trackState.typeFlags();
0395
0396 if (const auto it = inputMeasurements.find(&surface);
0397 it == inputMeasurements.end()) {
0398 typeFlagsMap.setIsHole();
0399 } else {
0400 SourceLink sourceLink = it->second;
0401
0402 trackState.setUncalibratedSourceLink(std::move(sourceLink));
0403 typeFlagsMap.setHasMeasurement();
0404 }
0405 }
0406 }
0407
0408
0409 using Calibrator =
0410 Delegate<void(const GeometryContext&, const CalibrationContext&,
0411 const SourceLink&, TrackStateProxy)>;
0412
0413
0414
0415
0416
0417
0418
0419
0420
0421 template <typename track_proxy_t>
0422 void calibrateMeasurements(const GeometryContext& geoContext,
0423 const CalibrationContext& calibrationContext,
0424 track_proxy_t trackProxy,
0425 const Calibrator& calibrator) const {
0426 for (auto trackState : trackProxy.trackStates()) {
0427 if (!trackState.typeFlags().hasMeasurement()) {
0428 continue;
0429 }
0430
0431 trackState.addComponents(TrackStatePropMask::Calibrated);
0432
0433 const SourceLink& sourceLink = trackState.getUncalibratedSourceLink();
0434 calibrator(geoContext, calibrationContext, sourceLink, trackState);
0435 }
0436 }
0437
0438
0439 using Updater = Delegate<Result<void>(const GeometryContext&, TrackStateProxy,
0440 const Logger&)>;
0441
0442
0443
0444
0445
0446
0447
0448
0449 template <typename track_proxy_t>
0450 Result<void> filter(const GeometryContext& geoContext,
0451 track_proxy_t trackProxy, const Updater& updater) const {
0452 std::optional<TrackStateProxy> lastTrackState;
0453 BoundVector accumulatedBoundDeltas = BoundVector::Zero();
0454 BoundMatrix predictedCovariance = BoundMatrix::Zero();
0455
0456 for (auto trackState : trackProxy.trackStates()) {
0457 if (lastTrackState.has_value()) {
0458
0459
0460
0461 accumulatedBoundDeltas = trackState.jacobian() * accumulatedBoundDeltas;
0462 trackState.predicted() += accumulatedBoundDeltas;
0463
0464 predictedCovariance = trackState.jacobian() * predictedCovariance *
0465 trackState.jacobian().transpose();
0466
0467 {
0468 const FreeVector freeParams = transformBoundToFreeParameters(
0469 trackState.referenceSurface(), geoContext,
0470 trackState.predicted());
0471
0472 const MaterialSlab materialSlab = detail::evaluateMaterialSlab(
0473 geoContext, trackState.referenceSurface(), Direction::Forward(),
0474 freeParams.segment<3>(eFreePos0),
0475 freeParams.segment<3>(eFreeDir0), MaterialUpdateMode::PreUpdate);
0476
0477 const detail::PointwiseMaterialEffects materialEffects =
0478 detail::computeMaterialEffects(
0479 materialSlab, trackProxy.particleHypothesis(),
0480 freeParams.segment<3>(eFreeDir0), freeParams[eFreeQOverP],
0481 true, true, true);
0482
0483 predictedCovariance(eBoundPhi, eBoundPhi) +=
0484 materialEffects.variancePhi;
0485 predictedCovariance(eBoundTheta, eBoundTheta) +=
0486 materialEffects.varianceTheta;
0487 predictedCovariance(eBoundQOverP, eBoundQOverP) +=
0488 materialEffects.varianceQoverP;
0489 }
0490
0491 trackState.predictedCovariance() = predictedCovariance;
0492 }
0493
0494 if (!trackState.typeFlags().hasMeasurement()) {
0495 trackState.shareFrom(trackState, TrackStatePropMask::Predicted,
0496 TrackStatePropMask::Filtered);
0497 } else {
0498 trackState.addComponents(TrackStatePropMask::Filtered);
0499
0500 const Result<void> updateResult =
0501 updater(geoContext, trackState, logger());
0502 if (!updateResult.ok()) {
0503 ACTS_DEBUG("Error in filter: " << updateResult.error());
0504 return updateResult.error();
0505 }
0506 }
0507
0508 lastTrackState = trackState;
0509 accumulatedBoundDeltas += trackState.filtered() - trackState.predicted();
0510 predictedCovariance = trackState.filteredCovariance();
0511
0512 {
0513 const FreeVector freeParams = transformBoundToFreeParameters(
0514 trackState.referenceSurface(), geoContext, trackState.filtered());
0515
0516 const MaterialSlab materialSlab = detail::evaluateMaterialSlab(
0517 geoContext, trackState.referenceSurface(), Direction::Forward(),
0518 freeParams.segment<3>(eFreePos0), freeParams.segment<3>(eFreeDir0),
0519 MaterialUpdateMode::PostUpdate);
0520
0521 const detail::PointwiseMaterialEffects materialEffects =
0522 detail::computeMaterialEffects(
0523 materialSlab, trackProxy.particleHypothesis(),
0524 freeParams.segment<3>(eFreeDir0), freeParams[eFreeQOverP], true,
0525 true, true);
0526
0527 predictedCovariance(eBoundPhi, eBoundPhi) +=
0528 materialEffects.variancePhi;
0529 predictedCovariance(eBoundTheta, eBoundTheta) +=
0530 materialEffects.varianceTheta;
0531 predictedCovariance(eBoundQOverP, eBoundQOverP) +=
0532 materialEffects.varianceQoverP;
0533 }
0534 }
0535
0536 return Result<void>::success();
0537 }
0538
0539 private:
0540 auto makePropagatorOptions(const Options& actorOptions,
0541 const std::vector<const Surface*>* sSequence,
0542 const Surface* targetSurface) const {
0543 using Actors = ActorList<Actor>;
0544 using PropagatorOptions = typename propagator_t::template Options<Actors>;
0545
0546 PropagatorOptions propagatorOptions(actorOptions.geoContext,
0547 actorOptions.magFieldContext);
0548 propagatorOptions.setPlainOptions(actorOptions.propagatorPlainOptions);
0549
0550 if constexpr (!isDirectNavigator) {
0551 if (sSequence != nullptr) {
0552 for (const Surface* surface : *sSequence) {
0553 propagatorOptions.navigation.appendExternalSurface(*surface);
0554 }
0555 }
0556 } else {
0557 assert(sSequence != nullptr &&
0558 "DirectNavigator requires a surface sequence for "
0559 "ReferenceTrajectory");
0560 propagatorOptions.navigation.externalSurfaces = *sSequence;
0561 }
0562
0563 auto& actor = propagatorOptions.actorList.template get<Actor>();
0564 actor.targetReached.surface = targetSurface;
0565 actor.multipleScattering = actorOptions.multipleScattering;
0566 actor.energyLoss = actorOptions.energyLoss;
0567 actor.freeToBoundCorrection = actorOptions.freeToBoundCorrection;
0568 actor.actorLogger = m_actorLogger.get();
0569
0570 return propagatorOptions;
0571 }
0572
0573 template <typename propagator_options_t,
0574 TrackContainerFrontend track_container_t>
0575 auto buildImpl(const BoundTrackParameters& sParameters,
0576 const propagator_options_t& propagatorOptions,
0577 track_container_t& trackContainer) const
0578 -> Result<typename track_container_t::TrackProxy> {
0579 auto propagatorState = m_propagator.makeState(propagatorOptions);
0580
0581 auto propagatorInitResult =
0582 m_propagator.initialize(propagatorState, sParameters);
0583 if (!propagatorInitResult.ok()) {
0584 ACTS_DEBUG("Propagation initialization failed: "
0585 << propagatorInitResult.error());
0586 return propagatorInitResult.error();
0587 }
0588
0589 auto& actorResult = propagatorState.template get<ResultType>();
0590 actorResult.trajectory = &trackContainer.trackStateContainer();
0591
0592 auto result = m_propagator.propagate(propagatorState);
0593
0594 if (!result.ok()) {
0595 ACTS_DEBUG("Propagation failed: " << result.error());
0596 return result.error();
0597 }
0598
0599 auto track = trackContainer.makeTrack();
0600 track.tipIndex() = actorResult.lastTrackStateIndex;
0601 if (actorResult.referenceParameters.has_value()) {
0602 const auto& params = *actorResult.referenceParameters;
0603 track.parameters() = params.parameters();
0604 track.covariance() = params.covariance().value();
0605 track.setReferenceSurface(params.referenceSurface().getSharedPtr());
0606 }
0607
0608 track.linkForward();
0609
0610 return track;
0611 }
0612 };
0613
0614 }