ATLAS Offline Software
Loading...
Searching...
No Matches
SegmentExtpTest.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "SegmentExtpTest.h"
6
11
12#include "Acts/Definitions/Units.hpp"
13#include "Acts/Surfaces/LineBounds.hpp"
14#include "Acts/Surfaces/PlaneSurface.hpp"
15#include "Acts/Definitions/Tolerance.hpp"
16
20
22#include "GaudiKernel/PhysicalConstants.h"
24
25#include "Acts/Visualization/ObjVisualization3D.hpp"
26#include "Acts/Visualization/GeometryView3D.hpp"
28
29#include "ActsInterop/Logger.h"
30
31using namespace Acts::UnitLiterals;
32using namespace MuonR4::SegmentFit;
33using namespace Acts::detail::LineHelper;
34
35namespace{
36 using CovIdx = MuonR4::SpacePoint::CovIdx;
37
38 constexpr auto etaIdx = Acts::toUnderlying(CovIdx::etaCov);
39 constexpr auto phiIdx = Acts::toUnderlying(CovIdx::phiCov);
40}
41
42namespace MuonValR4{
44 ATH_CHECK(m_readKey.initialize());
45 ATH_CHECK(m_geoCtxKey.initialize());
46 ATH_CHECK(m_idHelperSvc.retrieve());
48 ATH_CHECK(detStore()->retrieve(m_detMgr));
49 return StatusCode::SUCCESS;
50 }
51 StatusCode SegmentExtpTest::execute(const EventContext& ctx) const {
52 const xAOD::MuonSegmentContainer* segments{nullptr};
53 ATH_CHECK(SG::get(segments, m_readKey, ctx));
54 const ActsTrk::GeometryContext* gctx{nullptr};
55 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
56 const Acts::GeometryContext tgContext{gctx->context()};
57
58 auto extrapolate = [&](const Acts::BoundTrackParameters& start,
59 const MuonR4::SpacePoint& sp) {
60 const Amg::Transform3D& trf = sp.msSector()->localToGlobalTransform(*gctx);
61 const Acts::Surface& target = xAOD::muonSurface(sp.primaryMeasurement());
62 const Amg::Vector3D n = target.normal(tgContext,
63 Amg::Vector3D::Zero(),
64 Amg::Vector3D::Zero());
65
66 auto lambda = sp.isStraw() ? Amg::intersect<3>(trf * sp.localPosition(),
67 trf.linear() * sp.sensorDirection(),
68 start.position(tgContext),
69 start.direction())
70 : Amg::intersect<3>(start.position(tgContext),
71 start.direction(), n,
72 n.dot(target.center(tgContext)));
73
74 const auto* detEl = static_cast<const ActsTrk::IDetectorElementBase*>(target.surfacePlacement());
75 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Propagate "<<Amg::toString(start.position(tgContext))<<" + "
76 <<Amg::toString(start.direction())<<" onto surface: "<<target.toString(tgContext)
77 <<"\n, "<<m_idHelperSvc->toString(detEl->identify())
78 << " geoId: "<<target.geometryId()<<", "<<( lambda.value_or(0.) > 0 ? "forward" : "backward"));
79
80 return m_extrapolationTool->propagate(ctx, start, target, lambda.value_or(0.) > 0
81 ? Acts::Direction::Forward()
82 : Acts::Direction::Backward(), 100._m);
83
84 };
85 StatusCode retCode = StatusCode::SUCCESS;
86
87 SeedingAux::Config cfg{};
88 cfg.parsToUse.clear();
89 cfg.calcAlongStrip = true;
90 SeedingAux pullCalculator{cfg, makeActsAthenaLogger(this, "PullCalculator")};
91
92 cfg.calcAlongStrip = false;
93 SeedingAux pullCalculatorChi2{cfg, makeActsAthenaLogger(this, "PullcalculatorXAOD")};
94
95 SeedingAux::Line_t line{};
96 SeedingAux::ChiSqWithDerivatives chiSqObj{};
97
98
99 for (const xAOD::MuonSegment* segment : *segments) {
100 const MuonR4::Segment* detSeg = MuonR4::detailedSegment(*segment);
101
102 line.updateParameters(localSegmentPars(*segment));
103
104 const MuonGMR4::SpectrometerSector* sector = m_detMgr->getSectorEnvelope(segment->chamberIndex(),
105 segment->sector(),
106 segment->etaIndex());
107 auto startPars = boundSegmentPars(*gctx, *detSeg);
108
109 Acts::ObjVisualization3D visualHelper{};
110 if (m_drawEvent) {
112 drawSegmentLine(*gctx, *segment, visualHelper,
113 Acts::ViewConfig{.color = {220, 0, 0}});
114 drawSegmentMeasurements(*gctx, *segment, visualHelper, Acts::s_viewSurface);
115 }
116
117 if (msgLvl(MSG::VERBOSE)) {
118 std::stringstream sstr{};
119 sstr<<"pos: "<<Amg::toString(segment->position())
120 <<", dir: "<<Amg::toString(segment->direction())
121 <<", chi2/nDoF: "<<segment->chiSquared() / segment->numberDoF()
122 <<", nDoF: "<<segment->numberDoF()<<", "
123 <<segment->nPrecisionHits()<<", "<<segment->nPhiLayers()<<std::endl;
124 for (const auto& meas : detSeg->measurements()) {
125 sstr<<" **** "<<(*meas)<<", chi2: "<<SeedingAux::chi2Term(line, *meas)
126 <<", sign: "<<(meas->isStraw() ?
127 (SeedingAux::strawSign(line, *meas) == 1 ? "R" : "L") : "-")
128 <<", geoId: "<<(meas->type() != xAOD::UncalibMeasType::Other ?
129 xAOD::muonSurface(meas->spacePoint()->primaryMeasurement()).geometryId()
130 : Acts::GeometryIdentifier{})
131 <<std::endl;
132 }
133 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Run propagation test on "
134 <<sector->identString()<<std::endl<<sstr.str());
135 }
136 for (const auto& meas: detSeg->measurements()) {
137 if (!meas->spacePoint() ||
138 meas->fitState() != MuonR4::CalibratedSpacePoint::State::Valid) {
139 continue;
140 }
141 const auto sp = meas->spacePoint();
142 const Acts::Surface& targetSurf{xAOD::muonSurface(sp->primaryMeasurement())};
143
144 const auto& bounds = targetSurf.bounds();
145 Amg::Vector2D lPos{Amg::Vector2D::Zero()}, mPos{Amg::Vector2D::Zero()};
146 const Amg::Transform3D toSurf = targetSurf.localToGlobalTransform(tgContext).inverse() *
147 sector->surface().localToGlobalTransform(tgContext);
148 if (targetSurf.type() == Acts::Surface::SurfaceType::Plane) {
149 lPos = (toSurf * SeedingAux::extrapolateToPlane(line, *meas)).block<2,1>(0,0);
150 if (!bounds.inside(lPos, Acts::BoundaryTolerance::AbsoluteEuclidean(-2._mm))){
151 ATH_MSG_WARNING("The position "<<Amg::toString(lPos)
152 <<" is outside the trapezoid "<<bounds
153 <<" "<<(*meas));
154 continue;
155 }
156 mPos = (toSurf * meas->localPosition()).block<2,1>(0,0);
157 } else if (targetSurf.type() == Acts::Surface::SurfaceType::Straw) {
158 const auto cIsect = lineIntersect<3>(meas->localPosition(), meas->sensorDirection(),
159 line.position(), line.direction());
160 const auto cIsectPos = cIsect.position();
161 const Amg::Vector3D closePos = toSurf * cIsectPos;
162 lPos[0] = Acts::copySign(closePos.perp(), SeedingAux::strawSign(line, *meas));
163 lPos[1] = closePos.z();
164 mPos[0] = Acts::copySign(meas->driftRadius(), lPos[0]);
165 if (meas->measuresPhi()) {
166 mPos[1] = meas->localPosition().x();
167 }
168 const auto& lBounds = static_cast<const Acts::LineBounds&>(bounds);
169 if (std::abs(closePos.z()) > lBounds.get(Acts::LineBounds::eHalfLengthZ) - 2._cm ||
170 closePos.perp() >lBounds.get(Acts::LineBounds::eR) - 0.2_mm) {
171 ATH_MSG_WARNING("The line does not cross tube "
172 <<(*meas)
173 <<" "<<Amg::toString(lPos)<<" vs. "<<bounds<<".");
174 continue;
175 }
176 }
177 auto extpPars = extrapolate(startPars, *sp);
178 if (!extpPars.ok()) {
179 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - Failed to propagate to "<<(*meas)
180 <<",\n lPos: "<<Amg::toString(toSurf * meas->localPosition())
181 <<", expected: "<<Amg::toString(lPos)<<", "<<targetSurf.bounds());
182 retCode = StatusCode::FAILURE;
183 continue;
184 }
185 if (m_drawEvent) {
187 drawBoundParameters(*gctx, *extpPars, visualHelper,
188 Acts::ViewConfig{.color = {0, 0, 220}}, 6._cm);
189 }
190 chiSqObj.reset();
191
192 pullCalculator.updateSpatialResidual(line, *meas);
193 pullCalculatorChi2.updateSpatialResidual(line, *meas);
194 pullCalculatorChi2.updateChiSq(chiSqObj, meas->covariance());
195
196 const double segChi2 = chiSqObj.chi2;
197 const double fastChi2Term = SeedingAux::chi2Term(line, *meas);
198
199 if (targetSurf.type() == Acts::Surface::SurfaceType::Plane) {
200 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Position on "
201 <<m_idHelperSvc->toString(sp->identify())
202 <<" plane "<<Amg::toString(lPos)<<" vs. "
203 <<Amg::toString((*extpPars).localPosition()));
205 const Amg::Vector2D dPos = (*extpPars).localPosition() - lPos;
206 if (dPos.mag() > 0.1_mm) {
207 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - Too large deviation for "<<(*meas)
208 <<", "<<Amg::toString(dPos));
209 retCode = StatusCode::FAILURE;
210 }
212 const Amg::Vector3D b1 = toSurf.linear()*(meas->measuresEta() ? meas->toNextSensor() : meas->sensorDirection());
213 const Amg::Vector3D b2 = toSurf.linear()*(!meas->measuresEta() ? meas->toNextSensor() : meas->sensorDirection());
214 const Amg::Vector2D lineRes = (pullCalculator.residual()[etaIdx] * b1 +
215 pullCalculator.residual()[phiIdx] * b2).block<2,1>(0,0);
216
218 const Amg::Vector2D surfRes = lPos - mPos;
219
221 AmgSymMatrix(2) covMat{AmgSymMatrix(2)::Identity()};
222 covMat(0,0) = meas->covariance()[etaIdx];
223 covMat(1,1) = meas->covariance()[phiIdx];
224 // Transform the covariance to take the stereo angles into account
225 AmgSymMatrix(2) surfTrf{AmgSymMatrix(2)::Identity()};
226 surfTrf.row(0) = b1.block<2,1>(0,0);
227 surfTrf.row(1) = b2.block<2,1>(0,0);
228 AmgSymMatrix(2) stereoTrf{AmgSymMatrix(2)::Identity()};
229 const double dirDots = b1.dot(b2);
230 const double invDist = 1. / (1. - Acts::square(dirDots));
231 stereoTrf(0, 0) = stereoTrf(1, 1) = invDist;
232 stereoTrf(0, 1) = stereoTrf(1, 0) = -dirDots * invDist;
233
234 stereoTrf = (stereoTrf * surfTrf).inverse();
235
236 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Basis vectors b1: "<<Amg::toString(b1)
237 <<", b2: "<<Amg::toString(b2) <<", product: "<<dirDots
238 <<", invdist: "<<invDist<<" -> trf: \n"<<stereoTrf
239 <<",\ncovariance:\n"<<covMat
240 <<" -> transformed:\n"<<(stereoTrf * covMat * stereoTrf.transpose()));
241
242 covMat = stereoTrf * covMat * stereoTrf.transpose();
243
244 const double matChi2 = surfRes.dot(covMat.inverse() * surfRes);
245
246 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Analyze plane residual residual for "
247 <<m_idHelperSvc->toString(sp->identify())
248 <<" / "<<targetSurf.geometryId()
249 <<"\n --- measurement: "<<Amg::toString(mPos)
250 <<", extrapolated: "<<Amg::toString(lPos)
251 <<" --> residual: "<<Amg::toString(surfRes)
252 <<", projected: "<<Amg::toString(stereoTrf*surfRes)
253 <<", chi2: "<<matChi2
254 <<"\n --- line fitter - projected: "
255 <<Amg::toString(pullCalculator.residual())
256 <<", cartesian: "<<Amg::toString(lineRes)<<", chi2: "
257 <<segChi2);
258
259 const Amg::Vector2D dRes = (surfRes - lineRes);
260 if (dRes.mag() > 0.1_mm) {
261 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - Surface and line residuals are too much apart for "
262 <<(*meas)
263 <<", difference: "<<Amg::toString(dPos));
264 retCode = StatusCode::FAILURE;
265 }
266 const double dChi2 = std::abs(matChi2 - segChi2);
267 if (dChi2 > 0.01) {
268 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - Too large deviation in chi2 calculation "<<
269 (*meas)<<" -- line fitter: "<<segChi2<<", matrix: "<<matChi2);
270 retCode = StatusCode::FAILURE;
271 }
272
275 if (Acts::abs(segChi2 - fastChi2Term) > 1.e-3) {
276 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The fast & full chi2 calculations from ACTS don't match for "
277 <<(*meas)<<" - full: "<<segChi2<<", fast: "<<fastChi2Term);
278 retCode = StatusCode::FAILURE;
279 }
280 if (sp->dimension() == 2) {
281 const auto [xPos, xCov] = xAOD::positionAndCovariance(sp->primaryMeasurement(),
282 sp->secondaryMeasurement());
283
284 if ((xPos - mPos).mag() > 1.e-3) {
285 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The calibrated position from the xAOD util function "<<
286 " does not match the expectation from this test. xAOD: "
287 <<Amg::toString(xPos)<<", test: "<<Amg::toString(mPos));
288 retCode = StatusCode::FAILURE;
289 }
290 if (!xCov.isApprox(covMat, 1.e-3)) {
291 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The calibrated covariance from the xAOD util function "<<
292 " does not match the expectation from this test. xAOD:\n"
293 <<Amg::toString(xCov)<<",\ntest:\n"<<Amg::toString(covMat));
294 retCode = StatusCode::FAILURE;
295 }
296 } else {
297 chiSqObj.reset();
298 pullCalculatorChi2.updateSpatialResidual(line, *sp);
299 pullCalculatorChi2.updateChiSq(chiSqObj, sp->covariance());
300
301 const bool mPhi = sp->measuresPhi();
302
303 const auto [xPos, xCov] = xAOD::positionAndCovariance(sp->primaryMeasurement());
304
305 const Amg::Vector2D refPos = (toSurf * sp->localPosition()).block<2,1>(0,0);
306 const double refCov = sp->covariance()[!mPhi];
307 if ((xPos - refPos).mag() > 1.e-3) {
308 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The calibrated position from the xAOD util function "<<
309 " does not match the expectation from this test. xAOD: "
310 <<Amg::toString(xPos)<<", test: "<<Amg::toString(refPos));
311 retCode = StatusCode::FAILURE;
312 }
313 if (std::abs(refCov - xCov(mPhi,mPhi)) > 1.e-3) {
314 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - The calibrated covariance from the xAOD util function "<<
315 " does not match the expectation from this test. xAOD: "
316 <<xCov(mPhi, mPhi)<<", test: "<<refCov);
317 }
318 const Amg::Vector2D xAODRes = (xPos - lPos);
319 const double xAODChi2 = xAODRes.dot(xCov.inverse()*xAODRes);
320
321 if (std::abs(xAODChi2 - chiSqObj.chi2) > 1.e-3) {
322 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The calculated chi2 term from the xAOD util function: "
323 <<xAODChi2<<" deviates from the line fitter chi2: "<<chiSqObj.chi2
324 <<"\n measurement: "<<xPos<<", extp: "<<Amg::toString(lPos)<<" ("
325 <<sp->measuresPhi()<<") cov:\n"<<xCov
326 <<"\n-> inverse:\n"<<xCov.inverse()
327 <<"\n "<<Amg::toString(pullCalculatorChi2.residual())<<", xAOD: "
328 <<Amg::toString(xAODRes));
329 retCode = StatusCode::FAILURE;
330 }
331 }
332 } else if (targetSurf.type() == Acts::Surface::SurfaceType::Straw) {
333 const double dist = lPos[0];
334 const double extDist = (*extpPars).parameters()[Acts::eBoundLoc0];
335 const double extLocZ = (*extpPars).parameters()[Acts::eBoundLoc1];
336 const double cov = meas->covariance()[Acts::toUnderlying(AxisDefs::etaCov)];
337 ATH_MSG_DEBUG("Distance on surface "<<m_idHelperSvc->toString(sp->identify())
338 <<" straight: "<<dist<<", extrapolated: "<<extDist
339 <<"--> "<<(extDist - dist ) / std::sqrt(cov)
340 <<", along the tube: "<<lPos[1]<<", extrapolated: "<<extLocZ);
341 if (std::abs(dist - extDist) / std::sqrt(cov) > 0.05 ||
342 std::abs(lPos[1] - extLocZ) > 0.1_mm) {
343 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - Too large deviation on "<<(*meas)
344 <<",\n"<<Amg::toString(lPos)<<" vs. ("<<extDist<<", "<<extLocZ<<")"
345 <<", deviate R: "<<(std::abs(dist -extDist) / std::sqrt(cov))
346 <<", deviate Z: "<<std::abs(lPos[1] - extLocZ));
347 retCode = StatusCode::FAILURE;
348 }
349 }
350 }
351 if (m_drawEvent) {
352 visualHelper.write(std::format("ExtTpTest_{:}_{:}_{:}.obj",
353 ctx.eventID().event_number(), segment->index(),
354 MuonR4::printID(*segment)));
355 }
356 }
357
358 return retCode;
359 }
360
361}
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 ATH_MSG_DEBUG(x)
#define AmgSymMatrix(dim)
static Double_t sp
Handle class for reading from StoreGate.
std::unique_ptr< const Acts::Logger > makeActsAthenaLogger(IMessageSvc *svc, const std::string &name, int level, std::optional< std::string > parent_name)
Acts::GeometryContext context() const
base class interface providing the bare minimal interface extension.
const ServiceHandle< StoreGateSvc > & detStore() const
bool msgLvl(const MSG::Level lvl) const
A spectrometer sector forms the envelope of all chambers that are placed in the same MS sector & laye...
std::string identString() const
Returns a string encoding the chamber index & the sector of the MS sector.
const Acts::PlaneSurface & surface() const
Returns the associated surface.
Placeholder for what will later be the muon segment EDM representation.
const MeasVec & measurements() const
Returns the associated measurements.
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
SG::ReadHandleKey< ActsTrk::GeometryContext > m_geoCtxKey
Dependency on the geometry alignment.
Gaudi::Property< bool > m_drawEvent
Option to draw every extrapolation asan obj file.
virtual StatusCode execute(const EventContext &ctx) const override final
const MuonGMR4::MuonDetectorManager * m_detMgr
Detector manager to fetch the sector surfaces.
ToolHandle< ActsTrk::IExtrapolationTool > m_extrapolationTool
Track extrapolation tool.
SG::ReadHandleKey< xAOD::MuonSegmentContainer > m_readKey
Declare the data dependency on the standard Mdt+Rpc+Tgc segment container.
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
IdHelperSvc to decode the Identifiers.
virtual StatusCode initialize() override final
std::optional< double > intersect(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
Calculates the point B' along the line B that's closest to a second line A.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
SpacePoint::SeedingAux SeedingAux
Abrivation of the CompSpacePointAuxiliaries.
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
Acts::BoundTrackParameters boundSegmentPars(const MuonGMR4::MuonDetectorManager &detMgr, const xAOD::MuonSegment &segment, std::optional< Acts::BoundMatrix > cov=std::nullopt, Acts::ParticleHypothesis hypot=Acts::ParticleHypothesis::muon())
Returns the segment parameters as boundTrackParameters.
std::string printID(const xAOD::MuonSegment &seg)
Print the chamber ID of a segment, e.g.
const Segment * detailedSegment(const xAOD::MuonSegment &seg)
Helper function to navigate from the xAOD::MuonSegment to the MuonR4::Segment.
Lightweight algorithm to read xAOD MDT sim hits and (fast-digitised) drift circles from SG and fill a...
void drawBoundParameters(const ActsTrk::GeometryContext &gctx, const Acts::BoundTrackParameters &pars, Acts::ObjVisualization3D &visualHelper, const Acts::ViewConfig &viewConfig=Acts::s_viewLine, const double standardLength=3.*Gaudi::Units::cm)
Draw a line representing the bound track parameters.
void drawSegmentMeasurements(const ActsTrk::GeometryContext &gctx, const xAOD::MuonSegment &segment, Acts::ObjVisualization3D &visualHelper, const Acts::ViewConfig &viewConfig=Acts::s_viewSensitive)
Draw all uncalibrated measurements associated to the segment.
void drawSegmentLine(const ActsTrk::GeometryContext &gctx, const xAOD::MuonSegment &segment, Acts::ObjVisualization3D &visualHelper, const Acts::ViewConfig &viewConfig=Acts::s_viewLine, const double standardLength=1.*Gaudi::Units::m)
Draw a segment line inside the obj file.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".
std::pair< Amg::Vector2D, AmgSymMatrix(2)> positionAndCovariance(const MuonMeasurement *oneDimMeas)
Returns the 1D position of the uncalibrated measurement expressed in the coordinate system of the mea...
MuonSegment_v1 MuonSegment
Reference the current persistent version:
const Acts::Surface & muonSurface(const UncalibratedMeasurement *meas)
Returns the associated Acts surface to the measurement.