ATLAS Offline Software
Loading...
Searching...
No Matches
PatternVisualizationTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
5
10
14
20
21#include "Acts/Utilities/Helpers.hpp"
22#include "Acts/Surfaces/RectangleBounds.hpp"
23#include "Acts/Surfaces/TrapezoidBounds.hpp"
24#include "Acts/Surfaces/PlaneSurface.hpp"
25#include "Acts/Surfaces/StrawSurface.hpp"
26#include "Acts/Visualization/GeometryView3D.hpp"
27
28
29#include <format>
30#include <sstream>
31#include <filesystem>
32
33
34#include "TH1F.h"
35#include "TH2F.h"
36#include "TMarker.h"
37#include "TColor.h"
38
39
40namespace {
41 constexpr int truthColor = kOrange +2;
42 constexpr int parLineColor = kRed;
43 using SpacePointSet = std::unordered_set<const MuonR4::SpacePoint*>;
44}
45
46
47namespace MuonValR4 {
48 using namespace MuonR4;
49 using namespace SegmentFit;
51
53 if (m_canvasLimit > 0) {
54 m_clientToken.canvasLimit = m_canvasLimit;
55 m_clientToken.preFixName = m_canvasPrefix;
56 m_clientToken.saveSinglePlots = m_saveSinglePDFs;
57 m_clientToken.saveSummaryPlot = m_saveSummaryPDF;
58 m_clientToken.subDirectory = m_subDir;
59 ATH_CHECK(m_visualSvc.retrieve());
60 ATH_CHECK(m_visualSvc->registerClient(m_clientToken));
61 } else {
62 m_plotsDone = true;
63 }
64 ATH_CHECK(m_prepContainerKeys.initialize(!m_truthSegLinks.empty()));
66 ATH_MSG_INFO("Hits linked to the following segment decorations are considered as truth");
67 for (const std::string& decorName : m_truthSegLinks) {
68 ATH_MSG_INFO(" **** "<<decorName);
69 if (decorName.empty()) {
70 ATH_MSG_FATAL("Decoration must not be empty");
71 return StatusCode::FAILURE;
72 }
74 m_truthLinkDecorKeys.emplace_back(key, decorName);
75 m_truthLinkDecors.push_back(SegLinkDecor_t{decorName});
76 }
77 }
78 ATH_CHECK(m_truthLinkDecorKeys.initialize());
79 m_displayOnlyTruth.value() &= !m_truthLinkDecorKeys.empty();
80
81 ATH_CHECK(m_idHelperSvc.retrieve());
82 ATH_CHECK(m_geoCtxKey.initialize());
83 ATH_CHECK(detStore()->retrieve(m_detMgr));
84 return StatusCode::SUCCESS;
85 }
87 return isLabeled(*hit.primaryMeasurement()) ||
88 (hit.secondaryMeasurement() && isLabeled(*hit.secondaryMeasurement()));
89 }
91 return std::find_if(m_truthLinkDecors.begin(), m_truthLinkDecors.end(),
92 [&hit](const SegLinkDecor_t& decor){
93 return !decor(hit).empty();
94 }) != m_truthLinkDecors.end();
95 }
96
97 LabeledSegmentSet PatternVisualizationTool::getLabeledSegments(const std::vector<const MuonR4::SpacePoint*>& hits) const {
98 std::vector<const xAOD::UncalibratedMeasurement*> measurements{};
99 measurements.reserve(2* hits.size());
100 for (const SpacePoint* hit: hits) {
101 measurements.push_back(hit->primaryMeasurement());
102 if(hit->secondaryMeasurement()) {
103 measurements.push_back(hit->secondaryMeasurement());
104 }
105 }
106 return getLabeledSegments(measurements);
107 }
108 LabeledSegmentSet PatternVisualizationTool::getLabeledSegments(const std::vector<const xAOD::UncalibratedMeasurement*>& hits) const {
109 LabeledSegmentSet truthSegs{};
110 for (const xAOD::UncalibratedMeasurement* hit : hits) {
111 for (const SegLinkDecor_t& decor: m_truthLinkDecors) {
112 for (const SegLink_t& link : decor(*hit)) {
113 truthSegs.insert(*link);
114 }
115 }
116 }
117 return truthSegs;
118 }
120 const MuonR4::HoughPlane& accumulator,
121 const Acts::HoughTransformUtils::HoughAxisRanges& axisRanges,
122 const MaximumVec& maxima,
123 const std::string& extraLabel) const {
124 PrimitiveVec primitives{};
125 visualizeAccumulator(ctx, accumulator, axisRanges, maxima, extraLabel, std::move(primitives));
126 }
128 const MuonR4::HoughPlane& accumulator,
129 const Acts::HoughTransformUtils::HoughAxisRanges& axisRanges,
130 const MaximumVec& maxima,
131 const std::string& extraLabel,
132 PrimitiveVec&& primitives) const {
133
135 if (m_plotsDone) {
136 return;
137 }
138
139 ATH_MSG_VERBOSE("Visualize accumulator");
140
141 if (accumulator.getNonEmptyBins().empty()) {
142 ATH_MSG_WARNING("Hough accumulator is empty");
143 return;
144 }
145
146 auto accHisto = std::make_unique<TH2F>("AccumulatorHisto", "histo",
147 accumulator.nBinsX(), axisRanges.xMin, axisRanges.xMax,
148 accumulator.nBinsY(), axisRanges.yMin, axisRanges.yMax);
149 accHisto->SetDirectory(nullptr);
150
151 std::vector<const SpacePoint*> spacePointsInAcc{};
152 for (const std::size_t bin : accumulator.getNonEmptyBins()) {
153 const auto [xBin, yBin] = accumulator.axisBins(bin);
154 auto hitIds = accumulator.hitIds(xBin, yBin);
155 spacePointsInAcc.insert(spacePointsInAcc.end(),hitIds.begin(), hitIds.end());
156 accHisto->SetBinContent(xBin+1, yBin+1, accumulator.nHits(bin));
157 }
158
159 const LabeledSegmentSet truthSegs{getLabeledSegments(spacePointsInAcc)};
160 if (truthSegs.empty() && m_displayOnlyTruth) {
161 ATH_MSG_VERBOSE("visualizeAccumulator skipped: no truth segments found and displayOnlyTruth=true");
162 return;
163 }
164
165 auto canvas = m_visualSvc->prepareCanvas(ctx, m_clientToken, extraLabel);
166 if (!canvas){
167 m_plotsDone = true;
168 return;
169 }
170
171
172 const double maxBins = std::max(accumulator.nBinsX(), accumulator.nBinsY());
173 double markerTextSize = 0.035;
174 // Scale down for large accumulators
175 markerTextSize *= 25. / std::max(25., maxBins);
176 // Avoid absurdly tiny/large text
177 markerTextSize = std::clamp(markerTextSize, 0.006, 0.035);
178 accHisto->SetMarkerSize(markerTextSize);
179 accHisto->SetMarkerColor(kBlack);
180 accHisto->SetMinimum(0.);
181
182 canvas->expandPad(axisRanges.xMin, axisRanges.yMin);
183 canvas->expandPad(axisRanges.xMax, axisRanges.yMax);
184 canvas->setAxisTitles(std::format("tan#{}", m_accumlIsEta ? "beta" : "#alpha"),
185 std::format("{:}_{{0}} [mm]", m_accumlIsEta ? "y" : "x"));
186 canvas->add(std::move(accHisto), "HIST SAME");
187
188 // Draw explicit bin labels at bin centers. This is more robust than TH2 TEXT
189 // when plotting into the visualization service's prebuilt frame.
190 const double binLabelPx = std::clamp(1.2 * 500. / maxBins, 7.0, 18.0);
191 for (const std::size_t bin : accumulator.getNonEmptyBins()) {
192 const auto [xBin, yBin] = accumulator.axisBins(bin);
193 const double xCenter = axisRanges.xMin + (xBin + 0.5) * (axisRanges.xMax - axisRanges.xMin) / accumulator.nBinsX();
194 const double yCenter = axisRanges.yMin + (yBin + 0.5) * (axisRanges.yMax - axisRanges.yMin) / accumulator.nBinsY();
195
196 //Extract number of entries in the bin with no decimal precision just int
197 std::string binLabel = std::format("{:0}", int(accumulator.nHits(bin)));
198
199 auto binTxt = drawLabel(binLabel, xCenter, yCenter, binLabelPx, false, kBlack);
200 binTxt->SetTextAlign(22);
201 canvas->add(std::move(binTxt));
202 }
203
204 int iseg = 0;
205 for (const xAOD::MuonSegment* segment : truthSegs) {
206 const auto [pos, dir] = makeLine(localSegmentPars(*segment));
207 const double tan = m_accumlIsEta ? houghTanBeta(dir) : houghTanAlpha(dir);
208 const double icept = pos[m_accumlIsEta ? objViewEta : objViewPhi];
209 auto truthMarker = std::make_unique<TMarker>(tan, icept, kFullCrossX);
210 truthMarker->SetMarkerColor(truthColor);
211 truthMarker->SetMarkerSize(4);
212 canvas->add(std::move(truthMarker));
213 canvas->add(drawLabel(std::format("true parameters: {:}",
214 makeLabel(localSegmentPars(*segment))),0.2, 0.9 - 0.05*iseg));
215 iseg += 1;
216 }
217 for (const auto& maximum : maxima) {
218 auto maxMarker = std::make_unique<TMarker>(maximum.x, maximum.y, kFullTriangleUp);
219 maxMarker->SetMarkerColor(parLineColor);
220 maxMarker->SetMarkerSize(4);
221 canvas->add(std::move(maxMarker));
222
223 // Draw a bounding box around the maximum island center (x,y) and widths (wx,wy).
224 const double xMin = maximum.x - maximum.wx;
225 const double xMax = maximum.x + maximum.wx;
226 const double yMin = maximum.y - maximum.wy;
227 const double yMax = maximum.y + maximum.wy;
228
229 auto maxBox = drawBox(xMin, yMin, xMax, yMax, parLineColor, hollowFilling);
230 maxBox->SetLineStyle(kDashed);
231 maxBox->SetLineWidth(2);
232 canvas->add(std::move(maxBox));
233 }
234
235 canvas->add(std::move(primitives));
236 }
237 void PatternVisualizationTool::paintSimHits(const EventContext& ctx,
238 const xAOD::MuonSegment& truthSeg,
239 Canvas_t& canvas,
240 const int view) const {
241 if (!m_paintTruthHits) {
242 return;
243 }
244 auto truthHits = getMatchingSimHits(truthSeg);
245 const ActsTrk::GeometryContext* geoCtx{nullptr};
246 if (!SG::get(geoCtx, m_geoCtxKey, ctx).isSuccess()) {
247 return;
248 }
249 for (const xAOD::MuonSimHit* simHit : truthHits) {
250 const MuonGMR4::MuonReadoutElement* re = m_detMgr->getReadoutElement(simHit->identify());
251 const IdentifierHash hash = re->detectorType() == ActsTrk::DetectorType::Mdt ?
252 re->measurementHash(simHit->identify()) :
253 re->layerHash(simHit->identify());
254 const Amg::Transform3D trf = re->msSector()->globalToLocalTransform(*geoCtx) *
255 re->localToGlobalTransform(*geoCtx, hash);
256 const Amg::Vector3D locPos = trf * xAOD::toEigen(simHit->localPosition());
257 const Amg::Vector3D locDir = trf.linear() * xAOD::toEigen(simHit->localDirection());
258 canvas.add(drawArrow(locPos, locDir, truthColor, kDashed, view));
259 }
260 }
261 void PatternVisualizationTool::visualizeSeed(const EventContext& ctx,
262 const MuonR4::SegmentSeed& seed,
263 const std::string& extraLabel) const {
264 PrimitiveVec primitives{};
265 visualizeSeed(ctx, seed, extraLabel, std::move(primitives));
266 }
267 void PatternVisualizationTool::visualizeSeed(const EventContext& ctx,
268 const MuonR4::SegmentSeed& seed,
269 const std::string& extraLabel,
270 PrimitiveVec&& primitives) const {
271
272 ATH_MSG_VERBOSE("Visualizing seed with " << seed.getHitsInMax().size() << " hits");
274 if (m_plotsDone) {
275 ATH_MSG_VERBOSE("visualizeSeed skipped: canvas quota already exhausted for " << extraLabel);
276 return;
277 }
278 const LabeledSegmentSet truthSegs{getLabeledSegments(seed.getHitsInMax())};
279 if (truthSegs.empty() && m_displayOnlyTruth) {
280 ATH_MSG_VERBOSE("visualizeSeed skipped: no truth segments found and displayOnlyTruth=true");
281 return;
282 }
283
284
285
286 for (const int view : {objViewEta, objViewPhi}) {
287 if ((view == objViewEta && !m_doEtaBucketViews) ||
288 (view == objViewPhi && !m_doPhiBucketViews)){
289 continue;
290 }
291
292 std::string_view viewName = view == objViewEta ? "eta" : "phi";
293
294 auto canvas = m_visualSvc->prepareCanvas(ctx, m_clientToken, std::format("{}_{}", extraLabel, viewName));
295
296 if (!canvas) {
297 m_plotsDone = true;
298 return;
299 }
300
301 DynamicLegend legend;
302 canvas->add(clone(primitives));
303
304 canvas->setAxisTitles(std::format("{:} [mm]", view == objViewEta ? 'y' : 'x'), "z [mm]");
305
306 if (!drawHits(*seed.parentBucket(), seed.getHitsInMax(), *canvas, legend, view)) {
307 continue;
308 }
309 for (const xAOD::MuonSegment* segment : truthSegs) {
310 canvas->add(drawLine(localSegmentPars(*segment),
311 canvas->corner(Edges::yLow), canvas->corner(Edges::yHigh),
312 truthColor, kDotted, view));
313 paintSimHits(ctx,*segment, *canvas, view);
314 }
315 canvas->add(drawLine(seed.parameters(), canvas->corner(Edges::yLow),
316 canvas->corner(Edges::yHigh), parLineColor, kDashed, view));
317
318 writeChi2(seed.parameters(), seed.getHitsInMax(), *canvas);
319
320 std::string legendLabel = std::format("Event: {:}, chamber : {:}, #{:}-view ({:})",
321 ctx.eventID().event_number(),
322 m_idHelperSvc->toStringChamber(seed.getHitsInMax().front()->identify()),
323 view ==objViewEta ? "eta" : "phi",
324 extraLabel);
325 canvas->add(drawLabel(legendLabel, 0.1, 0.96));
326 canvas->add(drawLabel( "Seed parameters: " + makeLabel(seed.parameters()),0.25, 0.89));
327 }
328 }
329
330 void PatternVisualizationTool::visualizeBucket(const EventContext& ctx,
331 const MuonR4::SpacePointBucket& bucket,
332 const std::string& extraLabel) const {
333 PrimitiveVec primitives{};
334 visualizeBucket(ctx, bucket, extraLabel, std::move(primitives));
335 }
336 void PatternVisualizationTool::visualizeBucket(const EventContext& ctx,
337 const MuonR4::SpacePointBucket& bucket,
338 const std::string& extraLabel,
339 PrimitiveVec&& primitives) const {
341 if (m_plotsDone) {
342 return;
343 }
344 ATH_MSG_VERBOSE("visualizeBucket called for " << extraLabel);
345
346 LabeledSegmentSet truthSegs{getLabeledSegments(Acts::unpackConstSmartPointers(bucket))};
347 if (truthSegs.empty() && m_displayOnlyTruth) {
348 ATH_MSG_VERBOSE("visualizeBucket skipped: no truth segments found and displayOnlyTruth=true");
349 return;
350 }
351
352 for (const int view : {objViewEta, objViewPhi}) {
353 if ((view == objViewEta && !m_doEtaBucketViews) ||
354 (view == objViewPhi && !m_doPhiBucketViews)){
355 continue;
356 }
357
358 const std::string_view viewName = view == objViewEta ? "eta" : "phi";
359
360 auto canvas = m_visualSvc->prepareCanvas( ctx, m_clientToken, std::format("{}_{}view", extraLabel, viewName));
361
362
363 if (!canvas) {
364 m_plotsDone = true;
365 return;
366 }
367
368 DynamicLegend legend(0.8, 0.2, 0.95, 0.5);
369
370 canvas->add(clone(primitives));
371
373 if (!drawHits(bucket, bucket, *canvas, legend, view)) {
374 canvas->trash();
375 continue;
376 }
377 bool drawnTrueLabel{false};
378 for (const xAOD::MuonSegment* segment : truthSegs) {
379
380 auto truthLine = drawLine(localSegmentPars(*segment), canvas->corner(Edges::yLow), canvas->corner(Edges::yHigh), truthColor, kDotted, view);
381 TLine* line = static_cast<TLine*>(truthLine.get());
382 canvas->add( std::move(truthLine) );
383 legend.addOnce( LegendItem::TruthSegmentLine, line, "Truth Segment", "l" );
384
385 if (!drawnTrueLabel) {
386 canvas->add(drawLabel(std::format("true parameters: {:}",makeLabel(localSegmentPars(*segment))),0.2, 0.89));
387 drawnTrueLabel = true;
388 }
389 paintSimHits(ctx,*segment, *canvas, view);
390 }
391
392 std::string legendLabel = std::format("Event: {:}, chamber : {:}, #{:}-view ({:})",
393 ctx.eventID().event_number(),
394 bucket.msSector()->identString(),
395 view ==objViewEta ? "eta" : "phi",
396 extraLabel);
397 canvas->add(drawLabel(legendLabel, 0.15, 0.96));
398
399 canvas->setAxisTitles(std::format("{:} [mm]", view == objViewEta ? 'y' : 'x'), "z [mm]");
400
401 if (!legend.empty()) {
402 canvas->add(legend.release(), "same");
403 }
404
405 }
406 }
407
408 void PatternVisualizationTool::visualizeSegment(const EventContext& ctx,
409 const MuonR4::Segment& segment,
410 const std::string& extraLabel) const {
411 PrimitiveVec primitives{};
412 visualizeSegment(ctx, segment,extraLabel, std::move(primitives));
413 }
414
415 void PatternVisualizationTool::visualizeSegment(const EventContext& ctx,
416 const MuonR4::Segment& segment,
417 const std::string& extraLabel,
418 PrimitiveVec&& primitives) const {
420 if (m_plotsDone) {
421 return;
422 }
423
424 ATH_MSG_VERBOSE("Visualizing segment with " << segment.measurements().size() << " hits");
425
426 const LabeledSegmentSet truthSegs{getLabeledSegments(segment.parent()->getHitsInMax())};
427 if (truthSegs.empty() && m_displayOnlyTruth) {
428 return;
429 }
430 const ActsTrk::GeometryContext* geoCtx{nullptr};
431 if (!SG::get(geoCtx, m_geoCtxKey, ctx).isSuccess()) {
432 return;
433 }
434
435
436 const Parameters segPars = SegmentFit::localSegmentPars(*geoCtx, segment);
437
438 for (const int view : {objViewEta, objViewPhi}) {
439 if ((view == objViewEta && !m_doEtaBucketViews) ||
440 (view == objViewPhi && !m_doPhiBucketViews)){
441 continue;
442 }
443
444 const std::string_view viewName = view == objViewEta ? "eta" : "phi";
445
446 auto canvas = m_visualSvc->prepareCanvas(ctx, m_clientToken, std::format("{}_{}view", extraLabel, viewName));
447 if (!canvas) {
448 m_plotsDone = true;
449 return;
450 }
451 canvas->setAxisTitles(std::format("{:} [mm]", view == objViewEta ? 'y' : 'x'), "z [mm]");
452 canvas->add(clone(primitives));
453
454 DynamicLegend legend(0.8, 0.2, 0.95, 0.5);
455
456
457 ATH_MSG_DEBUG("Drawing hits!");
458 if (!drawHits(*segment.parent()->parentBucket(), segment.measurements(),
459 *canvas, legend, view)) {
460 canvas->trash();
461 continue;
462 }
463
464 ATH_MSG_DEBUG("Drawing chi2!");
465 writeChi2(segPars, segment.measurements(), *canvas);
466
467
468 {
469 auto segmentline = drawLine(segPars, canvas->corner(Edges::yLow), canvas->corner(Edges::yHigh), parLineColor, kDashed, view);
470 TLine *line = static_cast<TLine*>(segmentline.get());
471 canvas->add(std::move(segmentline));
472 legend.addOnce(LegendItem::RecoSegmentLine, line, "Reco Segment", "l");
473 }
474
475 for (const xAOD::MuonSegment* tru_segment : truthSegs) {
476 if(!tru_segment) continue;
477
478 auto truthLine = drawLine(localSegmentPars(*tru_segment), canvas->corner(Edges::yLow), canvas->corner(Edges::yHigh), truthColor, kDotted, view);
479 TLine* line = static_cast<TLine*>(truthLine.get());
480 canvas->add( std::move(truthLine) );
481 legend.addOnce( LegendItem::TruthSegmentLine, line, "Truth Segment", "l" );
482 paintSimHits(ctx,*tru_segment, *canvas, view);
483 }
484
485 const Identifier canvasId{segment.parent()->getHitsInMax().front()->identify()};
486 std::string legendLabel=std::format("Event: {:}, chamber: {:}, #chi^{{2}} / nDoF: {:.2f} ({:d}), #{:}-view (:)",
487 ctx.eventID().event_number(), m_idHelperSvc->toStringChamber(canvasId),
488 segment.chi2() /std::max(1u, segment.nDoF()), segment.nDoF(),
489 view ==objViewEta ? "eta" : "phi", extraLabel);
490
491 canvas->add(drawLabel(legendLabel, 0.2, 0.96));
492 canvas->add(drawLabel(makeLabel(segPars),0.25, 0.91));
493
494 if (!legend.empty()) {
495 canvas->add(legend.release(), "same");
496 }
497 }
498 }
499
500 template<class SpacePointType>
501 const SpacePoint*
502 PatternVisualizationTool::drawHit(const SpacePointType& hit, Canvas_t& canvas, DynamicLegend& legend,
503 const unsigned int view,
504 unsigned int fillStyle) const {
505
507 if ((view == objViewEta && !hit.measuresEta()) || (view == objViewPhi && !hit.measuresPhi())) {
508 return nullptr;
509 }
510
511 if (hit.type() != xAOD::UncalibMeasType::Other) {
512 canvas.expandPad(hit.localPosition()[view] - hit.driftRadius(),
513 hit.localPosition().z() - hit.driftRadius());
514 canvas.expandPad(hit.localPosition()[view] + hit.driftRadius(),
515 hit.localPosition().z() + hit.driftRadius());
516 }
517
518 const SpacePoint* underlyingSp{nullptr};
520 constexpr int invalidCalibFill = 3305;
521 if constexpr (std::is_same_v<SpacePointType, SpacePoint>) {
522 ATH_MSG_VERBOSE("drawHit called with SpacePoint");
523 underlyingSp = &hit;
525 const auto* dc = static_cast<const xAOD::MdtDriftCircle*>(hit.primaryMeasurement());
526 if (dc && dc->status() != Muon::MdtDriftCircleStatus::MdtStatusDriftTime) {
527 fillStyle = invalidCalibFill;
528 }
529 }
530 } else if constexpr(std::is_same_v<SpacePointType, CalibratedSpacePoint>) {
531 ATH_MSG_VERBOSE("drawHit called with CalibratedSpacePoint");
532 underlyingSp = hit.spacePoint();
533 if (hit.fitState() == CalibratedSpacePoint::State::Valid) {
534 fillStyle = fullFilling;
535 } else if (hit.fitState() == CalibratedSpacePoint::State::FailedCalib) {
536 fillStyle = invalidCalibFill;
537 } else {
538 fillStyle = hatchedFilling;
539 }
540 }
541 else{
542 ATH_MSG_VERBOSE("drawHit called with unsupported SpacePointType");
543 }
544
545 //TODO: investigate MuonSegmentFittingAlg.PatternVisualizationTool throws seg fault without check but the calibrated space point should have reference space point no?
546 if (!underlyingSp){
547 ATH_MSG_VERBOSE("Underlying space point not found, skipping hit");
548 return nullptr;
549 }
550
551 const auto* primaryMeas = underlyingSp->primaryMeasurement();
552 const auto* secondaryMeas = underlyingSp->secondaryMeasurement();
553
554 //If there is a secondary measurement and it is not matched, choose a different colour to the cases where we have primary and it is matched
555 //Need to cast secondary measurement to the respective det type?
556 auto labeledColor = (secondaryMeas && !isLabeled(*secondaryMeas)) ? kOrange - 3 : truthColor;
557
558 const auto covarianceWidth = [&]() -> double {
559 const double variance = std::max(0., underlyingSp->covariance()[view]);
560 //In case of strip it would be too small to visualize the hit, so we set a minimum width of 5 mm, should we add sqrt(12) to get the full width of the strip?
561 return std::max(5. * Gaudi::Units::mm, std::sqrt(variance));
562 };
563
564 const auto addLabeledOutline = [&](const double boxWidth, const double boxHeight) {
565 if (!isLabeled(*primaryMeas)) {
566 return;
567 }
568 // Draw a dedicated hollow overlay so the truth-color border is always visible.
569 auto outline = drawBox(hit.localPosition(), boxWidth, boxHeight, labeledColor, hollowFilling, view);
570 outline->SetFillStyle(hollowFilling);
571 outline->SetLineColor(labeledColor);
572 TObject* p_outline = outline.get();
573 canvas.add(std::move(outline), "l");
574 legend.addOnce(LegendItem::LabeledBox, p_outline, "TrueSimHitMatch", "f");
575 };
576
577 // Add the hit to the legend depending on the fill style (outlier, full, or hatched)
578 const auto addHitLegendByFill = [&](TObject* object,
579 const LegendItem bucketItem,
580 const char* bucketLabel,
581 const LegendItem onObjectItem,
582 const char* onObjectLabel, const char* option = "f") {
583
584 if (fillStyle == hatchedFilling) {
585 legend.addOnce(LegendItem::OutlierHit, object, "Outlier", option);
586 return;
587 }
588 if (fillStyle == fullFilling) {
589 legend.addOnce(onObjectItem, object, onObjectLabel, option);
590 return;
591 }
592
593 // Hits in the bucket with a hollow filling
594 legend.addOnce(bucketItem, object, bucketLabel, option);
595 };
596
597 switch(hit.type()) {
599 const auto* dc = static_cast<const xAOD::MdtDriftCircle*>(primaryMeas);
600 canvas.add(drawDriftCircle(hit.localPosition(), dc->readoutElement()->innerTubeRadius(), kBlack, hollowFilling));
601 auto driftCircle = drawDriftCircle(hit.localPosition(), hit.driftRadius(), kBlue, fillStyle);
602 TObject* p_driftCircle = driftCircle.get();
603 canvas.add(std::move(driftCircle));
604
605 //Unforunately the legend will be a box ... to have a point we could make TGraph with a point and add it to the legend but then we would have to draw the hit as a TGrapg as well, so for now we keep it as a box
606 addHitLegendByFill(p_driftCircle,
608 "MDT inBucket",
610 "MDT onObject", "f");
611
612 //In case the hit is also a truth hit, change the line colour to indicate that
613 if (isLabeled(*dc)) {
614 auto labeledDriftCircle = drawDriftCircle(hit.localPosition(), hit.driftRadius(), labeledColor, hollowFilling);
615 TObject* p_labeledDriftCircle = labeledDriftCircle.get();
616 canvas.add(std::move(labeledDriftCircle));
617 legend.addOnce( LegendItem::LabeledTube, p_labeledDriftCircle, "TrueSimMdtHitMatch", "f");
618 }
619 break;
621 const auto* meas{static_cast<const xAOD::RpcMeasurement*>(primaryMeas)};
622 const double boxWidth = 0.5*std::sqrt(12)*std::sqrt(underlyingSp->covariance()[view]);
623 auto rpcBox = drawBox(hit.localPosition(), boxWidth, 0.5*meas->readoutElement()->gasGapPitch(),
624 kGreen + 2, fillStyle);
625 TObject* p_rpcBox = rpcBox.get();
626 canvas.add(std::move(rpcBox));
627 addHitLegendByFill(p_rpcBox,
629 "RPC inBucket",
631 "RPC onObject");
632 addLabeledOutline(boxWidth, 0.5*meas->readoutElement()->gasGapPitch());
633 break;
635 const auto* meas{static_cast<const xAOD::TgcStrip*>(primaryMeas)};
636 const double boxWidth = 0.5*std::sqrt(12)*std::sqrt(underlyingSp->covariance()[view]);
637 auto tgcBox = drawBox(hit.localPosition(), boxWidth, 0.5*meas->readoutElement()->gasGapPitch(),
638 kCyan + 2, fillStyle);
639 TObject* p_tgcBox = tgcBox.get();
640 canvas.add(std::move(tgcBox));
641 addHitLegendByFill(p_tgcBox,
643 "TGC inBucket",
645 "TGC onObject");
646 addLabeledOutline(boxWidth, 0.5*meas->readoutElement()->gasGapPitch());
647 break;
649 const double boxWidth = covarianceWidth();
650 constexpr double boxHeight = 10.*Gaudi::Units::mm;
651 auto mmBox = drawBox(hit.localPosition(), boxWidth, boxHeight, kAquamarine, fillStyle, view);
652 TObject* p_mmBox = mmBox.get();
653 canvas.add(std::move(mmBox));
654
655 addHitLegendByFill(p_mmBox,
657 "MMG inBucket",
659 "MMG onObject");
660 addLabeledOutline(boxWidth, boxHeight);
661 break;
663 break;
665 const double boxWidth = covarianceWidth();
666 const double boxHeight = 10.*Gaudi::Units::mm;
667 auto stgcBox = drawBox(hit.localPosition(), boxWidth, boxHeight, kTeal, fillStyle, view);
668
669
670 //In case of pad use a transparent filling to see the underlying strip or wire
671 //TODO: do more splitting into strip+wire, strip+pad, wire+pad, strip+wire+pad and add legend accordingly?
673 const auto* primary = static_cast<const xAOD::sTgcMeasurement*>( primaryMeas );
674 if (primary && primary->channelType() == xAOD::sTgcMeasurement::sTgcChannelTypes::Pad) {
675 stgcBox->SetFillColorAlpha(kTeal, 0.1);
676 legendType = LegendItem::StgcPadHit;
677 }
678 TObject* p_stgcBox = stgcBox.get();
679
680 canvas.add(std::move(stgcBox));
681 if (legendType == LegendItem::StgcPadHit) {
682 addHitLegendByFill(p_stgcBox,
684 "sTGCp inBucket",
686 "sTGCp onObject");
687 } else {
688 addHitLegendByFill(p_stgcBox,
690 "sTGCs+w inBucket",
692 "sTGCs+w onObject");
693 }
694 addLabeledOutline(boxWidth, boxHeight);
695 break;
696 } default: {
697 ATH_MSG_WARNING("Please implement proper drawings of the new small wheel.. "<<__FILE__<<":"<<__LINE__);
698 break;
699 }
700 }
701 return underlyingSp;
702 }
703
704 template<class SpacePointType>
706 const std::vector<SpacePointType>& hitsToDraw,
707 Canvas_t& canvas, DynamicLegend& legend,
708 unsigned int view) const {
709
710 SpacePointSet drawnPoints{};
711
712 for (const SpacePointType& hit : hitsToDraw) {
713 drawnPoints.insert(drawHit(*hit, canvas, legend, view, fullFilling));
714 }
715 if (m_displayBucket) {
716 for (const SpacePointBucket::value_type& hit : bucket) {
717 // Don't redraw the other points
718 if (drawnPoints.count(hit.get())) {
719 continue;
720 }
721
722 drawHit(*hit, canvas, legend, view, hollowFilling);
723 }
724 }
725 return drawnPoints.size() - drawnPoints.count(nullptr) > 0;
726 }
727 template<class SpacePointType>
729 const std::vector<SpacePointType>& hits,
730 Canvas_t& canvas,
731 const double legX, double startLegY,
732 const double endLegY) const {
733 const auto [pos, dir] = makeLine(pars);
734
735 unsigned int nHits = 0;
736 for (const SpacePointType& hit : hits) {
737 const SpacePoint* underlyingSp{nullptr};
738 bool displayChi2{true};
739 if constexpr(std::is_same_v<SpacePointType, Segment::MeasType>) {
740 underlyingSp = hit->spacePoint();
741 displayChi2 = (hit->fitState() == CalibratedSpacePoint::State::Valid);
742 } else {
743 underlyingSp = hit;
744 }
745
746 ATH_MSG_VERBOSE("Writing chi2 for hit " << nHits << " of type " << hit->type());
747
748 const Identifier hitId = underlyingSp ? underlyingSp->identify(): Identifier{};
749 std::string legendstream{};
750 switch(hit->type()) {
752 const int driftSign{SeedingAux::strawSign(pos, dir, *hit)};
753 const MdtIdHelper& idHelper{m_idHelperSvc->mdtIdHelper()};
754 legendstream = std::format("ML: {:1d}, TL: {:1d}, T: {:3d}, {:}",
755 idHelper.multilayer(hitId), idHelper.tubeLayer(hitId),
756 idHelper.tube(hitId), driftSign == -1 ? "L" : "R");
757 break;
759 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
760 legendstream= std::format("DR: {:1d}, DZ: {:1d}, GAP: {:1d}, #eta/#phi: {:}/{:}",
761 idHelper.doubletR(hitId), idHelper.doubletZ(hitId), idHelper.gasGap(hitId),
762 hit->measuresEta() ? "si" : "nay", hit->measuresPhi() ? "si" : "nay");
763 break;
765 const TgcIdHelper& idHelper{m_idHelperSvc->tgcIdHelper()};
766 legendstream = std::format("ST: {:}, GAP: {:1d}, #eta/#phi: {:}/{:}",
767 m_idHelperSvc->stationNameString(hitId), idHelper.gasGap(hitId),
768 hit->measuresEta() ? "si" : "nay", hit->measuresPhi() ? "si" : "nay");
769 break;
771 const MmIdHelper& idHelper{m_idHelperSvc->mmIdHelper()};
772 const auto* clus = static_cast<const xAOD::MMCluster*>(underlyingSp->primaryMeasurement());
773 const MuonGMR4::StripDesign& design = clus->readoutElement()->stripLayer(clus->layerHash()).design();
774 legendstream = std::format("ML: {:1d}, GAP: {:1d}, {:}", idHelper.multilayer(hitId), idHelper.gasGap(hitId),
775 !design.hasStereoAngle() ? "X" : design.stereoAngle() > 0 ? "U" :"V");
776 break;
778 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
779 legendstream = std::format("ML: {:1d}, GAP: {:1d}, #eta/#phi: {:}/{:}",
780 idHelper.multilayer(hitId), idHelper.gasGap(hitId),
781 hit->measuresEta() ? "si" : "nay", hit->measuresPhi() ? "si" : "nay");
782 break;
784 legendstream = "Ext. constaint";
785 }
786 default:
787 break;
788 }
789 if (displayChi2) {
790 //Make thi chi2 view specific?
791 const double chi2 = SeedingAux::chi2Term(pos, dir,*hit);
792 legendstream+=std::format(", #chi^{{2}}: {:.2f}", chi2);
793 } else {
794 legendstream+=", NotValidState";
795 }
796
797
798 ++nHits;
799
800 canvas.add(drawLabel(legendstream, legX, startLegY, 8));
801 startLegY -= 0.02;
802 if (startLegY<= endLegY) {
803 break;
804 }
805 }
806 }
807
808}
const std::regex re(r_e)
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
bool hit(const Container &ids, int pdgId)
static const uint32_t nHits
This is a "hash" representation of an Identifier.
int multilayer(const Identifier &id) const
Access to components of the ID.
int tube(const Identifier &id) const
int tubeLayer(const Identifier &id) const
int gasGap(const Identifier &id) const override
get the hashes
int multilayer(const Identifier &id) const
MuonReadoutElement is an abstract class representing the geometry of a muon detector.
std::string identString() const
Returns a string encoding the chamber index & the sector of the MS sector.
double stereoAngle() const
Returns the value of the stereo angle.
bool hasStereoAngle() const
Returns whether a stereo angle is defined.
Representation of a segment seed (a fully processed hough maximum) produced by the hough transform.
Definition SegmentSeed.h:14
const std::vector< HitType > & getHitsInMax() const
Returns the list of assigned hits.
const SpacePointBucket * parentBucket() const
Returns the bucket out of which the seed was formed.
Placeholder for what will later be the muon segment EDM representation.
unsigned int nDoF() const
Returns the number of degrees of freedom.
const SegmentSeed * parent() const
Returns the seed out of which the segment was built.
const MeasVec & measurements() const
Returns the associated measurements.
: The muon space point bucket represents a collection of points that will bre processed together in t...
const MuonGMR4::SpectrometerSector * msSector() const
returns th associated muonChamber
The muon space point is the combination of two uncalibrated measurements one of them measures the eta...
const Cov_t & covariance() const
Returns the covariance array.
const Identifier & identify() const
: Identifier of the primary measurement
Gaudi::Property< std::string > m_subDir
Define the subdirectory in which the plots shall be saved.
Gaudi::Property< bool > m_doEtaBucketViews
Switch to visualize the eta view of the bucket event.
ActsTrk::GeoContextReadKey_t m_geoCtxKey
Geometry context key to retrieve the alignment.
virtual StatusCode initialize() override final
Gaudi::Property< bool > m_accumlIsEta
Swtich toggling whether the accumulator view are in the eta or phi plane.
virtual void visualizeAccumulator(const EventContext &ctx, const MuonR4::HoughPlane &accumulator, const Acts::HoughTransformUtils::HoughAxisRanges &axisRanges, const MaximumVec &maxima, const std::string &extraLabel) const override final
virtual void visualizeSegment(const EventContext &ctx, const MuonR4::Segment &segment, const std::string &extraLabel) const override final
Gaudi::Property< unsigned int > m_canvasLimit
Maximum canvases to draw.
Gaudi::Property< bool > m_displayBucket
Display the surrounding hits from the bucket not part of the seed.
Gaudi::Property< std::string > m_canvasPrefix
Prefix of the individual canvas file names <MANDATORY>.
bool drawHits(const MuonR4::SpacePointBucket &bucket, const std::vector< SpacePointType > &hitsToDraw, Canvas_t &canvasDim, DynamicLegend &legend, unsigned int view) const
Translates the Spacepoint information into TObjects that are dawn on the canvas & evaluates the size ...
virtual void visualizeBucket(const EventContext &ctx, const MuonR4::SpacePointBucket &bucket, const std::string &extraLabel) const override final
Gaudi::Property< bool > m_saveSinglePDFs
If set to true each canvas is saved into a dedicated pdf file.
std::unordered_set< const xAOD::MuonSegment * > LabeledSegmentSet
virtual void visualizeSeed(const EventContext &ctx, const MuonR4::SegmentSeed &seed, const std::string &extraLabel) const override final
IRootVisualizationService::ClientToken m_clientToken
Token to present to the visualization service such that the display froms this tool are grouped toget...
std::vector< SegLinkDecor_t > m_truthLinkDecors
Gaudi::Property< bool > m_doPhiBucketViews
Switch to visualize the phi view of the bucket event.
SG::ReadDecorHandleKeyArray< xAOD::UncalibratedMeasurementContainer > m_truthLinkDecorKeys
Declaration of the dependency on the decorations.
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
Service Handle to the IMuonIdHelperSvc.
SG::ReadHandleKeyArray< xAOD::UncalibratedMeasurementContainer > m_prepContainerKeys
Declare dependency on the prep data containers.
virtual LabeledSegmentSet getLabeledSegments(const std::vector< const MuonR4::SpacePoint * > &hits) const override final
Returns whether the hit has been used on the labeled segments we refer to (e.g.
IRootVisualizationService::ICanvasObject Canvas_t
Gaudi::Property< std::set< std::string > > m_truthSegLinks
List of truth segment links to fetch.
void paintSimHits(const EventContext &ctx, const xAOD::MuonSegment &truthSeg, Canvas_t &canvas, const int view) const
Paints the truth sim hits associated with the segment.
ElementLink< xAOD::MuonSegmentContainer > SegLink_t
ServiceHandle< IRootVisualizationService > m_visualSvc
Service handle of the visualization service.
const MuonR4::SpacePoint * drawHit(const SpacePointType &hit, Canvas_t &canvas, DynamicLegend &legend, const unsigned int view, unsigned int fillStyle) const
Converts a Hit into a particular TBox/ TEllipse for drawing.
virtual bool isLabeled(const MuonR4::SpacePoint &hit) const override final
Fetches all labeled (e.g.
Gaudi::Property< bool > m_saveSummaryPDF
If set to true a summary Canvas is created.
void writeChi2(const MuonR4::SegmentFit::Parameters &pars, const std::vector< SpacePointType > &hits, Canvas_t &canvas, const double legX=0.2, double startLegY=0.8, const double endLegY=0.3) const
Writes the chi2 of the hits onto the Canvas.
const MuonGMR4::MuonDetectorManager * m_detMgr
pointer to the Detector manager
Gaudi::Property< bool > m_paintTruthHits
Switch to visualize the truth hits.
std::atomic< bool > m_plotsDone
Flag toggling whether all Canvases have been exhausted.
SG::AuxElement::ConstAccessor< SegLinkVec_t > SegLinkDecor_t
int gasGap(const Identifier &id) const override
get the hashes
int doubletR(const Identifier &id) const
int doubletZ(const Identifier &id) const
Property holding a SG store/key/clid from which a ReadHandle is made.
int gasGap(const Identifier &id) const override
get the hashes
int multilayer(const Identifier &id) const
int gasGap(const Identifier &id) const override
get the hashes
double chi2(TH1 *h0, TH1 *h1)
@ Mdt
MuonSpectrometer.
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
std::pair< Amg::Vector3D, Amg::Vector3D > makeLine(const Parameters &pars)
Returns the parsed parameters into an Eigen line parametrization.
Acts::Experimental::CompositeSpacePointLineFitter::ParamVec_t Parameters
std::string makeLabel(const Parameters &pars)
Dumps the parameters into a string in the form of TLatex.
double houghTanBeta(const Amg::Vector3D &v)
Returns the hough tanBeta [y] / [z].
std::unordered_set< const xAOD::MuonSimHit * > getMatchingSimHits(const xAOD::MuonSegment &segment)
: Returns all sim hits matched to a xAOD::MuonSegment
MuonValR4::IPatternVisualizationTool::PrimitiveVec PrimitiveVec
Acts::HoughTransformUtils::HoughPlane< HoughHitType > HoughPlane
double houghTanAlpha(const Amg::Vector3D &v)
: Returns the hough tanAlpha [x] / [z]
Lightweight algorithm to read xAOD MDT sim hits and (fast-digitised) drift circles from SG and fill a...
constexpr int objViewEta
ObjectView.
PatternVisualizationTool::LabeledSegmentSet LabeledSegmentSet
constexpr int hollowFilling
Filling codes for hollow / fullFilling / hatched filling.
std::unique_ptr< TLine > drawLine(const MuonR4::SegmentFit::Parameters &pars, const double lowEnd, const double highEnd, const int color=kRed+1, const int lineStyle=kDashed, const int view=objViewEta)
Draws a line from the segment fit parameters.
constexpr int hatchedFilling
std::unique_ptr< TLatex > drawLabel(const std::string &text, const double xPos, const double yPos, const double textSize=18, const bool useNDC=true, const int color=kBlack)
Create a TLatex label,.
std::vector< std::unique_ptr< TObject > > clone(const std::vector< std::unique_ptr< TObject > > &cloneMe)
std::unique_ptr< TEllipse > drawDriftCircle(const Amg::Vector3D &center, const double radius, const int color=kViolet, const int fillStyle=hollowFilling)
Create a TEllipse for drawing a drift circle.
constexpr int fullFilling
constexpr int objViewPhi
std::unique_ptr< TArrow > drawArrow(const Amg::Vector3D &start, const Amg::Vector3D &dir, const int color=kRed+1, const int lineStyle=kDashed, const int view=objViewEta)
Draw an arror between two endpoints in the y-z or the x-z plane.
std::unique_ptr< TBox > drawBox(const Amg::Vector3D &boxCenter, const double boxWidth, const double boxHeight, const int color=kGreen+2, const int fillStyle=hollowFilling, const int view=objViewEta)
Creates a box for drawing, e.g strip measurements.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
std::vector< const SpacePoint * > SpacePointSet
vector of space points
Definition FitterTypes.h:38
MdtDriftCircle_v1 MdtDriftCircle
UncalibratedMeasurement_v1 UncalibratedMeasurement
Define the version of the uncalibrated measurement class.
MuonSimHit_v1 MuonSimHit
Defined the version of the MuonSimHit.
Definition MuonSimHit.h:12
TgcStrip_v1 TgcStrip
Definition TgcStripFwd.h:9
RpcMeasurement_v1 RpcMeasurement
MMCluster_v1 MMCluster
sTgcMeasurement_v1 sTgcMeasurement
MuonSegment_v1 MuonSegment
Reference the current persistent version: