ATLAS Offline Software
Loading...
Searching...
No Matches
ActsToTrkConverterTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 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/TrackParameters.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::BoundSquareMatrix &covpc,
73 const Acts::BoundVector &targetPars, const Acts::BoundSquareMatrix &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->associatedDetectorElement());
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->associatedDetectorElement())->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 Acts::Surface &actsSurface) const {
133
134 const auto *detEleBase= dynamic_cast<const IDetectorElementBase*>(actsSurface.associatedDetectorElement());
135 if (!detEleBase) {
136 ATH_MSG_ERROR(actsSurface.toString(m_trackingGeometryTool->getNominalGeometryContext().context()));
137 throw std::domain_error("ActsToTrkConverterTool() - Surface does not have an associated detector element. ");
138 }
139 switch (detEleBase->detectorType()) {
140 using enum DetectorType;
141 case Pixel:
142 case Sct:
143 case Hgtd:
144 case Trt: {
145 const auto actsElement = dynamic_cast<const ActsDetectorElement*>(detEleBase);
146 if (actsElement) {
147 return actsElement->atlasSurface();
148 }
149 break;
150 }
151 case Mdt:
152 case Rpc:
153 case Tgc:
154 case Csc:
155 case sTgc:
156 case Mm: {
157 const MuonGM::MuonDetectorManager* detMgr{nullptr};
158 if (!SG::get(detMgr, m_muonMgrKey, Gaudi::Hive::currentContext()).isSuccess() || !detMgr) {
159 THROW_EXCEPTION("Failed to retrieve the muon detector manager");
160 }
161 return detMgr->getReadoutElement(detEleBase->identify())->surface(detEleBase->identify());
162
163 } default:
164 break;
165 }
166 throw std::domain_error("ActsToTrkConverterTool() - No ATLAS surface corresponding to the Acts one");
167}
168
170 const Trk::Surface &atlasSurface) const {
171
172 Identifier atlasID = atlasSurface.associatedDetectorElementIdentifier();
173 auto it = m_actsSurfaceMap.find(atlasID);
174 if (it != m_actsSurfaceMap.end()) {
175 return *it->second;
176 }
177 ATH_MSG_ERROR("No Acts surface corresponding to this ATLAS surface: "<<atlasID);
178 ATH_MSG_ERROR(atlasSurface);
179 throw std::domain_error("No Acts surface corresponding to the ATLAS one");
180}
181
182std::vector<Acts::SourceLink>
184 std::vector<Acts::SourceLink> sourceLinks{};
185 sourceLinks.reserve(track.measurementsOnTrack()->size() +
186 track.outliersOnTrack()->size());
187 toSourceLinks(track.measurementsOnTrack()->stdcont(), sourceLinks);
188 toSourceLinks(track.outliersOnTrack()->stdcont(), sourceLinks);
189 return sourceLinks;
190}
191
192
193void ActsToTrkConverterTool::toSourceLinks(const std::vector<const Trk::MeasurementBase*>& measSet,
194 std::vector<Acts::SourceLink>& sourceLinks) const{
195 if (sourceLinks.capacity() < sourceLinks.size() + measSet.size()) {
196 sourceLinks.reserve(sourceLinks.size() + measSet.size());
197 }
198 std::ranges::transform(measSet, std::back_inserter(sourceLinks), [](const Trk::MeasurementBase* meas) {
200 });
201}
202void ActsToTrkConverterTool::toSourceLinks(const std::vector<const Trk::PrepRawData*>& prdSet,
203 std::vector<Acts::SourceLink>& links) const {
204 if (links.capacity() < links.size() + prdSet.size()) {
205 links.reserve(links.size() + prdSet.size());
206 }
207 std::ranges::transform(prdSet, std::back_inserter(links), [](const Trk::PrepRawData* prd) {
209 });
210}
211
212const Acts::BoundTrackParameters
214 const Acts::GeometryContext & gctx,
215 Trk::ParticleHypothesis hypothesis) const {
216
217 using namespace Acts::UnitLiterals;
218 std::shared_ptr<const Acts::Surface> actsSurface;
219 Acts::BoundVector params;
220
221 // get the associated surface
222 if (atlasParameter.hasSurface() &&
224 try {
225 actsSurface = trkSurfaceToActsSurface(atlasParameter.associatedSurface()).getSharedPtr();
226 } catch (const std::exception &e) {
227 ATH_MSG_ERROR("Could not find ACTS detector surface for this TrackParameter:");
228 ATH_MSG_ERROR(atlasParameter);
229 throw; // Nothing we can do, so just pass exception on...
230 }
231 }
232 // no associated surface create a perigee one
233 else {
235 "trkTrackParametersToActsParameters:: No associated surface found (owner: "<<atlasParameter.associatedSurface().owner()<<
236 "). Creating a free surface. Trk parameters:");
237 ATH_MSG_VERBOSE(atlasParameter);
238 const Amg::Transform3D& trf{atlasParameter.associatedSurface().transform()};
239 switch (atlasParameter.associatedSurface().type()){
241 actsSurface = Acts::Surface::makeShared<const Acts::PlaneSurface>(trf);
242 break;
244 actsSurface = Acts::Surface::makeShared<const Acts::PerigeeSurface>(trf);
245 break;
247 actsSurface = Acts::Surface::makeShared<const Acts::StrawSurface>(trf);
248 break;
249 // TODO - implement the missing types?
250 default: {
251 std::stringstream surfStr{};
252 atlasParameter.dump(surfStr);
253 throw std::domain_error(std::format("Failed to translate parameters {:}", surfStr.str()));
254 }
255 }
256 }
257
258 // Construct track parameters
259 const auto& atlasParam{atlasParameter.parameters()};
260 if (actsSurface->bounds().type() == Acts::SurfaceBounds::BoundsType::eAnnulus) {
261 // Annulus surfaces are constructed differently in Acts/Trk so we need to
262 // convert local coordinates
263 const Amg::Vector3D& position{atlasParameter.position()};
264 auto result = actsSurface->globalToLocal(gctx, position, atlasParameter.momentum());
265 if (result.ok()) {
266 params << (*result)[0], (*result)[1], atlasParam[Trk::phi0],
267 atlasParam[Trk::theta],
268 atlasParameter.charge() / (atlasParameter.momentum().mag() * 1_MeV),
269 0.;
270 } else {
271 ATH_MSG_WARNING("Unable to convert annulus surface - globalToLocal failed");
272 }
273 } else {
274 params << atlasParam[Trk::locX], atlasParam[Trk::locY],
275 atlasParam[Trk::phi0], atlasParam[Trk::theta],
276 atlasParameter.charge() / (atlasParameter.momentum().mag() * 1_MeV), 0.;
277 }
278
279 Acts::BoundSquareMatrix cov = Acts::BoundSquareMatrix::Identity();
280 if (atlasParameter.covariance()) {
281 cov.topLeftCorner(5, 5) = *atlasParameter.covariance();
282
283 // Convert the covariance matrix from MeV
284 // FIXME: This needs to handle the annulus case as well - currently the cov
285 // is wrong for annulus surfaces
286 for (int i = 0; i < cov.rows(); i++) {
287 cov(i, 4) = cov(i, 4) / 1_MeV;
288 }
289 for (int i = 0; i < cov.cols(); i++) {
290 cov(4, i) = cov(4, i) / 1_MeV;
291 }
292 }
293
294 return Acts::BoundTrackParameters(actsSurface, params,cov,
295 ParticleHypothesis::convert(hypothesis));
296}
297
298std::unique_ptr<Trk::TrackParameters>
300 const Acts::BoundTrackParameters &actsParameter,
301 const Acts::GeometryContext &gctx) const {
302
303 using namespace Acts::UnitLiterals;
304 std::optional<AmgSymMatrix(5)> cov = std::nullopt;
305 if (actsParameter.covariance()) {
306 AmgSymMatrix(5) newcov(actsParameter.covariance()->topLeftCorner<5, 5>());
307 // Convert the covariance matrix to GeV
308 for (int i = 0; i < newcov.rows(); i++) {
309 newcov(i, 4) = newcov(i, 4) * 1_MeV;
310 }
311 for (int i = 0; i < newcov.cols(); i++) {
312 newcov(4, i) = newcov(4, i) * 1_MeV;
313 }
314 cov = std::optional<AmgSymMatrix(5)>(newcov);
315 }
316
317 const Acts::Surface &actsSurface = actsParameter.referenceSurface();
318 switch (actsSurface.type()) {
319 case Acts::Surface::SurfaceType::Cone: {
320 const auto &coneSurface = static_cast<const Trk::ConeSurface&>(actsSurfaceToTrkSurface(actsSurface));
321 return std::make_unique<Trk::AtaCone>(
322 actsParameter.get<Acts::eBoundLoc0>(),
323 actsParameter.get<Acts::eBoundLoc1>(),
324 actsParameter.get<Acts::eBoundPhi>(),
325 actsParameter.get<Acts::eBoundTheta>(),
326 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, coneSurface, cov);
327 } case Acts::Surface::SurfaceType::Cylinder: {
328 const auto &cylSurface{static_cast<const Trk::CylinderSurface&>(actsSurfaceToTrkSurface(actsSurface))};
329 return std::make_unique<Trk::AtaCylinder>(
330 actsParameter.get<Acts::eBoundLoc0>(),
331 actsParameter.get<Acts::eBoundLoc1>(),
332 actsParameter.get<Acts::eBoundPhi>(),
333 actsParameter.get<Acts::eBoundTheta>(),
334 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, cylSurface, cov);
335 } case Acts::Surface::SurfaceType::Disc: {
336 const Trk::Surface& trkSurface{actsSurfaceToTrkSurface(actsSurface)};
337 if (trkSurface.type() == Trk::SurfaceType::Disc) {
338 const auto& discSurface{static_cast<const Trk::DiscSurface&>(trkSurface)};
339 return std::make_unique<Trk::AtaDisc>(
340 actsParameter.get<Acts::eBoundLoc0>(),
341 actsParameter.get<Acts::eBoundLoc1>(),
342 actsParameter.get<Acts::eBoundPhi>(),
343 actsParameter.get<Acts::eBoundTheta>(),
344 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, discSurface, cov);
345 } else if (trkSurface.type() == Trk::SurfaceType::Plane) {
346 auto& planeSurface{static_cast<const Trk::PlaneSurface&>(trkSurface)};
347 // need to convert to plane position on plane surface (annulus bounds)
348 auto helperSurface = Acts::Surface::makeShared<Acts::PlaneSurface>(planeSurface.transform());
349
350 auto covpc = actsParameter.covariance().value();
352 Acts::FreeVector freePars = Acts::transformBoundToFreeParameters(actsSurface, gctx,
353 actsParameter.parameters());
354
356 Acts::BoundVector targetPars = Acts::transformFreeToBoundParameters(freePars,
357 *helperSurface, gctx).value();
358
359 Acts::FreeMatrix freeTransportJacobian{Acts::FreeMatrix::Identity()};
360
361 Acts::FreeVector freeToPathDerivatives{Acts::FreeVector::Zero()};
362 freeToPathDerivatives.head<3>() = freePars.segment<3>(Acts::eFreeDir0);
363
364 auto boundToFreeJacobian = actsSurface.boundToFreeJacobian(gctx, freePars.segment<3>(Acts::eFreePos0),
365 freePars.segment<3>(Acts::eFreeDir0));
366
367 Acts::BoundMatrix boundToBoundJac = Acts::detail::boundToBoundTransportJacobian(gctx, freePars,
368 boundToFreeJacobian, freeTransportJacobian, freeToPathDerivatives, *helperSurface);
369
370 Acts::BoundMatrix targetCov{boundToBoundJac * covpc * boundToBoundJac.transpose()};
371
372 auto pars = std::make_unique<Trk::AtaPlane>(
373 targetPars[Acts::eBoundLoc0], targetPars[Acts::eBoundLoc1],
374 targetPars[Acts::eBoundPhi], targetPars[Acts::eBoundTheta],
375 targetPars[Acts::eBoundQOverP] * 1_MeV, planeSurface,
376 targetCov.topLeftCorner<5, 5>());
377
379 ActsTrackParameterCheck(actsParameter, gctx, covpc, targetPars,
380 targetCov, &planeSurface);
381 }
382 return pars;
383
384 } else {
385 throw std::domain_error("Acts::DiscSurface is not associated with ATLAS disc or plane surface");
386 }
387 break;
388 } case Acts::Surface::SurfaceType::Perigee: {
389 const Trk::PerigeeSurface perSurface(actsSurface.center(gctx));
390 return std::make_unique<Trk::Perigee>(
391 actsParameter.get<Acts::eBoundLoc0>(),
392 actsParameter.get<Acts::eBoundLoc1>(),
393 actsParameter.get<Acts::eBoundPhi>(),
394 actsParameter.get<Acts::eBoundTheta>(),
395 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, perSurface, cov);
396 } case Acts::Surface::SurfaceType::Plane: {
397 auto &plaSurface{static_cast<const Trk::PlaneSurface&>(actsSurfaceToTrkSurface(actsSurface))};
398 return std::make_unique<Trk::AtaPlane>(
399 actsParameter.get<Acts::eBoundLoc0>(),
400 actsParameter.get<Acts::eBoundLoc1>(),
401 actsParameter.get<Acts::eBoundPhi>(),
402 actsParameter.get<Acts::eBoundTheta>(),
403 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, plaSurface, cov);
404 } case Acts::Surface::SurfaceType::Straw: {
405 auto& lineSurface{static_cast<const Trk::StraightLineSurface&>(actsSurfaceToTrkSurface(actsSurface))};
406 return std::make_unique<Trk::AtaStraightLine>(
407 actsParameter.get<Acts::eBoundLoc0>(),
408 actsParameter.get<Acts::eBoundLoc1>(),
409 actsParameter.get<Acts::eBoundPhi>(),
410 actsParameter.get<Acts::eBoundTheta>(),
411 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, lineSurface, cov);
412 } case Acts::Surface::SurfaceType::Curvilinear: {
413 return std::make_unique<Trk::CurvilinearParameters>(
414 actsParameter.position(gctx), actsParameter.get<Acts::eBoundPhi>(),
415 actsParameter.get<Acts::eBoundTheta>(),
416 actsParameter.get<Acts::eBoundQOverP>() * 1_MeV, cov);
417 break;
418 } case Acts::Surface::SurfaceType::Other: {
419 break;
420 }
421 }
422 throw std::domain_error("Surface type not found");
423}
424
426 const TrackCollection &trackColl,
427 const Acts::GeometryContext & gctx) const {
428 ATH_MSG_VERBOSE("Calling trkTrackCollectionToActsTrackContainer with "
429 << trackColl.size() << " tracks.");
430 unsigned int trkCount = 0;
431 std::vector<Identifier> failedIds; // Keep track of Identifiers of failed conversions
432 for (const Trk::Track* trk : trackColl) {
433 // Do conversions!
434 const Trk::TrackStates *trackStates = trk->trackStateOnSurfaces();
435
436 auto actsTrack = tc.getTrack(tc.addTrack());
437 auto& trackStateContainer = tc.trackStateContainer();
438
439 ATH_MSG_VERBOSE("Track "<<trkCount++<<" has " << trackStates->size()
440 << " track states on surfaces.");
441 // basic quantities copy
442 actsTrack.chi2() = trk->fitQuality()->chiSquared();
443 actsTrack.nDoF() = trk->fitQuality()->numberDoF();
444
445 // loop over track states on surfaces, convert and add them to the ACTS
446 // container
447 bool first_tsos = true; // We need to handle the first one differently
448 int measurementsCount = 0;
449 for (const Trk::TrackStateOnSurface* tsos : *trackStates) {
450
451 // Setup the mask
452 Acts::TrackStatePropMask mask = Acts::TrackStatePropMask::None;
453 if (tsos->measurementOnTrack()) {
454 mask |= Acts::TrackStatePropMask::Calibrated;
455 }
456 if (tsos->trackParameters()) {
457 mask |= Acts::TrackStatePropMask::Smoothed;
458 }
459
460 // Setup the index of the trackstate
461 auto index = Acts::MultiTrajectoryTraits::kInvalid;
462 if (!first_tsos) {
463 index = actsTrack.tipIndex();
464 }
465 auto actsTSOS = trackStateContainer.getTrackState(trackStateContainer.addTrackState(mask, index));
466 ATH_MSG_VERBOSE("TipIndex: " << actsTrack.tipIndex() << " TSOS index within trajectory: "<< actsTSOS.index());
467 actsTrack.tipIndex() = actsTSOS.index();
468
469 if (tsos->trackParameters()) {
470 // TODO This try/catch is temporary and should be removed once the sTGC problem is fixed.
471 try {
472 ATH_MSG_VERBOSE("Converting track parameters.");
473 // TODO - work out whether we should set predicted, filtered, smoothed
474 const Acts::BoundTrackParameters parameters =
475 trkTrackParametersToActsParameters(*(tsos->trackParameters()), gctx);
476 ATH_MSG_VERBOSE("Track parameters: " << parameters.parameters());
477 // Sanity check on positions
478 if (!actsTrackParameterPositionCheck(parameters, *(tsos->trackParameters()), gctx)){
479 failedIds.push_back(tsos->trackParameters()->associatedSurface().associatedDetectorElementIdentifier());
480 }
481
482 if (first_tsos) {
483 // This is the first track state, so we need to set the track
484 // parameters
485 actsTrack.parameters() = parameters.parameters();
486 actsTrack.covariance() = *parameters.covariance();
487 actsTrack.setReferenceSurface(parameters.referenceSurface().getSharedPtr());
488 first_tsos = false;
489 } else {
490 actsTSOS.setReferenceSurface(parameters.referenceSurface().getSharedPtr());
491 // Since we're converting final Trk::Tracks, let's assume they're smoothed
492 actsTSOS.smoothed() = parameters.parameters();
493 actsTSOS.smoothedCovariance() = *parameters.covariance();
494 // Not yet implemented in MultiTrajectory.icc
495 // actsTSOS.typeFlags() |= Acts::TrackStateFlag::ParameterFlag;
496 if (!(actsTSOS.hasSmoothed() && actsTSOS.hasReferenceSurface())) {
497 ATH_MSG_WARNING("TrackState does not have smoothed state ["
498 << actsTSOS.hasSmoothed()
499 << "] or reference surface ["
500 << actsTSOS.hasReferenceSurface() << "].");
501 } else {
502 ATH_MSG_VERBOSE("TrackState has smoothed state and reference surface.");
503 }
504 }
505 } catch (const std::exception& e){
506 ATH_MSG_ERROR("Unable to convert TrackParameter with exception ["<<e.what()<<"]. Will be missing from ACTS track."
507 <<(*tsos->trackParameters()));
508 }
509 }
510 if (tsos->measurementOnTrack()) {
511 auto &measurement = *(tsos->measurementOnTrack());
512
513 measurementsCount++;
514 // const Acts::Surface &surface =
515 // trkSurfaceToActsSurface(measurement.associatedSurface());
516 // Commented for the moment because Surfaces not yet implemented in
517 // MultiTrajectory.icc
518
519 int dim = measurement.localParameters().dimension();
520 actsTSOS.allocateCalibrated(dim);
521 if (dim == 1) {
522 actsTSOS.calibrated<1>() = measurement.localParameters();
523 actsTSOS.calibratedCovariance<1>() = measurement.localCovariance();
524 } else if (dim == 2) {
525 actsTSOS.calibrated<2>() = measurement.localParameters();
526 actsTSOS.calibratedCovariance<2>() = measurement.localCovariance();
527 } else {
528 throw std::domain_error("Cannot handle measurement dim>2");
529 }
530 actsTSOS.setUncalibratedSourceLink(detail::TrkMeasurementCalibrator::pack(tsos->measurementOnTrack()));
531
532 } // end if measurement
533 } // end loop over track states
534 actsTrack.nMeasurements() = measurementsCount;
535 ATH_MSG_VERBOSE("TrackProxy has " << actsTrack.nTrackStates()
536 << " track states on surfaces.");
537 }
538 ATH_MSG_VERBOSE("Finished converting " << trackColl.size() << " tracks.");
539
540 if (!failedIds.empty()){
541 ATH_MSG_WARNING("Failed to convert "<<failedIds.size()<<" track parameters.");
542 for (auto id : failedIds){
543 ATH_MSG_WARNING("-> Failed for Identifier "<<m_idHelperSvc->toString(id));
544 }
545 }
546 ATH_MSG_VERBOSE("ACTS Track container has " << tc.size() << " tracks.");
547}
548
550 const Acts::BoundTrackParameters &parameters,
551 const Trk::TrackParameters &trkparameters,
552 const Acts::GeometryContext &gctx) const {
553 auto actsPos = parameters.position(gctx);
554 // ATH_MSG_VERBOSE("Acts position: \n"
555 // << actsPos << " vs trk position: \n"
556 // << trkparameters.position());
557 // ATH_MSG_VERBOSE(parameters.referenceSurface().toString(gctx));
558 // ATH_MSG_VERBOSE("GeometryId "<<parameters.referenceSurface().geometryId().value());
559
560 if ( (actsPos - trkparameters.position()).mag() > 0.1) {
561 ATH_MSG_WARNING("Parameter position mismatch. Acts \n"
562 << actsPos << " vs Trk \n"
563 << trkparameters.position());
564 ATH_MSG_WARNING("Acts surface:");
565 ATH_MSG_WARNING(parameters.referenceSurface().toString(gctx));
566 ATH_MSG_WARNING("Trk surface:");
567 ATH_MSG_WARNING(trkparameters.associatedSurface());
568 return false;
569 }
570 return true;
571}
572
573// Local functions to check/debug Annulus bounds
574
575#pragma GCC diagnostic push
576#pragma GCC diagnostic ignored "-Wunused-function"
578 const Acts::GeometryContext &gctx, const Trk::MeasurementBase &measurement,
579 const Acts::Surface &surface, const Acts::BoundVector &loc) {
580 const Trk::Surface &surf = measurement.associatedSurface();
581 // only check Annulus for the moment
582 if (surf.bounds().type() != Trk::SurfaceBounds::Annulus) {
583 return;
584 }
585 const auto *bounds = dynamic_cast<const Trk::AnnulusBounds *>(&surf.bounds());
586 if (bounds == nullptr) {
587 throw std::runtime_error{"Annulus but not XY"};
588 }
589
590 Amg::Vector2D locxy = loc.head<2>();
591
592 Acts::ActsMatrix<2, 2> covxy = measurement.localCovariance();
593
594 Amg::Vector3D global = surf.localToGlobal(locxy, Amg::Vector3D{});
595 Acts::Vector2 locpc;
596 if (auto res = surface.globalToLocal(gctx, global, Acts::Vector3{});
597 res.ok()) {
598 locpc = *res;
599 } else {
600 throw std::runtime_error{"Global position not on target surface"};
601 }
602
603 // use ACTS jacobian math to convert cluster covariance from cartesian to
604 // polar
605 auto planeSurface =
606 Acts::Surface::makeShared<Acts::PlaneSurface>(surf.transform());
607 Acts::BoundVector locxypar;
608 locxypar.head<2>() = locxy;
609 locxypar[2] = 0;
610 locxypar[3] = M_PI_2;
611 locxypar[4] = 1;
612 locxypar[5] = 1;
613 Acts::FreeVector globalxypar = Acts::transformBoundToFreeParameters(
614 *planeSurface, gctx, locxypar);
615 auto boundToFree = planeSurface->boundToFreeJacobian(
616 gctx, globalxypar.segment<3>(Acts::eFreePos0),
617 globalxypar.segment<3>(Acts::eFreeDir0));
618 Acts::ActsSquareMatrix<2> xyToXyzJac = boundToFree.topLeftCorner<2, 2>();
619
620 Acts::BoundVector locpcpar;
621 locpcpar.head<2>() = locpc;
622 locpcpar[2] = 0;
623 locpcpar[3] = M_PI_2;
624 locpcpar[4] = 1;
625 locpcpar[5] = 1;
626 Acts::FreeVector globalpcpar = Acts::transformBoundToFreeParameters(
627 surface, gctx, locpcpar);
628
629 boundToFree = surface.boundToFreeJacobian(
630 gctx, globalpcpar.segment<3>(Acts::eFreePos0),
631 globalpcpar.segment<3>(Acts::eFreeDir0));
632 Acts::ActsSquareMatrix<2> pcToXyzJac = boundToFree.topLeftCorner<2, 2>();
633 Acts::ActsSquareMatrix<2> xyzToPcJac = pcToXyzJac.inverse();
634
635 // convert cluster covariance
636 Acts::ActsMatrix<2, 2> covpc = covxy;
637 covpc = xyToXyzJac * covpc * xyToXyzJac.transpose();
638 covpc = xyzToPcJac * covpc * xyzToPcJac.transpose();
639
640 std::mt19937 gen{42 + surface.geometryId().value()};
641 std::normal_distribution<double> normal{0, 1};
642 std::uniform_real_distribution<double> uniform{-1, 1};
643
644 Acts::ActsMatrix<2, 2> lltxy = covxy.llt().matrixL();
645 Acts::ActsMatrix<2, 2> lltpc = covpc.llt().matrixL();
646
647 for (size_t i = 0; i < 1e4; i++) {
648 std::cout << "ANNULUS COV: ";
649 std::cout << surface.geometryId();
650
651 Amg::Vector2D rnd{normal(gen), normal(gen)};
652
653 // XY
654 {
655 Amg::Vector2D xy = lltxy * rnd + locxy;
657 std::cout << "," << xy.x() << "," << xy.y();
658 std::cout << "," << xyz.x() << "," << xyz.y() << "," << xyz.z();
659 }
660 // PC
661 {
662 // Amg::Vector2D xy = lltpc * rnd + loc.head<2>();
663 Amg::Vector2D rt = lltpc * rnd + locpc;
664 Amg::Vector3D xyz = surface.localToGlobal(gctx, rt, Acts::Vector3{});
665 // Amg::Vector3D xyz = surface.transform(gctx).rotation() *
666 // Acts::Vector3{rt.x(), rt.y(), 0};
667
668 std::cout << "," << rt.x() << "," << rt.y();
669 std::cout << "," << xyz.x() << "," << xyz.y() << "," << xyz.z();
670 }
671
672 std::cout << std::endl;
673 }
674}
675#pragma GCC diagnostic pop
676
678 const Acts::BoundTrackParameters &actsParameter,
679 const Acts::GeometryContext &gctx, const Acts::BoundSquareMatrix &covpc,
680 const Acts::BoundVector &targetPars, const Acts::BoundSquareMatrix &targetCov,
681 const Trk::PlaneSurface *planeSurface) {
682
683 std::cout << "ANNULUS PAR COV: ";
684 std::cout << actsParameter.referenceSurface().geometryId();
685 for (unsigned int i = 0; i < 5; i++) {
686 for (unsigned int j = 0; j < 5; j++) {
687 std::cout << "," << covpc(i, j);
688 }
689 }
690 for (unsigned int i = 0; i < 5; i++) {
691 for (unsigned int j = 0; j < 5; j++) {
692 std::cout << "," << targetCov(i, j);
693 }
694 }
695 std::cout << std::endl;
696
697 std::mt19937 gen{4242 +
698 actsParameter.referenceSurface().geometryId().value()};
699 std::normal_distribution<double> normal{0, 1};
700
701 Acts::ActsMatrix<2, 2> lltxy =
702 targetCov.topLeftCorner<2, 2>().llt().matrixL();
703 Acts::ActsMatrix<2, 2> lltpc = covpc.topLeftCorner<2, 2>().llt().matrixL();
704
705 for (size_t i = 0; i < 1e4; i++) {
706 std::cout << "ANNULUS PAR: ";
707 std::cout << actsParameter.referenceSurface().geometryId();
708
709 Acts::ActsVector<2> rnd;
710 rnd << normal(gen), normal(gen);
711
712 // XY
713 {
714 Acts::ActsVector<2> xy =
715 lltxy.topLeftCorner<2, 2>() * rnd + targetPars.head<2>();
717 planeSurface->localToGlobal(Amg::Vector2D{xy.head<2>()}, Amg::Vector3D{},
718 xyz);
719 for (unsigned int i = 0; i < 2; i++) {
720 std::cout << "," << xy[i];
721 }
722 std::cout << "," << xyz.x() << "," << xyz.y() << "," << xyz.z();
723 }
724 // PC
725 {
726 Acts::ActsVector<2> rt = lltpc.topLeftCorner<2, 2>() * rnd +
727 actsParameter.parameters().head<2>();
728 Amg::Vector3D xyz = actsParameter.referenceSurface().localToGlobal(
729 gctx, Acts::Vector2{rt.head<2>()}, Acts::Vector3{});
730
731 for (unsigned int i = 0; i < 2; i++) {
732 std::cout << "," << rt[i];
733 }
734 std::cout << "," << xyz.x() << "," << xyz.y() << "," << xyz.z();
735 }
736
737 std::cout << std::endl;
738 }
739}
740
741std::unique_ptr<Trk::Track> ActsToTrkConverterTool::convertFitResult(const EventContext& ctx,
742 MutableTrackContainer& tracks,
743 TrackFitResult_t& fitResult,
744 const Trk::TrackInfo::TrackFitter fitAuthor,
745 const detail::SourceLinkType slType) const {
748
749 if (not fitResult.ok()) {
750 ATH_MSG_VERBOSE("Fit did not converge");
751 return nullptr;
752 }
753 const Acts::CalibrationContext cctx{getCalibrationContext(ctx)};
754 const Acts::GeometryContext gctx{m_trackingGeometryTool->getGeometryContext(ctx).context()};
755 Acts::ParticleHypothesis hypothesis{Acts::ParticleHypothesis::pion()};
756
757 // Get the fit output object
758 const auto& acts_track = fitResult.value();
759
760 auto finalTrajectory = std::make_unique<Trk::TrackStates>();
761 // initialise the number of dead Pixel and Acts strip
762 unsigned int numberOfDeadPixel{0}, numberOfDeadSCT{0};
763 int nDoF{0};
764
765 double chi2{0};
766
767 // Loop over all the output state to create track state
768 tracks.trackStateContainer().visitBackwards(acts_track.tipIndex(),
769 [&] (const auto &state) -> void {
770 // First only consider state with an associated detector element
771 const auto* associatedDetEl = dynamic_cast<const IDetectorElementBase*>(
772 state.referenceSurface().associatedDetectorElement());
773
774 if (not associatedDetEl) {
775 ATH_MSG_VERBOSE("State is not associated with a measurement sruface");
776 return;
777 }
778 ATH_MSG_VERBOSE("Associated det: "<<to_string(associatedDetEl->detectorType()));
779 auto flag = state.typeFlags();
780
781 // We need to determine the type of state
782 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typePattern;
783 std::unique_ptr<Trk::TrackParameters> measPars{};
784 std::unique_ptr<Trk::MeasurementBase> measState{};
785
786 // State is a hole (no associated measurement), use predicted parameters
787 if (flag.test(Acts::TrackStateFlag::HoleFlag)){
788 const Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
789 state.parameters(),
790 state.covariance(),
791 hypothesis);
792 measPars = actsTrackParametersToTrkParameters(actsParam, gctx);
793 if (associatedDetEl->detectorType() == DetectorType::Pixel ||
794 associatedDetEl->detectorType() == DetectorType::Sct) {
795 ATH_MSG_VERBOSE("Check if this is a hole, a dead sensors or a state outside the sensor boundary");
796 switch (m_boundaryCheckTool->boundaryCheck(*measPars)) {
797 case Trk::BoundaryCheckResult::DeadElement:
798 numberOfDeadPixel += (associatedDetEl->detectorType() == DetectorType::Pixel);
799 numberOfDeadSCT+= (associatedDetEl->detectorType() == DetectorType::Sct);
800 break;
801 case Trk::BoundaryCheckResult::Candidate:
802 break;
803 default:
804 return;
805 }
806 }
807 typePattern.set(Trk::TrackStateOnSurface::Hole);
808 }
809 // The state is a measurement state, use smoothed parameters
810 else if (flag.test(Acts::TrackStateFlag::MeasurementFlag)) {
811 Acts::BoundTrackParameters actsParam(state.referenceSurface().getSharedPtr(),
812 state.parameters(),
813 state.covariance(),
814 hypothesis);
815 typePattern.set(Trk::TrackStateOnSurface::Measurement);
816 switch (slType) {
817 using enum detail::SourceLinkType;
818 case TrkMeasurement:
819 measState = measCalib.unpack(state.getUncalibratedSourceLink())->uniqueClone();
820 break;
821 case TrkPrepRawData:
822 measState = prdCalib.createROT(gctx, cctx, state.getUncalibratedSourceLink(), state);
823 break;
824 case xAODUnCalibMeas:
825 ATH_MSG_WARNING("Uncalibrated measurement is not implemented");
826 return;
827 case nTypes:
828 ATH_MSG_WARNING("Invalid type enumaration parsed");
829 return;
830 }
831 nDoF = state.calibratedSize();
832 chi2 = state.chi2();
833
834 }
835 auto perState = std::make_unique<Trk::TrackStateOnSurface>(Trk::FitQualityOnSurface{chi2, nDoF},
836 std::move(measState),
837 std::move(measPars), nullptr, typePattern);
838 // If a state was succesfully created add it to the trajectory
839 ATH_MSG_VERBOSE("State succesfully creates, adding it to the trajectory");
840 finalTrajectory->insert(finalTrajectory->begin(), std::move(perState));
841 });
842 // Convert the perigee state and add it to the trajectory
843 const Acts::BoundTrackParameters actsPer(acts_track.referenceSurface().getSharedPtr(),
844 acts_track.parameters(),
845 acts_track.covariance(),
846 acts_track.particleHypothesis());
847 std::unique_ptr<Trk::TrackParameters> per = actsTrackParametersToTrkParameters(actsPer, gctx);
848 std::bitset<Trk::TrackStateOnSurface::NumberOfTrackStateOnSurfaceTypes> typePattern;
849 typePattern.set(Trk::TrackStateOnSurface::Perigee);
850 auto perState = std::make_unique<Trk::TrackStateOnSurface>(nullptr, std::move(per), nullptr, typePattern);
851 finalTrajectory->insert(finalTrajectory->begin(), std::move(perState));
852
853 // Create the track using the states
854 Trk::TrackInfo newInfo{fitAuthor, Trk::noHypothesis};
855 auto newtrack = std::make_unique<Trk::Track>(newInfo, std::move(finalTrajectory), nullptr);
856 if (newtrack) {
857 // Create the track summary and update the holes information
858 if (!newtrack->trackSummary()) {
859 newtrack->setTrackSummary(std::make_unique<Trk::TrackSummary>());
860 newtrack->trackSummary()->update(Trk::numberOfPixelDeadSensors, numberOfDeadPixel);
861 newtrack->trackSummary()->update(Trk::numberOfSCTDeadSensors, numberOfDeadSCT);
862 }
863 m_trkSummaryTool->updateTrackSummary(ctx, *newtrack, true);
864 }
865 return newtrack;
866 }
867} // 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
if(febId1==febId2)
static TRandom * rnd
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
virtual const Trk::Surface & actsSurfaceToTrkSurface(const Acts::Surface &actsSurface) const override
Find the ATLAS surface corresponding to the Acts surface Only work if the Acts surface has an associa...
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 std::unique_ptr< Trk::TrackParameters > actsTrackParametersToTrkParameters(const Acts::BoundTrackParameters &actsParameter, const Acts::GeometryContext &gctx) const override
Create ATLAS TrackParameter from Acts one.
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
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 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
Returns the list of all detector 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.
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...
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::BoundSquareMatrix &covpc, const Acts::BoundVector &targetPars, const Acts::BoundSquareMatrix &targetCov, const Trk::PlaneSurface *planeSurface)
static void ActsMeasurementCheck(const Acts::GeometryContext &gctx, const Trk::MeasurementBase &measurement, const Acts::Surface &surface, const Acts::BoundVector &loc)
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.
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
@ 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