ATLAS Offline Software
Loading...
Searching...
No Matches
MuonChamberToolTest.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#if defined(FLATTEN) && defined(__GNUC__)
6// Avoid warning in dbg build
7#pragma GCC optimize "-fno-var-tracking-assignments"
8#endif
9
10#include "MuonChamberToolTest.h"
11
19#include <GaudiKernel/SystemOfUnits.h>
20
21#include "Acts/Geometry/TrapezoidVolumeBounds.hpp"
22#include "Acts/Geometry/TrackingGeometry.hpp"
23#include "Acts/Geometry/DiamondVolumeBounds.hpp"
24#include "Acts/Surfaces/TrapezoidBounds.hpp"
25#include "Acts/Surfaces/CylinderBounds.hpp"
26#include "Acts/Surfaces/RadialBounds.hpp"
27#include "Acts/Surfaces/CylinderSurface.hpp"
28#include "Acts/Surfaces/DiscSurface.hpp"
29#include "Acts/Geometry/VolumePlacementBase.hpp"
30
31#include "Acts/Visualization/ObjVisualization3D.hpp"
32#include "Acts/Visualization/GeometryView3D.hpp"
33#include "Acts/Definitions/Units.hpp"
34
36
37#include <format>
38
39using namespace Acts::UnitLiterals;
40using namespace Muon::MuonStationIndex;
41
42namespace{
43 constexpr double tolerance = 10. *Gaudi::Units::micrometer;
44
45 std::vector<std::shared_ptr<const Acts::Volume>> chamberVolumes(const ActsTrk::GeometryContext& gctx,
46 const MuonGMR4::SpectrometerSector& sector) {
47 std::vector<std::shared_ptr<const Acts::Volume>> vols{};
48 std::ranges::transform(sector.chambers(),std::back_inserter(vols),
49 [&gctx](const auto& ch){ return ch->boundingVolume(gctx); });
50 return vols;
51 }
52 std::vector<const Acts::Volume*> chamberVolumes(const Acts::TrackingVolume& vol) {
53 std::vector<const Acts::Volume*> children {};
54 for (const Acts::TrackingVolume& childVol : vol.volumes()) {
55 std::vector<const Acts::Volume*> grandChildren = chamberVolumes(childVol);
56 children.insert(children.end(), grandChildren.begin(), grandChildren.end());
57 }
58 return children;
59 }
60
61 std::vector<const Acts::Surface*> extractSurfaces(const std::vector<const MuonGMR4::MuonReadoutElement*>& reEles){
62 std::vector<const Acts::Surface*> surfaces{};
63 for (const auto* re : reEles) {
64 std::ranges::transform(re->getSurfaces(), std::back_inserter(surfaces),
65 [](const std::shared_ptr<Acts::Surface>& surface) { return surface.get() ; });
66 }
67 return surfaces;
68 }
69
70 std::vector<const Acts::Surface*> extractSurfaces(const Acts::TrackingVolume& volume) {
71 std::vector<const Acts::Surface*> surfaces{};
72 std::ranges::for_each(volume.surfaces(), [&surfaces](const Acts::Surface& surface){
73 if (surface.isSensitive()) {
74 surfaces.push_back(&surface);
75 }
76 });
77 for (const Acts::TrackingVolume& subVol : volume.volumes()) {
78 std::vector<const Acts::Surface*> childSurfaces = extractSurfaces(subVol);
79 surfaces.insert(surfaces.end(), childSurfaces.begin(), childSurfaces.end());
80
81 }
82 return surfaces;
83 }
84
85 Identifier identify(const Acts::Surface& surface) {
86 const auto* detEl = dynamic_cast<const ActsTrk::IDetectorElementBase*>(surface.surfacePlacement());
87 return detEl ? detEl->identify(): Identifier{};
88 }
89
90 bool checkOverlapWithCylinder(const Acts::GeometryContext& gctx,
91 const Acts::Surface* testSurf,
92 const Amg::Vector3D& center, double radius, double halfZ){
93
94 //cylinder-cylinder overlap check
95 if (testSurf->type() == Acts::Surface::SurfaceType::Cylinder) {
96 const auto& testBounds = static_cast<const Acts::CylinderBounds&>(testSurf->bounds());
97 using BoundEnum = Acts::CylinderBounds::BoundValues;
98 const double testR = testBounds.get(BoundEnum::eR);
99 const auto& testCenter = testSurf->center(gctx);
100 double dr = std::abs(testCenter.perp() - center.perp());
101 double dz = std::abs(testCenter.z() - center.z());
102 //the cylinders do not overlap
103 if (dr > testR + radius ||
104 (dr <= Acts::s_epsilon && std::abs(testR-radius) > Acts::s_epsilon) ||
105 (dz > halfZ + testBounds.get(BoundEnum::eHalfLengthZ))) {
106 return false;
107 }
108 } else if (testSurf->type() == Acts::Surface::SurfaceType::Disc) {
109 // Handle disc overlap logic
110 using BoundEnum = Acts::RadialBounds::BoundValues;
111 const auto& bounds = static_cast<const Acts::RadialBounds&>(testSurf->bounds());
112 const auto& testCenter = testSurf->center(gctx);
113 double dz = std::abs(testCenter.z() - center.z());
114 //cylinder and disc do not overlap
115 if (dz > halfZ ||
116 (dz < halfZ && bounds.get(BoundEnum::eMaxR) < (radius))) {
117 return false;
118 }
119 } else {
120 std::cerr << "Overlap check with surface type " << testSurf->type() << " is not implemented yet\n";
121 return false;
122 }
123 return true;
124 }
125
126 bool checkOverlapWithDisc(const Acts::GeometryContext& gctx,
127 const Acts::Surface* testSurf,
128 const Amg::Vector3D& center, double radius){
129 if (testSurf->type() == Acts::Surface::SurfaceType::Cylinder) {
130 const auto& testBounds = static_cast<const Acts::CylinderBounds&>(testSurf->bounds());
131 using BoundEnum = Acts::CylinderBounds::BoundValues;
132 const double testR = testBounds.get(BoundEnum::eR);
133 const auto& testCenter = testSurf->center(gctx);
134 double dz = std::abs(testCenter.z() - center.z());
135 //the cylinder and the disc do not overlap
136 if (dz > testBounds.get(BoundEnum::eHalfLengthZ) ||
137 (dz < testBounds.get(BoundEnum::eHalfLengthZ) && radius < testR)) {
138 return false;
139 }
140 } else if (testSurf->type() == Acts::Surface::SurfaceType::Disc) {
141 using BoundEnum = Acts::RadialBounds::BoundValues;
142 const auto& bounds = static_cast<const Acts::RadialBounds&>(testSurf->bounds());
143 const auto& testCenter = testSurf->center(gctx);
144 double dz = std::abs(testCenter.z() - center.z());
145 double dr = std::abs(testCenter.perp() - center.perp());
146 //the discs do not overlap
147 if (dz > Acts::s_epsilon ||
148 dr > (radius+bounds.get(BoundEnum::eMaxR))){
149 return false;
150 }
151 } else {
152 std::cerr << "Overlap check with surface type " << testSurf->type() << " is not implemented yet\n";
153 return false;
154 }
155 return true;
156 }
157}
158
159namespace MuonGMR4 {
160
162 ATH_CHECK(m_idHelperSvc.retrieve());
163 ATH_CHECK(m_geoCtxKey.initialize());
165 ATH_CHECK(detStore()->retrieve(m_detMgr));
166 return StatusCode::SUCCESS;
167 }
168 template <class EnvelopeType>
169#if defined(FLATTEN) && defined(__GNUC__)
170// We compile this function with optimization, even in debug builds; otherwise,
171// the heavy use of Eigen makes it too slow. However, from here we may call
172// to out-of-line Eigen code that is linked from other DSOs; in that case,
173// it would not be optimized. Avoid this by forcing all Eigen code
174// to be inlined here if possible.
175[[gnu::flatten]]
176#endif
178 const EnvelopeType& chamb,
179 const Acts::Volume& boundVol,
180 const Amg::Vector3D& point,
181 const std::string& descr,
182 const Identifier& channelId) const {
183
184 // Explicitly inline Volume::inside here so that it gets
185 // flattened in debug builds. Gives a significant speedup.
186 //if (boundVol.inside(gctx.context(), point, tolerance)) {
187 const Amg::Vector3D locPos{boundVol.globalToLocalTransform(gctx.context()) * point};
188 if (boundVol.volumeBounds().inside(locPos,tolerance)) {
189 ATH_MSG_VERBOSE("In channel "<<m_idHelperSvc->toString(channelId)
190 <<", point "<<descr <<" is inside of the chamber "<<std::endl<<chamb<<std::endl
191 <<"Local position:" <<Amg::toString(boundVol.globalToLocalTransform(gctx.context()) * point));
192 return StatusCode::SUCCESS;
193 }
194
195 StripDesign planeTrapezoid{};
196 planeTrapezoid.defineTrapezoid(chamb.halfXShort(), chamb.halfXLong(), chamb.halfY());
197 planeTrapezoid.setLevel(MSG::VERBOSE);
199 static const Eigen::Rotation2D axisSwap{90. *Gaudi::Units::deg};
200 if (std::abs(locPos.z()) - chamb.halfZ() < -tolerance &&
201 planeTrapezoid.insideTrapezoid(axisSwap*locPos.block<2,1>(0,0))) {
202 return StatusCode::SUCCESS;
203 }
204 planeTrapezoid.defineStripLayout(locPos.y() * Amg::Vector2D::UnitX(), 1, 1, 1);
205 ATH_MSG_ERROR("In channel "<<m_idHelperSvc->toString(channelId) <<", the point "
206 << descr <<" "<<Amg::toString(point)<<" is not part of the chamber volume."
207 <<std::endl<<std::endl<<chamb<<std::endl<<"Local position "<<Amg::toString(locPos)
208 <<", "<<planeTrapezoid
209 <<", box left edge: "<<Amg::toString(planeTrapezoid.leftEdge(1).value_or(Amg::Vector2D::Zero()))
210 <<", box right edge "<<Amg::toString(planeTrapezoid.rightEdge(1).value_or(Amg::Vector2D::Zero())));
211 return StatusCode::FAILURE;
212 }
213
215 const Acts::TrackingVolume& volume,
216 const Amg::Vector3D& point,
217 const std::string& descr,
218 const Identifier& chamberId) const {
219 if (volume.inside(gctx.context(), point, tolerance)) {
220 return StatusCode::SUCCESS;
221 }
222 ATH_MSG_ERROR("In channel "<<m_idHelperSvc->toString(chamberId) <<", the point "
223 << descr <<" "<<Amg::toString(volume.globalToLocalTransform(gctx.context())* point)
224 <<" is not part of the chamber volume. The corners of the volume are:");
225 for(const Amg::Vector3D& corner : cornerPoints(gctx, volume)) {
226 ATH_MSG_ERROR(" "<<Amg::toString(volume.globalToLocalTransform(gctx.context())*corner));
227 }
228 return StatusCode::FAILURE;
229 }
230
231 template <class EnvelopeType>
233 const EnvelopeType& envelope) const {
234 std::shared_ptr<Acts::Volume> boundVol = envelope.boundingVolume(gctx);
235 const Chamber::ReadoutSet reEles = envelope.readoutEles();
236 for(const MuonReadoutElement* readOut : reEles) {
237 if constexpr (std::is_same_v<EnvelopeType, SpectrometerSector>) {
238 if (readOut->msSector() != &envelope) {
239 ATH_MSG_ERROR("Mismatch in the sector association "<<m_idHelperSvc->toStringDetEl(readOut->identify())
240 <<std::endl<<(*readOut->msSector())<<std::endl<<envelope);
241 return StatusCode::FAILURE;
242 }
243 } else if constexpr (std::is_same_v<EnvelopeType, Chamber>) {
244 if (readOut->chamber() != &envelope) {
245 ATH_MSG_ERROR("Mismatch in the chamber association "<<m_idHelperSvc->toStringDetEl(readOut->identify())
246 <<std::endl<<(*readOut->chamber())<<std::endl<<envelope);
247 return StatusCode::FAILURE;
248 }
249 }
250 switch (readOut->detectorType()) {
252 const auto* detEle = static_cast<const TgcReadoutElement*>(readOut);
253 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
254 break;
256 const auto* detEle = static_cast<const MdtReadoutElement*>(readOut);
257 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
258 break;
260 const auto* detEle = static_cast<const RpcReadoutElement*>(readOut);
261 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
262 break;
264 const auto* detEle = static_cast<const MmReadoutElement*>(readOut);
265 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
266 break;
268 const auto* detEle = static_cast<const sTgcReadoutElement*>(readOut);
269 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
270 break;
271 } default: {
272 ATH_MSG_ERROR("Who came up with putting "<<readOut->detectorType()<<" into the MS");
273 return StatusCode::FAILURE;
274 }
275 }
276 }
277 ATH_MSG_DEBUG("All "<<reEles.size()<<" readout elements are embedded in "<<envelope);
278 return StatusCode::SUCCESS;
279 }
280
281 std::vector<Amg::Vector3D> MuonChamberToolTest::cornerPoints(const ActsTrk::GeometryContext& gctx,
282 const Acts::Volume& volume) const {
283
284 const auto& bounds = volume.volumeBounds();
285 unsigned int edgeIdx{0};
286 //diamond volume bounds case - there are 12 edges
287 if(bounds.type() == Acts::VolumeBounds::BoundsType::eDiamond){
288 const auto& diamondBounds = static_cast<const Acts::DiamondVolumeBounds&>(bounds);
289 using BoundEnum = Acts::DiamondVolumeBounds::BoundValues;
290 std::vector<Amg::Vector3D> edges(12, Amg::Vector3D::Zero());
291 double xCord{0.}, yCord{0};
292 for(double signX : {-1.,1.}){
293 for(double signY : {-1., 0., 1.}){
294 for(double signZ : {-1.,1.}){
295 if(signY == 0){
296 xCord = diamondBounds.get(BoundEnum::eHalfLengthX2);
297 }else if(signY < 0){
298 xCord = diamondBounds.get(BoundEnum::eHalfLengthX1);
299 yCord = diamondBounds.get(BoundEnum::eLengthY1);
300 } else{
301 xCord = diamondBounds.get(BoundEnum::eHalfLengthX3);
302 yCord = diamondBounds.get(BoundEnum::eLengthY2);
303 }
304
305 const Amg::Vector3D edge{signX*xCord,
306 signY*yCord,
307 signZ*diamondBounds.get(BoundEnum::eHalfLengthZ)};
308 edges[edgeIdx] = volume.localToGlobalTransform(gctx.context())*edge;
309 ++edgeIdx;
310 }
311 }
312 }
313 return edges;
314 }
315
316 //trapezoid or rectangular bounds case
317 std::vector<Amg::Vector3D> edges{};
318 ATH_MSG_VERBOSE("Fetch volume bounds "<<Amg::toString(volume.localToGlobalTransform(gctx.context())));
319 for (const double signX : {-1., 1.}) {
320 for (const double signY : { -1., 1.}) {
321 for (const double signZ: {-1., 1.}) {
322 const Amg::Vector3D edge{signX* (signY>0 ? MuonGMR4::halfXhighY(bounds) : MuonGMR4::halfXlowY(bounds)),
323 signY*MuonGMR4::halfY(bounds),
324 signZ*MuonGMR4::halfZ(bounds)};
325 edges.push_back(volume.localToGlobalTransform(gctx.context()) * edge);
326 ATH_MSG_VERBOSE("Local edge "<<Amg::toString(edge)<<", global edge: "<<Amg::toString(edges[edgeIdx]));
327 ++edgeIdx;
328 }
329 }
330 }
331 return edges;
332 }
333
334 std::array<Amg::Vector3D, 8> MuonChamberToolTest::cornerPoints(const ActsTrk::GeometryContext& gctx, const Acts::StrawSurface& surface) const {
335 std::array<Amg::Vector3D, 8> edges{make_array<Amg::Vector3D,8>(Amg::Vector3D::Zero())};
336 using BoundEnum = Acts::LineBounds::BoundValues;
337 const auto& bounds = static_cast<const Acts::LineBounds&>(surface.bounds());
338 unsigned int edgeIdx{0};
339
340 ATH_MSG_VERBOSE("Fetch volume bounds "<<Amg::toString(surface.localToGlobalTransform(gctx.context())));
341 for (const double signX : {-1., 1.}) {
342 for (const double signY : { -1., 1.}) {
343 for (const double signZ: {-1., 1.}) {
344 const Amg::Vector3D edge{signX*bounds.get(BoundEnum::eR),
345 signY*bounds.get(BoundEnum::eR),
346 signZ*bounds.get(BoundEnum::eHalfLengthZ)};
347 edges[edgeIdx] = surface.localToGlobalTransform(gctx.context()) * edge;
348 ++edgeIdx;
349 }
350 }
351 }
352 return edges;
353 }
354
355 std::array<Amg::Vector3D, 4> MuonChamberToolTest::cornerPoints(const ActsTrk::GeometryContext& gctx, const Acts::PlaneSurface& surface) const {
356 std::array<Amg::Vector3D, 4> edges{make_array<Amg::Vector3D,4>(Amg::Vector3D::Zero())};
357 if(surface.bounds().type() == Acts::SurfaceBounds::BoundsType::eRectangle) { //RPC surfaces are rectangles
358 const Acts::RectangleBounds& bounds = static_cast<const Acts::RectangleBounds&>(surface.bounds());
359 using BoundEnum = Acts::RectangleBounds::BoundValues;
360
361 unsigned int edgeIdx{0};
362 for(const double signX : {-1., 1.}) {
363 for (const double signY : { -1., 1.}) {
364 const Amg::Vector3D edge{signX < 0 ? bounds.get(BoundEnum::eMinX) : bounds.get(BoundEnum::eMaxX),
365 signY < 0 ? bounds.get(BoundEnum::eMinY) : bounds.get(BoundEnum::eMaxY), 0.};
366 edges[edgeIdx] = surface.localToGlobalTransform(gctx.context()) * edge;
367 ++edgeIdx;
368 }
369 }
370 return edges;
371 } else if(surface.bounds().type() == Acts::SurfaceBounds::BoundsType::eTrapezoid) {
372 using BoundEnum = Acts::TrapezoidBounds::BoundValues;
373 const auto& bounds = static_cast<const Acts::TrapezoidBounds&>(surface.bounds());
374 unsigned int edgeIdx{0};
375
376 ATH_MSG_VERBOSE("Fetch volume bounds "<<Amg::toString(surface.localToGlobalTransform(gctx.context())));
377 for (const double signX : {-1., 1.}) {
378 for (const double signY : { -1., 1.}) {
379 const Amg::Vector3D edge{Amg::getRotateZ3D(-1.*bounds.get(BoundEnum::eRotationAngle)) *
380 Amg::Vector3D(signX*bounds.get(signY < 0 ? BoundEnum::eHalfLengthXnegY : BoundEnum::eHalfLengthXposY),
381 signY*bounds.get(BoundEnum::eHalfLengthY), 0.)};
382
383 edges[edgeIdx] = surface.localToGlobalTransform(gctx.context()) * edge;
384 ++edgeIdx;
385 }
386 }
387
388 return edges;
389 } else {
390 ATH_MSG_ERROR("The surface bounds are neither a rectangle nor a trapezoid, this is not supported yet");
391 return edges;
392 }
393 }
394
395
396#if defined(FLATTEN) && defined(__GNUC__)
397// We compile this function with optimization, even in debug builds; otherwise,
398// the heavy use of Eigen makes it too slow. However, from here we may call
399// to out-of-line Eigen code that is linked from other DSOs; in that case,
400// it would not be optimized. Avoid this by forcing all Eigen code
401// to be inlined here if possible.
402[[gnu::flatten]]
403#endif
405 const std::vector<Amg::Vector3D>& chamberEdges,
406 const Acts::Volume& volume) const {
407
409 const Amg::Vector3D center{volume.center(gctx.context())};
410 double minDist = 1._km;
411 for (const Amg::Vector3D& edge : chamberEdges) {
412 minDist = std::min(minDist, (edge - center).mag());
413 }
416 if (std::ranges::none_of(volume.volumeBounds().values(),
417 [minDist](const double bound){
418 return minDist < 2.5*bound;
419 })) {
420 return false;
421 }
422 const double stepLength = 1. / m_overlapSamples;
423
424 const Acts::VolumeBounds& volBounds = volume.volumeBounds();
425 const Acts::Transform3& transform = volume.globalToLocalTransform(gctx.context());
426 for (unsigned edge1 = 1; edge1 < chamberEdges.size(); ++edge1) {
427 for (unsigned edge2 = 0; edge2 < edge1; ++edge2) {
428 for (unsigned step = 0 ; step <= m_overlapSamples; ++step) {
429 const double section = stepLength * step;
430 const Amg::Vector3D testPoint = section* chamberEdges[edge1] + (1. -section) *chamberEdges[edge2];
431 // Using acts::Volume::inside is horribly slow in dbg builds.
432 // Using the bounds method directly is much faster.
433 if (volBounds.inside (transform * testPoint)) {
434 return true;
435 }
436 }
437 }
438 }
439 return false;
440 }
442
443 std::vector<const MuonReadoutElement*> allRE = m_detMgr->getAllReadoutElements();
445 const ChamberSet chambers = m_detMgr->getAllChambers();
446 ATH_MSG_INFO("Fetched "<<chambers.size()<<" chambers.");
447 std::vector<const Chamber*> chamberVec{chambers.begin(), chambers.end()};
448
449 const auto missChamb = std::ranges::find_if(allRE, [&chamberVec](const MuonGMR4::MuonReadoutElement* re){
450 return std::ranges::find(chamberVec, re->chamber()) == chamberVec.end();
451 });
452 if (missChamb != allRE.end()) {
453 ATH_MSG_ERROR("The chamber "<<(*(*missChamb)->chamber())<<" is not in the chamber set");
454 return StatusCode::FAILURE;
455 }
456
457 // Retrieve bounds here rather than inside the loop below,
458 // so we only need to do it O(N) rather than O(N^2) times.
459 std::vector<std::shared_ptr<Acts::Volume> > chamberBoundsVec;
460 chamberBoundsVec.reserve (chamberVec.size());
461 for (const Chamber* ch : chamberVec)
462 chamberBoundsVec.push_back (ch->boundingVolume(gctx));
463
464 std::set<const Chamber*> overlapChambers{};
465 std::stringstream overlapstream{};
466 for (std::size_t chIdx = 0; chIdx< chamberVec.size(); ++chIdx) {
467 const Chamber& chamber{*chamberVec[chIdx]};
468 const Acts::Volume& chamberBounds = *chamberBoundsVec[chIdx];
469 if (m_dumpObjs) {
470 saveEnvelope(gctx, std::format("Chamber_{:}{:}{:}{:}{:}",
471 chamber.detectorType(),
472 chName(chamber.chamberIndex()),
473 std::abs(chamber.stationEta()),
474 chamber.stationEta() > 0 ? 'A' : 'C',
475 chamber.stationPhi()),
476 chamberBounds, extractSurfaces(chamber.readoutEles()));
477 }
478 ATH_CHECK(allReadoutInEnvelope(gctx, chamber));
479 const std::vector<Amg::Vector3D> chambCorners = cornerPoints(gctx, chamberBounds);
481 std::vector<const Chamber*> overlaps{};
482 for (std::size_t chIdx1 = 0; chIdx1<chamberVec.size(); ++chIdx1) {
483 if (chIdx == chIdx1) {
484 continue;
485 }
486 const Chamber* overlapTest{chamberVec[chIdx1]};
487 if (hasOverlap(gctx, chambCorners, *chamberBoundsVec[chIdx1])) {
488 overlaps.push_back(overlapTest);
489 }
490 }
491 if (overlaps.empty()) {
492 continue;
493 }
494 overlapstream<<"The chamber "<<chamber<<" overlaps with "<<std::endl;
495 for (const Chamber* itOverlaps : overlaps) {
496 overlapstream<<" *** "<<(*itOverlaps)<<std::endl;
497 }
498 overlapstream<<std::endl<<std::endl;
499 overlapChambers.insert(overlaps.begin(), overlaps.end());
500 overlapChambers.insert(chamberVec[chIdx]);
501 }
502 if (!overlapChambers.empty()) {
503 Acts::ObjVisualization3D visualHelper{};
504 for (const Chamber* hasOverlap: overlapChambers) {
505 Acts::GeometryView3D::drawVolume(visualHelper, *hasOverlap->boundingVolume(gctx), gctx.context());
506 visualHelper.write(m_overlapChambObj.value());
507 }
508 if (m_ignoreOverlapCh) {
509 ATH_MSG_WARNING(overlapstream.str());
510 } else {
511 ATH_MSG_ERROR(overlapstream.str());
512 }
513 }
514 ATH_MSG_INFO("Chamber test completed. Found "<<overlapChambers.size()<<" overlapping chambers");
515 return overlapChambers.empty() || m_ignoreOverlapCh ? StatusCode::SUCCESS : StatusCode::FAILURE;
516 }
517
519
520 std::vector<const MuonReadoutElement*> allREs = m_detMgr->getAllReadoutElements();
521 for (const MuonReadoutElement* re : allREs) {
522 if (!re->msSector()) {
523 ATH_MSG_ERROR("The readout element "<<m_idHelperSvc->toStringDetEl(re->identify())<<" does not have any sector associated ");
524 return StatusCode::FAILURE;
525 }
526 const SpectrometerSector* sectorFromDet = m_detMgr->getSectorEnvelope(re->chamberIndex(),
527 m_idHelperSvc->sector(re->identify()),
528 re->stationEta());
529 if (sectorFromDet != re->msSector()) {
530 ATH_MSG_ERROR("The sector attached to "<<m_idHelperSvc->toStringDetEl(re->identify())
531 <<", chIdx: "<<chName(re->chamberIndex())<<", sector: "<<m_idHelperSvc->sector(re->identify())
532 <<" is not the one attached to the readout geometry \n"<<(*re->msSector())<<"\n"<<(*sectorFromDet));
533 return StatusCode::FAILURE;
534 }
535 }
536 using SectorSet = MuonDetectorManager::MuonSectorSet;
537 const SectorSet sectors = m_detMgr->getAllSectors();
538 ATH_MSG_INFO(__func__<<"() "<<__LINE__<<" - Fetched "<<sectors.size()<<" sectors. ");
539 for (const SpectrometerSector* sector : sectors) {
540 if (m_dumpObjs) {
541 const auto subVols = chamberVolumes(gctx, *sector);
542 saveEnvelope(gctx, std::format("Sector_{:}{:}{:}",
543 chName(sector->chamberIndex()),
544 sector->side() >0? 'A' :'C',
545 sector->stationPhi() ),
546 *sector->boundingVolume(gctx),
547 extractSurfaces(sector->readoutEles()),
548 Acts::unpackSmartPointers(subVols));
549 }
550 ATH_CHECK(allReadoutInEnvelope(gctx, *sector));
551 const std::shared_ptr<Acts::Volume> secVolume = sector->boundingVolume(gctx);
552 for (const SpectrometerSector::ChamberPtr& chamber : sector->chambers()){
553 const std::vector<Amg::Vector3D> edges = cornerPoints(gctx, *chamber->boundingVolume(gctx));
554 unsigned int edgeCount{0};
555 for (const Amg::Vector3D& edge : edges) {
556 ATH_CHECK(pointInside(gctx, *sector, *secVolume, edge, std::format("Edge {:}", ++edgeCount),
557 chamber->readoutEles().front()->identify()));
558 }
559 }
560 }
561 ATH_MSG_INFO(__func__<<"() "<<__LINE__<<" - Sector envelope test completed.");
562 return StatusCode::SUCCESS;
563 }
565 const Acts::TrackingVolume& volume) const {
566 if (!volume.isAlignable()) {
567 return StatusCode::SUCCESS;
568 }
569 const Acts::GeometryContext geoCtx = gctx.context();
570 std::vector<std::shared_ptr<const Acts::Surface>> portals{};
571 for (const Acts::Portal& portal : volume.portals()) {
572 if (portal.surface().geometryId().withBoundary(0) != volume.geometryId()) {
573 continue;
574 }
575 portals.push_back(portal.surface().getSharedPtr());
576 }
577 const auto unAlignedPortals = volume.volumeBounds().orientedSurfaces(volume.localToGlobalTransform(geoCtx));
578
579 if (unAlignedPortals.size() != portals.size()) {
580 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The size of the aligned and unaligned portals don't match for volume "
581 <<volume.volumeName()<<". Aligned: "<<portals.size()<<", unaligned: "<<unAlignedPortals.size());
582 return StatusCode::FAILURE;
583 }
584 StatusCode retCode = StatusCode::SUCCESS;
585 for (std::size_t p =0 ; p < portals.size(); ++p){
587 if (portals[p]->bounds() != unAlignedPortals[p].surface->bounds()) {
588 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The bounds of the "<<p
589 <<"-th portal differ:\n -- aligned: "<<portals[p]->bounds()
590 <<"\n -- unaligned: "<<unAlignedPortals[p].surface->bounds());
591 retCode = StatusCode::FAILURE;
592 }
593 const Amg::Transform3D& uTrf{unAlignedPortals[p].surface->localToGlobalTransform(geoCtx)};
594 const Amg::Transform3D& aTrf{portals[p]->localToGlobalTransform(geoCtx)};
595
596 if (!Amg::isIdentity(uTrf * aTrf.inverse())) {
597 ATH_MSG_ERROR(__func__<<"() "<<__LINE__
598 <<" - The unaligned and aligned portals don't end up at the same point \n"
599 <<" -- aligned: "<<Amg::toString(aTrf)<<"\n"<<" -- unaligned: "<<Amg::toString(uTrf));
600 retCode = StatusCode::FAILURE;
601 }
602 }
603 return retCode;
604 }
605
606
608 const Acts::TrackingGeometry& trackingGeometry) const {
609
610 //visit the volumes and check the overlaps with the other volumes in the tracking geometry
611 // also check overlaps between volumes and surfaces (e.g surfaces where the passive material is mapped)
612 std::vector<const Acts::TrackingVolume*> volumeVec{};
613 std::vector<const Acts::Surface*> passiveSurfaces{};
614
615 std::unordered_set<const Acts::TrackingVolume*> overlapVolumes{};
616 std::unordered_set<const Acts::Surface*> overlapSurfaces{};
617
618
619 //keep onyl the chamber volumes - not the cylinders
620 trackingGeometry.visitVolumes([&](const Acts::TrackingVolume* vol) {
621 //for the cylinder type volumes , fetch the inner surfaces only (e.g passive material surfaces)
622 if(vol->volumeBounds().type() == Acts::VolumeBounds::BoundsType::eCylinder){
623 ATH_MSG_DEBUG("checkTrackingGeometry() "<<__LINE__<<" - Fetch "<<vol->surfaces().size()
624 <<" passive surfaces from "<<vol->volumeName()<<".");
625 std::ranges::for_each(vol->surfaces(), [&](const Acts::Surface& surf){
626 ATH_MSG_VERBOSE(" --- "<<surf.type()<<" @"<<Amg::toString(surf.center(gctx.context()))
627 <<" "<<surf.bounds());
628 passiveSurfaces.push_back(&surf);
629 });
630 return;
631 }
632 const auto* placement = dynamic_cast<const ActsTrk::VolumePlacement*>(vol->volumePlacement());
633 // Not a senitive muon volume
634 if (!placement || !MuonGMR4::isMuon(placement->detectorType())) {
635 ATH_MSG_DEBUG("checkTrackingGeometry() "<<__LINE__<<" - Skip volume "
636 <<vol->volumeName()<<".");
637 return;
638 }
639 volumeVec.push_back(vol);
640 });
641
642 ATH_MSG_INFO(__func__<<"() "<<__LINE__<<" - Fetched "
643 << passiveSurfaces.size()<< " passive surfaces");
644 {
645 Acts::ObjVisualization3D visualHelper{};
646 std::ranges::for_each(passiveSurfaces,
647 [&visualHelper, &gctx](const Acts::Surface* surface) {
648 Acts::GeometryView3D::drawSurface(visualHelper, *surface, gctx.context());
649 });
650 visualHelper.write("MsTrackTest_passiveSurfaces.obj");
651
652 }
653 StatusCode retCode = StatusCode::SUCCESS;
654 for(std::size_t vIdx = 0; vIdx < volumeVec.size(); ++vIdx) {
655 const Acts::TrackingVolume* testVol{volumeVec.at(vIdx)};
656 ATH_CHECK(checkPortals(gctx, *testVol));
657
658 std::vector<const Acts::TrackingVolume*> overlaps{};
659 const std::vector<Amg::Vector3D> edges = cornerPoints(gctx, *testVol);
660
661 for(const auto& surface : testVol->surfaces()) {
662 //only plane or straw surfaces expected
663 std::vector<Amg::Vector3D> surfEdges = {};
664 if(surface.type() == Acts::Surface::SurfaceType::Straw){
665 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<" Checking "<<surface.type()<<" surface "<<identify(surface)
666 <<" / "<<surface.geometryId() <<" in volume "<<testVol->volumeName());
667
668 auto edges = cornerPoints(gctx, dynamic_cast<const Acts::StrawSurface&>(surface));
669 surfEdges.insert(surfEdges.end() , edges.begin(), edges.end());
670 } else if(surface.type() == Acts::Surface::SurfaceType::Plane){
671 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<" Checking "<<surface.type()<<" surface "<<identify(surface)
672 <<" / "<<surface.geometryId() <<" in volume "<<testVol->volumeName());
673
674 auto edges = cornerPoints(gctx, dynamic_cast<const Acts::PlaneSurface&>(surface));
675 surfEdges.insert(surfEdges.end() , edges.begin(), edges.end());
676 } else {
677 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The "<<surface.type()<<"-surface "
678 << m_idHelperSvc->toString(identify(surface))<<" / "
679 <<surface.geometryId() <<" is neither a straw nor a plane surface");
680 return StatusCode::FAILURE;
681 }
682
683 for(const auto& edge : surfEdges) {
684 if(!testVol->inside(gctx.context(), edge, 0.01)) {
685 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The "<<surface.type()<<"-surface "
686 << m_idHelperSvc->toString(identify(surface))<<" / "
687 <<surface.geometryId() <<" @vertex point "
688 <<Amg::toString(testVol->globalToLocalTransform(gctx.context()) *edge)<<", local: "
689 <<Amg::toString(surface.localToGlobalTransform(gctx.context()).inverse() * edge)
690 <<" is outside the parent volume: " << testVol->volumeName()
691 <<", "<<Amg::toString(testVol->localToGlobalTransform(gctx.context()))
692 <<", "<<testVol->volumeBounds());
693 overlapSurfaces.insert(&surface);
694 overlapVolumes.insert(testVol);
695 if (!m_ignoreOutsideSurf) {
696 retCode = StatusCode::FAILURE;
697 }
698 }
699 }
700 }
701
702 //check if the child volume is entirely enclosed by the mother volume
703 for (const Acts::TrackingVolume& child : testVol->volumes()) {
704 for(const auto& edge : cornerPoints(gctx, child)){
705 if(!testVol->inside(gctx.context(), edge, 0.01)){
706 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The children volume's "
707 << child.volumeName() <<" vertex point " <<Amg::toString(edge)
708 <<" is outside the parent volume" << testVol->volumeName());
709 return StatusCode::FAILURE;
710 }
711 }
712 }
714 if (!testVol->motherVolume()->isAlignable() && m_dumpObjs) {
715 std::vector<const Acts::Surface*> surfaces = extractSurfaces(*testVol);
716 const Identifier volId = identify(*surfaces.front());
717 const int eta = m_idHelperSvc->stationEta(volId);
718 saveEnvelope(gctx, std::format("TrackingVolume_{:}{:}{:}{:}_{:}",
719 chName(m_idHelperSvc->chamberIndex(volId)),
720 std::abs(eta), eta > 0 ? 'A' : 'C',
721 m_idHelperSvc->stationPhi(volId), vIdx),
722 *testVol, surfaces , chamberVolumes(*testVol));
723
724 }
725 // Check that there is not overlap with other volumes
726 for (std::size_t vIdx1 = 0 ; vIdx1 < vIdx; ++vIdx1) {
727 const Acts::TrackingVolume* overlapTest{volumeVec.at(vIdx1)};
728 if (overlapTest->motherVolume() == testVol ||
729 testVol->motherVolume() == overlapTest){
730 continue;
731 }
732 if (hasOverlap(gctx, edges, *overlapTest)) {
733 overlaps.push_back(overlapTest);
734 std::ranges::copy(extractSurfaces(*testVol),
735 std::inserter(overlapSurfaces, overlapSurfaces.begin()));
736 std::ranges::copy(extractSurfaces(*overlapTest),
737 std::inserter(overlapSurfaces, overlapSurfaces.begin()));
738 }
739 }
740 /*check if the tracking volume overlaps with surfaces of the tracking geometry
741 (e.g cylinders of the barrel where material is mapped) */
742 const Identifier volId = identify(*extractSurfaces(*testVol).front());
743 double volHalfR{0.}, volHalfZ{0.};
744 const double halfX = MuonGMR4::halfXhighY(testVol->volumeBounds());
745 const bool isBarrel = Muon::MuonStationIndex::isBarrel(m_idHelperSvc->chamberIndex(volId));
746 if (isBarrel){
747 volHalfR = MuonGMR4::halfZ(testVol->volumeBounds());
748 volHalfZ = MuonGMR4::halfY(testVol->volumeBounds());
749 } else {
750 volHalfZ = MuonGMR4::halfZ(testVol->volumeBounds());
751 volHalfR = MuonGMR4::halfY(testVol->volumeBounds());
752 }
753 const Amg::Vector3D center{testVol->center(gctx.context())};
754 const double rMin = center.perp() - volHalfR;
755 // Calculate the global r from the local half X which is always along phi
756 // and the halfR which is along Y (Z) for endcap (barrel) chambers.
757 const double rMax = (testVol->localToGlobalTransform(gctx.context()) *(
758 halfX * Amg::Vector3D::UnitX() +
759 volHalfR * Amg::Vector3D::Unit(1 + isBarrel))).perp();
760
761 double zMin = center.z() - volHalfZ;
762 double zMax = center.z() + volHalfZ;
764 if (testVol->volumeBounds().type() == Acts::VolumeBounds::eDiamond) {
765 zMin = 1._km; zMax = -1._km;
766 for (const Amg::Vector3D& p : cornerPoints(gctx, *testVol)){
767 zMin = std::min(zMin, p.z());
768 zMax = std::max(zMax, p.z());
769 }
770 }
771
772 for(std::size_t i = 0; i < passiveSurfaces.size(); ++i) {
773
774 const Acts::Surface* surf = passiveSurfaces[i];
775
776 const Amg::Vector3D center = surf->center(gctx.context());
777 if(surf->type() == Acts::Surface::SurfaceType::Cylinder) {
778 using BoundEnum = Acts::CylinderBounds::BoundValues;
779 const auto& bounds = static_cast<const Acts::CylinderBounds&>(surf->bounds());
780 const double passiveR = bounds.get(BoundEnum::eR);
781 const double passiveZ = bounds.get(BoundEnum:: eHalfLengthZ);
782 if (rMin < passiveR || rMax > passiveR){
783 continue;
784 }
785 if (passiveZ < zMin || -passiveZ > zMax) {
786 continue;
787 }
788 } else if(surf->type() == Acts::Surface::SurfaceType::Disc){
789 using BoundEnum = Acts::RadialBounds::BoundValues;
790 const auto& bounds = static_cast<const Acts::RadialBounds&>(surf->bounds());
791 if (center.z() < zMin || center.z() > zMax) {
792 continue;
793 }
794 const double surfRMax = bounds.get(BoundEnum::eMaxR);
795 const double surfRMin = bounds.get(BoundEnum::eMinR);
796 if (surfRMax < rMin || surfRMin > rMax){
797 continue;
798 }
799 // continue;
800 } else {
801 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The surface "<< surf->geometryId()
802 <<", "<< surf->name() <<" is not a cylinder surface or disc");
803 return StatusCode::FAILURE;
804 }
805
806 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The volume "
807 << testVol->volumeName() << " overlaps with the surface "
808 << surf->name() << " with geo id" << surf->geometryId()
809 <<" -- volume radius: ["<<rMin<<";"<<rMax<<"] z: ["<<zMin<<";"<<zMax<<"]"
810 <<" "<<surf->bounds());
811 if (m_ignoreOutsideSurf) {
812 retCode = StatusCode::FAILURE;
813 }
814 overlapSurfaces.insert(surf);
815 overlapVolumes.insert(testVol);
816
817
818 }
819
820 if(overlaps.empty()) {
821 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - No overlaps detected for the volume "<<testVol->volumeName());
822 continue;
823 }
824
825 overlapVolumes.insert(overlaps.begin(), overlaps.end());
826 overlapVolumes.insert(testVol);
827
828 std::stringstream overlapStream{};
829 overlapStream<<__func__<<"() "<<__LINE__<<" - The volume "
830 <<testVol->volumeName() << " overlaps with: "<<std::endl;
831
832 for(const Acts::TrackingVolume* overlap: overlaps){
833 overlapStream<<" --- Volume: " << overlap->volumeName()<<", "<<overlap->volumeBounds()
834 <<", "<<Amg::toString(overlap->localToGlobalTransform(gctx.context()))<<std::endl;;
835 }
836 ATH_MSG_ALWAYS(overlapStream.str());
837 }
838
839 //check passive surfaces overlaps with each other
840 for(std::size_t i = 0; i < passiveSurfaces.size(); ++i) {
841 const Acts::Surface* surf = passiveSurfaces[i];
842 const Amg::Vector3D center = surf->center(gctx.context());
843 for(std::size_t j = i+1; j < passiveSurfaces.size(); ++j) {
844 const Acts::Surface* testSurf = passiveSurfaces[j];
845 ATH_MSG_INFO(__func__<<"() "<<__LINE__<<" - Checking passive surface "<<surf->name()<<" geo id "<<surf->geometryId()
846 <<" with passive surface "<<testSurf->name()<<" geo id "<<testSurf->geometryId());
847 if(testSurf->geometryId().volume() != surf->geometryId().volume()){
848 continue;
849 }
850 if(surf->type() == Acts::Surface::SurfaceType::Cylinder){
851 using BoundEnum = Acts::CylinderBounds::BoundValues;
852 const auto& bounds = static_cast<const Acts::CylinderBounds&>(surf->bounds());
853 double passiveR = bounds.get(BoundEnum::eR);
854 double passiveZ = bounds.get(BoundEnum:: eHalfLengthZ);
855 bool overlap = checkOverlapWithCylinder(gctx.context(), testSurf, center, passiveR, passiveZ);
856 if(overlap) {
857 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The surface "<<surf->name()<<"geo id "<<surf->geometryId()
858 <<" overlaps with surface "<<testSurf->name()<<"geo id "<<testSurf->geometryId()
859 <<" in the same volume "<<surf->geometryId().volume());
860 overlapSurfaces.insert(surf);
861 overlapSurfaces.insert(testSurf);
862 if (!m_ignoreOutsideSurf) {
863 retCode = StatusCode::FAILURE;
864 }
865 }
866 }else if(surf->type() == Acts::Surface::SurfaceType::Disc){
867 using BoundEnum = Acts::RadialBounds::BoundValues;
868 const auto& bounds = static_cast<const Acts::RadialBounds&>(surf->bounds());
869 bool overlap = checkOverlapWithDisc(gctx.context(), testSurf, center, bounds.get(BoundEnum::eMaxR));
870 if(overlap) {
871 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The surface "<<surf->name()<<"geo id "<<surf->geometryId()
872 <<" overlaps with surface "<<testSurf->name()<<"geo id "<<testSurf->geometryId()
873 <<" in the same volume "<<surf->geometryId().volume());
874 overlapSurfaces.insert(surf);
875 overlapSurfaces.insert(testSurf);
876 if (!m_ignoreOutsideSurf) {
877 retCode = StatusCode::FAILURE;
878 }
879 }
880 } else {
881 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The surface "<< surf->geometryId()
882 <<", "<< surf->name() <<" is not a cylinder surface or disc");
883 return StatusCode::FAILURE;
884 }
885 }
886 }
887
888 if (overlapVolumes.size() || overlapSurfaces.size()) {
889 const Acts::Volume* refVolume = (*overlapVolumes.begin());
890 std::vector<const Acts::Volume*> childVols{};
891 childVols.insert(childVols.begin(),std::next(overlapVolumes.begin()), overlapVolumes.end());
892 std::vector<const Acts::Surface*> childSurfs{overlapSurfaces.begin(), overlapSurfaces.end()};
893 saveEnvelope(gctx, "TrackingGeometryOverlaps", *refVolume,
894 childSurfs, childVols);
895 }
896
897
898 if(overlapVolumes.empty()) {
899 ATH_MSG_ALWAYS("No overlaps detected in the tracking geometry!!");
900 } else if (!m_ignoreOverlapCh) {
901 retCode = StatusCode::FAILURE;
902 }
903 return retCode;
904 }
905
907 const std::string& envName,
908 const Acts::Volume& envelopeVol,
909 const std::vector<const Acts::Surface*>& assocSurfaces,
910 const std::vector<const Acts::Volume*>& subVols) const {
911 Acts::ObjVisualization3D visualHelper{};
912 std::ranges::for_each(assocSurfaces, [&visualHelper, &gctx](const Acts::Surface* surface) {
913 Acts::GeometryView3D::drawSurface(visualHelper, *surface, gctx.context());
914
915 });
916 std::ranges::for_each(subVols, [&visualHelper, &gctx](const Acts::Volume* subVol) {
917 Acts::GeometryView3D::drawVolume(visualHelper,*subVol, gctx.context(), Amg::Transform3D::Identity(),
918 Acts::s_viewPassive);
919 });
920 Acts::GeometryView3D::drawVolume(visualHelper, envelopeVol, gctx.context());
921 ATH_MSG_DEBUG("Save new envelope 'MsTrackTest_"<<envName<<".obj'");
922 visualHelper.write(std::format("MsTrackTest_{:}.obj", envName));
923 }
924
925 StatusCode MuonChamberToolTest::execute(const EventContext& ctx) const {
926 const ActsTrk::GeometryContext* gctx{nullptr};
927 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
929 ATH_CHECK(checkChambers(*gctx));
931 ATH_CHECK(checkTrackingGeometry(*gctx, *m_trackingGeometrySvc->trackingGeometry()));
932
933 return StatusCode::SUCCESS;
934 }
935 template <class EnvelopeType>
937 const MdtReadoutElement& mdtMl,
938 const EnvelopeType& chamber,
939 const Acts::Volume& detVol) const {
940 ATH_MSG_VERBOSE("Test whether "<<m_idHelperSvc->toStringDetEl(mdtMl.identify())<<std::endl<<mdtMl.getParameters());
941
942 for (unsigned int layer = 1; layer <= mdtMl.numLayers(); ++layer) {
943 for (unsigned int tube = 1; tube <= mdtMl.numTubesInLay(); ++tube) {
944 const IdentifierHash idHash = mdtMl.measurementHash(layer, tube);
945 if (!mdtMl.isValid(idHash)){
946 continue;
947 }
948 const Amg::Transform3D& locToGlob{mdtMl.localToGlobalTransform(gctx, idHash)};
949 const Identifier measId{mdtMl.measurementId(idHash)};
950
951 ATH_CHECK(pointInside(gctx, chamber, detVol, mdtMl.globalTubePos(gctx, idHash), "tube center", measId));
952
953 ATH_CHECK(pointInside(gctx, chamber, detVol, mdtMl.readOutPos(gctx, idHash), "tube readout", measId));
954 ATH_CHECK(pointInside(gctx, chamber, detVol, mdtMl.highVoltPos(gctx, idHash), "tube HV", measId));
955
956 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(-mdtMl.innerTubeRadius() * Amg::Vector3D::UnitX()),
957 "bottom of the tube box", measId));
958 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(mdtMl.innerTubeRadius() * Amg::Vector3D::UnitX()),
959 "sealing of the tube box", measId));
960
961 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(-mdtMl.innerTubeRadius() * Amg::Vector3D::UnitY()),
962 "wall to the previous tube", measId));
963 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(-mdtMl.innerTubeRadius() * Amg::Vector3D::UnitY()),
964 "wall to the next tube", measId));
965 }
966 }
967 return StatusCode::SUCCESS;
968 }
969 template<class EnvelopeType>
971 const RpcReadoutElement& rpc,
972 const EnvelopeType& chamber,
973 const Acts::Volume& detVol) const {
974
975 ATH_MSG_VERBOSE("Test whether "<<m_idHelperSvc->toStringDetEl(rpc.identify())<<std::endl<<rpc.getParameters());
976
977 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
978 for (unsigned int gasGap = 1 ; gasGap <= rpc.nGasGaps(); ++gasGap) {
979 for (int doubletPhi = rpc.doubletPhi(); doubletPhi <= rpc.doubletPhiMax(); ++doubletPhi){
980 for (bool measPhi : {false, true}) {
981 const int nStrips = measPhi ? rpc.nPhiStrips() : rpc.nEtaStrips();
982 for (int strip = 1; strip <= nStrips; ++strip) {
983 const Identifier stripId = idHelper.channelID(rpc.identify(),rpc.doubletZ(),
984 doubletPhi, gasGap, measPhi, strip);
985 ATH_CHECK(pointInside(gctx, chamber, detVol, rpc.stripPosition(gctx, stripId), "center", stripId));
986 ATH_CHECK(pointInside(gctx, chamber, detVol, rpc.leftStripEdge(gctx, stripId), "right edge", stripId));
987 ATH_CHECK(pointInside(gctx, chamber, detVol, rpc.rightStripEdge(gctx, stripId), "left edge", stripId));
988 }
989 }
990 }
991 }
992 return StatusCode::SUCCESS;
993 }
994 template <class EnevelopeType>
996 const TgcReadoutElement& tgc,
997 const EnevelopeType& chamber,
998 const Acts::Volume& detVol) const {
999 for (unsigned int gasGap = 1; gasGap <= tgc.nGasGaps(); ++gasGap){
1000 for (bool isStrip : {false}) {
1001 const IdentifierHash layHash = tgc.constructHash(0, gasGap, isStrip);
1002 const unsigned int nChannel = tgc.numChannels(layHash);
1003 for (unsigned int channel = 1; channel <= nChannel ; ++channel) {
1004 const IdentifierHash measHash = tgc.constructHash(channel, gasGap, isStrip);
1005 ATH_CHECK(pointInside(gctx, chamber, detVol, tgc.channelPosition(gctx, measHash),
1006 "center", tgc.measurementId(measHash)));
1007 }
1008 }
1009 }
1010 return StatusCode::SUCCESS;
1011 }
1012 template <class EnevelopeType>
1014 const MmReadoutElement& mm,
1015 const EnevelopeType& chamber,
1016 const Acts::Volume& detVol) const {
1017
1018 const MmIdHelper& idHelper{m_idHelperSvc->mmIdHelper()};
1019 for(unsigned int gasGap = 1; gasGap <= mm.nGasGaps(); ++gasGap){
1020 IdentifierHash gasGapHash = MmReadoutElement::createHash(gasGap,0);
1021 unsigned int firstStrip = mm.firstStrip(gasGapHash);
1022 for(unsigned int strip = firstStrip; strip <= mm.numStrips(gasGapHash); ++strip){
1023 const Identifier stripId = idHelper.channelID(mm.identify(), mm.multilayer(), gasGap, strip);
1024 ATH_CHECK(pointInside(gctx, chamber, detVol, mm.stripPosition(gctx, stripId), "center", stripId));
1025 ATH_CHECK(pointInside(gctx, chamber, detVol, mm.leftStripEdge(gctx, mm.measurementHash(stripId)), "left edge", stripId));
1026 ATH_CHECK(pointInside(gctx, chamber, detVol, mm.rightStripEdge(gctx, mm.measurementHash(stripId)), "right edge", stripId));
1027 }
1028 }
1029
1030 return StatusCode::SUCCESS;
1031 }
1032 template <class EnvelopeType>
1034 const sTgcReadoutElement& stgc,
1035 const EnvelopeType& chamber,
1036 const Acts::Volume& detVol) const{
1037
1038 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
1039 for(unsigned int gasGap = 1; gasGap <= stgc.numLayers(); ++gasGap){
1040
1041 for(unsigned int nch = 1; nch <= stgc.nChTypes(); ++nch){
1042 IdentifierHash gasGapHash = sTgcReadoutElement::createHash(gasGap, nch, 0, 0);
1043 const unsigned int nStrips = stgc.numChannels(gasGapHash);
1045
1046 for(unsigned int strip = 1; strip <= nStrips; ++strip){
1047 const Identifier stripId = idHelper.channelID(stgc.identify(), stgc.multilayer(), gasGap, nch, strip);
1048 const IdentifierHash stripHash = stgc.measurementHash(stripId);
1049 ATH_CHECK(pointInside(gctx, chamber, detVol, stgc.globalChannelPosition(gctx, stripHash), "channel position", stripId));
1050
1052 ATH_CHECK(pointInside(gctx, chamber, detVol, stgc.rightStripEdge(gctx, stripHash), "channel position", stripId));
1053 ATH_CHECK(pointInside(gctx, chamber, detVol, stgc.leftStripEdge(gctx, stripHash), "channel position", stripId));
1054 }
1055 }
1056 }
1057 }
1058 return StatusCode::SUCCESS;
1059
1060 }
1061}
1062
const std::regex re(r_e)
Scalar eta() const
pseudorapidity method
Scalar mag() const
mag method
constexpr std::array< T, N > make_array(const T &def_val)
Helper function to initialize in-place arrays with non-zero values.
Definition ArrayHelper.h:10
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_ALWAYS(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
void section(const std::string &sec)
Acts::GeometryContext context() const
base class interface providing the bare minimal interface extension.
virtual Identifier identify() const =0
Return the ATLAS identifier.
Implementation to make a (tracking) volume alignable.
const ServiceHandle< StoreGateSvc > & detStore() const
void setLevel(MSG::Level lvl)
Change the current logging level.
This is a "hash" representation of an Identifier.
Identifier channelID(int stationName, int stationEta, int stationPhi, int multilayer, int gasGap, int channel) const
Chamber represent the volume enclosing a muon station.
Definition Chamber.h:29
std::vector< const MuonReadoutElement * > ReadoutSet
Define the list of read out elements of the chamber.
Definition Chamber.h:32
Readout element to describe the Monitored Drift Tube (Mdt) chambers Mdt chambers usually comrpise out...
Amg::Vector3D highVoltPos(const ActsTrk::GeometryContext &ctx, const Identifier &measId) const
Returns the endpoint of the tube connected to the high voltage in the ATLAS coordinate frame.
unsigned numLayers() const
Returns how many tube layers are inside the multi layer [1;4].
bool isValid(const IdentifierHash &measHash) const
Checks whether the passed meaurement hash corresponds to a valid tube described by the readout elemen...
Amg::Vector3D readOutPos(const ActsTrk::GeometryContext &ctx, const Identifier &measId) const
Returns the endpoint of the tube where the readout card is mounted in the ATLAS coordinate frame.
const parameterBook & getParameters() const
Get a const reference to the parameter book.
Amg::Vector3D globalTubePos(const ActsTrk::GeometryContext &ctx, const Identifier &measId) const
Returns the position of the tube mid point in the ATLAS coordinate frame.
double innerTubeRadius() const
Returns the inner tube radius.
unsigned numTubesInLay() const
Returns the number of tubes in a layer.
static IdentifierHash measurementHash(unsigned layerNumber, unsigned tubeNumber)
Constructs a Measurement hash from layer && tube number.
Identifier measurementId(const IdentifierHash &measHash) const override final
Back conversion of the measurement hash towards a full identifier Tube & layer number are extracted f...
static IdentifierHash createHash(const int gasGap, const int strip)
const MuonDetectorManager * m_detMgr
Gaudi::Property< bool > m_ignoreOverlapCh
The overlap of chamber volumes does not lead to a failure.
ActsTrk::GeoContextReadKey_t m_geoCtxKey
StatusCode checkEnvelopes(const ActsTrk::GeometryContext &gctx) const
Check envelopes.
StatusCode checkPortals(const ActsTrk::GeometryContext &gctx, const Acts::TrackingVolume &volume) const
StatusCode execute(const EventContext &ctx) const override
StatusCode checkChambers(const ActsTrk::GeometryContext &gctx) const
Check whether the chamber envelopes are consistent.
void saveEnvelope(const ActsTrk::GeometryContext &gctx, const std::string &envName, const Acts::Volume &envelopeVol, const std::vector< const Acts::Surface * > &assocSurfaces, const std::vector< const Acts::Volume * > &subVolumes={}) const
Gaudi::Property< bool > m_dumpObjs
Dump the chambers & sectors as separate obj files.
StatusCode pointInside(const ActsTrk::GeometryContext &gctx, const EnvelopeType &envelope, const Acts::Volume &boundVol, const Amg::Vector3D &point, const std::string &descr, const Identifier &channelId) const
Checks whether the point is inside of an envelope object, i.e.
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
Gaudi::Property< std::string > m_overlapChambObj
Name of the chamber output obj file.
StatusCode testReadoutEle(const ActsTrk::GeometryContext &gctx, const MdtReadoutElement &readOutEle, const EnvelopeType &envelope, const Acts::Volume &boundVol) const
Checks whether all channels of a given readout element are fully covered by the envelope.
Gaudi::Property< unsigned > m_overlapSamples
Number of points to scan along the lines between two volume corners to check whether they belong to a...
ServiceHandle< ActsTrk::ITrackingGeometrySvc > m_trackingGeometrySvc
Gaudi::Property< bool > m_ignoreOutsideSurf
The exceeding surfaces does not lead to a failure.
StatusCode allReadoutInEnvelope(const ActsTrk::GeometryContext &ctx, const EnvelopeType &envelope) const
Checks whether the readout elements of an enevelope are completely embedded into the envelope.
StatusCode checkTrackingGeometry(const ActsTrk::GeometryContext &gctx, const Acts::TrackingGeometry &trackingGeometry) const
Check tracking geometry volumes.
std::vector< Amg::Vector3D > cornerPoints(const ActsTrk::GeometryContext &gctx, const Acts::Volume &volume) const
Returns the edge points from a trapezoidal / cuboid /diamond volume.
bool hasOverlap(const ActsTrk::GeometryContext &gctx, const std::vector< Amg::Vector3D > &chamberEdges, const Acts::Volume &volume) const
Checks whether the edge points from a trapezoid/cuboid/diamond form a volume overlapping with the giv...
MuonReadoutElement is an abstract class representing the geometry of a muon detector.
const Amg::Transform3D & localToGlobalTransform(const ActsTrk::GeometryContext &ctx) const
Returns the transformation from the local coordinate system of the readout element into the global AT...
Identifier identify() const override final
Return the ATLAS identifier.
unsigned nPhiStrips() const
Number of strips measuring the phi coordinate.
Amg::Vector3D leftStripEdge(const ActsTrk::GeometryContext &ctx, const Identifier &measId) const
Returns the global posiition of the strip edge at positive local Y.
int doubletZ() const
Returns the doublet Z field of the MuonReadoutElement identifier.
int doubletPhi() const
Returns the doublet Phi field of the MuonReadoutElement identifier.
Amg::Vector3D rightStripEdge(const ActsTrk::GeometryContext &ctx, const Identifier &measId) const
Returns the global position of the strip edge at negative local Y.
unsigned nEtaStrips() const
Number of strips measuring the eta coordinate.
int doubletPhiMax() const
Returns the maximum phi panel.
Amg::Vector3D stripPosition(const ActsTrk::GeometryContext &ctx, const Identifier &measId) const
Returns the position of the strip center.
unsigned nGasGaps() const
Returns the number of gasgaps described by this ReadOutElement (usally 2 or 3).
A spectrometer sector forms the envelope of all chambers that are placed in the same MS sector & laye...
const ChamberSet & chambers() const
Returns the associated chambers with this sector.
GeoModel::TransientConstSharedPtr< Chamber > ChamberPtr
void defineStripLayout(Amg::Vector2D &&posFirst, const double stripPitch, const double stripWidth, const int numStrips, const int numFirst=1)
Defines the layout of the strip detector by specifing the position of the first strip w....
CheckVector2D leftEdge(int stripNumb) const
Returns the left edge of the strip (Global numbering scheme).
void defineTrapezoid(double HalfShortY, double HalfLongY, double HalfHeight)
Defines the edges of the trapezoid.
bool insideTrapezoid(const Amg::Vector2D &extPos) const
Checks whether an external point is inside the trapezoidal area.
CheckVector2D rightEdge(int stripNumb) const
Returns the right edge of the strip (Global numbering scheme).
Amg::Vector3D channelPosition(const ActsTrk::GeometryContext &ctx, const Identifier &measId) const
Returns the center of the measurement channel eta measurement: wire gang center phi measurement: stri...
Identifier measurementId(const IdentifierHash &measHash) const override final
Back conversion of the measurement hash to a full Athena Identifier The behaviour is undefined if a l...
static IdentifierHash constructHash(unsigned measCh, unsigned gasGap, const bool isStrip)
Constructs the Hash out of the Identifier fields (channel, gasGap, isStrip).
unsigned numChannels(const IdentifierHash &measHash) const
Returns the number of readout channels.
unsigned nGasGaps() const
Returns the number of gasgaps described by this ReadOutElement (usally 2 or 3).
unsigned numChannels(const IdentifierHash &measHash) const
Returns the number of strips / wires / pads in a given gasGap.
IdentifierHash measurementHash(const Identifier &measId) const override final
Constructs the identifier hash from the full measurement Identifier.
Amg::Vector3D leftStripEdge(const ActsTrk::GeometryContext &ctx, const IdentifierHash &measHash) const
int multilayer() const
Returns the multilayer of the sTgcReadoutElement.
unsigned nChTypes() const
Number of Channel Types.
Amg::Vector3D rightStripEdge(const ActsTrk::GeometryContext &ctx, const IdentifierHash &measHash) const
unsigned numLayers() const
Returns the number of gas gap layers.
ReadoutChannelType
ReadoutChannelType to distinguish the available readout channels Pad - pad readout channel Strip - et...
Amg::Vector3D globalChannelPosition(const ActsTrk::GeometryContext &ctx, const IdentifierHash &measHash) const
Returns the global pad/strip/wireGroup position.
static IdentifierHash createHash(const unsigned gasGap, const unsigned channelType, const unsigned channel, const unsigned wireInGrp=0)
Create a measurement hash from the Identifier fields.
Identifier channelID(int stationName, int stationEta, int stationPhi, int doubletR, int doubletZ, int doubletPhi, int gasGap, int measuresPhi, int strip) const
Identifier channelID(int stationName, int stationEta, int stationPhi, int multilayer, int gasGap, int channelType, int channel) const
@ Mm
Maybe not needed in the migration.
@ Tgc
Resitive Plate Chambers.
@ sTgc
Micromegas (NSW).
@ Rpc
Monitored Drift Tubes.
@ Mdt
MuonSpectrometer.
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
bool isIdentity(const Amg::Transform3D &trans)
Checks whether the transformation is the Identity transformation.
Amg::Transform3D getRotateZ3D(double angle)
get a rotation transformation around Z-axis
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
The ReadoutGeomCnvAlg converts the Run4 Readout geometry build from the GeoModelXML into the legacy M...
double halfY(const Acts::VolumeBounds &bounds)
Returns the half-Y length for the parsed volume bounds (Trapezoid/ Cuboid).
SpectrometerSector::ChamberSet ChamberSet
bool isMuon(const ActsTrk::DetectorType type)
Returns whether the parsed type is muon.
double halfZ(const Acts::VolumeBounds &bounds)
Returns the half-Z length for the parsed volume bounds (Trapezoid/ Cuboid).
double halfXhighY(const Acts::VolumeBounds &bounds)
Returns the half-Y length @ posiive Y for the parsed volume bounds (Trapezoid/ Cuboid).
double halfXlowY(const Acts::VolumeBounds &bounds)
Returns the half-X length @ negative Y for the parsed volume bounds (Trapezoid/ Cuboid).
bool isBarrel(const ChIndex index)
Returns true if the chamber index points to a barrel chamber.
const std::string & chName(ChIndex index)
convert ChIndex into a string
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
const Identifier & identify(const UncalibratedMeasurement *meas)
Returns the associated identifier from the muon measurement.