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 }
59 return children;
60 }
61
62 std::vector<const Acts::Surface*> extractSurfaces(const std::vector<const MuonGMR4::MuonReadoutElement*>& reEles){
63 std::vector<const Acts::Surface*> surfaces{};
64 for (const auto* re : reEles) {
65 std::ranges::transform(re->getSurfaces(), std::back_inserter(surfaces),
66 [](const std::shared_ptr<Acts::Surface>& surface) { return surface.get() ; });
67 }
68 return surfaces;
69 }
70
71 std::vector<const Acts::Surface*> extractSurfaces(const Acts::TrackingVolume& volume) {
72 std::vector<const Acts::Surface*> surfaces{};
73 std::ranges::for_each(volume.surfaces(), [&surfaces](const Acts::Surface& surface){
74 if (surface.isSensitive()) {
75 surfaces.push_back(&surface);
76 }
77 });
78 for (const Acts::TrackingVolume& subVol : volume.volumes()) {
79 std::vector<const Acts::Surface*> childSurfaces = extractSurfaces(subVol);
80 surfaces.insert(surfaces.end(), childSurfaces.begin(), childSurfaces.end());
81
82 }
83 return surfaces;
84 }
85
86 Identifier identify(const Acts::Surface& surface) {
87 const auto* detEl = dynamic_cast<const ActsTrk::IDetectorElementBase*>(surface.surfacePlacement());
88 return detEl ? detEl->identify(): Identifier{};
89 }
90
91 bool insideInterval(const double testLow, const double testHigh,
92 const double envLow, const double envHigh) {
93 return envLow < testLow && testHigh < envHigh;
94 }
95
96 bool outsideInterval(const double testLow, const double testHigh,
97 const double envLow, const double envHigh) {
98 return testHigh < envLow || envHigh < testLow;
99 }
100
101 bool overlapsInterval(const double testLow, const double testHigh,
102 const double envLow, const double envHigh) {
103 return !insideInterval(testLow, testHigh, envLow, envHigh) &&
104 !outsideInterval(testLow, testHigh, envLow, envHigh);
105 }
106}
107
108namespace MuonGMR4 {
109
111 ATH_CHECK(m_idHelperSvc.retrieve());
112 ATH_CHECK(m_geoCtxKey.initialize());
114 ATH_CHECK(detStore()->retrieve(m_detMgr));
115 return StatusCode::SUCCESS;
116 }
117 template <class EnvelopeType>
118#if defined(FLATTEN) && defined(__GNUC__)
119// We compile this function with optimization, even in debug builds; otherwise,
120// the heavy use of Eigen makes it too slow. However, from here we may call
121// to out-of-line Eigen code that is linked from other DSOs; in that case,
122// it would not be optimized. Avoid this by forcing all Eigen code
123// to be inlined here if possible.
124[[gnu::flatten]]
125#endif
127 const EnvelopeType& chamb,
128 const Acts::Volume& boundVol,
129 const Amg::Vector3D& point,
130 const std::string& descr,
131 const Identifier& channelId) const {
132
133 // Explicitly inline Volume::inside here so that it gets
134 // flattened in debug builds. Gives a significant speedup.
135 //if (boundVol.inside(gctx.context(), point, tolerance)) {
136 const Amg::Vector3D locPos{boundVol.globalToLocalTransform(gctx.context()) * point};
137 if (boundVol.volumeBounds().inside(locPos,tolerance)) {
138 ATH_MSG_VERBOSE("In channel "<<m_idHelperSvc->toString(channelId)
139 <<", point "<<descr <<" is inside of the chamber "<<std::endl<<chamb<<std::endl
140 <<"Local position:" <<Amg::toString(boundVol.globalToLocalTransform(gctx.context()) * point));
141 return StatusCode::SUCCESS;
142 }
143
144 StripDesign planeTrapezoid{};
145 planeTrapezoid.defineTrapezoid(chamb.halfXShort(), chamb.halfXLong(), chamb.halfY());
146 planeTrapezoid.setLevel(MSG::VERBOSE);
148 static const Eigen::Rotation2D axisSwap{90. *Gaudi::Units::deg};
149 if (std::abs(locPos.z()) - chamb.halfZ() < -tolerance &&
150 planeTrapezoid.insideTrapezoid(axisSwap*locPos.block<2,1>(0,0))) {
151 return StatusCode::SUCCESS;
152 }
153 planeTrapezoid.defineStripLayout(locPos.y() * Amg::Vector2D::UnitX(), 1, 1, 1);
154 ATH_MSG_ERROR("In channel "<<m_idHelperSvc->toString(channelId) <<", the point "
155 << descr <<" "<<Amg::toString(point)<<" is not part of the chamber volume."
156 <<std::endl<<std::endl<<chamb<<std::endl<<"Local position "<<Amg::toString(locPos)
157 <<", "<<planeTrapezoid
158 <<", box left edge: "<<Amg::toString(planeTrapezoid.leftEdge(1).value_or(Amg::Vector2D::Zero()))
159 <<", box right edge "<<Amg::toString(planeTrapezoid.rightEdge(1).value_or(Amg::Vector2D::Zero())));
160 return StatusCode::FAILURE;
161 }
162
164 const Acts::TrackingVolume& volume,
165 const Amg::Vector3D& point,
166 const std::string& descr,
167 const Identifier& chamberId) const {
168 if (volume.inside(gctx.context(), point, tolerance)) {
169 return StatusCode::SUCCESS;
170 }
171 const std::vector<Amg::Vector3D> volumeCorners = cornerPoints(gctx, volume);
172 ATH_MSG_ERROR("In channel "<<m_idHelperSvc->toString(chamberId) <<", the point "
173 << descr <<" "<<Amg::toString(volume.globalToLocalTransform(gctx.context())* point)
174 <<" is not part of the chamber volume. The corners of the volume are:");
175 for(const auto& corner : volumeCorners) {
176 ATH_MSG_ERROR(" "<<Amg::toString(volume.globalToLocalTransform(gctx.context())*corner));
177 }
178 return StatusCode::FAILURE;
179 }
180
181 template <class EnvelopeType>
183 const EnvelopeType& envelope) const {
184 std::shared_ptr<Acts::Volume> boundVol = envelope.boundingVolume(gctx);
185 const Chamber::ReadoutSet reEles = envelope.readoutEles();
186 for(const MuonReadoutElement* readOut : reEles) {
187 if constexpr (std::is_same_v<EnvelopeType, SpectrometerSector>) {
188 if (readOut->msSector() != &envelope) {
189 ATH_MSG_ERROR("Mismatch in the sector association "<<m_idHelperSvc->toStringDetEl(readOut->identify())
190 <<std::endl<<(*readOut->msSector())<<std::endl<<envelope);
191 return StatusCode::FAILURE;
192 }
193 } else if constexpr (std::is_same_v<EnvelopeType, Chamber>) {
194 if (readOut->chamber() != &envelope) {
195 ATH_MSG_ERROR("Mismatch in the chamber association "<<m_idHelperSvc->toStringDetEl(readOut->identify())
196 <<std::endl<<(*readOut->chamber())<<std::endl<<envelope);
197 return StatusCode::FAILURE;
198 }
199 }
200 switch (readOut->detectorType()) {
202 const auto* detEle = static_cast<const TgcReadoutElement*>(readOut);
203 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
204 break;
206 const auto* detEle = static_cast<const MdtReadoutElement*>(readOut);
207 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
208 break;
210 const auto* detEle = static_cast<const RpcReadoutElement*>(readOut);
211 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
212 break;
214 const auto* detEle = static_cast<const MmReadoutElement*>(readOut);
215 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
216 break;
218 const auto* detEle = static_cast<const sTgcReadoutElement*>(readOut);
219 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
220 break;
221 } default: {
222 ATH_MSG_ERROR("Who came up with putting "<<ActsTrk::to_string(readOut->detectorType())
223 <<" into the MS");
224 return StatusCode::FAILURE;
225 }
226 }
227 }
228 ATH_MSG_DEBUG("All "<<reEles.size()<<" readout elements are embedded in "<<envelope);
229 return StatusCode::SUCCESS;
230 }
231
232 std::vector<Amg::Vector3D> MuonChamberToolTest::cornerPoints(const ActsTrk::GeometryContext& gctx, const Acts::Volume& volume) const {
233
234 const auto& bounds = volume.volumeBounds();
235 unsigned int edgeIdx{0};
236 //diamond volume bounds case - there are 12 edges
237 if(bounds.type() == Acts::VolumeBounds::BoundsType::eDiamond){
238 const auto& diamondBounds = static_cast<const Acts::DiamondVolumeBounds&>(bounds);
239 using BoundEnum = Acts::DiamondVolumeBounds::BoundValues;
240 std::vector<Amg::Vector3D> edges(12, Amg::Vector3D::Zero());
241 double xCord{0};
242 double yCord{0};
243 for(double signX : {-1.,1.}){
244 for(double signY : {-1., 0., 1.}){
245 for(double signZ : {-1.,1.}){
246 if(signY == 0){
247 xCord = diamondBounds.get(BoundEnum::eHalfLengthX2);
248 }else if(signY < 0){
249 xCord = diamondBounds.get(BoundEnum::eHalfLengthX1);
250 yCord = diamondBounds.get(BoundEnum::eLengthY1);
251 } else{
252 xCord = diamondBounds.get(BoundEnum::eHalfLengthX3);
253 yCord = diamondBounds.get(BoundEnum::eLengthY2);
254 }
255
256 const Amg::Vector3D edge{signX*xCord,
257 signY*yCord,
258 signZ*diamondBounds.get(BoundEnum::eHalfLengthZ)};
259 edges[edgeIdx] = volume.localToGlobalTransform(gctx.context())*edge;
260 edgeIdx++;
261 }
262 }
263 }
264
265 return edges;
266 }
267
268 //trapezoid or rectangular bounds case
269 std::vector<Amg::Vector3D> edges{};
270 ATH_MSG_VERBOSE("Fetch volume bounds "<<Amg::toString(volume.localToGlobalTransform(gctx.context())));
271 for (const double signX : {-1., 1.}) {
272 for (const double signY : { -1., 1.}) {
273 for (const double signZ: {-1., 1.}) {
274 const Amg::Vector3D edge{signX* (signY>0 ? MuonGMR4::halfXhighY(bounds) : MuonGMR4::halfXlowY(bounds)),
275 signY*MuonGMR4::halfY(bounds),
276 signZ*MuonGMR4::halfZ(bounds)};
277 edges.push_back(volume.localToGlobalTransform(gctx.context()) * edge);
278 ATH_MSG_VERBOSE("Local edge "<<Amg::toString(edge)<<", global edge: "<<Amg::toString(edges[edgeIdx]));
279 ++edgeIdx;
280 }
281 }
282 }
283 return edges;
284 }
285
286 std::array<Amg::Vector3D, 8> MuonChamberToolTest::cornerPoints(const ActsTrk::GeometryContext& gctx, const Acts::StrawSurface& surface) const {
287 std::array<Amg::Vector3D, 8> edges{make_array<Amg::Vector3D,8>(Amg::Vector3D::Zero())};
288 using BoundEnum = Acts::LineBounds::BoundValues;
289 const auto& bounds = static_cast<const Acts::LineBounds&>(surface.bounds());
290 unsigned int edgeIdx{0};
291
292 ATH_MSG_VERBOSE("Fetch volume bounds "<<Amg::toString(surface.localToGlobalTransform(gctx.context())));
293 for (const double signX : {-1., 1.}) {
294 for (const double signY : { -1., 1.}) {
295 for (const double signZ: {-1., 1.}) {
296 const Amg::Vector3D edge{signX*bounds.get(BoundEnum::eR),
297 signY*bounds.get(BoundEnum::eR),
298 signZ*bounds.get(BoundEnum::eHalfLengthZ)};
299 edges[edgeIdx] = surface.localToGlobalTransform(gctx.context()) * edge;
300 ++edgeIdx;
301 }
302 }
303 }
304 return edges;
305 }
306
307 std::array<Amg::Vector3D, 4> MuonChamberToolTest::cornerPoints(const ActsTrk::GeometryContext& gctx, const Acts::PlaneSurface& surface) const {
308 std::array<Amg::Vector3D, 4> edges{make_array<Amg::Vector3D,4>(Amg::Vector3D::Zero())};
309 if(surface.bounds().type() == Acts::SurfaceBounds::BoundsType::eRectangle) { //RPC surfaces are rectangles
310 const Acts::RectangleBounds& bounds = static_cast<const Acts::RectangleBounds&>(surface.bounds());
311 using BoundEnum = Acts::RectangleBounds::BoundValues;
312
313 unsigned int edgeIdx{0};
314 for(const double signX : {-1., 1.}) {
315 for (const double signY : { -1., 1.}) {
316 const Amg::Vector3D edge{signX < 0 ? bounds.get(BoundEnum::eMinX) : bounds.get(BoundEnum::eMaxX),
317 signY < 0 ? bounds.get(BoundEnum::eMinY) : bounds.get(BoundEnum::eMaxY), 0.};
318 edges[edgeIdx] = surface.localToGlobalTransform(gctx.context()) * edge;
319 ++edgeIdx;
320 }
321 }
322 return edges;
323 } else if(surface.bounds().type() == Acts::SurfaceBounds::BoundsType::eTrapezoid) {
324 using BoundEnum = Acts::TrapezoidBounds::BoundValues;
325 const auto& bounds = static_cast<const Acts::TrapezoidBounds&>(surface.bounds());
326 unsigned int edgeIdx{0};
327
328 ATH_MSG_VERBOSE("Fetch volume bounds "<<Amg::toString(surface.localToGlobalTransform(gctx.context())));
329 for (const double signX : {-1., 1.}) {
330 for (const double signY : { -1., 1.}) {
331 const Amg::Vector3D edge{Amg::getRotateZ3D(-1.*bounds.get(BoundEnum::eRotationAngle)) *
332 Amg::Vector3D(signX*bounds.get(signY < 0 ? BoundEnum::eHalfLengthXnegY : BoundEnum::eHalfLengthXposY),
333 signY*bounds.get(BoundEnum::eHalfLengthY), 0.)};
334
335 edges[edgeIdx] = surface.localToGlobalTransform(gctx.context()) * edge;
336 ++edgeIdx;
337 }
338 }
339
340 return edges;
341 } else {
342 ATH_MSG_ERROR("The surface bounds are neither a rectangle nor a trapezoid, this is not supported yet");
343 return edges;
344 }
345 }
346
347
348#if defined(FLATTEN) && defined(__GNUC__)
349// We compile this function with optimization, even in debug builds; otherwise,
350// the heavy use of Eigen makes it too slow. However, from here we may call
351// to out-of-line Eigen code that is linked from other DSOs; in that case,
352// it would not be optimized. Avoid this by forcing all Eigen code
353// to be inlined here if possible.
354[[gnu::flatten]]
355#endif
357 const std::vector<Amg::Vector3D>& chamberEdges,
358 const Acts::Volume& volume) const {
359
361 const Amg::Vector3D center{volume.center(gctx.context())};
362 double minDist = 1._km;
363 for (const Amg::Vector3D& edge : chamberEdges) {
364 minDist = std::min(minDist, (edge - center).mag());
365 }
368 if (std::ranges::none_of(volume.volumeBounds().values(),
369 [minDist](const double bound){
370 return minDist < 2.5*bound;
371 })) {
372 return false;
373 }
374 const double stepLength = 1. / m_overlapSamples;
375
376 const Acts::VolumeBounds& volBounds = volume.volumeBounds();
377 const Acts::Transform3& transform = volume.globalToLocalTransform(gctx.context());
378 for (unsigned edge1 = 1; edge1 < chamberEdges.size(); ++edge1) {
379 for (unsigned edge2 = 0; edge2 < edge1; ++edge2) {
380 for (unsigned step = 0 ; step <= m_overlapSamples; ++step) {
381 const double section = stepLength * step;
382 const Amg::Vector3D testPoint = section* chamberEdges[edge1] + (1. -section) *chamberEdges[edge2];
383 // Using acts::Volume::inside is horribly slow in dbg builds.
384 // Using the bounds method directly is much faster.
385 //if (volume.inside (gctx.context(), testPoint)) {
386 if (volBounds.inside (transform * testPoint)) {
387 return true;
388 }
389 }
390 }
391 }
392 return false;
393 }
395
396 std::vector<const MuonReadoutElement*> allRE = m_detMgr->getAllReadoutElements();
398 const ChamberSet chambers = m_detMgr->getAllChambers();
399 ATH_MSG_INFO("Fetched "<<chambers.size()<<" chambers.");
400 std::vector<const Chamber*> chamberVec{chambers.begin(), chambers.end()};
401
402 const auto missChamb = std::ranges::find_if(allRE, [&chamberVec](const MuonGMR4::MuonReadoutElement* re){
403 return std::ranges::find(chamberVec, re->chamber()) == chamberVec.end();
404 });
405 if (missChamb != allRE.end()) {
406 ATH_MSG_ERROR("The chamber "<<(*(*missChamb)->chamber())<<" is not in the chamber set");
407 return StatusCode::FAILURE;
408 }
409
410 // Retrieve bounds here rather than inside the loop below,
411 // so we only need to do it O(N) rather than O(N^2) times.
412 std::vector<std::shared_ptr<Acts::Volume> > chamberBoundsVec;
413 chamberBoundsVec.reserve (chamberVec.size());
414 for (const Chamber* ch : chamberVec)
415 chamberBoundsVec.push_back (ch->boundingVolume(gctx));
416
417 std::set<const Chamber*> overlapChambers{};
418 std::stringstream overlapstream{};
419 for (std::size_t chIdx = 0; chIdx< chamberVec.size(); ++chIdx) {
420 const Chamber& chamber{*chamberVec[chIdx]};
421 const Acts::Volume& chamberBounds = *chamberBoundsVec[chIdx];
422 if (m_dumpObjs) {
423 saveEnvelope(gctx, std::format("Chamber_{:}{:}{:}{:}{:}",
424 ActsTrk::to_string(chamber.detectorType()),
425 chName(chamber.chamberIndex()),
426 Acts::abs(chamber.stationEta()),
427 chamber.stationEta() > 0 ? 'A' : 'C',
428 chamber.stationPhi()),
429 chamberBounds, extractSurfaces(chamber.readoutEles()));
430 }
431 ATH_CHECK(allReadoutInEnvelope(gctx, chamber));
432 const std::vector<Amg::Vector3D> chambCorners = cornerPoints(gctx, chamberBounds);
434 std::vector<const Chamber*> overlaps{};
435 for (std::size_t chIdx1 = 0; chIdx1<chamberVec.size(); ++chIdx1) {
436 if (chIdx == chIdx1) {
437 continue;
438 }
439 const Chamber* overlapTest{chamberVec[chIdx1]};
440 if (hasOverlap(gctx, chambCorners, *chamberBoundsVec[chIdx1])) {
441 overlaps.push_back(overlapTest);
442 }
443 }
444 if (overlaps.empty()) {
445 continue;
446 }
447 overlapstream<<"The chamber "<<chamber<<" overlaps with "<<std::endl;
448 for (const Chamber* itOverlaps : overlaps) {
449 overlapstream<<" *** "<<(*itOverlaps)<<std::endl;
450 }
451 overlapstream<<std::endl<<std::endl;
452 overlapChambers.insert(overlaps.begin(), overlaps.end());
453 overlapChambers.insert(chamberVec[chIdx]);
454 }
455 if (!overlapChambers.empty()) {
456 Acts::ObjVisualization3D visualHelper{};
457 for (const Chamber* hasOverlap: overlapChambers) {
458 Acts::GeometryView3D::drawVolume(visualHelper, *hasOverlap->boundingVolume(gctx), gctx.context());
459 visualHelper.write(m_overlapChambObj.value());
460 }
461 if (m_ignoreOverlapCh) {
462 ATH_MSG_WARNING(overlapstream.str());
463 } else {
464 ATH_MSG_ERROR(overlapstream.str());
465 }
466 }
467 ATH_MSG_INFO("Chamber test completed. Found "<<overlapChambers.size()<<" overlapping chambers");
468 return overlapChambers.empty() || m_ignoreOverlapCh ? StatusCode::SUCCESS : StatusCode::FAILURE;
469 }
470
472
473 std::vector<const MuonReadoutElement*> allREs = m_detMgr->getAllReadoutElements();
474 for (const MuonReadoutElement* re : allREs) {
475 if (!re->msSector()) {
476 ATH_MSG_ERROR("The readout element "<<m_idHelperSvc->toStringDetEl(re->identify())<<" does not have any sector associated ");
477 return StatusCode::FAILURE;
478 }
479 const SpectrometerSector* sectorFromDet = m_detMgr->getSectorEnvelope(re->chamberIndex(),
480 m_idHelperSvc->sector(re->identify()),
481 re->stationEta());
482 if (sectorFromDet != re->msSector()) {
483 ATH_MSG_ERROR("The sector attached to "<<m_idHelperSvc->toStringDetEl(re->identify())
484 <<", chIdx: "<<chName(re->chamberIndex())<<", sector: "<<m_idHelperSvc->sector(re->identify())
485 <<" is not the one attached to the readout geometry \n"<<(*re->msSector())<<"\n"<<(*sectorFromDet));
486 return StatusCode::FAILURE;
487 }
488 }
489 using SectorSet = MuonDetectorManager::MuonSectorSet;
490 const SectorSet sectors = m_detMgr->getAllSectors();
491 ATH_MSG_INFO(__func__<<"() "<<__LINE__<<" - Fetched "<<sectors.size()<<" sectors. ");
492 for (const SpectrometerSector* sector : sectors) {
493 if (m_dumpObjs) {
494 const auto subVols = chamberVolumes(gctx, *sector);
495 saveEnvelope(gctx, std::format("Sector_{:}{:}{:}",
496 chName(sector->chamberIndex()),
497 sector->side() >0? 'A' :'C',
498 sector->stationPhi() ),
499 *sector->boundingVolume(gctx),
500 extractSurfaces(sector->readoutEles()),
501 Acts::unpackSmartPointers(subVols));
502 }
503 ATH_CHECK(allReadoutInEnvelope(gctx, *sector));
504 const std::shared_ptr<Acts::Volume> secVolume = sector->boundingVolume(gctx);
505 for (const SpectrometerSector::ChamberPtr& chamber : sector->chambers()){
506 const std::vector<Amg::Vector3D> edges = cornerPoints(gctx, *chamber->boundingVolume(gctx));
507 unsigned int edgeCount{0};
508 for (const Amg::Vector3D& edge : edges) {
509 ATH_CHECK(pointInside(gctx, *sector, *secVolume, edge, std::format("Edge {:}", ++edgeCount),
510 chamber->readoutEles().front()->identify()));
511 }
512 }
513 }
514 ATH_MSG_INFO(__func__<<"() "<<__LINE__<<" - Sector envelope test completed.");
515 return StatusCode::SUCCESS;
516 }
518 const Acts::TrackingVolume& volume) const {
519 if (!volume.isAlignable()) {
520 return StatusCode::SUCCESS;
521 }
522 const Acts::GeometryContext geoCtx = gctx.context();
523 std::vector<std::shared_ptr<const Acts::Surface>> portals{};
524 for (const Acts::Portal& portal : volume.portals()) {
525 if (portal.surface().geometryId().withBoundary(0) != volume.geometryId()) {
526 continue;
527 }
528 portals.push_back(portal.surface().getSharedPtr());
529 }
530 const auto unAlignedPortals = volume.volumeBounds().orientedSurfaces(volume.localToGlobalTransform(geoCtx));
531
532 if (unAlignedPortals.size() != portals.size()) {
533 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The size of the aligned and unaligned portals don't match for volume "
534 <<volume.volumeName()<<". Aligned: "<<portals.size()<<", unaligned: "<<unAlignedPortals.size());
535 return StatusCode::FAILURE;
536 }
537 StatusCode retCode = StatusCode::SUCCESS;
538 for (std::size_t p =0 ; p < portals.size(); ++p){
540 if (portals[p]->bounds() != unAlignedPortals[p].surface->bounds()) {
541 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The bounds of the "<<p
542 <<"-th portal differ:\n -- aligned: "<<portals[p]->bounds()
543 <<"\n -- unaligned: "<<unAlignedPortals[p].surface->bounds());
544 retCode = StatusCode::FAILURE;
545 }
546 const Amg::Transform3D& uTrf{unAlignedPortals[p].surface->localToGlobalTransform(geoCtx)};
547 const Amg::Transform3D& aTrf{portals[p]->localToGlobalTransform(geoCtx)};
548
549 if (!Amg::isIdentity(uTrf * aTrf.inverse())) {
550 ATH_MSG_ERROR(__func__<<"() "<<__LINE__
551 <<" - The unaligned and aligned portals don't end up at the same point \n"
552 <<" -- aligned: "<<Amg::toString(aTrf)<<"\n"<<" -- unaligned: "<<Amg::toString(uTrf));
553 retCode = StatusCode::FAILURE;
554 }
555 }
556 return retCode;
557 }
558
559
561 const Acts::TrackingGeometry& trackingGeometry) const {
562
563 //visit the volumes and check the overlaps with the other volumes in the tracking geometry
564 // also check overlaps between volumes and surfaces (e.g surfaces where the passive material is mapped)
565 std::vector<const Acts::TrackingVolume*> volumeVec{};
566 std::vector<const Acts::Surface*> surfacesVec{};
567
568 std::unordered_set<const Acts::TrackingVolume*> overlapVolumes{};
569 std::unordered_set<const Acts::Surface*> overlapSurfaces{};
570
571
572 //keep onyl the chamber volumes - not the cylinders
573 trackingGeometry.visitVolumes([&](const Acts::TrackingVolume* vol) {
574 //for the cylinder type volumes , fetch the inner surfaces only (e.g passive material surfaces)
575 if(vol->volumeBounds().type() == Acts::VolumeBounds::BoundsType::eCylinder){
576 std::ranges::for_each(vol->surfaces(), [&](const auto& surf){
577 surfacesVec.push_back(&surf);
578 });
579 return;
580 }
581 const auto* placement = dynamic_cast<const ActsTrk::VolumePlacement*>(vol->volumePlacement());
582 // Not a senitive muon volume
583 if (!placement || !MuonGMR4::isMuon(placement->detectorType())) {
584 ATH_MSG_DEBUG("checkTrackingGeometry() "<<__LINE__<<" - Skip volume "
585 <<vol->volumeName()<<".");
586 return;
587 }
588 volumeVec.push_back(vol);
589 });
590
591 ATH_MSG_INFO(__func__<<"() "<<__LINE__<<" - Fetched "
592 << surfacesVec.size()<< " surfaces");
593 StatusCode retCode = StatusCode::SUCCESS;
594 for(std::size_t vIdx = 0; vIdx < volumeVec.size(); ++vIdx) {
595 const Acts::TrackingVolume* testVol{volumeVec.at(vIdx)};
596 ATH_CHECK(checkPortals(gctx, *testVol));
597
598 std::vector<const Acts::TrackingVolume*> overlaps{};
599 const std::vector<Amg::Vector3D> edges = cornerPoints(gctx, *testVol);
600
601 for(const auto& surface : testVol->surfaces()) {
602 //only plane or straw surfaces expected
603 std::vector<Amg::Vector3D> surfEdges = {};
604 if(surface.type() == Acts::Surface::SurfaceType::Straw){
605 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<" Checking "<<surface.type()<<" surface "<<identify(surface)
606 <<" / "<<surface.geometryId() <<" in volume "<<testVol->volumeName());
607
608 auto edges = cornerPoints(gctx, dynamic_cast<const Acts::StrawSurface&>(surface));
609 surfEdges.insert(surfEdges.end() , edges.begin(), edges.end());
610 } else if(surface.type() == Acts::Surface::SurfaceType::Plane){
611 ATH_MSG_VERBOSE(__func__<<"() - "<<__LINE__<<" Checking "<<surface.type()<<" surface "<<identify(surface)
612 <<" / "<<surface.geometryId() <<" in volume "<<testVol->volumeName());
613
614 auto edges = cornerPoints(gctx, dynamic_cast<const Acts::PlaneSurface&>(surface));
615 surfEdges.insert(surfEdges.end() , edges.begin(), edges.end());
616 } else {
617 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The "<<surface.type()<<"-surface "
618 << m_idHelperSvc->toString(identify(surface))<<" / "
619 <<surface.geometryId() <<" is neither a straw nor a plane surface");
620 return StatusCode::FAILURE;
621 }
622
623 for(const auto& edge : surfEdges) {
624 if(!testVol->inside(gctx.context(), edge, 0.01)) {
625 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The "<<surface.type()<<"-surface "
626 << m_idHelperSvc->toString(identify(surface))<<" / "
627 <<surface.geometryId() <<" @vertex point "
628 <<Amg::toString(testVol->globalToLocalTransform(gctx.context()) *edge)<<", local: "
629 <<Amg::toString(surface.localToGlobalTransform(gctx.context()).inverse() * edge)
630 <<" is outside the parent volume: " << testVol->volumeName()
631 <<", "<<Amg::toString(testVol->localToGlobalTransform(gctx.context()))
632 <<", "<<testVol->volumeBounds());
633 overlapSurfaces.insert(&surface);
634 overlapVolumes.insert(testVol);
635 if (!m_ignoreOutsideSurf) {
636 retCode = StatusCode::FAILURE;
637 }
638 }
639 }
640 }
641
642 //check if the child volume is entirely enclosed by the mother volume
643 for (const Acts::TrackingVolume& child : testVol->volumes()) {
644 for(const auto& edge : cornerPoints(gctx, child)){
645 if(!testVol->inside(gctx.context(), edge, 0.01)){
646 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The children volume's "
647 << child.volumeName() <<" vertex point " <<Amg::toString(edge)
648 <<" is outside the parent volume" << testVol->volumeName());
649 return StatusCode::FAILURE;
650 }
651 }
652 }
654 if (!testVol->motherVolume()->isAlignable() && m_dumpObjs) {
655 std::vector<const Acts::Surface*> surfaces = extractSurfaces(*testVol);
656 const Identifier volId = identify(*surfaces.front());
657 const int eta = m_idHelperSvc->stationEta(volId);
658 saveEnvelope(gctx, std::format("TrackingVolume_{:}{:}{:}{:}_{:}",
659 chName(m_idHelperSvc->chamberIndex(volId)),
660 Acts::abs(eta), eta > 0 ? 'A' : 'C',
661 m_idHelperSvc->stationPhi(volId), vIdx),
662 *testVol, surfaces , chamberVolumes(*testVol));
663
664 }
665 // Check that there is not overlap with other volumes
666 for (std::size_t vIdx1 = 0 ; vIdx1 < vIdx; ++vIdx1) {
667 const Acts::TrackingVolume* overlapTest{volumeVec.at(vIdx1)};
668 if (overlapTest->motherVolume() == testVol ||
669 testVol->motherVolume() == overlapTest){
670 continue;
671 }
672 if (hasOverlap(gctx, edges, *overlapTest)) {
673 overlaps.push_back(overlapTest);
674 std::ranges::copy(extractSurfaces(*testVol),
675 std::inserter(overlapSurfaces, overlapSurfaces.begin()));
676 std::ranges::copy(extractSurfaces(*overlapTest),
677 std::inserter(overlapSurfaces, overlapSurfaces.begin()));
678 }
679 }
680 /*check if the tracking volume overlaps with surfaces of the tracking geometry
681 (e.g cylinders of the barrel where material is mapped) */
682 const Identifier volId = identify(*extractSurfaces(*testVol).front());
683 double volHalfR{0.}, volHalfZ{0.};
684 const double halfX = MuonGMR4::halfXhighY(testVol->volumeBounds());
685 const bool isBarrel = Muon::MuonStationIndex::isBarrel(m_idHelperSvc->chamberIndex(volId));
686 if (isBarrel){
687 volHalfR = MuonGMR4::halfZ(testVol->volumeBounds());
688 volHalfZ = MuonGMR4::halfY(testVol->volumeBounds());
689 } else {
690 volHalfZ = MuonGMR4::halfZ(testVol->volumeBounds());
691 volHalfR = MuonGMR4::halfY(testVol->volumeBounds());
692 }
693 const Amg::Vector3D center{testVol->center(gctx.context())};
694 const double rMin = center.perp() - volHalfR;
695 // Calculate the global r from the local half X which is always along phi
696 // and the halfR which is along Y (Z) for endcap (barrel) chambers.
697 const double rMax = (testVol->localToGlobalTransform(gctx.context()) *(
698 halfX * Amg::Vector3D::UnitX() +
699 volHalfR * Amg::Vector3D::Unit(1 + isBarrel))).perp();
700
701 const double zMin = center.z() - volHalfZ;
702 const double zMax = center.z() + volHalfZ;
703
704 for(const Acts::Surface* surf : surfacesVec) {
705 double surfRMin{0.}, surfRMax{0.}, halfZ{0.};
706
707 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Check "<<surf->type()
708 <<" surface "<<surf->name()<< " , "<<surf->geometryId());
709 const Amg::Vector3D center = surf->center(gctx.context());
710 if(surf->type() == Acts::Surface::SurfaceType::Cylinder) {
711 using BoundEnum = Acts::CylinderBounds::BoundValues;
712 const auto& bounds = static_cast<const Acts::CylinderBounds&>(surf->bounds());
713 surfRMax = bounds.get(BoundEnum::eR) + 2._mm;
714 surfRMin = bounds.get(BoundEnum::eR) - 2._mm;
715
716 halfZ = bounds.get(BoundEnum:: eHalfLengthZ);
717 } else if(surf->type() == Acts::Surface::SurfaceType::Disc){
718 using BoundEnum = Acts::RadialBounds::BoundValues;
719 const auto& bounds = static_cast<const Acts::RadialBounds&>(surf->bounds());
720 surfRMax = bounds.get(BoundEnum::eMaxR);
721 surfRMin = bounds.get(BoundEnum::eMinR);
722 } else {
723 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The surface "<< surf->geometryId()
724 <<", "<< surf->name() <<" is not a cylinder surface or disc");
725 return StatusCode::FAILURE;
726 }
727
728 //check for overlap in R in case the surface and the volume have overlapping Z position
729
730 const double surfZMin = center.z() - halfZ;
731 const double surfZMax = center.z() + halfZ;
732
733 const bool rOverlap = overlapsInterval(rMin, rMax, surfRMin, surfRMax);
734 const bool zOverlap = overlapsInterval(zMin, zMax, surfZMax, surfZMax);
735
736 const bool rOutside = outsideInterval(rMin, rMax, surfRMin, surfRMax);
737 const bool zOutside = outsideInterval(zMin, zMax, surfZMax, surfZMax);
738 if ( (!rOverlap && !zOverlap) || (rOverlap && zOutside) ||
739 (zOverlap && rOutside)) {
740 continue;
741 }
742
743 ATH_MSG_ERROR(__func__<<"() "<<__LINE__<<" - The volume "
744 << testVol->volumeName() << " overlaps with the surface "
745 << surf->name() << " with geo id" << surf->geometryId()
746 <<" -- volume radius: ["<<rMin<<";"<<rMax<<"] z: ["<<zMin<<";"<<zMax<<"]"
747 <<" -- surface radius: ["<<surfRMin<<";"<<surfRMax<<"] z: ["<<surfZMin<<";"<<surfZMax<<"]");
748 if (m_ignoreOutsideSurf) {
749 retCode = StatusCode::FAILURE;
750 }
751 overlapSurfaces.insert(surf);
752 overlapVolumes.insert(testVol);
753 }
754
755 if(overlaps.empty()) {
756 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - No overlaps detected for the volume "<<testVol->volumeName());
757 continue;
758 }
759
760 overlapVolumes.insert(overlaps.begin(), overlaps.end());
761 overlapVolumes.insert(testVol);
762
763 std::stringstream overlapStream{};
764 overlapStream<<__func__<<"() "<<__LINE__<<" - The volume "
765 <<testVol->volumeName() << " overlaps with: "<<std::endl;
766
767 for(const Acts::TrackingVolume* overlap: overlaps){
768 overlapStream<<" --- Volume: " << overlap->volumeName()<<", "<<overlap->volumeBounds()
769 <<", "<<Amg::toString(overlap->localToGlobalTransform(gctx.context()))<<std::endl;;
770 }
771 ATH_MSG_ALWAYS(overlapStream.str());
772 }
773
774 if (overlapVolumes.size() || overlapSurfaces.size()) {
775 const Acts::Volume* refVolume = (*overlapVolumes.begin());
776 std::vector<const Acts::Volume*> childVols{};
777 childVols.insert(childVols.begin(),std::next(overlapVolumes.begin()), overlapVolumes.end());
778 std::vector<const Acts::Surface*> childSurfs{overlapSurfaces.begin(), overlapSurfaces.end()};
779 saveEnvelope(gctx, "TrackingGeometryOverlaps", *refVolume,
780 childSurfs, childVols);
781 }
782
783
784 if(overlapVolumes.empty()) {
785 ATH_MSG_ALWAYS("No overlaps detected in the tracking geometry!!");
786 } else if (!m_ignoreOverlapCh) {
787 retCode = StatusCode::FAILURE;
788 }
789 return retCode;
790 }
791
793 const std::string& envName,
794 const Acts::Volume& envelopeVol,
795 const std::vector<const Acts::Surface*>& assocSurfaces,
796 const std::vector<const Acts::Volume*>& subVols) const {
797 Acts::ObjVisualization3D visualHelper{};
798 std::ranges::for_each(assocSurfaces, [&visualHelper, &gctx](const Acts::Surface* surface) {
799 Acts::GeometryView3D::drawSurface(visualHelper, *surface, gctx.context());
800
801 });
802 std::ranges::for_each(subVols, [&visualHelper, &gctx](const Acts::Volume* subVol) {
803 Acts::GeometryView3D::drawVolume(visualHelper,*subVol, gctx.context(), Amg::Transform3D::Identity(),
804 Acts::s_viewPassive);
805 });
806 Acts::GeometryView3D::drawVolume(visualHelper, envelopeVol, gctx.context());
807 ATH_MSG_DEBUG("Save new envelope 'MsTrackTest_"<<envName<<".obj'");
808 visualHelper.write(std::format("MsTrackTest_{:}.obj", envName));
809 }
810
811 StatusCode MuonChamberToolTest::execute(const EventContext& ctx) const {
812 const ActsTrk::GeometryContext* gctx{nullptr};
813 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
815 ATH_CHECK(checkChambers(*gctx));
817 ATH_CHECK(checkTrackingGeometry(*gctx, *m_trackingGeometrySvc->trackingGeometry()));
818
819 return StatusCode::SUCCESS;
820 }
821 template <class EnvelopeType>
823 const MdtReadoutElement& mdtMl,
824 const EnvelopeType& chamber,
825 const Acts::Volume& detVol) const {
826 ATH_MSG_VERBOSE("Test whether "<<m_idHelperSvc->toStringDetEl(mdtMl.identify())<<std::endl<<mdtMl.getParameters());
827
828 for (unsigned int layer = 1; layer <= mdtMl.numLayers(); ++layer) {
829 for (unsigned int tube = 1; tube <= mdtMl.numTubesInLay(); ++tube) {
830 const IdentifierHash idHash = mdtMl.measurementHash(layer, tube);
831 if (!mdtMl.isValid(idHash)){
832 continue;
833 }
834 const Amg::Transform3D& locToGlob{mdtMl.localToGlobalTransform(gctx, idHash)};
835 const Identifier measId{mdtMl.measurementId(idHash)};
836
837 ATH_CHECK(pointInside(gctx, chamber, detVol, mdtMl.globalTubePos(gctx, idHash), "tube center", measId));
838
839 ATH_CHECK(pointInside(gctx, chamber, detVol, mdtMl.readOutPos(gctx, idHash), "tube readout", measId));
840 ATH_CHECK(pointInside(gctx, chamber, detVol, mdtMl.highVoltPos(gctx, idHash), "tube HV", measId));
841
842 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(-mdtMl.innerTubeRadius() * Amg::Vector3D::UnitX()),
843 "bottom of the tube box", measId));
844 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(mdtMl.innerTubeRadius() * Amg::Vector3D::UnitX()),
845 "sealing of the tube box", measId));
846
847 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(-mdtMl.innerTubeRadius() * Amg::Vector3D::UnitY()),
848 "wall to the previous tube", measId));
849 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(-mdtMl.innerTubeRadius() * Amg::Vector3D::UnitY()),
850 "wall to the next tube", measId));
851 }
852 }
853 return StatusCode::SUCCESS;
854 }
855 template<class EnvelopeType>
857 const RpcReadoutElement& rpc,
858 const EnvelopeType& chamber,
859 const Acts::Volume& detVol) const {
860
861 ATH_MSG_VERBOSE("Test whether "<<m_idHelperSvc->toStringDetEl(rpc.identify())<<std::endl<<rpc.getParameters());
862
863 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
864 for (unsigned int gasGap = 1 ; gasGap <= rpc.nGasGaps(); ++gasGap) {
865 for (int doubletPhi = rpc.doubletPhi(); doubletPhi <= rpc.doubletPhiMax(); ++doubletPhi){
866 for (bool measPhi : {false, true}) {
867 const int nStrips = measPhi ? rpc.nPhiStrips() : rpc.nEtaStrips();
868 for (int strip = 1; strip <= nStrips; ++strip) {
869 const Identifier stripId = idHelper.channelID(rpc.identify(),rpc.doubletZ(),
870 doubletPhi, gasGap, measPhi, strip);
871 ATH_CHECK(pointInside(gctx, chamber, detVol, rpc.stripPosition(gctx, stripId), "center", stripId));
872 ATH_CHECK(pointInside(gctx, chamber, detVol, rpc.leftStripEdge(gctx, stripId), "right edge", stripId));
873 ATH_CHECK(pointInside(gctx, chamber, detVol, rpc.rightStripEdge(gctx, stripId), "left edge", stripId));
874 }
875 }
876 }
877 }
878 return StatusCode::SUCCESS;
879 }
880 template <class EnevelopeType>
882 const TgcReadoutElement& tgc,
883 const EnevelopeType& chamber,
884 const Acts::Volume& detVol) const {
885 for (unsigned int gasGap = 1; gasGap <= tgc.nGasGaps(); ++gasGap){
886 for (bool isStrip : {false}) {
887 const IdentifierHash layHash = tgc.constructHash(0, gasGap, isStrip);
888 const unsigned int nChannel = tgc.numChannels(layHash);
889 for (unsigned int channel = 1; channel <= nChannel ; ++channel) {
890 const IdentifierHash measHash = tgc.constructHash(channel, gasGap, isStrip);
891 ATH_CHECK(pointInside(gctx, chamber, detVol, tgc.channelPosition(gctx, measHash),
892 "center", tgc.measurementId(measHash)));
893 }
894 }
895 }
896 return StatusCode::SUCCESS;
897 }
898 template <class EnevelopeType>
900 const MmReadoutElement& mm,
901 const EnevelopeType& chamber,
902 const Acts::Volume& detVol) const {
903
904 const MmIdHelper& idHelper{m_idHelperSvc->mmIdHelper()};
905 for(unsigned int gasGap = 1; gasGap <= mm.nGasGaps(); ++gasGap){
906 IdentifierHash gasGapHash = MmReadoutElement::createHash(gasGap,0);
907 unsigned int firstStrip = mm.firstStrip(gasGapHash);
908 for(unsigned int strip = firstStrip; strip <= mm.numStrips(gasGapHash); ++strip){
909 const Identifier stripId = idHelper.channelID(mm.identify(), mm.multilayer(), gasGap, strip);
910 ATH_CHECK(pointInside(gctx, chamber, detVol, mm.stripPosition(gctx, stripId), "center", stripId));
911 ATH_CHECK(pointInside(gctx, chamber, detVol, mm.leftStripEdge(gctx, mm.measurementHash(stripId)), "left edge", stripId));
912 ATH_CHECK(pointInside(gctx, chamber, detVol, mm.rightStripEdge(gctx, mm.measurementHash(stripId)), "right edge", stripId));
913 }
914 }
915
916 return StatusCode::SUCCESS;
917 }
918 template <class EnvelopeType>
920 const sTgcReadoutElement& stgc,
921 const EnvelopeType& chamber,
922 const Acts::Volume& detVol) const{
923
924 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
925 for(unsigned int gasGap = 1; gasGap <= stgc.numLayers(); ++gasGap){
926
927 for(unsigned int nch = 1; nch <= stgc.nChTypes(); ++nch){
928 IdentifierHash gasGapHash = sTgcReadoutElement::createHash(gasGap, nch, 0, 0);
929 const unsigned int nStrips = stgc.numChannels(gasGapHash);
931
932 for(unsigned int strip = 1; strip <= nStrips; ++strip){
933 const Identifier stripId = idHelper.channelID(stgc.identify(), stgc.multilayer(), gasGap, nch, strip);
934 const IdentifierHash stripHash = stgc.measurementHash(stripId);
935 ATH_CHECK(pointInside(gctx, chamber, detVol, stgc.globalChannelPosition(gctx, stripHash), "channel position", stripId));
936
938 ATH_CHECK(pointInside(gctx, chamber, detVol, stgc.rightStripEdge(gctx, stripHash), "channel position", stripId));
939 ATH_CHECK(pointInside(gctx, chamber, detVol, stgc.leftStripEdge(gctx, stripHash), "channel position", stripId));
940 }
941 }
942 }
943 }
944 return StatusCode::SUCCESS;
945
946 }
947}
948
const boost::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.
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.
SG::ReadHandleKey< ActsTrk::GeometryContext > m_geoCtxKey
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
std::string to_string(const DetectorType &type)
@ 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.