ATLAS Offline Software
Loading...
Searching...
No Matches
MuonChamberToolTest.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 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
26#include "Acts/Visualization/ObjVisualization3D.hpp"
27#include "Acts/Visualization/GeometryView3D.hpp"
28#include "Acts/Definitions/Units.hpp"
29
31
32#include <format>
33
34using namespace Acts::UnitLiterals;
35using namespace Muon::MuonStationIndex;
36
37namespace{
38 constexpr double tolerance = 10. *Gaudi::Units::micrometer;
39
40 std::vector<std::shared_ptr<Acts::Volume>> chamberVolumes(const ActsTrk::GeometryContext& gctx,
41 const MuonGMR4::SpectrometerSector& sector) {
42 std::vector<std::shared_ptr<Acts::Volume>> vols{};
43 std::ranges::transform(sector.chambers(),std::back_inserter(vols),
44 [&gctx](const auto& ch){ return ch->boundingVolume(gctx); });
45 return vols;
46 }
47}
48
49namespace MuonGMR4 {
50
52 ATH_CHECK(m_idHelperSvc.retrieve());
53 ATH_CHECK(m_geoCtxKey.initialize());
55 ATH_CHECK(detStore()->retrieve(m_detMgr));
56 return StatusCode::SUCCESS;
57 }
58 template <class EnvelopeType>
59#if defined(FLATTEN) && defined(__GNUC__)
60// We compile this function with optimization, even in debug builds; otherwise,
61// the heavy use of Eigen makes it too slow. However, from here we may call
62// to out-of-line Eigen code that is linked from other DSOs; in that case,
63// it would not be optimized. Avoid this by forcing all Eigen code
64// to be inlined here if possible.
65[[gnu::flatten]]
66#endif
68 const EnvelopeType& chamb,
69 const Acts::Volume& boundVol,
70 const Amg::Vector3D& point,
71 const std::string& descr,
72 const Identifier& channelId) const {
73
74 if (boundVol.inside(gctx.context(), point, tolerance)) {
75 ATH_MSG_VERBOSE("In channel "<<m_idHelperSvc->toString(channelId)
76 <<", point "<<descr <<" is inside of the chamber "<<std::endl<<chamb<<std::endl
77 <<"Local position:" <<Amg::toString(boundVol.globalToLocalTransform(gctx.context()) * point));
78 return StatusCode::SUCCESS;
79 }
80 const Amg::Vector3D locPos{boundVol.globalToLocalTransform(gctx.context()) * point};
81
82 StripDesign planeTrapezoid{};
83 planeTrapezoid.defineTrapezoid(chamb.halfXShort(), chamb.halfXLong(), chamb.halfY());
84 planeTrapezoid.setLevel(MSG::VERBOSE);
86 static const Eigen::Rotation2D axisSwap{90. *Gaudi::Units::deg};
87 if (std::abs(locPos.z()) - chamb.halfZ() < -tolerance &&
88 planeTrapezoid.insideTrapezoid(axisSwap*locPos.block<2,1>(0,0))) {
89 return StatusCode::SUCCESS;
90 }
91 planeTrapezoid.defineStripLayout(locPos.y() * Amg::Vector2D::UnitX(), 1, 1, 1);
92 ATH_MSG_FATAL("In channel "<<m_idHelperSvc->toString(channelId) <<", the point "
93 << descr <<" "<<Amg::toString(point)<<" is not part of the chamber volume."
94 <<std::endl<<std::endl<<chamb<<std::endl<<"Local position "<<Amg::toString(locPos)
95 <<", "<<planeTrapezoid
96 <<", box left edge: "<<Amg::toString(planeTrapezoid.leftEdge(1).value_or(Amg::Vector2D::Zero()))
97 <<", box right edge "<<Amg::toString(planeTrapezoid.rightEdge(1).value_or(Amg::Vector2D::Zero())));
98 return StatusCode::FAILURE;
99 }
100
102 const Acts::TrackingVolume& volume,
103 const Amg::Vector3D& point,
104 const std::string& descr,
105 const Identifier& chamberId) const {
106 if (volume.inside(gctx.context(), point, tolerance)) {
107 return StatusCode::SUCCESS;
108 }
109 const std::vector<Amg::Vector3D> volumeCorners = cornerPoints(gctx, volume);
110 ATH_MSG_FATAL("In channel "<<m_idHelperSvc->toString(chamberId) <<", the point "
111 << descr <<" "<<Amg::toString(volume.globalToLocalTransform(gctx.context())* point)
112 <<" is not part of the chamber volume. The corners of the volume are:");
113 for(const auto& corner : volumeCorners) {
114 ATH_MSG_FATAL(" "<<Amg::toString(volume.globalToLocalTransform(gctx.context())*corner));
115 }
116 return StatusCode::FAILURE;
117 }
118
119 template <class EnvelopeType>
121 const EnvelopeType& envelope) const {
122 std::shared_ptr<Acts::Volume> boundVol = envelope.boundingVolume(gctx);
123 const Chamber::ReadoutSet reEles = envelope.readoutEles();
124 for(const MuonReadoutElement* readOut : reEles) {
125 if constexpr (std::is_same_v<EnvelopeType, SpectrometerSector>) {
126 if (readOut->msSector() != &envelope) {
127 ATH_MSG_FATAL("Mismatch in the sector association "<<m_idHelperSvc->toStringDetEl(readOut->identify())
128 <<std::endl<<(*readOut->msSector())<<std::endl<<envelope);
129 return StatusCode::FAILURE;
130 }
131 } else if constexpr (std::is_same_v<EnvelopeType, Chamber>) {
132 if (readOut->chamber() != &envelope) {
133 ATH_MSG_FATAL("Mismatch in the chamber association "<<m_idHelperSvc->toStringDetEl(readOut->identify())
134 <<std::endl<<(*readOut->chamber())<<std::endl<<envelope);
135 return StatusCode::FAILURE;
136 }
137 }
138 switch (readOut->detectorType()) {
140 const auto* detEle = static_cast<const TgcReadoutElement*>(readOut);
141 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
142 break;
144 const auto* detEle = static_cast<const MdtReadoutElement*>(readOut);
145 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
146 break;
148 const auto* detEle = static_cast<const RpcReadoutElement*>(readOut);
149 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
150 break;
152 const auto* detEle = static_cast<const MmReadoutElement*>(readOut);
153 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
154 break;
156 const auto* detEle = static_cast<const sTgcReadoutElement*>(readOut);
157 ATH_CHECK(testReadoutEle(gctx, *detEle, envelope, *boundVol));
158 break;
159 } default: {
160 ATH_MSG_FATAL("Who came up with putting "<<ActsTrk::to_string(readOut->detectorType())
161 <<" into the MS");
162 return StatusCode::FAILURE;
163 }
164 }
165 }
166 ATH_MSG_DEBUG("All "<<reEles.size()<<" readout elements are embedded in "<<envelope);
167 return StatusCode::SUCCESS;
168 }
169
170 std::vector<Amg::Vector3D> MuonChamberToolTest::cornerPoints(const ActsTrk::GeometryContext& gctx, const Acts::Volume& volume) const {
171
172 const auto& bounds = volume.volumeBounds();
173 unsigned int edgeIdx{0};
174 //diamond volume bounds case - there are 12 edges
175 if(bounds.type() == Acts::VolumeBounds::BoundsType::eDiamond){
176 const auto& diamondBounds = static_cast<const Acts::DiamondVolumeBounds&>(bounds);
177 using BoundEnum = Acts::DiamondVolumeBounds::BoundValues;
178 std::vector<Amg::Vector3D> edges(12, Amg::Vector3D::Zero());
179 double xCord{0};
180 double yCord{0};
181 for(double signX : {-1.,1.}){
182 for(double signY : {-1., 0., 1.}){
183 for(double signZ : {-1.,1.}){
184 if(signY == 0){
185 xCord = diamondBounds.get(BoundEnum::eHalfLengthX2);
186 }else if(signY < 0){
187 xCord = diamondBounds.get(BoundEnum::eHalfLengthX1);
188 yCord = diamondBounds.get(BoundEnum::eLengthY1);
189
190 }else{
191 xCord = diamondBounds.get(BoundEnum::eHalfLengthX3);
192 yCord = diamondBounds.get(BoundEnum::eLengthY2);
193
194 }
195
196 const Amg::Vector3D edge{signX*xCord,
197 signY*yCord,
198 signZ*diamondBounds.get(BoundEnum::eHalfLengthZ)};
199 edges[edgeIdx] = volume.localToGlobalTransform(gctx.context())*edge;
200 edgeIdx++;
201 }
202 }
203 }
204
205 return edges;
206 }
207
208 //trapezoid or rectangular bounds case
209 std::vector<Amg::Vector3D> edges(8, Amg::Vector3D::Zero());
210 ATH_MSG_VERBOSE("Fetch volume bounds "<<Amg::toString(volume.localToGlobalTransform(gctx.context())));
211 for (const double signX : {-1., 1.}) {
212 for (const double signY : { -1., 1.}) {
213 for (const double signZ: {-1., 1.}) {
214 const Amg::Vector3D edge{signX* (signY>0 ? MuonGMR4::halfXhighY(bounds) : MuonGMR4::halfXlowY(bounds)),
215 signY*MuonGMR4::halfY(bounds),
216 signZ*MuonGMR4::halfZ(bounds)};
217 edges[edgeIdx] = volume.localToGlobalTransform(gctx.context()) * edge;
218 ATH_MSG_VERBOSE("Local edge "<<Amg::toString(edge)<<", global edge: "<<Amg::toString(edges[edgeIdx]));
219 ++edgeIdx;
220 }
221 }
222 }
223 return edges;
224 }
225
226 std::array<Amg::Vector3D, 8> MuonChamberToolTest::cornerPoints(const ActsTrk::GeometryContext& gctx, const Acts::StrawSurface& surface) const {
227 std::array<Amg::Vector3D, 8> edges{make_array<Amg::Vector3D,8>(Amg::Vector3D::Zero())};
228 using BoundEnum = Acts::LineBounds::BoundValues;
229 const auto& bounds = static_cast<const Acts::LineBounds&>(surface.bounds());
230 unsigned int edgeIdx{0};
231
232 ATH_MSG_VERBOSE("Fetch volume bounds "<<Amg::toString(surface.localToGlobalTransform(gctx.context())));
233 for (const double signX : {-1., 1.}) {
234 for (const double signY : { -1., 1.}) {
235 for (const double signZ: {-1., 1.}) {
236 const Amg::Vector3D edge{signX*bounds.get(BoundEnum::eR),
237 signY*bounds.get(BoundEnum::eR),
238 signZ*bounds.get(BoundEnum::eHalfLengthZ)};
239 edges[edgeIdx] = surface.localToGlobalTransform(gctx.context()) * edge;
240 ++edgeIdx;
241 }
242 }
243 }
244 return edges;
245 }
246
247 std::array<Amg::Vector3D, 4> MuonChamberToolTest::cornerPoints(const ActsTrk::GeometryContext& gctx, const Acts::PlaneSurface& surface) const {
248 std::array<Amg::Vector3D, 4> edges{make_array<Amg::Vector3D,4>(Amg::Vector3D::Zero())};
249 if(surface.bounds().type() == Acts::SurfaceBounds::BoundsType::eRectangle) { //RPC surfaces are rectangles
250 const Acts::RectangleBounds& bounds = static_cast<const Acts::RectangleBounds&>(surface.bounds());
251 using BoundEnum = Acts::RectangleBounds::BoundValues;
252
253 unsigned int edgeIdx{0};
254 for(const double signX : {-1., 1.}) {
255 for (const double signY : { -1., 1.}) {
256 const Amg::Vector3D edge{signX < 0 ? bounds.get(BoundEnum::eMinX) : bounds.get(BoundEnum::eMaxX),
257 signY < 0 ? bounds.get(BoundEnum::eMinY) : bounds.get(BoundEnum::eMaxY), 0.};
258 edges[edgeIdx] = surface.localToGlobalTransform(gctx.context()) * edge;
259 ++edgeIdx;
260 }
261 }
262 return edges;
263 } else if(surface.bounds().type() == Acts::SurfaceBounds::BoundsType::eTrapezoid) {
264 using BoundEnum = Acts::TrapezoidBounds::BoundValues;
265 const auto& bounds = static_cast<const Acts::TrapezoidBounds&>(surface.bounds());
266 unsigned int edgeIdx{0};
267
268 ATH_MSG_VERBOSE("Fetch volume bounds "<<Amg::toString(surface.localToGlobalTransform(gctx.context())));
269 for (const double signX : {-1., 1.}) {
270 for (const double signY : { -1., 1.}) {
271 const Amg::Vector3D edge{Amg::getRotateZ3D(-1.*bounds.get(BoundEnum::eRotationAngle)) *
272 Amg::Vector3D(signX*bounds.get(signY < 0 ? BoundEnum::eHalfLengthXnegY : BoundEnum::eHalfLengthXposY),
273 signY*bounds.get(BoundEnum::eHalfLengthY), 0.)};
274
275 edges[edgeIdx] = surface.localToGlobalTransform(gctx.context()) * edge;
276 ++edgeIdx;
277 }
278 }
279
280 return edges;
281 } else {
282 ATH_MSG_FATAL("The surface bounds are neither a rectangle nor a trapezoid, this is not supported yet");
283 return edges;
284 }
285 }
286
287#if defined(FLATTEN) && defined(__GNUC__)
288// We compile this function with optimization, even in debug builds; otherwise,
289// the heavy use of Eigen makes it too slow. However, from here we may call
290// to out-of-line Eigen code that is linked from other DSOs; in that case,
291// it would not be optimized. Avoid this by forcing all Eigen code
292// to be inlined here if possible.
293[[gnu::flatten]]
294#endif
296 const std::vector<Amg::Vector3D>& chamberEdges,
297 const Acts::Volume& volume) const {
298
300 const Amg::Vector3D center{volume.center(gctx.context())};
301 double minDist = 1._km;
302 for (const Amg::Vector3D& edge : chamberEdges) {
303 minDist = std::min(minDist, (edge - center).mag());
304 }
307 if (std::ranges::none_of(volume.volumeBounds().values(),
308 [minDist](const double bound){
309 return minDist < 2.5*bound;
310 })) {
311 return false;
312 }
313 const double stepLength = 1. / m_overlapSamples;
314
315 for (unsigned edge1 = 1; edge1 < chamberEdges.size(); ++edge1) {
316 for (unsigned edge2 = 0; edge2 < edge1; ++edge2) {
317 for (unsigned step = 0 ; step <= m_overlapSamples; ++step) {
318 const double section = stepLength * step;
319 const Amg::Vector3D testPoint = section* chamberEdges[edge1] + (1. -section) *chamberEdges[edge2];
320 if (volume.inside (gctx.context(), testPoint)) {
321 return true;
322 }
323 }
324 }
325 }
326 return false;
327 }
329
330 std::vector<const MuonReadoutElement*> allRE = m_detMgr->getAllReadoutElements();
332 const ChamberSet chambers = m_detMgr->getAllChambers();
333 ATH_MSG_INFO("Fetched "<<chambers.size()<<" chambers.");
334 std::vector<const Chamber*> chamberVec{chambers.begin(), chambers.end()};
335
336 const auto missChamb = std::ranges::find_if(allRE, [&chamberVec](const MuonGMR4::MuonReadoutElement* re){
337 return std::ranges::find(chamberVec, re->chamber()) == chamberVec.end();
338 });
339 if (missChamb != allRE.end()) {
340 ATH_MSG_FATAL("The chamber "<<(*(*missChamb)->chamber())<<" is not in the chamber set");
341 return StatusCode::FAILURE;
342 }
343
344 std::set<const Chamber*> overlapChambers{};
345 std::stringstream overlapstream{};
346 for (std::size_t chIdx = 0; chIdx< chamberVec.size(); ++chIdx) {
347 const Chamber& chamber{*chamberVec[chIdx]};
348 if (m_dumpObjs) {
349 saveEnvelope(gctx, std::format("Chamber_{:}{:}{:}{:}{:}",
350 ActsTrk::to_string(chamber.detectorType()),
351 chName(chamber.chamberIndex()),
352 Acts::abs(chamber.stationEta()),
353 chamber.stationEta() > 0 ? 'A' : 'C',
354 chamber.stationPhi()),
355 *chamber.boundingVolume(gctx), chamber.readoutEles());
356 }
357 ATH_CHECK(allReadoutInEnvelope(gctx, chamber));
358 const std::vector<Amg::Vector3D> chambCorners = cornerPoints(gctx, *chamber.boundingVolume(gctx));
360 std::vector<const Chamber*> overlaps{};
361 for (std::size_t chIdx1 = 0; chIdx1<chamberVec.size(); ++chIdx1) {
362 if (chIdx == chIdx1) {
363 continue;
364 }
365 const Chamber* overlapTest{chamberVec[chIdx1]};
366 if (hasOverlap(gctx, chambCorners, *(overlapTest->boundingVolume(gctx)))) {
367 overlaps.push_back(overlapTest);
368 }
369 }
370 if (overlaps.empty()) {
371 continue;
372 }
373 overlapstream<<"The chamber "<<chamber<<" overlaps with "<<std::endl;
374 for (const Chamber* itOverlaps : overlaps) {
375 overlapstream<<" *** "<<(*itOverlaps)<<std::endl;
376 }
377 overlapstream<<std::endl<<std::endl;
378 overlapChambers.insert(overlaps.begin(), overlaps.end());
379 overlapChambers.insert(chamberVec[chIdx]);
380 }
381 if (!overlapChambers.empty()) {
382 Acts::ObjVisualization3D visualHelper{};
383 for (const Chamber* hasOverlap: overlapChambers) {
384 Acts::GeometryView3D::drawVolume(visualHelper, *hasOverlap->boundingVolume(gctx), gctx.context());
385 visualHelper.write(m_overlapChambObj.value());
386 }
387 if (m_ignoreOverlapCh) {
388 ATH_MSG_WARNING(overlapstream.str());
389 } else {
390 ATH_MSG_FATAL(overlapstream.str());
391 }
392 }
393 return overlapChambers.empty() || m_ignoreOverlapCh ? StatusCode::SUCCESS : StatusCode::FAILURE;
394 }
395
397
398 std::vector<const MuonReadoutElement*> allREs = m_detMgr->getAllReadoutElements();
399 for (const MuonReadoutElement* re : allREs) {
400 if (!re->msSector()) {
401 ATH_MSG_FATAL("The readout element "<<m_idHelperSvc->toStringDetEl(re->identify())<<" does not have any sector associated ");
402 return StatusCode::FAILURE;
403 }
404 const SpectrometerSector* sectorFromDet = m_detMgr->getSectorEnvelope(re->chamberIndex(),
405 m_idHelperSvc->sector(re->identify()),
406 re->stationEta());
407 if (sectorFromDet != re->msSector()) {
408 ATH_MSG_FATAL("The sector attached to "<<m_idHelperSvc->toStringDetEl(re->identify())
409 <<", chIdx: "<<chName(re->chamberIndex())<<", sector: "<<m_idHelperSvc->sector(re->identify())
410 <<" is not the one attached to the readout geometry \n"<<(*re->msSector())<<"\n"<<(*sectorFromDet));
411 return StatusCode::FAILURE;
412 }
413 }
414 using SectorSet = MuonDetectorManager::MuonSectorSet;
415 const SectorSet sectors = m_detMgr->getAllSectors();
416 ATH_MSG_INFO("Fetched "<<sectors.size()<<" sectors. ");
417 for (const SpectrometerSector* sector : sectors) {
418 if (m_dumpObjs) {
419 saveEnvelope(gctx, std::format("Sector_{:}{:}{:}",
420 chName(sector->chamberIndex()),
421 sector->side() >0? 'A' :'C',
422 sector->stationPhi() ),
423 *sector->boundingVolume(gctx), sector->readoutEles(),
424 chamberVolumes(gctx, *sector));
425 }
426 ATH_CHECK(allReadoutInEnvelope(gctx, *sector));
427 const std::shared_ptr<Acts::Volume> secVolume = sector->boundingVolume(gctx);
428 for (const SpectrometerSector::ChamberPtr& chamber : sector->chambers()){
429 const std::vector<Amg::Vector3D> edges = cornerPoints(gctx, *chamber->boundingVolume(gctx));
430 unsigned int edgeCount{0};
431 for (const Amg::Vector3D& edge : edges) {
432 ATH_CHECK(pointInside(gctx, *sector, *secVolume, edge, std::format("Edge {:}", ++edgeCount),
433 chamber->readoutEles().front()->identify()));
434 }
435 }
436 }
437 return StatusCode::SUCCESS;
438 }
439
441 std::shared_ptr<const Acts::TrackingGeometry>& trackingGeometry) const {
442 //visit the volumes and check the overlaps
443 std::vector<const Acts::TrackingVolume*> volumeVec{};
444 std::vector<const Acts::TrackingVolume*> overlapVolumes{};
445 //keep onyl the chamber volumes - not the cylinders
446 trackingGeometry->visitVolumes([&](const Acts::TrackingVolume* vol){
447 if(!(vol->volumeBounds().type() == Acts::VolumeBounds::BoundsType::eCylinder)){
448 //dump the tracking volumes into obj
449 if(m_dumpObjs){
450 Acts::ObjVisualization3D visualHelper{};
451 for(const auto& surf : vol->surfaces()){
452
453 Acts::GeometryView3D::drawSurface(visualHelper, surf, gctx.context());
454
455 }
456 std::string volName = vol->volumeName();
457 Acts::GeometryView3D::drawVolume(visualHelper, *vol, gctx.context());
458 ATH_MSG_DEBUG("Save new tracking volume 'MsTrackingVol_"<<volName<<".obj'");
459 visualHelper.write(std::format("MsTrackingVol_{:}.obj", volName));
460
461 }
462 volumeVec.push_back(vol);
463 }
464 });
465
466 for(std::size_t vIdx = 0; vIdx < volumeVec.size(); vIdx++){
467 const Acts::TrackingVolume* testVol{volumeVec[vIdx]};
468 std::vector<const Acts::TrackingVolume*> overlaps{};
469 const std::vector<Amg::Vector3D> edges = cornerPoints(gctx, *testVol);
470 const auto childrenVol = testVol->volumes();
471 const auto innerSurfaces = testVol->surfaces();
472
473 for(const auto& surface : innerSurfaces){
474 //only plane or straw surfaces expected
475 std::vector<Amg::Vector3D> surfEdges = {};
476 if(const auto* strawSurf = dynamic_cast<const Acts::StrawSurface*>(&surface)){
477 ATH_MSG_VERBOSE("Checking straw surface "<<surface.geometryId()
478 <<" in volume "<<testVol->volumeName());
479
480 auto edges = cornerPoints(gctx, *strawSurf);
481 surfEdges = {edges.begin(), edges.end()};
482 }else if(const auto* planeSurf = dynamic_cast<const Acts::PlaneSurface*>(&surface)){
483 ATH_MSG_VERBOSE("Checking plane surface "<<surface.geometryId()
484 <<" in volume "<<testVol->volumeName());
485
486 auto edges = cornerPoints(gctx, *planeSurf);
487 surfEdges = {edges.begin(), edges.end()};
488 } else{
489 ATH_MSG_FATAL("The surface "<< surface.geometryId()
490 <<" is neither a straw nor a plane surface");
491 return StatusCode::FAILURE;
492 }
493
494
495 for(const auto& edge : surfEdges){
496 if(!(testVol->inside(gctx.context(), edge,0.01))){
497 ATH_MSG_FATAL("The surface " << surface.geometryId() <<" at vertex point " <<Amg::toString(edge)
498 <<" is outside the parent volume" << testVol->volumeName());
499 return m_ignoreOutsideSurf ? StatusCode::SUCCESS : StatusCode::FAILURE;
500
501 }
502 }
503 }
504
505 //check if this test volume overlaps with the other volumes in the tracking geometry
506 for(std::size_t vIdx1 = 0 ; vIdx1 < volumeVec.size(); vIdx1++){
507 bool isChild = std::ranges::find_if(childrenVol,
508 [&](const Acts::TrackingVolume& tv){ return &tv == volumeVec[vIdx1]; }
509 ) != childrenVol.end();
510
511 bool isMother = (testVol->motherVolume() == volumeVec[vIdx1]);
512
513 if(vIdx1 == vIdx || isMother){
514 continue;
515 }
516 //check if the child volume is entirely enclosed by the mother volume
517 if(isChild){
518 std::vector<Amg::Vector3D> childEdges = cornerPoints(gctx,*volumeVec[vIdx1]);
519 for(const auto& edge : childEdges){
520 if(!(testVol->inside(gctx.context(), edge, 0.01))){
521 ATH_MSG_FATAL("The children volume's " << volumeVec[vIdx1]->volumeName()
522 <<" vertex point " <<Amg::toString(edge)
523 <<" is outside the parent volume" << testVol->volumeName());
524 return StatusCode::FAILURE;
525 }
526 }
527 continue;
528
529 }
530 if(hasOverlap(gctx, edges, *volumeVec[vIdx1])){
531 overlaps.push_back(volumeVec[vIdx1]);
532 }
533 }
534 if(overlaps.empty()){
535 ATH_MSG_DEBUG("No overlaps detected for the volume "<<testVol->volumeName());
536 continue;
537 }else{
538 overlapVolumes.push_back(testVol);
539 ATH_MSG_ALWAYS("The volume " << testVol->volumeName() << " overlaps with: ");
540 for(const auto& overlap: overlaps){
541 ATH_MSG_ALWAYS(" Volume: " << overlap->volumeName());
542 }
543 }
544 }
545 if(overlapVolumes.empty()){
546 ATH_MSG_ALWAYS("No overlaps detected in the tracking geometry!!");
547 }
548 return overlapVolumes.empty() || m_ignoreOverlapCh ? StatusCode::SUCCESS : StatusCode::FAILURE;
549 }
550
552 const std::string& envName,
553 const Acts::Volume& envelopeVol,
554 const std::vector<const MuonGMR4::MuonReadoutElement*>& assocRE,
555 const std::vector<std::shared_ptr<Acts::Volume>>& subVols) const {
556 Acts::ObjVisualization3D visualHelper{};
557 std::ranges::for_each(assocRE, [&visualHelper, &gctx](const MuonReadoutElement* re){
558 std::ranges::for_each(re->getSurfaces(),[&visualHelper, &gctx](const std::shared_ptr<Acts::Surface>& surface){
559 Acts::GeometryView3D::drawSurface(visualHelper, *surface, gctx.context());
560 });
561 });
562 std::ranges::for_each(subVols, [&visualHelper, &gctx](const std::shared_ptr<Acts::Volume>& subVol ){
563 Acts::GeometryView3D::drawVolume(visualHelper,*subVol, gctx.context(), Amg::Transform3D::Identity(),
564 Acts::s_viewPassive);
565 });
566 Acts::GeometryView3D::drawVolume(visualHelper, envelopeVol, gctx.context());
567 ATH_MSG_DEBUG("Save new envelope 'MsTrackTest_"<<envName<<".obj'");
568 visualHelper.write(std::format("MsTrackTest_{:}.obj", envName));
569 }
570
571 StatusCode MuonChamberToolTest::execute(const EventContext& ctx) const {
572 const ActsTrk::GeometryContext* gctx{nullptr};
573 ATH_CHECK(SG::get(gctx, m_geoCtxKey, ctx));
574 std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry = m_trackingGeometrySvc->trackingGeometry();
576 ATH_CHECK(checkChambers(*gctx));
578 ATH_CHECK(checkTrackingGeometry(*gctx, trackingGeometry));
579
580 return StatusCode::SUCCESS;
581 }
582 template <class EnvelopeType>
584 const MdtReadoutElement& mdtMl,
585 const EnvelopeType& chamber,
586 const Acts::Volume& detVol) const {
587 ATH_MSG_VERBOSE("Test whether "<<m_idHelperSvc->toStringDetEl(mdtMl.identify())<<std::endl<<mdtMl.getParameters());
588
589 for (unsigned int layer = 1; layer <= mdtMl.numLayers(); ++layer) {
590 for (unsigned int tube = 1; tube <= mdtMl.numTubesInLay(); ++tube) {
591 const IdentifierHash idHash = mdtMl.measurementHash(layer, tube);
592 if (!mdtMl.isValid(idHash)){
593 continue;
594 }
595 const Amg::Transform3D& locToGlob{mdtMl.localToGlobalTransform(gctx, idHash)};
596 const Identifier measId{mdtMl.measurementId(idHash)};
597
598 ATH_CHECK(pointInside(gctx, chamber, detVol, mdtMl.globalTubePos(gctx, idHash), "tube center", measId));
599
600 ATH_CHECK(pointInside(gctx, chamber, detVol, mdtMl.readOutPos(gctx, idHash), "tube readout", measId));
601 ATH_CHECK(pointInside(gctx, chamber, detVol, mdtMl.highVoltPos(gctx, idHash), "tube HV", measId));
602
603 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(-mdtMl.innerTubeRadius() * Amg::Vector3D::UnitX()),
604 "bottom of the tube box", measId));
605 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(mdtMl.innerTubeRadius() * Amg::Vector3D::UnitX()),
606 "sealing of the tube box", measId));
607
608 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(-mdtMl.innerTubeRadius() * Amg::Vector3D::UnitY()),
609 "wall to the previous tube", measId));
610 ATH_CHECK(pointInside(gctx, chamber, detVol, locToGlob*(-mdtMl.innerTubeRadius() * Amg::Vector3D::UnitY()),
611 "wall to the next tube", measId));
612 }
613 }
614 return StatusCode::SUCCESS;
615 }
616 template<class EnvelopeType>
618 const RpcReadoutElement& rpc,
619 const EnvelopeType& chamber,
620 const Acts::Volume& detVol) const {
621
622 ATH_MSG_VERBOSE("Test whether "<<m_idHelperSvc->toStringDetEl(rpc.identify())<<std::endl<<rpc.getParameters());
623
624 const RpcIdHelper& idHelper{m_idHelperSvc->rpcIdHelper()};
625 for (unsigned int gasGap = 1 ; gasGap <= rpc.nGasGaps(); ++gasGap) {
626 for (int doubletPhi = rpc.doubletPhi(); doubletPhi <= rpc.doubletPhiMax(); ++doubletPhi){
627 for (bool measPhi : {false, true}) {
628 const int nStrips = measPhi ? rpc.nPhiStrips() : rpc.nEtaStrips();
629 for (int strip = 1; strip <= nStrips; ++strip) {
630 const Identifier stripId = idHelper.channelID(rpc.identify(),rpc.doubletZ(),
631 doubletPhi, gasGap, measPhi, strip);
632 ATH_CHECK(pointInside(gctx, chamber, detVol, rpc.stripPosition(gctx, stripId), "center", stripId));
633 ATH_CHECK(pointInside(gctx, chamber, detVol, rpc.leftStripEdge(gctx, stripId), "right edge", stripId));
634 ATH_CHECK(pointInside(gctx, chamber, detVol, rpc.rightStripEdge(gctx, stripId), "left edge", stripId));
635 }
636 }
637 }
638 }
639 return StatusCode::SUCCESS;
640 }
641 template <class EnevelopeType>
643 const TgcReadoutElement& tgc,
644 const EnevelopeType& chamber,
645 const Acts::Volume& detVol) const {
646 for (unsigned int gasGap = 1; gasGap <= tgc.nGasGaps(); ++gasGap){
647 for (bool isStrip : {false}) {
648 const IdentifierHash layHash = tgc.constructHash(0, gasGap, isStrip);
649 const unsigned int nChannel = tgc.numChannels(layHash);
650 for (unsigned int channel = 1; channel <= nChannel ; ++channel) {
651 const IdentifierHash measHash = tgc.constructHash(channel, gasGap, isStrip);
652 ATH_CHECK(pointInside(gctx, chamber, detVol, tgc.channelPosition(gctx, measHash),
653 "center", tgc.measurementId(measHash)));
654 }
655 }
656 }
657 return StatusCode::SUCCESS;
658 }
659 template <class EnevelopeType>
661 const MmReadoutElement& mm,
662 const EnevelopeType& chamber,
663 const Acts::Volume& detVol) const {
664
665 const MmIdHelper& idHelper{m_idHelperSvc->mmIdHelper()};
666 for(unsigned int gasGap = 1; gasGap <= mm.nGasGaps(); ++gasGap){
667 IdentifierHash gasGapHash = MmReadoutElement::createHash(gasGap,0);
668 unsigned int firstStrip = mm.firstStrip(gasGapHash);
669 for(unsigned int strip = firstStrip; strip <= mm.numStrips(gasGapHash); ++strip){
670 const Identifier stripId = idHelper.channelID(mm.identify(), mm.multilayer(), gasGap, strip);
671 ATH_CHECK(pointInside(gctx, chamber, detVol, mm.stripPosition(gctx, stripId), "center", stripId));
672 ATH_CHECK(pointInside(gctx, chamber, detVol, mm.leftStripEdge(gctx, mm.measurementHash(stripId)), "left edge", stripId));
673 ATH_CHECK(pointInside(gctx, chamber, detVol, mm.rightStripEdge(gctx, mm.measurementHash(stripId)), "right edge", stripId));
674 }
675 }
676
677 return StatusCode::SUCCESS;
678 }
679 template <class EnvelopeType>
681 const sTgcReadoutElement& stgc,
682 const EnvelopeType& chamber,
683 const Acts::Volume& detVol) const{
684
685 const sTgcIdHelper& idHelper{m_idHelperSvc->stgcIdHelper()};
686 for(unsigned int gasGap = 1; gasGap <= stgc.numLayers(); ++gasGap){
687
688 for(unsigned int nch = 1; nch <= stgc.nChTypes(); ++nch){
689 IdentifierHash gasGapHash = sTgcReadoutElement::createHash(gasGap, nch, 0, 0);
690 const unsigned int nStrips = stgc.numChannels(gasGapHash);
692
693 for(unsigned int strip = 1; strip <= nStrips; ++strip){
694 const Identifier stripId = idHelper.channelID(stgc.identify(), stgc.multilayer(), gasGap, nch, strip);
695 const IdentifierHash stripHash = stgc.measurementHash(stripId);
696 ATH_CHECK(pointInside(gctx, chamber, detVol, stgc.globalChannelPosition(gctx, stripHash), "channel position", stripId));
697
699 ATH_CHECK(pointInside(gctx, chamber, detVol, stgc.rightStripEdge(gctx, stripHash), "channel position", stripId));
700 ATH_CHECK(pointInside(gctx, chamber, detVol, stgc.leftStripEdge(gctx, stripHash), "channel position", stripId));
701 }
702 }
703 }
704 }
705 return StatusCode::SUCCESS;
706
707 }
708}
709
const boost::regex re(r_e)
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_FATAL(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
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:28
std::shared_ptr< Acts::Volume > boundingVolume(const ActsTrk::GeometryContext &gctx) const
Returns the Acts::Volume representation of the chamber.
Definition Chamber.cxx:79
std::vector< const MuonReadoutElement * > ReadoutSet
Define the list of read out elements of the chamber.
Definition Chamber.h:31
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.
void saveEnvelope(const ActsTrk::GeometryContext &gctx, const std::string &envName, const Acts::Volume &envelopeVol, const std::vector< const MuonGMR4::MuonReadoutElement * > &assocRE, const std::vector< std::shared_ptr< Acts::Volume > > &subVolumes={}) const
StatusCode execute(const EventContext &ctx) const override
StatusCode checkChambers(const ActsTrk::GeometryContext &gctx) const
Check whether the chamber envelopes are consistent.
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 checkTrackingGeometry(const ActsTrk::GeometryContext &gctx, std::shared_ptr< const Acts::TrackingGeometry > &trackingGeometry) const
Check tracking geometry volumes.
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.
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.
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
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)
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.