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