ATLAS Offline Software
Loading...
Searching...
No Matches
ActsToTrkConverterTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7// Trk
10#include "TrkSurfaces/Surface.h"
11#include "TrkTrack/Track.h"
12
13// ATHENA
14#include "GaudiKernel/IInterface.h"
19#include "TrkSurfaces/Surface.h"
23
24// PACKAGE
33
34// ACTS
35#include "Acts/Surfaces/StrawSurface.hpp"
36#include "Acts/Surfaces/PerigeeSurface.hpp"
37#include "Acts/Surfaces/PlaneSurface.hpp"
38
39#include "Acts/Definitions/Units.hpp"
40#include "Acts/EventData/BoundTrackParameters.hpp"
41#include "Acts/EventData/VectorTrackContainer.hpp"
42#include "Acts/EventData/TransformationHelpers.hpp"
43#include "Acts/Geometry/TrackingGeometry.hpp"
44#include "Acts/Propagator/detail/JacobianEngine.hpp"
46#include "Acts/EventData/TrackStatePropMask.hpp"
47#include "Acts/EventData/SourceLink.hpp"
48
51// STL
52#include <cmath>
53#include <iostream>
54#include <memory>
55#include <random>
56#include <format>
57
58namespace ActsTrk {
59
60// Forward definitions of local functions
61// @TODO unused, remove ?
62#pragma GCC diagnostic push
63#pragma GCC diagnostic ignored "-Wunused-function"
64static void ActsMeasurementCheck(const Acts::GeometryContext &gctx,
65 const Trk::MeasurementBase &measurement,
66 const Acts::Surface &surface,
67 const Acts::BoundVector &loc);
68#pragma GCC diagnostic pop
69
70static void ActsTrackParameterCheck(
71 const Acts::BoundTrackParameters &actsParameter,
72 const Acts::GeometryContext &gctx, const Acts::BoundMatrix &covpc,
73 const Acts::BoundVector &targetPars, const Acts::BoundMatrix &targetCov,
74 const Trk::PlaneSurface *planeSurface);
75
76
77
79 ATH_MSG_VERBOSE("Initializing ACTS to ATLAS converter tool");
80 if (!m_trackingGeometryTool.empty()) {
82 m_trackingGeometry = m_trackingGeometryTool->trackingGeometry();
83
84 m_trackingGeometry->visitSurfaces([&](const Acts::Surface *surface) {
85 // find acts surface with the same detector element ID
86 if (!surface)
87 return;
88 const auto *actsElement = dynamic_cast<const IDetectorElementBase*>(
89 surface->surfacePlacement());
90 if (!actsElement) {
91 return;
92 }
93 // Conversion from Acts to ATLAS surface impossible for the TRT so the TRT
94 // surfaces are not stored in this map
95 if (actsElement->detectorType() == DetectorType::Trt) {
96 return;
97 }
98
99 auto [it, ok] = m_actsSurfaceMap.insert({actsElement->identify(), surface});
100 if (!ok) {
101 ATH_MSG_WARNING("ATLAS ID " << actsElement->identify()
102 << " has two ACTS surfaces: "
103 << it->second->geometryId() << " and "
104 << surface->geometryId());
105 }
106 });
107 }
108
109 ATH_CHECK(m_trkSummaryTool.retrieve());
110 ATH_CHECK(m_boundaryCheckTool.retrieve(EnableTool{!m_boundaryCheckTool.empty()}));
111 ATH_CHECK(m_ROTcreator.retrieve());
112
115 ATH_CHECK(m_idHelperSvc.retrieve());
116 const MuonGMR4::MuonDetectorManager* muonMgr{nullptr};
117 ATH_CHECK(detStore()->retrieve(muonMgr));
118 unsigned int mapSize = m_actsSurfaceMap.size(); // For debugging message later
119 for (auto readoutElement : muonMgr->getAllReadoutElements()) {
120 std::vector<std::shared_ptr<Acts::Surface>> reSurfaces = readoutElement->getSurfaces();
121 for ( const auto& surf : reSurfaces) {
122 const Identifier id = static_cast<const SurfaceCache*>(surf->surfacePlacement())->identify();
123 m_actsSurfaceMap.insert(std::make_pair(id, surf.get()));
124 }
125 }
126 ATH_MSG_VERBOSE("After adding muon surfaces, the map has grown from "<<mapSize<<" to "<<m_actsSurfaceMap.size());
127 }
128 return StatusCode::SUCCESS;
129}
130
132 const EventContext& ctx,
133 const Acts::Surface &actsSurface) const {
134
135 const auto *detEleBase= dynamic_cast<const IDetectorElementBase*>(actsSurface.surfacePlacement());
136 if (!detEleBase) {
137 ATH_MSG_ERROR(actsSurface.toString(m_trackingGeometryTool->getNominalGeometryContext().context()));
138 throw std::domain_error("ActsToTrkConverterTool() - Surface does not have an associated detector element. ");
139 }
140 switch (detEleBase->detectorType()) {
141 using enum DetectorType;
142 case Pixel:
143 case Sct:
144 case Hgtd:
145 case Trt: {
146 const auto actsElement = dynamic_cast<const ActsDetectorElement*>(detEleBase);
147 if (actsElement) {
148 return actsElement->atlasSurface();
149 }
150 break;
151 }
152 case Mdt:
153 case Rpc:
154 case Tgc:
155 case Csc:
156 case sTgc:
157 case Mm: {
158 const MuonGM::MuonDetectorManager* detMgr{nullptr};
159 if (!SG::get(detMgr, m_muonMgrKey, ctx).isSuccess() || !detMgr) {
160 THROW_EXCEPTION("Failed to retrieve the muon detector manager");
161 }
162 return detMgr->getReadoutElement(detEleBase->identify())->surface(detEleBase->identify());
163
164 } default:
165 break;
166 }
167 throw std::domain_error("ActsToTrkConverterTool() - No ATLAS surface corresponding to the Acts one");
168}
169
171 const Trk::Surface &atlasSurface) const {
172
173 Identifier atlasID = atlasSurface.associatedDetectorElementIdentifier();
174 auto it = m_actsSurfaceMap.find(atlasID);
175 if (it != m_actsSurfaceMap.end()) {
176 return *it->second;
177 }
178 ATH_MSG_ERROR("No Acts surface corresponding to this ATLAS surface: "<<atlasID);
179 ATH_MSG_ERROR(atlasSurface);
180 throw std::domain_error("No Acts surface corresponding to the ATLAS one");
181}
182
183std::vector<Acts::SourceLink>
185 std::vector<Acts::SourceLink> sourceLinks{};
186 sourceLinks.reserve(track.measurementsOnTrack()->size() +
187 track.outliersOnTrack()->size());
188 toSourceLinks(track.measurementsOnTrack()->stdcont(), sourceLinks);
189 toSourceLinks(track.outliersOnTrack()->stdcont(), sourceLinks);
190 return sourceLinks;
191}
192
193
194void ActsToTrkConverterTool::toSourceLinks(const std::vector<const Trk::MeasurementBase*>& measSet,
195 std::vector<Acts::SourceLink>& sourceLinks) const{
196 if (sourceLinks.capacity() < sourceLinks.size() + measSet.size()) {
197 sourceLinks.reserve(sourceLinks.size() + measSet.size());
198 }
199 std::ranges::transform(measSet, std::back_inserter(sourceLinks), [](const Trk::MeasurementBase* meas) {
201 });
202}
203void ActsToTrkConverterTool::toSourceLinks(const std::vector<const Trk::PrepRawData*>& prdSet,
204 std::vector<Acts::SourceLink>& links) const {
205 if (links.capacity() < links.size() + prdSet.size()) {
206 links.reserve(links.size() + prdSet.size());
207 }
208 std::ranges::transform(prdSet, std::back_inserter(links), [](const Trk::PrepRawData* prd) {
210 });
211}
212
213const Acts::BoundTrackParameters
215 const Acts::GeometryContext & gctx,
216 Trk::ParticleHypothesis hypothesis) const {
217
218 using namespace Acts::UnitLiterals;
219 std::shared_ptr<const Acts::Surface> actsSurface;
220 Acts::BoundVector params;
221
222 // get the associated surface
223 if (atlasParameter.hasSurface() &&
225 try {
226 actsSurface = trkSurfaceToActsSurface(atlasParameter.associatedSurface()).getSharedPtr();
227 } catch (const std::exception &e) {
228 ATH_MSG_ERROR("Could not find ACTS detector surface for this TrackParameter:");
229 ATH_MSG_ERROR(atlasParameter);
230 throw; // Nothing we can do, so just pass exception on...
231 }
232 }
233 // no associated surface create a perigee one
234 else {
236 "trkTrackParametersToActsParameters:: No associated surface found (owner: "<<atlasParameter.associatedSurface().owner()<<
237 "). Creating a free surface. Trk parameters:");
238 ATH_MSG_VERBOSE(atlasParameter);
239 const Amg::Transform3D& trf{atlasParameter.associatedSurface().transform()};
240 switch (atlasParameter.associatedSurface().type()){
242 actsSurface = Acts::Surface::makeShared<const Acts::PlaneSurface>(trf);
243 break;
245 actsSurface = Acts::Surface::makeShared<const Acts::PerigeeSurface>(trf);
246 break;
248 actsSurface = Acts::Surface::makeShared<const Acts::StrawSurface>(trf);
249 break;
250 // TODO - implement the missing types?
251 default: {
252 std::stringstream surfStr{};
253 atlasParameter.dump(surfStr);
254 throw std::domain_error(std::format("Failed to translate parameters {:}", surfStr.str()));
255 }
256 }
257 }
258
259 // Construct track parameters
260 const auto& atlasParam{atlasParameter.parameters()};
261 if (actsSurface->bounds().type() == Acts::SurfaceBounds::BoundsType::eAnnulus) {
262 // Annulus surfaces are constructed differently in Acts/Trk so we need to
263 // convert local coordinates
264 const Amg::Vector3D& position{atlasParameter.position()};
265 auto result = actsSurface->globalToLocal(gctx, position, atlasParameter.momentum());
266 if (result.ok()) {
267 params << (*result)[0], (*result)[1], atlasParam[Trk::phi0],
268 atlasParam[Trk::theta],
269 atlasParameter.charge() / (atlasParameter.momentum().mag() * 1_MeV),
270 0.;
271 } else {
272 ATH_MSG_WARNING("Unable to convert annulus surface - globalToLocal failed");
273 }
274 } else {
275 params << atlasParam[Trk::locX], atlasParam[Trk::locY],
276 atlasParam[Trk::phi0], atlasParam[Trk::theta],
277 atlasParameter.charge() / (atlasParameter.momentum().mag() * 1_MeV), 0.;
278 }
279
280 Acts::BoundMatrix cov = Acts::BoundMatrix::Identity();
281 if (atlasParameter.covariance()) {
282 cov.topLeftCorner(5, 5) = *atlasParameter.covariance();
283
284 // Convert the covariance matrix from MeV
285 // FIXME: This needs to handle the annulus case as well - currently the cov
286 // is wrong for annulus surfaces
287 for (int i = 0; i < cov.rows(); i++) {
288 cov(i, 4) = cov(i, 4) / 1_MeV;
289 }
290 for (int i = 0; i < cov.cols(); i++) {
291 cov(4, i) = cov(4, i) / 1_MeV;
292 }
293 }
294
295 return Acts::BoundTrackParameters(actsSurface, params,cov,
296 ParticleHypothesis::convert(hypothesis));
297}
298
299std::unique_ptr<Trk::TrackParameters>
301 const EventContext& ctx,
302 const Acts::BoundTrackParameters &actsParameter,
303 const Acts::GeometryContext &gctx) const {
304
305 using namespace Acts::UnitLiterals;
306 std::optional<AmgSymMatrix(5)> cov = std::nullopt;
307 if (actsParameter.covariance()) {
308 AmgSymMatrix(5) newcov(actsParameter.covariance()->topLeftCorner<5, 5>());
309 // Convert the covariance matrix to GeV
310 for (int i = 0; i < newcov.rows(); i++) {
311 newcov(i, 4) = newcov(i, 4) * 1_MeV;
312 }
313 for (int i = 0; i < newcov.cols(); i++) {
314 newcov(4, i) = newcov(4, i) * 1_MeV;
315 }
316 cov = std::optional<AmgSymMatrix(5)>(newcov);
317 }
318
319 const Acts::Surface &actsSurface = actsParameter.referenceSurface();
320 switch (actsSurface.type()) {
321 case Acts::Surface::SurfaceType::Cone: {
322 const auto &coneSurface = static_cast<const Trk::ConeSurface&>(actsSurfaceToTrkSurface(ctx, actsSurface));
323 return std::make_unique<Trk::AtaCone>(
324 actsParameter.get<Acts::eBoundLoc0>(),
325 actsParameter.get<Acts::eBoundLoc1>(),
326 actsParameter.get<Acts::eBoundPhi>(),
327 actsParameter.get<Acts::eBoundTheta>(),
328 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, coneSurface, cov);
329 } case Acts::Surface::SurfaceType::Cylinder: {
330 const auto &cylSurface{static_cast<const Trk::CylinderSurface&>(actsSurfaceToTrkSurface(ctx, actsSurface))};
331 return std::make_unique<Trk::AtaCylinder>(
332 actsParameter.get<Acts::eBoundLoc0>(),
333 actsParameter.get<Acts::eBoundLoc1>(),
334 actsParameter.get<Acts::eBoundPhi>(),
335 actsParameter.get<Acts::eBoundTheta>(),
336 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, cylSurface, cov);
337 } case Acts::Surface::SurfaceType::Disc: {
338 const Trk::Surface& trkSurface{actsSurfaceToTrkSurface(ctx, actsSurface)};
339 if (trkSurface.type() == Trk::SurfaceType::Disc) {
340 const auto& discSurface{static_cast<const Trk::DiscSurface&>(trkSurface)};
341 return std::make_unique<Trk::AtaDisc>(
342 actsParameter.get<Acts::eBoundLoc0>(),
343 actsParameter.get<Acts::eBoundLoc1>(),
344 actsParameter.get<Acts::eBoundPhi>(),
345 actsParameter.get<Acts::eBoundTheta>(),
346 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, discSurface, cov);
347 } else if (trkSurface.type() == Trk::SurfaceType::Plane) {
348 auto& planeSurface{static_cast<const Trk::PlaneSurface&>(trkSurface)};
349 // need to convert to plane position on plane surface (annulus bounds)
350 auto helperSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(planeSurface.transform());
351
352 auto covpc = actsParameter.covariance().value();
354 Acts::FreeVector freePars = Acts::transformBoundToFreeParameters(actsSurface, gctx,
355 actsParameter.parameters());
356
358 Acts::BoundVector targetPars = Acts::transformFreeToBoundParameters(freePars,
359 *helperSurface, gctx).value();
360
361 Acts::FreeMatrix freeTransportJacobian{Acts::FreeMatrix::Identity()};
362
363 Acts::FreeVector freeToPathDerivatives{Acts::FreeVector::Zero()};
364 freeToPathDerivatives.head<3>() = freePars.segment<3>(Acts::eFreeDir0);
365
366 auto boundToFreeJacobian = actsSurface.boundToFreeJacobian(gctx, freePars.segment<3>(Acts::eFreePos0),
367 freePars.segment<3>(Acts::eFreeDir0));
368
369 Acts::BoundMatrix boundToBoundJac = Acts::detail::boundToBoundTransportJacobian(gctx, freePars,
370 boundToFreeJacobian, freeTransportJacobian, freeToPathDerivatives, *helperSurface);
371
372 Acts::BoundMatrix targetCov{boundToBoundJac * covpc * boundToBoundJac.transpose()};
373
374 auto pars = std::make_unique<Trk::AtaPlane>(
375 targetPars[Acts::eBoundLoc0], targetPars[Acts::eBoundLoc1],
376 targetPars[Acts::eBoundPhi], targetPars[Acts::eBoundTheta],
377 targetPars[Acts::eBoundQOverP] * 1_MeV, planeSurface,
378 targetCov.topLeftCorner<5, 5>());
379
381 ActsTrackParameterCheck(actsParameter, gctx, covpc, targetPars,
382 targetCov, &planeSurface);
383 }
384 return pars;
385
386 } else {
387 throw std::domain_error("Acts::DiscSurface is not associated with ATLAS disc or plane surface");
388 }
389 break;
390 } case Acts::Surface::SurfaceType::Perigee: {
391 const Trk::PerigeeSurface perSurface(actsSurface.center(gctx));
392 return std::make_unique<Trk::Perigee>(
393 actsParameter.get<Acts::eBoundLoc0>(),
394 actsParameter.get<Acts::eBoundLoc1>(),
395 actsParameter.get<Acts::eBoundPhi>(),
396 actsParameter.get<Acts::eBoundTheta>(),
397 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, perSurface, cov);
398 } case Acts::Surface::SurfaceType::Plane: {
399 auto &plaSurface{static_cast<const Trk::PlaneSurface&>(actsSurfaceToTrkSurface(ctx, actsSurface))};
400 return std::make_unique<Trk::AtaPlane>(
401 actsParameter.get<Acts::eBoundLoc0>(),
402 actsParameter.get<Acts::eBoundLoc1>(),
403 actsParameter.get<Acts::eBoundPhi>(),
404 actsParameter.get<Acts::eBoundTheta>(),
405 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, plaSurface, cov);
406 } case Acts::Surface::SurfaceType::Straw: {
407 auto& lineSurface{static_cast<const Trk::StraightLineSurface&>(actsSurfaceToTrkSurface(ctx, actsSurface))};
408 return std::make_unique<Trk::AtaStraightLine>(
409 actsParameter.get<Acts::eBoundLoc0>(),
410 actsParameter.get<Acts::eBoundLoc1>(),
411 actsParameter.get<Acts::eBoundPhi>(),
412 actsParameter.get<Acts::eBoundTheta>(),
413 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, lineSurface, cov);
414 } case Acts::Surface::SurfaceType::Curvilinear: {
415 return std::make_unique<Trk::CurvilinearParameters>(
416 actsParameter.position(gctx), actsParameter.get<Acts::eBoundPhi>(),
417 actsParameter.get<Acts::eBoundTheta>(),
418 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, cov);
419 break;
420 } case Acts::Surface::SurfaceType::Other: {
421 break;
422 }
423 }
424 throw std::domain_error("Surface type not found");
425}
426
428 const TrackCollection &trackColl,
429 const Acts::GeometryContext & gctx) const {
430 ATH_MSG_VERBOSE("Calling trkTrackCollectionToActsTrackContainer with "
431 << trackColl.size() << " tracks.");
432 unsigned int trkCount = 0;
433 std::vector<Identifier> failedIds; // Keep track of Identifiers of failed conversions
434 for (const Trk::Track* trk : trackColl) {
435 // Do conversions!
436 const Trk::TrackStates *trackStates = trk->trackStateOnSurfaces();
437
438 auto actsTrack = tc.getTrack(tc.addTrack());
439 auto& trackStateContainer = tc.trackStateContainer();
440
441 ATH_MSG_VERBOSE("Track "<<trkCount++<<" has " << trackStates->size()
442 << " track states on surfaces.");
443 // basic quantities copy
444 actsTrack.chi2() = trk->fitQuality()->chiSquared();
445 actsTrack.nDoF() = trk->fitQuality()->numberDoF();
446
447 // loop over track states on surfaces, convert and add them to the ACTS
448 // container
449 bool first_tsos = true; // We need to handle the first one differently
450 int measurementsCount = 0;
451 for (const Trk::TrackStateOnSurface* tsos : *trackStates) {
452
453 // Setup the mask
454 Acts::TrackStatePropMask mask = Acts::TrackStatePropMask::None;
455 if (tsos->measurementOnTrack()) {
456 mask |= Acts::TrackStatePropMask::Calibrated;
457 }
458 if (tsos->trackParameters()) {
459 mask |= Acts::TrackStatePropMask::Smoothed;
460 }
461
462 // Setup the index of the trackstate
463 auto index = Acts::kTrackIndexInvalid;
464 if (!first_tsos) {
465 index = actsTrack.tipIndex();
466 }
467 auto actsTSOS = trackStateContainer.getTrackState(trackStateContainer.addTrackState(mask, index));
468 ATH_MSG_VERBOSE("TipIndex: " << actsTrack.tipIndex() << " TSOS index within trajectory: "<< actsTSOS.index());
469 actsTrack.tipIndex() = actsTSOS.index();
470
471 if (tsos->trackParameters()) {
472 // TODO This try/catch is temporary and should be removed once the sTGC problem is fixed.
473 try {
474 ATH_MSG_VERBOSE("Converting track parameters.");
475 // TODO - work out whether we should set predicted, filtered, smoothed
476 const Acts::BoundTrackParameters parameters =
477 trkTrackParametersToActsParameters(*(tsos->trackParameters()), gctx);
478 ATH_MSG_VERBOSE("Track parameters: " << parameters.parameters());
479 // Sanity check on positions
480 if (!actsTrackParameterPositionCheck(parameters, *(tsos->trackParameters()), gctx)){
481 failedIds.push_back(tsos->trackParameters()->associatedSurface().associatedDetectorElementIdentifier());
482 }
483
484 if (first_tsos) {
485 // This is the first track state, so we need to set the track
486 // parameters
487 actsTrack.parameters() = parameters.parameters();
488 actsTrack.covariance() = *parameters.covariance();
489 actsTrack.setReferenceSurface(parameters.referenceSurface().getSharedPtr());
490 first_tsos = false;
491 } else {
492 actsTSOS.setReferenceSurface(parameters.referenceSurface().getSharedPtr());
493 // Since we're converting final Trk::Tracks, let's assume they're smoothed
494 actsTSOS.smoothed() = parameters.parameters();
495 actsTSOS.smoothedCovariance() = *parameters.covariance();
496 // Not yet implemented in MultiTrajectory.icc
497 // actsTSOS.typeFlags().setHasParameters();
498 if (!(actsTSOS.hasSmoothed() && actsTSOS.hasReferenceSurface())) {
499 ATH_MSG_WARNING("TrackState does not have smoothed state ["
500 << actsTSOS.hasSmoothed()
501 << "] or reference surface ["
502 << actsTSOS.hasReferenceSurface() << "].");
503 } else {
504 ATH_MSG_VERBOSE("TrackState has smoothed state and reference surface.");
505 }
506 }
507 } catch (const std::exception& e){
508 ATH_MSG_ERROR("Unable to convert TrackParameter with exception ["<<e.what()<<"]. Will be missing from ACTS track."
509 <<(*tsos->trackParameters()));
510 }
511 }
512 if (tsos->measurementOnTrack()) {
513 auto &measurement = *(tsos->measurementOnTrack());
514
515 measurementsCount++;
516 // const Acts::Surface &surface =
517 // trkSurfaceToActsSurface(measurement.associatedSurface());
518 // Commented for the moment because Surfaces not yet implemented in
519 // MultiTrajectory.icc
520
521 int dim = measurement.localParameters().dimension();
522 actsTSOS.allocateCalibrated(dim);
523 if (dim == 1) {
524 actsTSOS.calibrated<1>() = measurement.localParameters();
525 actsTSOS.calibratedCovariance<1>() = measurement.localCovariance();
526 } else if (dim == 2) {
527 actsTSOS.calibrated<2>() = measurement.localParameters();
528 actsTSOS.calibratedCovariance<2>() = measurement.localCovariance();
529 } else {
530 throw std::domain_error("Cannot handle measurement dim>2");
531 }
532 actsTSOS.setUncalibratedSourceLink(detail::TrkMeasurementCalibrator::pack(tsos->measurementOnTrack()));
533
534 } // end if measurement
535 } // end loop over track states
536 actsTrack.nMeasurements() = measurementsCount;
537 ATH_MSG_VERBOSE("TrackProxy has " << actsTrack.nTrackStates()
538 << " track states on surfaces.");
539 }
540 ATH_MSG_VERBOSE("Finished converting " << trackColl.size() << " tracks.");
541
542 if (!failedIds.empty()){
543 ATH_MSG_WARNING("Failed to convert "<<failedIds.size()<<" track parameters.");
544 for (auto id : failedIds){
545 ATH_MSG_WARNING("-> Failed for Identifier "<<m_idHelperSvc->toString(id));
546 }
547 }
548 ATH_MSG_VERBOSE("ACTS Track container has " << tc.size() << " tracks.");
549}
550
552 const Acts::BoundTrackParameters &parameters,
553 const Trk::TrackParameters &trkparameters,
554 const Acts::GeometryContext &gctx) const {
555 auto actsPos = parameters.position(gctx);
556 // ATH_MSG_VERBOSE("Acts position: \n"
557 // << actsPos << " vs trk position: \n"
558 // << trkparameters.position());
559 // ATH_MSG_VERBOSE(parameters.referenceSurface().toString(gctx));
560 // ATH_MSG_VERBOSE("GeometryId "<<parameters.referenceSurface().geometryId().value());
561
562 if ( (actsPos - trkparameters.position()).mag() > 0.1) {
563 ATH_MSG_WARNING("Parameter position mismatch. Acts \n"
564 << actsPos << " vs Trk \n"
565 << trkparameters.position());
566 ATH_MSG_WARNING("Acts surface:");
567 ATH_MSG_WARNING(parameters.referenceSurface().toString(gctx));
568 ATH_MSG_WARNING("Trk surface:");
569 ATH_MSG_WARNING(trkparameters.associatedSurface());
570 return false;
571 }
572 return true;
573}
574
575// Local functions to check/debug Annulus bounds
576
577#pragma GCC diagnostic push
578#pragma GCC diagnostic ignored "-Wunused-function"
580 const Acts::GeometryContext &gctx, const Trk::MeasurementBase &measurement,
581 const Acts::Surface &surface, const Acts::BoundVector &loc) {
582 const Trk::Surface &surf = measurement.associatedSurface();
583 // only check Annulus for the moment
584 if (surf.bounds().type() != Trk::SurfaceBounds::Annulus) {
585 return;
586 }
587 const auto *bounds = dynamic_cast<const Trk::AnnulusBounds *>(&surf.bounds());
588 if (bounds == nullptr) {
589 throw std::runtime_error{"Annulus but not XY"};
590 }
591
592 Amg::Vector2D locxy = loc.head<2>();
593
594 Acts::Matrix<2, 2> covxy = measurement.localCovariance();
595
596 Amg::Vector3D global = surf.localToGlobal(locxy, Amg::Vector3D{});
597 Acts::Vector2 locpc;
598 if (auto res = surface.globalToLocal(gctx, global, Acts::Vector3{});
599 res.ok()) {
600 locpc = *res;
601 } else {
602 throw std::runtime_error{"Global position not on target surface"};
603 }
604
605 // use ACTS jacobian math to convert cluster covariance from cartesian to
606 // polar
607 auto planeSurface =
608 Acts::Surface::makeShared<Acts::PlaneSurface>(surf.transform());
609 Acts::BoundVector locxypar;
610 locxypar.head<2>() = locxy;
611 locxypar[2] = 0;
612 locxypar[3] = M_PI_2;
613 locxypar[4] = 1;
614 locxypar[5] = 1;
615 Acts::FreeVector globalxypar = Acts::transformBoundToFreeParameters(
616 *planeSurface, gctx, locxypar);
617 auto boundToFree = planeSurface->boundToFreeJacobian(
618 gctx, globalxypar.segment<3>(Acts::eFreePos0),
619 globalxypar.segment<3>(Acts::eFreeDir0));
620 Acts::SquareMatrix<2> xyToXyzJac = boundToFree.topLeftCorner<2, 2>();
621
622 Acts::BoundVector locpcpar;
623 locpcpar.head<2>() = locpc;
624 locpcpar[2] = 0;
625 locpcpar[3] = M_PI_2;
626 locpcpar[4] = 1;
627 locpcpar[5] = 1;
628 Acts::FreeVector globalpcpar = Acts::transformBoundToFreeParameters(
629 surface, gctx, locpcpar);
630
631 boundToFree = surface.boundToFreeJacobian(
632 gctx, globalpcpar.segment<3>(Acts::eFreePos0),
633 globalpcpar.segment<3>(Acts::eFreeDir0));
634 Acts::SquareMatrix<2> pcToXyzJac = boundToFree.topLeftCorner<2, 2>();
635 Acts::SquareMatrix<2> xyzToPcJac = pcToXyzJac.inverse();
636
637 // convert cluster covariance
638 Acts::SquareMatrix<2> covpc = covxy;
639 covpc = xyToXyzJac * covpc * xyToXyzJac.transpose();
640 covpc = xyzToPcJac * covpc * xyzToPcJac.transpose();
641
642 std::mt19937 gen{42 + surface.geometryId().value()};
643 std::normal_distribution<double> normal{0, 1};
644 std::uniform_real_distribution<double> uniform{-1, 1};
645
646 Acts::SquareMatrix<2> lltxy = covxy.llt().matrixL();
647 Acts::SquareMatrix<2> lltpc = covpc.llt().matrixL();
648
649 for (size_t i = 0; i < 1e4; i++) {
650 std::cout << "ANNULUS COV: ";
651 std::cout << surface.geometryId();
652
653 Amg::Vector2D rnd{normal(gen), normal(gen)};
654
655 // XY
656 {
657 Amg::Vector2D xy = lltxy * rnd + locxy;
659 std::cout << "," << xy.x() << "," << xy.y();
660 std::cout << "," << xyz.x() << "," << xyz.y() << "," << xyz.z();
661 }
662 // PC
663 {
664 // Amg::Vector2D xy = lltpc * rnd + loc.head<2>();
665 Amg::Vector2D rt = lltpc * rnd + locpc;
666 Amg::Vector3D xyz = surface.localToGlobal(gctx, rt, Acts::Vector3{});
667 // Amg::Vector3D xyz = surface.transform(gctx).rotation() *
668 // Acts::Vector3{rt.x(), rt.y(), 0};
669
670 std::cout << "," << rt.x() << "," << rt.y();
671 std::cout << "," << xyz.x() << "," << xyz.y() << "," << xyz.z();
672 }
673
674 std::cout << std::endl;
675 }
676}
677#pragma GCC diagnostic pop
678
680 const Acts::BoundTrackParameters &actsParameter,
681 const Acts::GeometryContext &gctx, const Acts::BoundMatrix &covpc,
682 const Acts::BoundVector &targetPars, const Acts::BoundMatrix &targetCov,
683 const Trk::PlaneSurface *planeSurface) {
684
685 std::cout << "ANNULUS PAR COV: ";
686 std::cout << actsParameter.referenceSurface().geometryId();
687 for (unsigned int i = 0; i < 5; i++) {
688 for (unsigned int j = 0; j < 5; j++) {
689 std::cout << "," << covpc(i, j);
690 }
691 }
692 for (unsigned int i = 0; i < 5; i++) {
693 for (unsigned int j = 0; j < 5; j++) {
694 std::cout << "," << targetCov(i, j);
695 }
696 }
697 std::cout << std::endl;
698
699 std::mt19937 gen{4242 +
700 actsParameter.referenceSurface().geometryId().value()};
701 std::normal_distribution<double> normal{0, 1};
702
703 Acts::SquareMatrix<2> lltxy =
704 targetCov.topLeftCorner<2, 2>().llt().matrixL();
705 Acts::SquareMatrix<2> lltpc = covpc.topLeftCorner<2, 2>().llt().matrixL();
706
707 for (size_t i = 0; i < 1e4; i++) {
708 std::cout << "ANNULUS PAR: ";
709 std::cout << actsParameter.referenceSurface().geometryId();
710
711 Acts::Vector<2> rnd;
712 rnd << normal(gen), normal(gen);
713
714 // XY
715 {
716 Acts::Vector<2> xy =
717 lltxy.topLeftCorner<2, 2>() * rnd + targetPars.head<2>();
719 planeSurface->localToGlobal(Amg::Vector2D{xy.head<2>()}, Amg::Vector3D{},
720 xyz);
721 for (unsigned int i = 0; i < 2; i++) {
722 std::cout << "," << xy[i];
723 }
724 std::cout << "," << xyz.x() << "," << xyz.y() << "," << xyz.z();
725 }
726 // PC
727 {
728 Acts::Vector<2> rt = lltpc.topLeftCorner<2, 2>() * rnd +
729 actsParameter.parameters().head<2>();
730 Amg::Vector3D xyz = actsParameter.referenceSurface().localToGlobal(
731 gctx, Acts::Vector2{rt.head<2>()}, Acts::Vector3{});
732
733 for (unsigned int i = 0; i < 2; i++) {
734 std::cout << "," << rt[i];
735 }
736 std::cout << "," << xyz.x() << "," << xyz.y() << "," << xyz.z();
737 }
738
739 std::cout << std::endl;
740 }
741}
742
743std::unique_ptr<Trk::Track> ActsToTrkConverterTool::convertFitResult(const EventContext& ctx,
744 MutableTrackContainer& tracks,
745 TrackFitResult_t& fitResult,
746 const Trk::TrackInfo::TrackFitter fitAuthor,
747 const detail::SourceLinkType slType) const {
750
751 if (not fitResult.ok()) {
752 ATH_MSG_VERBOSE("Fit did not converge");
753 return nullptr;
754 }
755 const Acts::CalibrationContext cctx{getCalibrationContext(ctx)};
756 const Acts::GeometryContext gctx{m_trackingGeometryTool->getGeometryContext(ctx).context()};
757 Acts::ParticleHypothesis hypothesis{Acts::ParticleHypothesis::pion()};
758 if(m_convertMuons) hypothesis = Acts::ParticleHypothesis::muon();
759
760 // Get the fit output object
761 const auto& acts_track = fitResult.value();
762
763 auto finalTrajectory = std::make_unique<Trk::TrackStates>();
764 // initialise the number of dead Pixel and Acts strip
765 unsigned int numberOfDeadPixel{0}, numberOfDeadSCT{0};
766 int nDoF{0};
767
768 double chi2{0};
769
770 // Loop over all the output state to create track state
771 tracks.trackStateContainer().visitBackwards(acts_track.tipIndex(),
772 [&] (const auto &state) -> void {
773 // First only consider state with an associated detector element
774 const auto* associatedDetEl = dynamic_cast<const IDetectorElementBase*>(
775 state.referenceSurface().surfacePlacement());
776
777 if (not associatedDetEl) {
778 ATH_MSG_VERBOSE("State is not associated with a measurement sruface");
779 return;
780 }
781 ATH_MSG_VERBOSE("Associated det: "<<to_string(associatedDetEl->detectorType()));
782 auto flag = state.typeFlags();
783
784 // We need to determine the type of state
785 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typePattern;
786 std::unique_ptr<Trk::TrackParameters> measPars{};
787 std::unique_ptr<Trk::MeasurementBase> measState{};
788
789 // State is a hole (no associated measurement), use predicted parameters
790 if (flag.isHole()){
791 const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
792 state.parameters(),
793 state.covariance(),
794 hypothesis);
795 measPars = actsTrackParametersToTrkParameters(ctx, actsParam, gctx);
796 if (associatedDetEl->detectorType() == DetectorType::Pixel ||
797 associatedDetEl->detectorType() == DetectorType::Sct) {
798 ATH_MSG_VERBOSE("Check if this is a hole, a dead sensors or a state outside the sensor boundary");
799 switch (m_boundaryCheckTool->boundaryCheck(*measPars)) {
800 case Trk::BoundaryCheckResult::DeadElement:
801 numberOfDeadPixel += (associatedDetEl->detectorType() == DetectorType::Pixel);
802 numberOfDeadSCT+= (associatedDetEl->detectorType() == DetectorType::Sct);
803 break;
804 case Trk::BoundaryCheckResult::Candidate:
805 break;
806 default:
807 return;
808 }
809 }
810 typePattern.set(Trk::TrackStateOnSurface::Hole);
811 }
812 // The state is a measurement state, use smoothed parameters
813 else if (flag.hasMeasurement()) {
814 Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
815 state.parameters(),
816 state.covariance(),
817 hypothesis);
818 typePattern.set(Trk::TrackStateOnSurface::Measurement);
819 switch (slType) {
820 using enum detail::SourceLinkType;
821 case TrkMeasurement:
822 measState = measCalib.unpack(state.getUncalibratedSourceLink())->uniqueClone();
823 break;
824 case TrkPrepRawData:
825 measState = prdCalib.createROT(gctx, cctx, state.getUncalibratedSourceLink(), state);
826 break;
827 case xAODUnCalibMeas:
828 ATH_MSG_WARNING("Uncalibrated measurement is not implemented");
829 return;
830 case nTypes:
831 ATH_MSG_WARNING("Invalid type enumaration parsed");
832 return;
833 }
834 nDoF = state.calibratedSize();
835 chi2 = state.chi2();
836
837 }
838 auto perState = std::make_unique<Trk::TrackStateOnSurface>(Trk::FitQualityOnSurface{chi2, nDoF},
839 std::move(measState),
840 std::move(measPars), nullptr, typePattern);
841 // If a state was succesfully created add it to the trajectory
842 ATH_MSG_VERBOSE("State succesfully creates, adding it to the trajectory");
843 finalTrajectory->insert(finalTrajectory->begin(), std::move(perState));
844 });
845 // Convert the perigee state and add it to the trajectory
846 const Acts::BoundTrackParameters actsPer(acts_track.referenceSurface().getSharedPtr(),
847 acts_track.parameters(),
848 acts_track.covariance(),
849 acts_track.particleHypothesis());
850 std::unique_ptr<Trk::TrackParameters> per = actsTrackParametersToTrkParameters(ctx, actsPer, gctx);
851 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typePattern;
852 typePattern.set(Trk::TrackStateOnSurface::Perigee);
853 auto perState = std::make_unique<Trk::TrackStateOnSurface>(nullptr, std::move(per), nullptr, typePattern);
854 finalTrajectory->insert(finalTrajectory->begin(), std::move(perState));
855
856 // Create the track using the states
857 Trk::TrackInfo newInfo{fitAuthor, Trk::noHypothesis};
858 auto newtrack = std::make_unique<Trk::Track>(newInfo, std::move(finalTrajectory), nullptr);
859 if (newtrack) {
860 // Create the track summary and update the holes information
861 if (!newtrack->trackSummary()) {
862 newtrack->setTrackSummary(std::make_unique<Trk::TrackSummary>());
863 newtrack->trackSummary()->update(Trk::numberOfPixelDeadSensors, numberOfDeadPixel);
864 newtrack->trackSummary()->update(Trk::numberOfSCTDeadSensors, numberOfDeadSCT);
865 }
866 m_trkSummaryTool->updateTrackSummary(ctx, *newtrack, true);
867 }
868 return newtrack;
869 }
870} // namespace ActsTrk
Scalar mag() const
mag method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define AmgSymMatrix(dim)
std::pair< std::vector< unsigned int >, bool > res
static Double_t tc
static TRandom * rnd
if(pathvar)
DataVector< Trk::Track > TrackCollection
This typedef represents a collection of Trk::Track objects.
#define xyz
virtual void toSourceLinks(const std::vector< const Trk::MeasurementBase * > &measSet, std::vector< Acts::SourceLink > &links) const override final
bool actsTrackParameterPositionCheck(const Acts::BoundTrackParameters &actsParameter, const Trk::TrackParameters &tsos, const Acts::GeometryContext &gctx) const
ToolHandle< Trk::IExtendedTrackSummaryTool > m_trkSummaryTool
Tools needed to create Trk::Tracks from the ACts fit result.
SG::ReadCondHandleKey< MuonGM::MuonDetectorManager > m_muonMgrKey
Detector manager to fetch the legacy Trk surfaces.
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
virtual StatusCode initialize() override
std::unordered_map< Identifier, const Acts::Surface * > m_actsSurfaceMap
Gaudi::Property< bool > m_visualDebugOutput
virtual const Acts::BoundTrackParameters trkTrackParametersToActsParameters(const Trk::TrackParameters &atlasParameter, const Acts::GeometryContext &gctx, Trk::ParticleHypothesis=Trk::pion) const override
Create Acts TrackParameter from ATLAS one.
ToolHandle< Trk::IBoundaryCheckTool > m_boundaryCheckTool
ToolHandle< Trk::IRIO_OnTrackCreator > m_ROTcreator
Gaudi::Property< bool > m_extractMuonSurfaces
virtual std::unique_ptr< Trk::TrackParameters > actsTrackParametersToTrkParameters(const EventContext &ctx, const Acts::BoundTrackParameters &actsParameter, const Acts::GeometryContext &gctx) const override
Create ATLAS TrackParameter from Acts one.
std::shared_ptr< const Acts::TrackingGeometry > m_trackingGeometry
virtual void trkTrackCollectionToActsTrackContainer(ActsTrk::MutableTrackContainer &tc, const TrackCollection &trackColl, const Acts::GeometryContext &gctx) const override
Convert TrackCollection to Acts track container.
virtual const Trk::Surface & actsSurfaceToTrkSurface(const EventContext &ctx, const Acts::Surface &actsSurface) const override
Find the ATLAS surface corresponding to the Acts surface Only work if the Acts surface has an associa...
virtual const Acts::Surface & trkSurfaceToActsSurface(const Trk::Surface &atlasSurface) const override
Find the Acts surface corresponding to the ATLAS surface Use a map associating ATLAS ID to Acts surfa...
virtual std::unique_ptr< Trk::Track > convertFitResult(const EventContext &ctx, ActsTrk::MutableTrackContainer &tracks, TrackFitResult_t &fitResult, const Trk::TrackInfo::TrackFitter fitAuthor, const detail::SourceLinkType slType) const override final
virtual std::vector< Acts::SourceLink > trkTrackToSourceLinks(const Trk::Track &track) const override
Transform an ATLAS track into a vector of SourceLink to be use in the avts tracking Transform both me...
PublicToolHandle< ActsTrk::ITrackingGeometryTool > m_trackingGeometryTool
base class interface providing the bare minimal interface extension.
: Helper class to connect the aligned transformations of each active sensor(layer) with the Acts::Sur...
Identifier identify() const override final
Returns the identifier of the Surface.
Calibrator class that links the legacy Trk::MeasurementBase objects with the Acts MultiTrajectory tra...
static Acts::SourceLink pack(const Trk::MeasurementBase *meas)
Packs the pointer to the track measurement into an Acts::SouceLink.
Class to calibrate the Acts track states with uncalibrated Trk::PrepRaw data objects.
static Acts::SourceLink pack(const SourceLink_t prd)
Pack the PrepRaw data measurement into a source link.
size_type size() const noexcept
Returns the number of elements in the collection.
std::vector< const MuonReadoutElement * > getAllReadoutElements(const ActsTrk::DetectorType type=ActsTrk::DetectorType::UnDefined) const
Returns all readout elements.
The MuonDetectorManager stores the transient representation of the Muon Spectrometer geometry and pro...
const MuonReadoutElement * getReadoutElement(const Identifier &id) const
Get any read out element.
Bounds for a annulus-like, planar Surface.
Class for a conical surface in the ATLAS detector.
Definition ConeSurface.h:51
Class for a CylinderSurface in the ATLAS detector.
Class for a DiscSurface in the ATLAS detector.
Definition DiscSurface.h:54
This class is the pure abstract base class for all fittable tracking measurements.
virtual const Surface & associatedSurface() const =0
Interface method to get the associated Surface.
const Amg::MatrixX & localCovariance() const
Interface method to get the localError.
virtual MsgStream & dump(MsgStream &out) const
Dumps relevant information about the track parameters into the ostream.
const Amg::Vector3D & momentum() const
Access method for the momentum.
const Amg::Vector3D & position() const
Access method for the position.
double charge() const
Returns the charge.
virtual bool hasSurface() const override=0
Test to see if there's a not null surface ptr.
virtual const Surface & associatedSurface() const override=0
Access to the Surface associated to the Parameters.
Class describing the Line to which the Perigee refers to.
Class for a planaer rectangular or trapezoidal surface in the ATLAS detector.
virtual void localToGlobal(const Amg::Vector2D &locp, const Amg::Vector3D &mom, Amg::Vector3D &glob) const override final
Specified for PlaneSurface: LocalToGlobal method without dynamic memory allocation.
Class for a StraightLineSurface in the ATLAS detector to describe dirft tube and straw like detectors...
virtual BoundsType type() const =0
Return the bounds type - for persistency optimization.
Abstract Base Class for tracking surfaces.
Definition Surface.h:79
const Amg::Transform3D & transform() const
Returns HepGeom::Transform3D by reference.
Identifier associatedDetectorElementIdentifier() const
return Identifier of the associated Detector Element
virtual constexpr SurfaceType type() const =0
Returns the Surface type to avoid dynamic casts.
virtual const SurfaceBounds & bounds() const =0
Surface Bounds method.
virtual void localToGlobal(const Amg::Vector2D &locp, const Amg::Vector3D &mom, Amg::Vector3D &glob) const =0
Specified by each surface type: LocalToGlobal method without dynamic memory allocation.
SurfaceOwner owner() const
return ownership
Contains information about the 'fitter' of this track.
TrackFitter
enums to identify who created this track and what propertis does it have.
represents the track state (measurement, material, fit parameters and quality) at a surface.
@ Perigee
This represents a perigee, and so will contain a Perigee object only.
@ Hole
A hole on the track - this is defined in the following way.
virtual const Surface & surface() const =0
Return surface associated with this detector element.
double chi2(TH1 *h0, TH1 *h1)
xAOD::ParticleHypothesis convert(Acts::ParticleHypothesis h)
SourceLinkType
Enumeration to distinguish between the ATLAS EDM -> Acts::SourceLink variants.
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
DetectorType
Simple enum to Identify the Type of the ACTS sub detector.
@ Mm
Maybe not needed in the migration.
@ Tgc
Resitive Plate Chambers.
@ sTgc
Micromegas (NSW).
@ Rpc
Monitored Drift Tubes.
@ Csc
Thin gap champers.
@ Trt
Maybe the Sct / Pixel for Itk become seperate entries?
@ Mdt
MuonSpectrometer.
std::string to_string(const DetectorType &type)
Acts::TrackContainer< MutableTrackBackend, MutableTrackStateBackend, Acts::detail::ValueHolder > MutableTrackContainer
Acts::CalibrationContext getCalibrationContext(const EventContext &ctx)
The Acts::Calibration context is piped through the Acts fitters to (re)calibrate the Acts::SourceLink...
static void ActsTrackParameterCheck(const Acts::BoundTrackParameters &actsParameter, const Acts::GeometryContext &gctx, const Acts::BoundMatrix &covpc, const Acts::BoundVector &targetPars, const Acts::BoundMatrix &targetCov, const Trk::PlaneSurface *planeSurface)
static void ActsMeasurementCheck(const Acts::GeometryContext &gctx, const Trk::MeasurementBase &measurement, const Acts::Surface &surface, const Acts::BoundVector &loc)
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
DataVector< const Trk::TrackStateOnSurface > TrackStates
@ DetElOwn
Definition Surface.h:59
@ locY
local cartesian
Definition ParamDefs.h:38
@ locX
Definition ParamDefs.h:37
@ phi0
Definition ParamDefs.h:65
@ theta
Definition ParamDefs.h:66
ParticleHypothesis
Enumeration for Particle hypothesis respecting the interaction with material.
ParametersBase< TrackParametersDim, Charged > TrackParameters
@ numberOfPixelDeadSensors
number of pixel hits with broad errors (width/sqrt(12))
Definition index.py:1
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10