ATLAS Offline Software
Loading...
Searching...
No Matches
MeasurementToTrackParticleDecorationAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
16#include "Acts/Surfaces/AnnulusBounds.hpp"
17#include "Acts/Utilities/TrackHelpers.hpp"
18
19using namespace Acts::UnitLiterals;
20
21
22namespace ActsTrk {
23
25 ISvcLocator *pSvcLocator) :
26 AthReentrantAlgorithm(name,pSvcLocator)
27 {}
28
30 {
31 ATH_MSG_DEBUG("Initializing " << name() << " ...");
32
33 ATH_CHECK(m_trackParticlesKey.initialize());
34
35 // Decorations
38 ATH_CHECK(m_measurementLayerKey.initialize());
39 ATH_CHECK(m_chi2HitPredictedKey.initialize());
40 ATH_CHECK(m_chi2HitFilteredKey.initialize());
41 ATH_CHECK(m_measurementTypeKey.initialize());
44 ATH_CHECK(m_residualLocXkey.initialize());
45 ATH_CHECK(m_pullLocXkey.initialize());
46 ATH_CHECK(m_measurementLocXkey.initialize());
50 ATH_CHECK(m_residualLocYkey.initialize());
51 ATH_CHECK(m_pullLocYkey.initialize());
52 ATH_CHECK(m_measurementLocYkey.initialize());
56
58
59 return StatusCode::SUCCESS;
60 }
61
62 StatusCode MeasurementToTrackParticleDecorationAlg::execute(const EventContext& ctx) const
63 {
64 ATH_MSG_DEBUG("Executing " << name() << " ...");
65
66 auto tgContext = m_trackingGeometryTool->getGeometryContext(ctx).context();
67
74
77
84
91
93 ATH_CHECK(trackParticlesHandle.isValid());
94 const xAOD::TrackParticleContainer *track_particles = trackParticlesHandle.cptr();
95
96 static const SG::AuxElement::ConstAccessor<ElementLink<ActsTrk::TrackContainer> > actsTrackLink("actsTrack");
97
98 for (const xAOD::TrackParticle *track_particle : *track_particles) {
99 ElementLink<ActsTrk::TrackContainer> link_to_track = actsTrackLink(*track_particle);
100 ATH_CHECK(link_to_track.isValid());
101
102 // to ensure that the code does not suggest something stupid (i.e. creating an unnecessary copy)
103 static_assert( std::is_same<ElementLink<ActsTrk::TrackContainer>::ElementConstReference,
104 std::optional<ActsTrk::TrackContainer::ConstTrackProxy> >::value);
105 std::optional<ActsTrk::TrackContainer::ConstTrackProxy> optional_track = *link_to_track;
106
107 if ( not optional_track.has_value() ) {
108 ATH_MSG_WARNING("Invalid track link for particle " << track_particle->index() << ". Skipping track..");
109 continue;
110 }
111
112 ATH_MSG_DEBUG("Track link found for track particle with index " << track_particle->index());
113 ActsTrk::TrackContainer::ConstTrackProxy track = optional_track.value();
114
115 std::vector<int>& regions{measurementRegionHandle(*track_particle)};
116 regions.reserve(track.nMeasurements());
117 std::vector<int>& detectors{measurementDetectorHandle(*track_particle)};
118 detectors.reserve(track.nMeasurements());
119 std::vector<int>& layers{measurementLayerHandle(*track_particle)};
120 layers.reserve(track.nMeasurements());
121 std::vector<int>& types{measurementTypeHandle(*track_particle)};
122 types.reserve(track.nMeasurements());
123 std::vector<float>& predchi2s{chi2HitPredictedHandle(*track_particle)};
124 predchi2s.reserve(track.nMeasurements());
125 std::vector<float>& filtchi2s{chi2HitFilteredHandle(*track_particle)};
126 filtchi2s.reserve(track.nMeasurements());
127
128 std::vector<int>& sizesPhi{measurementPhiWidthHandle(*track_particle)};
129 sizesPhi.reserve(track.nMeasurements());
130 std::vector<int>& sizesEta{measurementEtaWidthHandle(*track_particle)};
131 sizesEta.reserve(track.nMeasurements());
132 std::vector<float>& residualsLocX{residualLocXhandle(*track_particle)};
133 residualsLocX.reserve(track.nMeasurements());
134 std::vector<float>& pullsLocX{pullLocXhandle(*track_particle)};
135 pullsLocX.reserve(track.nMeasurements());
136 std::vector<float>& measurementsLocX{measurementLocXhandle(*track_particle)};
137 measurementsLocX.reserve(track.nMeasurements());
138 std::vector<float>& trackParametersLocX{trackParameterLocXhandle(*track_particle)};
139 trackParametersLocX.reserve(track.nMeasurements());
140 std::vector<float>& measurementsLocCovX{measurementLocCovXhandle(*track_particle)};
141 measurementsLocCovX.reserve(track.nMeasurements());
142 std::vector<float>& trackParametersLocCovX{trackParameterLocCovXhandle(*track_particle)};
143 trackParametersLocCovX.reserve(track.nMeasurements());
144 std::vector<float>& residualsLocY{residualLocYhandle(*track_particle)};
145 residualsLocY.reserve(track.nMeasurements());
146 std::vector<float>& pullsLocY{pullLocYhandle(*track_particle)};
147 pullsLocY.reserve(track.nMeasurements());
148 std::vector<float>& measurementsLocY{measurementLocYhandle(*track_particle)};
149 measurementsLocY.reserve(track.nMeasurements());
150 std::vector<float>& trackParametersLocY{trackParameterLocYhandle(*track_particle)};
151 trackParametersLocY.reserve(track.nMeasurements());
152 std::vector<float>& measurementsLocCovY{measurementLocCovYhandle(*track_particle)};
153 measurementsLocCovY.reserve(track.nMeasurements());
154 std::vector<float>& trackParametersLocCovY{trackParameterLocCovYhandle(*track_particle)};
155 trackParametersLocCovY.reserve(track.nMeasurements());
156
157 for (const auto state : track.trackStatesReversed()) {
158
159 auto flag = state.typeFlags();
160 // consider holes and measurements (also outliers)
161 bool anyHit = flag.test(Acts::TrackStateFlag::HoleFlag) or flag.test(Acts::TrackStateFlag::MeasurementFlag);
162 if (not anyHit) {
163 ATH_MSG_DEBUG("--- This is not a hit measurement, skipping...");
164 continue;
165 }
166
167 // starting with invalid values and setting them where needed.
168
169 int detector = -999;
170 int region = -999;
171 int layer = -999;
172 int type = -999;
173 float chi2_hit_predicted = -999.;
174 float chi2_hit_filtered = -999.;
175 int sizePhi = -999;
176 int sizeEta = -999;
177 float residualLocX = -999.;
178 float pullLocX = -999.;
179 float measurementLocX = -999.;
180 float trackParameterLocX = -999.;
181 float measurementLocCovX = -999.;
182 float trackParameterLocCovX = -999;
183 float residualLocY = -999.;
184 float pullLocY = -999.;
185 float measurementLocY = -999.;
186 float trackParameterLocY = -999.;
187 float measurementLocCovY = -999.;
188 float trackParameterLocCovY = -999.;
189 bool isAnnulusBound = false;
190
191 // Get the measurement type
192 if (flag.test(Acts::TrackStateFlag::HoleFlag)) {
194 ATH_MSG_DEBUG("--- This is a hole");
195 } else if (flag.test(Acts::TrackStateFlag::OutlierFlag)) {
197 ATH_MSG_DEBUG("--- This is an outlier");
198 } else {
200 ATH_MSG_DEBUG("--- This is a hit");
201 }
202
203 // Check the location of the state
204 if (state.hasReferenceSurface() and state.referenceSurface().associatedDetectorElement()) {
205 const ActsDetectorElement * detectorElement = dynamic_cast<const ActsDetectorElement *>(state.referenceSurface().associatedDetectorElement());
206 if (!detectorElement) {
207 ATH_MSG_WARNING("--- TrackState reference surface returned an invalid associated detector element");
208 continue;
209 }
210 const InDetDD::SiDetectorElement * siliconDetectorElement = dynamic_cast<const InDetDD::SiDetectorElement *>(detectorElement->upstreamDetectorElement());
211 if (!siliconDetectorElement) {
212 ATH_MSG_WARNING("--- TrackState associated detector element is not silicon");
213 continue;
214 }
215
216 const Acts::AnnulusBounds* annulusBounds = dynamic_cast<const Acts::AnnulusBounds*>(&(state.referenceSurface().bounds()));
217 isAnnulusBound = annulusBounds ? true : false;
218
219 if (siliconDetectorElement) {
220 Identifier detectorIdentifier = siliconDetectorElement->identify();
221 if (siliconDetectorElement->isPixel()) {
222 const PixelID* pixel_id = static_cast<const PixelID *>(siliconDetectorElement->getIdHelper());
223 int layer_disk = pixel_id->layer_disk(detectorIdentifier);
224 layer = layer_disk;
225 if (pixel_id->barrel_ec(detectorIdentifier) == 0) {
226 if (layer_disk == 0) {
228 } else detector = Subdetector::PIXEL;
229 region = Region::BARREL;
230 } else {
231 detector = Subdetector::PIXEL;
232 region = Region::ENDCAP;
233 }
234 } else if (siliconDetectorElement->isSCT()) {
235 const SCT_ID* sct_id = static_cast<const SCT_ID *>(siliconDetectorElement->getIdHelper());
236 detector = Subdetector::STRIP;
237 region = sct_id->barrel_ec(detectorIdentifier) == 0 ?
239 layer = sct_id->layer_disk(detectorIdentifier);;
240 } else ATH_MSG_WARNING("--- Unknown detector type - It is not pixel nor strip detecor element!");
241 } else ATH_MSG_WARNING("--- Missing silicon detector element!");
242 } else ATH_MSG_WARNING("--- Missing reference surface or associated detector element!");
243
244
245
246 // If I have a measurement (hit or outlier) then proceed with computing the residuals / pulls
247
249
250 //Get the Chi2 computation
251
252 if (type == MeasurementType::HIT) {
253 chi2_hit_filtered = state.chi2();
254 }
255
256 if (state.hasUncalibratedSourceLink()) {
257 chi2_hit_predicted = getChi2Contribution(state);
258 }
259
260 // Skip all states without smoothed parameters or without projector
261 if (!state.hasSmoothed() || !state.hasProjector())
262 continue;
263
264 // Calling effective Calibrated has some runtime overhead
265 const auto &calibratedParameters = state.effectiveCalibrated();
266 const auto &calibratedCovariance = state.effectiveCalibratedCovariance();
267
268 // We evaluate the unbiased parameters for:
269 // - measurements added to the fit. For outliers, the measurement is not part of the fit, hence track parameters are already unbiased
270 // - if the filtered parameters and the projector exist.
271 bool evaluateUnbiased = (!flag.test(Acts::TrackStateFlag::OutlierFlag));
272
273 if (evaluateUnbiased) {
274 ATH_MSG_DEBUG("--- Good for unbiased parameters evaluation!");
276 // if unbiased, access the associated uncalibrated measurement and store the size
277 if (state.hasUncalibratedSourceLink()) {
278 ATLASUncalibSourceLink sourceLink = state.getUncalibratedSourceLink().template get<ATLASUncalibSourceLink>();
279 const xAOD::UncalibratedMeasurement &uncalibratedMeasurement = getUncalibratedMeasurement(sourceLink);
280 const xAOD::UncalibMeasType measurementType = uncalibratedMeasurement.type();
281 if (measurementType == xAOD::UncalibMeasType::PixelClusterType) {
282 auto pixelCluster = static_cast<const xAOD::PixelCluster *>(&uncalibratedMeasurement);
283 sizePhi = pixelCluster->channelsInPhi();
284 sizeEta = pixelCluster->channelsInEta();
285 } else if (measurementType == xAOD::UncalibMeasType::StripClusterType) {
286 auto stripCluster = static_cast<const xAOD::StripCluster *>(&uncalibratedMeasurement);
287 sizePhi = stripCluster->channelsInPhi();
288 } else {
289 ATH_MSG_DEBUG("xAOD::UncalibratedMeasurement is neither xAOD::PixelCluster nor xAOD::StripCluster");
290 }
291 }
292 }
293
294 const auto& [unbiasedParameters, unbiasedCovariance] =
295 evaluateUnbiased ? Acts::calculateUnbiasedParametersCovariance(state) : std::make_pair(state.parameters(), state.covariance());
296
297 measurementLocX = calibratedParameters[Acts::eBoundLoc0];
298 measurementLocCovX = calibratedCovariance(Acts::eBoundLoc0, Acts::eBoundLoc0);
299
300 if (!isAnnulusBound) {
301
302 trackParameterLocX = unbiasedParameters[Acts::eBoundLoc0];
303 residualLocX = (measurementLocX - trackParameterLocX) / 1_um; //in um
304 trackParameterLocCovX = unbiasedCovariance(Acts::eBoundLoc0, Acts::eBoundLoc0);
305 }
306 else {
307 // TODO:: use directly phi instead of r*phi in the future
308
309 float locR = unbiasedParameters[Acts::eBoundLoc0];
310 float covR = unbiasedCovariance(Acts::eBoundLoc0,Acts::eBoundLoc0);
311 float locphi = unbiasedParameters[Acts::eBoundLoc1];
312 float covphi = unbiasedCovariance(Acts::eBoundLoc1,Acts::eBoundLoc1);
313 float covRphi = unbiasedCovariance(Acts::eBoundLoc0,Acts::eBoundLoc1);
314
315 trackParameterLocX = locphi;
316 residualLocX = locR * (measurementLocX - trackParameterLocX) / 1_um;
317 // Compute the error on the local rphi
318 trackParameterLocCovX = locR*locR*covphi + locphi*locphi*covR + 2*locphi*locR*covRphi;
319
320 // Rescale the error of the measurement to Rphi.
321 measurementLocCovX = locR*locR * measurementLocCovX;
322 }
323
324 pullLocX = evaluatePull(residualLocX, measurementLocCovX,
325 trackParameterLocCovX, evaluateUnbiased);
326
327 if (state.calibratedSize() == 2) {
328 measurementLocY = calibratedParameters[Acts::eBoundLoc1];
329 trackParameterLocY = unbiasedParameters[Acts::eBoundLoc1];
330 residualLocY = (measurementLocY - trackParameterLocY) / 1_um;
331 measurementLocCovY = calibratedCovariance(Acts::eBoundLoc1, Acts::eBoundLoc1);
332 trackParameterLocCovY = unbiasedCovariance(Acts::eBoundLoc1, Acts::eBoundLoc1);
333 pullLocY = evaluatePull(residualLocY, measurementLocCovY,
334 trackParameterLocCovY, evaluateUnbiased);
335 }
336
337 } // hit or outliers
338
339 else if (type == MeasurementType::HOLE) {
340
341 // Get the predicted position on sensor
342 auto pred = state.predicted();
343 trackParameterLocX = pred[Acts::eBoundLoc0];
344 trackParameterLocY = pred[Acts::eBoundLoc1];
345 }
346
347 // Always fill with this information
348
349 regions.push_back(region);
350 detectors.push_back(detector);
351 layers.push_back(layer);
352 types.push_back(type);
353 predchi2s.push_back(chi2_hit_predicted);
354 filtchi2s.push_back(chi2_hit_filtered);
355 sizesPhi.push_back(sizePhi);
356 sizesEta.push_back(sizeEta);
357 residualsLocX.push_back(residualLocX);
358 pullsLocX.push_back(pullLocX);
359 measurementsLocX.push_back(measurementLocX);
360 trackParametersLocX.push_back(trackParameterLocX);
361 measurementsLocCovX.push_back(measurementLocCovX);
362 trackParametersLocCovX.push_back(trackParameterLocCovX);
363 residualsLocY.push_back(residualLocY);
364 pullsLocY.push_back(pullLocY);
365 measurementsLocY.push_back(measurementLocY);
366 trackParametersLocY.push_back(trackParameterLocY);
367 measurementsLocCovY.push_back(measurementLocCovY);
368 trackParametersLocCovY.push_back(trackParameterLocCovY);
369
370
371 } // loop on states
372
373 } // loop on tracks
374
375 return StatusCode::SUCCESS;
376 }
377
378 float MeasurementToTrackParticleDecorationAlg::getChi2Contribution(const typename ActsTrk::TrackStateBackend::ConstTrackStateProxy &state) const {
379
380 auto pred = state.predicted();
381 auto predC = state.predictedCovariance();
382
383 return Acts::visit_measurement(
384 state.calibratedSize(),
385 [&]<std::size_t measdim>(std::integral_constant<std::size_t, measdim>) {
386 Acts::FixedBoundSubspaceHelper<measdim> subspaceHelper =
387 state.template projectorSubspaceHelper<measdim>();
388
389 // TODO use subspace helper for projection instead
390 auto H = subspaceHelper.projector();
391
392 const auto calibrated = state.template calibrated<measdim>();
393 const auto calibratedCov = state.template calibratedCovariance<measdim>();
394
395 auto residual = (H * pred - calibrated).eval();
396 auto rescov = (H * predC * H.transpose() + calibratedCov).eval();
397
398 return ((residual.transpose() * rescov.inverse() * residual).eval())(0,0);
399 });
400
401
402
403 }
404
405
407 const float measurementCovariance,
408 const float trackParameterCovariance,
409 const bool evaluateUnbiased) const {
410 float correlation = evaluateUnbiased ? 1. : -1.;
411 float residualCovariance = measurementCovariance + correlation*trackParameterCovariance;
412 if (residualCovariance<=0.) {
413 // If the total covariance is non-positive return 0
414 ATH_MSG_DEBUG("--- Total covariance for pull evaluation is non-positive! Returning pulls = 0!");
415 return 0.;
416 }
417 return 0.001 * residual/std::sqrt(residualCovariance);
418 }
419
420} // namespace
421
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
static const std::vector< std::string > types
static const std::vector< std::string > regions
This is an Identifier helper class for the Pixel subdetector.
This is an Identifier helper class for the SCT subdetector.
const GeoVDetectorElement * upstreamDetectorElement() const
Returns the underllying GeoModel detectorelement that this one is based on.
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_chi2HitPredictedKey
float evaluatePull(const float residual, const float measurementCovariance, const float trackParameterCovariance, const bool evaluateUnbiased) const
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementRegionKey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_residualLocYkey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_trackParameterLocYkey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementLayerKey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementDetectorKey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementLocYkey
PublicToolHandle< ActsTrk::ITrackingGeometryTool > m_trackingGeometryTool
float getChi2Contribution(const typename ActsTrk::TrackStateBackend::ConstTrackStateProxy &state) const
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementEtaWidthKey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_pullLocYkey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementLocXkey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementTypeKey
MeasurementToTrackParticleDecorationAlg(const std::string &name, ISvcLocator *pSvcLocator)
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_residualLocXkey
virtual StatusCode execute(const EventContext &ctx) const override
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_pullLocXkey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementLocCovXkey
SG::ReadHandleKey< xAOD::TrackParticleContainer > m_trackParticlesKey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_trackParameterLocXkey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_trackParameterLocCovYkey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementLocCovYkey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_trackParameterLocCovXkey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_chi2HitFilteredKey
SG::WriteDecorHandleKey< xAOD::TrackParticleContainer > m_measurementPhiWidthKey
An algorithm that can be simultaneously executed in multiple threads.
Class to hold geometrical description of a silicon detector element.
virtual Identifier identify() const override final
identifier of this detector element (inline)
const AtlasDetectorID * getIdHelper() const
Returns the id helper (inline)
This is an Identifier helper class for the Pixel subdetector.
Definition PixelID.h:67
int layer_disk(const Identifier &id) const
Definition PixelID.h:607
int barrel_ec(const Identifier &id) const
Values of different levels (failure returns 0)
Definition PixelID.h:600
This is an Identifier helper class for the SCT subdetector.
Definition SCT_ID.h:68
int layer_disk(const Identifier &id) const
Definition SCT_ID.h:687
int barrel_ec(const Identifier &id) const
Values of different levels (failure returns 0)
Definition SCT_ID.h:681
SG::ConstAccessor< T, ALLOC > ConstAccessor
Definition AuxElement.h:569
virtual bool isValid() override final
Can the handle be successfully dereferenced?
const_pointer_type cptr()
Dereference the pointer.
Handle class for adding a decoration to an object.
virtual xAOD::UncalibMeasType type() const =0
Returns the type of the measurement type as a simple enumeration.
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition hcg.cxx:130
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
const xAOD::UncalibratedMeasurement & getUncalibratedMeasurement(const ATLASUncalibSourceLink &source_link)
const xAOD::UncalibratedMeasurement * ATLASUncalibSourceLink
SG::ReadCondHandle< T > makeHandle(const SG::ReadCondHandleKey< T > &key, const EventContext &ctx=Gaudi::Hive::currentContext())
StripCluster_v1 StripCluster
Define the version of the strip cluster class.
TrackParticle_v1 TrackParticle
Reference the current persistent version:
PixelCluster_v1 PixelCluster
Define the version of the pixel cluster class.
UncalibMeasType
Define the type of the uncalibrated measurement.
TrackParticleContainer_v1 TrackParticleContainer
Definition of the current "TrackParticle container version".
UncalibratedMeasurement_v1 UncalibratedMeasurement
Define the version of the uncalibrated measurement class.