ATLAS Offline Software
Loading...
Searching...
No Matches
TrackParamsEstimationTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
8#include "Acts/Seeding/EstimateTrackParamsFromSeed.hpp"
9#include "Acts/SpacePointFormation/StripSpacePointCalibration.hpp"
10#include "Acts/EventData/StripSpacePointCalibrationDetails.hpp"
11#include "Acts/EventData/TransformationHelpers.hpp"
12#include "Acts/Utilities/MathHelpers.hpp"
13
14#include <algorithm>
15#include <cmath>
16#include <ranges>
17
18namespace ActsTrk {
19
20namespace {
21
22template <typename sp_range_t>
23Acts::FreeVector estimateTrackParamsFromSeed(
24 const sp_range_t& spRange,
25 const Acts::Vector3& bField,
26 const std::size_t stripCalibrationIterations) {
27 std::array<const xAOD::SpacePoint*, 3> spArray{};
28 std::array<Acts::Vector3, 3> spPositions{};
29
30 std::size_t i = 0;
31 for (const auto* sp : spRange) {
32 if (sp == nullptr) {
33 throw std::invalid_argument("Empty space point found.");
34 }
35 if (i >= spArray.size()) {
36 throw std::invalid_argument("More than 3 space points provided.");
37 }
38 spArray[i] = sp;
39 spPositions[i] = Acts::Vector3(sp->x(), sp->y(), sp->z());
40 ++i;
41 }
42 if (i < spArray.size()) {
43 throw std::invalid_argument("Less than 3 space points provided.");
44 }
45
46 const bool hasStrip = std::ranges::any_of(spArray, [](const xAOD::SpacePoint* sp) {
47 return sp->elementIdList().size() > 1;
48 });
49 if (hasStrip) {
50 std::array<Acts::Vector3, 3> spTangents{};
51
52 for (std::size_t i = 0; i < stripCalibrationIterations; ++i) {
53 Acts::estimateTrackParamsFromSeed(
54 spPositions[0], 0, spPositions[1], spPositions[2], bField,
55 &spTangents[0], &spTangents[1], &spTangents[2]);
56
57 for (std::size_t j = 0; j < spArray.size(); ++j) {
58 const xAOD::SpacePoint* sp = spArray[j];
59 const bool isStrip = sp->elementIdList().size() > 1;
60 if (!isStrip) {
61 continue;
62 }
63
64 Acts::OuterStripSpacePointCalibrationDetails calibrationDetails;
65 Eigen::Map<Eigen::Vector3f>(calibrationDetails.outerCenter.data()) = sp->topStripCenter();
66 Eigen::Map<Eigen::Vector3f>(calibrationDetails.innerToOuterSeparation.data()) = sp->stripCenterDistance();
67 Eigen::Map<Eigen::Vector3f>(calibrationDetails.outerHalfVector.data()) = sp->topHalfStripLength() * sp->topStripDirection();
68 Eigen::Map<Eigen::Vector3f>(calibrationDetails.innerHalfVector.data()) = sp->bottomHalfStripLength() * sp->bottomStripDirection();
69 const Acts::OuterStripSpacePointCalibrationDetailsDerived derivedCalibrationDetails =
70 Acts::deriveOuterStripSpacePointCalibrationDetails(calibrationDetails);
71
72 const std::optional<Eigen::Vector3f> calibratedPosition =
73 Acts::calibrateOuterStripSpacePoint(spTangents[j].cast<float>(), derivedCalibrationDetails);
74 if (!calibratedPosition.has_value()) {
75 continue;
76 }
77 spPositions[j] = calibratedPosition->cast<double>();
78 }
79 }
80 }
81
82 return Acts::estimateTrackParamsFromSeed(
83 spPositions[0], 0, spPositions[1], spPositions[2], bField);
84}
85
86}
87
89 const std::string& name,
90 const IInterface* parent)
91 : base_class(type, name, parent)
92 {}
93
95 {
96 ATH_MSG_INFO( "Initializing " << name() << "..." );
97
98 ATH_MSG_DEBUG( "Properties Summary:" );
99 ATH_MSG_DEBUG( " " << m_sigmaLoc0 );
100 ATH_MSG_DEBUG( " " << m_sigmaLoc1 );
101 ATH_MSG_DEBUG( " " << m_sigmaPhi );
102 ATH_MSG_DEBUG( " " << m_sigmaTheta );
104 ATH_MSG_DEBUG( " " << m_sigmaT0 );
107 ATH_MSG_DEBUG( " " << m_bFieldMode );
108 ATH_MSG_DEBUG( " " << m_firstSp );
109 ATH_MSG_DEBUG( " " << m_minDeltaR );
111 ATH_MSG_DEBUG( " " << m_refitSeeds );
112
113 ATH_CHECK(m_fitterTool.retrieve(EnableTool{m_refitSeeds}));
114
115 m_logger = makeActsAthenaLogger(this, "Acts");
116
117 m_extrapolator = Extrapolator(Stepper(std::make_shared<ATLASMagneticFieldWrapper>()), Navigator(), logger().cloneWithSuffix("Prop"));
118
120
121 if (m_refitSeeds) {
124 m_doRefitErrInflation = (m_refitErrInflation.value() != std::vector<double>{1., 1., 1., 1., 1., 1.});
125 }
126
127 return StatusCode::SUCCESS;
128 }
129
130 std::pair<std::optional<Acts::BoundTrackParameters>, TrackParamsEstimationTool::EstimationStatus>
132 const ActsTrk::Seed& seed,
133 bool reverseSearch,
134 const Acts::GeometryContext& geoContext,
135 const Acts::MagneticFieldContext& magFieldContext,
136 const Acts::CalibrationContext& calContext,
137 std::function<const Acts::Surface&(const ActsTrk::Seed& seed, bool useTopSp)> retrieveSurface) const
138 {
139 bool useTopSp = estimateFromTopSp(reverseSearch);
140
141 const auto& sp_collection = seed.sp();
142 if ( sp_collection.size() < 3 ) return {std::nullopt, kNoSeedRefit};
143 const xAOD::SpacePoint* bottom_sp = (useTopSp && m_bFieldMode != 2) ? sp_collection.back() : sp_collection.front();
144 if (m_parameterEstimationMode == 3 && useTopSp && m_bFieldMode != 2) {
145 // B-field at the first SP used for the estimate in search order
146 bottom_sp = sp_collection.at(sp_collection.size() - m_spacePointIndicesFun(sp_collection, useTopSp)[0] - 1);
147 }
148
149 // Magnetic Field
150 ATLASMagneticFieldWrapper magneticField;
151 Acts::MagneticFieldProvider::Cache magFieldCache = magneticField.makeCache( magFieldContext );
152 Acts::Vector3 bField = *magneticField.getField( Acts::Vector3(bottom_sp->x(), bottom_sp->y(), bottom_sp->z()),
153 magFieldCache );
154 if (m_bFieldMode == 1) {
155 bField[0] = 0.0;
156 bField[1] = 0.0;
157 }
158
159 // Get the surface
160 const Acts::Surface& surface = retrieveSurface(seed, useTopSp);
161
163 seed,
164 reverseSearch,
165 geoContext,
166 magFieldContext,
167 calContext,
168 surface,
169 bField);
170 }
171
172 std::pair<std::optional<Acts::BoundTrackParameters>, TrackParamsEstimationTool::EstimationStatus>
174 const ActsTrk::Seed& seed,
175 bool reverseSearch,
176 const Acts::GeometryContext& geoContext,
177 const Acts::MagneticFieldContext& magFieldContext,
178 const Acts::CalibrationContext& calContext,
179 const Acts::Surface& surface,
180 const Acts::Vector3& bField) const
181 {
182 bool useTopSp = estimateFromTopSp(reverseSearch);
183
184 // Get SPs
185 const auto& sp_collection = seed.sp();
186 const std::size_t nSp = sp_collection.size();
187 if (nSp < 3) return {std::nullopt, kNoSeedRefit};
188
189 // Function to extract the values from sp_collection
190 const auto sp_collection_extract = std::views::transform([&sp_collection, useTopSp](std::size_t i) {
191 return sp_collection.at(useTopSp ? sp_collection.size() - i - 1 : i);
192 });
193
194 // Compute free parameters
195 Acts::FreeVector freeParams = estimateTrackParamsFromSeed(m_spacePointIndicesFun(sp_collection, useTopSp) | sp_collection_extract, bField, m_stripCalibrationIterations);
196
197 if (m_parameterEstimationMode == 1 && nSp > 3ul) {
198 const auto spacePointIndicesFun2 = [](std::size_t nSp) -> std::array<std::size_t, 3> {
199 return {0, nSp / 2ul, nSp - 1};
200 };
201 const Acts::FreeVector freeParams2 = estimateTrackParamsFromSeed(spacePointIndicesFun2(nSp) | sp_collection_extract, bField, m_stripCalibrationIterations);
202 ATH_MSG_DEBUG("update seed p = " << 1.0 / freeParams[Acts::eFreeQOverP] << " to " << 1.0 / freeParams2[Acts::eFreeQOverP]);
203 freeParams[Acts::eFreeQOverP] = freeParams2[Acts::eFreeQOverP];
204 }
205
206 if (useTopSp) {
207 // reverse direction so momentum vector pointing outwards
208 freeParams = Acts::reflectFreeParameters(freeParams);
209 }
210
211 // Convert free params to curvilinear params for extrapolation
212 Acts::BoundTrackParameters curvilinearParams = Acts::BoundTrackParameters::createCurvilinear(
213 freeParams.segment<4>(Acts::eFreePos0),
214 freeParams.segment<3>(Acts::eFreeDir0),
215 freeParams[Acts::eFreeQOverP],
216 std::nullopt,
217 Acts::ParticleHypothesis::pion());
218
219 // Extrapolate to surface
220 Acts::PropagatorPlainOptions propOptions(geoContext, magFieldContext);
221 propOptions.direction = Acts::Direction::fromScalarZeroAsPositive(
222 surface.intersect(
223 geoContext,
224 freeParams.segment<3>(Acts::eFreePos0),
225 freeParams.segment<3>(Acts::eFreeDir0)
226 ).closest().pathLength());
227
228 std::optional<Acts::BoundTrackParameters> boundParams;
229 auto boundParamsResult =
230 m_extrapolator->propagateToSurface(curvilinearParams, surface, propOptions);
231
232 if (!boundParamsResult.ok()) {
233 ATH_MSG_DEBUG("Extrapolation from " << seed.sp().size() << "-SP seed (" << (useTopSp ? "top" : "bottom") << " start) failed - "
234 << (m_allowPropagatorFailure ? "use curvilinear parameters" : "skip seed"));
236 // Fallback: use curvilinear parameters instead of failing
237 boundParams = curvilinearParams;
238 } else {
239 return {std::nullopt, kNoSeedRefit};
240 }
241 } else {
242 boundParams = *boundParamsResult;
243 }
244
245 // Estimate covariance
246 Acts::EstimateTrackParamCovarianceConfig covarianceEstimationConfig = {
248 .initialSigmaPtRel = m_initialSigmaPtRel,
249 .initialVarInflation = Eigen::Map<const Acts::BoundVector>(m_initialVarInflation.value().data()),
250 .noTimeVarInflation = 1.0,
251 };
252 boundParams->covariance() = Acts::estimateTrackParamCovariance(
253 covarianceEstimationConfig,
254 boundParams->parameters(),
255 false);
256
257 if (!m_refitSeeds) {
258 ATH_MSG_DEBUG("estimateTrackParams from " << seed.sp().size() << "-SP seed (" << (useTopSp ? "top" : "bottom") << " start) succeeded");
259 return {boundParams, kNoSeedRefit};
260 }
261
262 auto refitResult = doRefit(seed, *boundParams, geoContext, magFieldContext, calContext, reverseSearch);
263 ATH_MSG_DEBUG("Refit " << seed.sp().size() << "-SP seed (" << (reverseSearch ? "top" : "bottom") << " start) " << (refitResult ? "succeeded" : "failed"));
264 if (refitResult) {
266 // scale r_i * C_ij * r_j -> C_ij
267 const auto refitErrInflation = Eigen::Map<const Acts::BoundVector>(m_refitErrInflation.value().data());
268 refitResult->covariance()->array().colwise() *= refitErrInflation.array();
269 refitResult->covariance()->array().rowwise() *= refitErrInflation.transpose().array();
270 }
271 return {refitResult, kSeedRefitSuccess};
272 } else {
273 return {boundParams, kSeedRefitFailed};
274 }
275
276 }
277
278 // Function to return which 3 SPs of a seed to use
280 // MinDeltaR
281 if (m_parameterEstimationMode == 3) {
282 const double minDeltaR = m_minDeltaR;
283 return [minDeltaR](const ActsTrk::SpacePointRange& spacePoints, bool useTopSp) -> std::array<std::size_t, 3> {
284 const std::size_t nSp = spacePoints.size();
285 std::array<std::size_t, 3> indices{};
286 std::size_t nSelected = 0;
287 double lastDistance = 0.;
288 for (std::size_t i = 0; i < nSp && nSelected < indices.size(); ++i) {
289 const xAOD::SpacePoint* sp = spacePoints[i];
290 const double distance = Acts::fastHypot(sp->x(), sp->y(), sp->z());
291 if (nSelected > 0 && std::abs(distance - lastDistance) <= minDeltaR) {
292 continue;
293 }
294 indices[nSelected++] = i;
295 lastDistance = distance;
296 }
297 if (nSelected < indices.size()) {
298 if (nSp > 3ul)
299 return {0, nSp / 2ul, nSp - 1};
300 else
301 return {0, 1, 2};
302 }
303 if (useTopSp)
304 return {nSp - indices[2] - 1, nSp - indices[1] - 1, nSp - indices[0] - 1};
305 return indices;
306 };
307 }
308 // FirstMiddleLast
309 if (m_parameterEstimationMode == 2) {
310 return [](const ActsTrk::SpacePointRange& spacePoints, bool) -> std::array<std::size_t, 3> {
311 const std::size_t nSp = spacePoints.size();
312 if (nSp > 3ul)
313 return {0, nSp / 2ul, nSp - 1};
314 else
315 return {0, 1, 2};
316 };
317 } else if (m_firstSp > 0ul) {
318 std::size_t firstSp = m_firstSp;
319 return [firstSp](const ActsTrk::SpacePointRange& spacePoints, bool) -> std::array<std::size_t, 3> {
320 const std::size_t nSp = spacePoints.size();
321 if (nSp > 3ul) {
322 std::size_t first = std::min(firstSp, nSp - 3ul);
323 return {first, first + 1, first + 2};
324 } else
325 return {0, 1, 2};
326 };
327 } else {
328 return [](const ActsTrk::SpacePointRange&, bool) -> std::array<std::size_t, 3> {
329 return {0, 1, 2};
330 };
331 }
332 };
333
334
335 // Refit track. Used if refitSeeds=True.
336 std::optional<Acts::BoundTrackParameters> TrackParamsEstimationTool::doRefit(
337 const ActsTrk::Seed &measurement,
338 const Acts::BoundTrackParameters &initialParameters,
339 const Acts::GeometryContext& geometry,
340 const Acts::MagneticFieldContext& magField,
341 const Acts::CalibrationContext& calib,
342 const bool paramsAtOutermostSurface) const {
343 // Perform KF before CKF
344 const Acts::Surface* targetSurface = nullptr;
345 // get the proper surface
346 if (not paramsAtOutermostSurface) {
347 // inner-most surface
348 targetSurface = m_uncalibMeasSurfAcc.get(measurement.sp().front()->measurements().front());
349 } else {
350 // outer-most surface
351 targetSurface = m_uncalibMeasSurfAcc.get(measurement.sp().back()->measurements().back());
352 }
353 if (not targetSurface) {
354 ATH_MSG_WARNING("Could not identify the target surface for fitting the provided seed");
355 return std::nullopt;
356 }
357 const auto fittedSeedCollection = m_fitterTool->fit(measurement, initialParameters,
358 geometry, magField, calib,
359 *targetSurface);
360 if (not fittedSeedCollection) {
361 ATH_MSG_VERBOSE("KF fit failure");
362 return std::nullopt;
363 }
364 if (fittedSeedCollection->size() != 1) {
365 ATH_MSG_WARNING("KF produced " << fittedSeedCollection->size() << " tracks but should produce 1!");
366 return std::nullopt;
367 }
368 const auto fittedSeed = fittedSeedCollection->getTrack(0);
369
370 // get the track state at the beginning of the track, where we started
371 std::optional<typename decltype(fittedSeed)::ConstTrackStateProxy> trackState {std::nullopt};
372 if (paramsAtOutermostSurface) {
373 trackState = fittedSeed.outermostTrackState();
374 } else {
375 trackState = fittedSeed.innermostTrackState();
376 if (!trackState) {
377 // if the track is not forward linked (fixed by #5666), then we need to search back to the innermost track state
378 for (auto st : fittedSeed.trackStatesReversed()) {
379 trackState = st;
380 }
381 }
382 }
383
384 if (!trackState) {
385 ATH_MSG_VERBOSE("Missing "
386 << (paramsAtOutermostSurface ? "outermost" : "innermost")
387 << " track state");
388 return std::nullopt;
389 }
390
391 // Return updated parameters
392 return fittedSeed.createParametersFromState(trackState.value());
393 };
394
395}
396// namespace ActsTrk
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_VERBOSE(x,...)
#define ATH_MSG_INFO(x,...)
std::vector< std::vector< int64_t > > indices
static Double_t sp
std::unique_ptr< const Acts::Logger > makeActsAthenaLogger(IMessageSvc *svc, const std::string &name, int level, std::optional< std::string > parent_name)
Definition Logger.cxx:64
Acts::Result< Acts::Vector3 > getField(const Acts::Vector3 &position, Acts::MagneticFieldProvider::Cache &gcache) const override
MagneticFieldProvider::Cache makeCache(const Acts::MagneticFieldContext &mctx) const override
std::function< std::array< std::size_t, 3 >(const ActsTrk::SpacePointRange &, bool)> SpacePointIndicesFun_t
bool estimateFromTopSp(bool reverseSearch) const override
const Acts::Logger & logger() const
Private access to the logger.
std::unique_ptr< const Acts::Logger > m_logger
logging instance
virtual std::pair< std::optional< Acts::BoundTrackParameters >, EstimationStatus > estimateTrackParameters(const ActsTrk::Seed &seed, bool reverseSearch, const Acts::GeometryContext &geoContext, const Acts::MagneticFieldContext &magFieldContext, const Acts::CalibrationContext &calContext, std::function< const Acts::Surface &(const ActsTrk::Seed &seed, bool useTopSp)> retrieveSurface) const override
Gaudi::Property< std::vector< double > > m_refitErrInflation
std::optional< Acts::BoundTrackParameters > doRefit(const ActsTrk::Seed &measurement, const Acts::BoundTrackParameters &initialParameters, const Acts::GeometryContext &geometry, const Acts::MagneticFieldContext &magField, const Acts::CalibrationContext &calib, const bool paramsAtOutermostSurface) const
ToolHandle< ActsTrk::IFitterTool > m_fitterTool
ServiceHandle< ActsTrk::ITrackingGeometrySvc > m_trackingGeometrySvc
SpacePointIndicesFun_t spacePointIndicesFun() const override
TrackParamsEstimationTool(const std::string &type, const std::string &name, const IInterface *parent)
Gaudi::Property< std::vector< double > > m_initialVarInflation
Gaudi::Property< std::size_t > m_firstSp
std::optional< Extrapolator > m_extrapolator
detail::xAODUncalibMeasSurfAcc m_uncalibMeasSurfAcc
Gaudi::Property< std::size_t > m_stripCalibrationIterations
Helper class to access the Acts::surface associated with an Uncalibrated xAOD measurement.
float z() const
float y() const
float x() const
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
float j(const xAOD::IParticle &, const xAOD::TrackMeasurementValidation &hit, const Eigen::Matrix3d &jab_inv)
SpacePointRange sp() const noexcept