Warning, file /acts/Examples/Algorithms/TrackFinding/src/TrackParamsEstimationAlgorithm.cpp was not indexed
or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/TrackFinding/TrackParamsEstimationAlgorithm.hpp"
0010
0011 #include "Acts/Definitions/Algebra.hpp"
0012 #include "Acts/Definitions/Direction.hpp"
0013 #include "Acts/Definitions/TrackParametrization.hpp"
0014 #include "Acts/EventData/BoundTrackParameters.hpp"
0015 #include "Acts/EventData/ParticleHypothesis.hpp"
0016 #include "Acts/Geometry/GeometryIdentifier.hpp"
0017 #include "Acts/Propagator/Propagator.hpp"
0018 #include "Acts/Propagator/PropagatorError.hpp"
0019 #include "Acts/Propagator/SympyStepper.hpp"
0020 #include "Acts/Propagator/VoidNavigator.hpp"
0021 #include "Acts/Seeding/EstimateTrackParamsFromSeed.hpp"
0022 #include "Acts/Surfaces/Surface.hpp"
0023 #include "Acts/Utilities/Intersection.hpp"
0024 #include "Acts/Utilities/Logger.hpp"
0025 #include "Acts/Utilities/VectorHelpers.hpp"
0026 #include "ActsExamples/EventData/IndexSourceLink.hpp"
0027 #include "ActsExamples/EventData/SpacePoint.hpp"
0028 #include "ActsExamples/EventData/Track.hpp"
0029 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0030
0031 #include <array>
0032 #include <cmath>
0033 #include <cstddef>
0034 #include <optional>
0035 #include <ostream>
0036 #include <span>
0037 #include <stdexcept>
0038 #include <utility>
0039 #include <vector>
0040
0041 namespace ActsExamples {
0042
0043 namespace {
0044
0045
0046
0047 using SeedPropagator =
0048 Acts::Propagator<Acts::SympyStepper, Acts::VoidNavigator>;
0049 using SeedPropagatorOptions = SeedPropagator::Options<>;
0050
0051 Acts::Result<Acts::FreeVector> estimateFreeParams(
0052 std::span<const Acts::Vector3> positions, const Acts::Vector3& bField,
0053 double t0, std::span<const double> weights, std::size_t refineIterations) {
0054
0055 if (positions.size() == 3) {
0056 return Acts::Result<Acts::FreeVector>::success(
0057 Acts::estimateTrackParamsFromSeed(positions[0], t0, positions[1],
0058 positions[2], bField));
0059 }
0060
0061 return Acts::estimateTrackParamsFromSpacePoints(positions, bField, t0,
0062 refineIterations, weights);
0063 }
0064
0065
0066 Acts::Result<Acts::BoundVector> transportToSurface(
0067 const SeedPropagator& propagator, const SeedPropagatorOptions& options,
0068 const Acts::FreeVector& freeParams, const Acts::Surface& surface,
0069 const Acts::ParticleHypothesis& hypothesis) {
0070 const Acts::Vector3 direction = freeParams.segment<3>(Acts::eFreeDir0);
0071
0072
0073 const Acts::Intersection3D intersection =
0074 surface
0075 .intersect(options.geoContext, freeParams.segment<3>(Acts::eFreePos0),
0076 direction)
0077 .closest();
0078 if (!intersection.isValid()) {
0079 return Acts::Result<Acts::BoundVector>::failure(
0080 Acts::PropagatorError::Failure);
0081 }
0082
0083 SeedPropagatorOptions surfaceOptions = options;
0084 surfaceOptions.direction =
0085 Acts::Direction::fromScalarZeroAsPositive(intersection.pathLength());
0086
0087 const Acts::BoundTrackParameters start =
0088 Acts::BoundTrackParameters::createCurvilinear(
0089 freeParams.segment<4>(Acts::eFreePos0), direction,
0090 freeParams[Acts::eFreeQOverP], std::nullopt, hypothesis);
0091
0092 auto result = propagator.propagate(start, surface, surfaceOptions);
0093 if (!result.ok()) {
0094 return Acts::Result<Acts::BoundVector>::failure(result.error());
0095 }
0096 return Acts::Result<Acts::BoundVector>::success(
0097 result->endParameters.value().parameters());
0098 }
0099
0100 }
0101
0102 TrackParamsEstimationAlgorithm::SpacePointWeight
0103 TrackParamsEstimationAlgorithm::inverseRadiusPowerWeight(double exponent) {
0104 return [exponent](const Acts::Vector3& position) {
0105 const double r = Acts::VectorHelpers::perp(position);
0106 if (r <= 0) {
0107 return 1.;
0108 }
0109 return std::pow(r, -exponent);
0110 };
0111 }
0112
0113 TrackParamsEstimationAlgorithm::TrackParamsEstimationAlgorithm(
0114 const Config& cfg, std::unique_ptr<const Acts::Logger> logger)
0115 : IAlgorithm("TrackParamsEstimationAlgorithm", std::move(logger)),
0116 m_cfg(cfg) {
0117 if (m_cfg.inputSeeds.empty()) {
0118 throw std::invalid_argument("Missing seeds input collection");
0119 }
0120 if (m_cfg.outputTrackParameters.empty()) {
0121 throw std::invalid_argument("Missing track parameters output collection");
0122 }
0123 if (!m_cfg.trackingGeometry) {
0124 throw std::invalid_argument("Missing tracking geometry");
0125 }
0126 if (!m_cfg.magneticField) {
0127 throw std::invalid_argument("Missing magnetic field");
0128 }
0129
0130 m_inputSeeds.initialize(m_cfg.inputSeeds);
0131 m_inputTracks.maybeInitialize(m_cfg.inputProtoTracks);
0132 m_inputParticleHypotheses.maybeInitialize(m_cfg.inputParticleHypotheses);
0133
0134 m_outputTrackParameters.initialize(m_cfg.outputTrackParameters);
0135 m_outputSeeds.maybeInitialize(m_cfg.outputSeeds);
0136 m_outputTracks.maybeInitialize(m_cfg.outputProtoTracks);
0137 }
0138
0139 ProcessCode TrackParamsEstimationAlgorithm::execute(
0140 const AlgorithmContext& ctx) const {
0141 auto const& seeds = m_inputSeeds(ctx);
0142 ACTS_VERBOSE("Read " << seeds.size() << " seeds");
0143
0144 TrackParametersContainer trackParameters;
0145 trackParameters.reserve(seeds.size());
0146
0147 SeedContainer outputSeeds;
0148 if (m_outputSeeds.isInitialized()) {
0149 outputSeeds.assignSpacePointContainer(seeds.spacePointContainer());
0150 outputSeeds.reserve(seeds.size());
0151 }
0152
0153 const ProtoTrackContainer* inputTracks = nullptr;
0154 ProtoTrackContainer outputTracks;
0155 if (m_inputTracks.isInitialized() && m_outputTracks.isInitialized()) {
0156 const auto& inputTracksRef = m_inputTracks(ctx);
0157 if (seeds.size() != inputTracksRef.size()) {
0158 ACTS_FATAL("Inconsistent number of seeds and proto tracks");
0159 return ProcessCode::ABORT;
0160 }
0161 inputTracks = &inputTracksRef;
0162 outputTracks.reserve(seeds.size());
0163 }
0164
0165 const std::vector<Acts::ParticleHypothesis>* inputParticleHypotheses =
0166 nullptr;
0167 if (m_inputParticleHypotheses.isInitialized()) {
0168 const auto& inputParticleHypothesesRef = m_inputParticleHypotheses(ctx);
0169 if (seeds.size() != inputParticleHypothesesRef.size()) {
0170 ACTS_FATAL("Inconsistent number of seeds and particle hypotheses");
0171 return ProcessCode::ABORT;
0172 }
0173 inputParticleHypotheses = &inputParticleHypothesesRef;
0174 }
0175
0176 auto bCache = m_cfg.magneticField->makeCache(ctx.magFieldContext);
0177
0178 IndexSourceLink::SurfaceAccessor surfaceAccessor{*m_cfg.trackingGeometry};
0179
0180 const SpacePointContainer& spacePoints = seeds.spacePointContainer();
0181
0182 const SeedPropagator propagator(Acts::SympyStepper(m_cfg.magneticField),
0183 Acts::VoidNavigator(),
0184 logger().cloneWithSuffix("Propagator"));
0185 const SeedPropagatorOptions propagatorOptions(ctx.recoGeoContext,
0186 ctx.magFieldContext);
0187
0188
0189 std::array<SpacePointIndex, 3> triplet{};
0190 std::vector<Acts::Vector3> positions;
0191 std::vector<double> weights;
0192
0193 struct {
0194 std::size_t selection = 0;
0195 std::size_t fit = 0;
0196 std::size_t degenerate = 0;
0197 std::size_t transport = 0;
0198
0199 std::size_t total() const {
0200 return selection + fit + degenerate + transport;
0201 }
0202 } skipped;
0203
0204
0205 for (std::size_t iseed = 0; iseed < seeds.size(); ++iseed) {
0206 const auto& seed = seeds[iseed];
0207 if (seed.spacePoints().size() < 3) {
0208 ACTS_WARNING("Seed " << iseed << " has less than 3 space points, skip");
0209 continue;
0210 }
0211
0212 std::span<const SpacePointIndex> selected = seed.spacePointIndices();
0213
0214
0215 if (m_cfg.spacePointSelection != SeedSpacePointSelection::All) {
0216 const std::optional<std::array<SpacePointIndex, 3>> selectedTriplet =
0217 selectSeedSpacePoints(spacePoints, seed.spacePointIndices(),
0218 m_cfg.spacePointSelection,
0219 m_cfg.minTransverseDistance);
0220 if (!selectedTriplet.has_value()) {
0221 ACTS_DEBUG("Seed " << iseed << " failed space point selection, skip");
0222 ++skipped.selection;
0223 continue;
0224 }
0225 triplet = *selectedTriplet;
0226 selected = triplet;
0227 }
0228
0229
0230 const ConstSpacePointProxy bottomSp = spacePoints.at(selected.front());
0231 if (bottomSp.sourceLinks().empty()) {
0232 ACTS_WARNING("Missing source link in the space point");
0233 continue;
0234 }
0235
0236 const Acts::Vector3 bottomSpVec{bottomSp.x(), bottomSp.y(), bottomSp.z()};
0237
0238 const Acts::SourceLink& bottomSourceLink = bottomSp.sourceLinks()[0];
0239 const Acts::Surface* bottomSurface = surfaceAccessor(bottomSourceLink);
0240 if (bottomSurface == nullptr) {
0241 ACTS_WARNING(
0242 "Surface from source link is not found in the tracking geometry");
0243 continue;
0244 }
0245
0246
0247 const auto fieldRes = m_cfg.magneticField->getField(bottomSpVec, bCache);
0248 if (!fieldRes.ok()) {
0249 ACTS_ERROR("Field lookup error: " << fieldRes.error());
0250 return ProcessCode::ABORT;
0251 }
0252 const Acts::Vector3& field = *fieldRes;
0253
0254 if (field.norm() < m_cfg.bFieldMin) {
0255 ACTS_WARNING("Magnetic field at seed " << iseed << " is too small "
0256 << field.norm());
0257 continue;
0258 }
0259
0260 positions.clear();
0261 for (const SpacePointIndex index : selected) {
0262 const ConstSpacePointProxy sp = spacePoints.at(index);
0263 positions.emplace_back(sp.x(), sp.y(), sp.z());
0264 }
0265
0266 if (m_cfg.spacePointWeight) {
0267 weights.clear();
0268 for (const Acts::Vector3& position : positions) {
0269 weights.emplace_back(m_cfg.spacePointWeight(position));
0270 }
0271 }
0272
0273 const double t0 = std::isnan(bottomSp.time()) ? 0.0 : bottomSp.time();
0274
0275 const Acts::Result<Acts::FreeVector> freeParams = estimateFreeParams(
0276 positions, field, t0, weights, m_cfg.geometricRefineIterations);
0277 if (!freeParams.ok()) {
0278 ACTS_DEBUG("Seed " << iseed << " could not be fitted: "
0279 << freeParams.error().message());
0280 ++skipped.fit;
0281 continue;
0282 }
0283
0284
0285 if (!freeParams->allFinite() || (*freeParams)[Acts::eFreeQOverP] == 0) {
0286 ACTS_DEBUG("Seed " << iseed << " has a degenerate estimate, skip");
0287 ++skipped.degenerate;
0288 continue;
0289 }
0290
0291 const Acts::ParticleHypothesis hypothesis =
0292 inputParticleHypotheses != nullptr ? inputParticleHypotheses->at(iseed)
0293 : m_cfg.particleHypothesis;
0294
0295 const Acts::Result<Acts::BoundVector> boundParams = transportToSurface(
0296 propagator, propagatorOptions, *freeParams, *bottomSurface, hypothesis);
0297 if (!boundParams.ok()) {
0298 ACTS_DEBUG("Seed " << iseed
0299 << " could not be transported to the surface of its "
0300 "first space point: "
0301 << boundParams.error().message());
0302 ++skipped.transport;
0303 continue;
0304 }
0305
0306 Acts::EstimateTrackParamCovarianceConfig config{
0307 .initialSigmas =
0308 Eigen::Map<const Acts::BoundVector>{m_cfg.initialSigmas.data()},
0309 .initialSigmaQoverPt = m_cfg.initialSigmaQoverPt,
0310 .initialSigmaPtRel = m_cfg.initialSigmaPtRel,
0311 .initialVarInflation = Eigen::Map<const Acts::BoundVector>{
0312 m_cfg.initialVarInflation.data()}};
0313
0314 const Acts::BoundMatrix cov = Acts::estimateTrackParamCovariance(
0315 config, *boundParams, !std::isnan(bottomSp.time()));
0316
0317 const TrackParameters& trackParams = trackParameters.emplace_back(
0318 bottomSurface->getSharedPtr(), *boundParams, cov, hypothesis);
0319 ACTS_VERBOSE("Estimated track parameters: " << trackParams);
0320 if (m_outputSeeds.isInitialized()) {
0321 auto newSp = outputSeeds.createSeed();
0322
0323 newSp.assignSpacePointIndices(seed.spacePointIndices());
0324 newSp.quality() = seed.quality();
0325 newSp.vertexZ() = seed.vertexZ();
0326 }
0327 if (m_outputTracks.isInitialized() && inputTracks != nullptr) {
0328 outputTracks.push_back(inputTracks->at(iseed));
0329 }
0330 }
0331
0332 ACTS_DEBUG("Estimated " << trackParameters.size() << " track parameters from "
0333 << seeds.size() << " seeds");
0334 if (skipped.total() > 0) {
0335 ACTS_DEBUG(
0336 "Skipped " << skipped.selection
0337 << " seeds without a space point selection, " << skipped.fit
0338 << " without a fit, " << skipped.degenerate
0339 << " with a degenerate estimate and " << skipped.transport
0340 << " without a transport to the surface");
0341 }
0342
0343 m_outputTrackParameters(ctx, std::move(trackParameters));
0344 if (m_outputSeeds.isInitialized()) {
0345 m_outputSeeds(ctx, std::move(outputSeeds));
0346 }
0347
0348 if (m_outputTracks.isInitialized()) {
0349 m_outputTracks(ctx, std::move(outputTracks));
0350 }
0351
0352 return ProcessCode::SUCCESS;
0353 }
0354
0355 }