ATLAS Offline Software
Loading...
Searching...
No Matches
MuonBlueprintNodeBuilder.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6
7#include <Acts/Geometry/BlueprintNode.hpp>
8#include <Acts/Geometry/StaticBlueprintNode.hpp>
9#include <Acts/Geometry/CylinderVolumeBounds.hpp>
10#include <Acts/Geometry/ContainerBlueprintNode.hpp>
11#include <Acts/Geometry/MultiWireVolumeBuilder.hpp>
12#include <Acts/Surfaces/TrapezoidBounds.hpp>
13#include <Acts/Geometry/MaterialDesignatorBlueprintNode.hpp>
14#include <Acts/Geometry/GeometryIdentifierBlueprintNode.hpp>
15#include <Acts/Geometry/VolumeAttachmentStrategy.hpp>
16#include <Acts/Geometry/VolumeResizeStrategy.hpp>
17#include <Acts/Geometry/TrackingVolume.hpp>
18#include <Acts/Geometry/TrapezoidVolumeBounds.hpp>
19#include <Acts/Geometry/CuboidVolumeBounds.hpp>
20#include <Acts/Geometry/DiamondVolumeBounds.hpp>
21#include <Acts/Surfaces/PlaneSurface.hpp>
22#include <Acts/Surfaces/CylinderSurface.hpp>
23#include <Acts/Surfaces/DiscSurface.hpp>
24#include <Acts/Surfaces/RadialBounds.hpp>
25#include <ActsPlugins/GeoModel/GeoModelMaterialConverter.hpp>
26#include <Acts/Visualization/ObjVisualization3D.hpp>
27#include <Acts/Visualization/GeometryView3D.hpp>
28#include <Acts/Surfaces/LineBounds.hpp>
29#include <Acts/Material/HomogeneousSurfaceMaterial.hpp>
30#include <Acts/Material/ProtoSurfaceMaterial.hpp>
31#include <Acts/Surfaces/RectangleBounds.hpp>
32
37
38#include "GeoModelValidation/GeoMaterialHelper.h"
39
40using namespace Acts::UnitLiterals;
41using namespace Muon::MuonStationIndex;
42namespace {
43
44 //Muon System IDs
45 constexpr std::size_t s_muonBarrelId = 80;
46 constexpr std::size_t s_muonEndcapAId = 81;
47 constexpr std::size_t s_muonEndcapCId = 82;
48 constexpr std::size_t s_muonEndcapMiddleAId = 83;
49 constexpr std::size_t s_muonEndcapMiddleCId = 84;
50
51 //Helper function to configure a material node with the correct Faces of the chambers' tracking volumes
52 void configureMaterialFaces(
53 Acts::Experimental::MaterialDesignatorBlueprintNode& node,
54 const Acts::VolumeBounds& bounds,
55 std::shared_ptr<const Acts::ISurfaceMaterial> material){
56
57 switch (bounds.type()) {
58 case Acts::VolumeBounds::BoundsType::eCuboid: {
59 node.configureFace(
60 Acts::CuboidVolumeBounds::Face::NegativeZFace, material);
61 node.configureFace(
62 Acts::CuboidVolumeBounds::Face::PositiveZFace, material);
63 break;
64 }
65 case Acts::VolumeBounds::BoundsType::eTrapezoid: {
66 node.configureFace(
67 Acts::TrapezoidVolumeBounds::Face::NegativeZFaceXY, material);
68 node.configureFace(
69 Acts::TrapezoidVolumeBounds::Face::PositiveZFaceXY, material);
70 break;
71 }
72 case Acts::VolumeBounds::BoundsType::eDiamond: {
73 node.configureFace(
74 Acts::DiamondVolumeBounds::Face::NegativeZFaceXY, material);
75 node.configureFace(
76 Acts::DiamondVolumeBounds::Face::PositiveZFaceXY, material);
77 break;
78 }
79 default:
81 "Unsupported volume bounds for material configuration");
82 }
83
84 }
85
86}
87
88namespace ActsTrk {
89
91 ATH_CHECK(detStore()->retrieve(m_detMgr));
92 return StatusCode::SUCCESS;
93 }
94
95
96std::shared_ptr<Acts::Experimental::BlueprintNode> MuonBlueprintNodeBuilder::buildBlueprintNode(const Acts::GeometryContext& gctx, std::shared_ptr<Acts::Experimental::BlueprintNode>&& childNode) {
97
98EnvelopeSet_t elements;
99EnvelopeSet_t barrelStations, endcapOuterAStations, endcapOuterCStations,
100 endcapMiddleAStations, endcapMiddleCStations;
101
102if (m_useSectors) {
103 elements = m_detMgr->getAllSectors();
104} else {
105 elements = m_detMgr->getAllChambers();
106}
107
108std::visit([&](auto& elems) {
109 using SetType = std::decay_t<decltype(elems)>;
110
111 // Initialize station containers of the same type
112 SetType barrel, endcapA, endcapC, endcapMiddleA, endcapMiddleC;
113
114 for (const auto& element : elems) {
115 if (isElementInTheStation(*element,
116 {StIdx::BI, StIdx::BM, StIdx::BO, StIdx::BE, StIdx::EE, StIdx::EI},
118 barrel.push_back(element);
119 } else if (isElementInTheStation(*element, {StIdx::EO}, EndcapSide::A)) {
120 endcapA.push_back(element);
121 } else if (isElementInTheStation(*element, {StIdx::EO}, EndcapSide::C)) {
122 endcapC.push_back(element);
123 } else if (isElementInTheStation(*element, {StIdx::EM}, EndcapSide::A)) {
124 endcapMiddleA.push_back(element);
125 } else if (isElementInTheStation(*element, {StIdx::EM}, EndcapSide::C)) {
126 endcapMiddleC.push_back(element);
127 } else {
128 ATH_MSG_WARNING("Element " << element->identString()
129 << " not assigned to any station!");
130 }
131 }
132
133 // Assign back into the outer variants
134 barrelStations = std::move(barrel);
135 endcapOuterAStations = std::move(endcapA);
136 endcapOuterCStations = std::move(endcapC);
137 endcapMiddleAStations = std::move(endcapMiddleA);
138 endcapMiddleCStations = std::move(endcapMiddleC);
139}, elements);
140
141 // Top level node for the Muon system
142auto muonNode = std::make_shared<Acts::Experimental::CylinderContainerBlueprintNode>("MuonNode", Acts::AxisDirection::AxisZ);
143
144Acts::VolumeBoundFactory boundsFactory{};
145auto barrelNode = buildMuonNode(gctx, barrelStations, "BI_BM_BO_EE_EI", Acts::GeometryIdentifier().withVolume(s_muonBarrelId), boundsFactory, {ChIdx::BIS, ChIdx::BML, ChIdx::BOL,
146 ChIdx::EIS, ChIdx::EIL});
147auto endcapANode = buildMuonNode(gctx, endcapOuterAStations, "EO_A", Acts::GeometryIdentifier().withVolume(s_muonEndcapAId), boundsFactory);
148auto endcapCNode = buildMuonNode(gctx, endcapOuterCStations, "EO_C", Acts::GeometryIdentifier().withVolume(s_muonEndcapCId), boundsFactory);
149auto endcapMiddleANode = buildMuonNode(gctx, endcapMiddleAStations, "EM_A", Acts::GeometryIdentifier().withVolume(s_muonEndcapMiddleAId), boundsFactory, {ChIdx::EML, ChIdx::EMS});
150auto endcapMiddleCNode = buildMuonNode(gctx, endcapMiddleCStations, "EM_C", Acts::GeometryIdentifier().withVolume(s_muonEndcapMiddleCId), boundsFactory, {ChIdx::EML, ChIdx::EMS});
151
152//Add to the muon barrel child node (e.g calo or Itk) - if existed
153if(childNode){
154 barrelNode->addChild(std::move(childNode));
155}
156muonNode->addChild(std::move(barrelNode));
157muonNode->addChild(std::move(endcapANode));
158muonNode->addChild(std::move(endcapCNode));
159muonNode->addChild(std::move(endcapMiddleANode));
160muonNode->addChild(std::move(endcapMiddleCNode));
161
162return muonNode;
163
164}
165
166std::shared_ptr<Acts::Experimental::StaticBlueprintNode>
167MuonBlueprintNodeBuilder::buildMuonNode(const Acts::GeometryContext& gctx,
168 const EnvelopeSet_t& elements,
169 const std::string& name,
170 const Acts::GeometryIdentifier& id,
171 Acts::VolumeBoundFactory& boundsFactory,
172 const std::vector<ChIdx>& passiveStationIds) const {
173
174 const ActsTrk::GeometryContext* context = gctx.get<const ActsTrk::GeometryContext* >();
175 std::vector<std::string> stationNames;
176
177 std::vector<staticNodePtr> nodes;
178 //build the material nodes that will have as children the static nodes bult from the tracking volumes of the chambers
179 std::vector<materialNodePtr> materialNodes;
180
181 double innerRadius{0.0};
182 double outerRadius{std::numeric_limits<double>::lowest()};
183 double maxZ{std::numeric_limits<double>::lowest()};
184 double minZ{std::numeric_limits<double>::max()};
185 int chamberId = 1;
186 std::vector<std::shared_ptr<Acts::Surface>> passiveSurfaces;
187
188 std::visit([&](const auto& elems){
189
190 using SetType = std::decay_t<decltype(elems)>;
191 std::unordered_map<unsigned int, SetType> elementsPerStation;
192
193 for(const auto& element : elems){
194 std::unique_ptr<Acts::TrackingVolume> vol{};
195 if (m_alignableVolumes) {
196 vol = std::make_unique<Acts::TrackingVolume>(*element->boundingVolume(*context),
197 element->identString());
198 } else {
199 vol = std::make_unique<Acts::TrackingVolume>(element->localToGlobalTransform(*context),
200 element->bounds(),
201 element->identString());
202 }
203 // //the chamber geometry id
204 Acts::GeometryIdentifier chId = id.withLayer(chamberId++);
205 vol->assignGeometryId(chId);
206 //build the inner structure of the chamber this will return inner sensitive surfaces
207 //or volumes that have already constructed as blueptint nodes and will nbe assigned as children to the element node
208 std::pair<std::vector<blueprintNodePtr>,std::vector<surfacePtr>> innerStructure = getSensitiveElements(*context, *element, chId, boundsFactory);
209 for(auto& surface: innerStructure.second){
210 vol->addSurface(surface);
211 }
212
213 //calculate the bounds of the cylinder container
214 for(const auto& surface: vol->volumeBounds().orientedSurfaces(vol->localToGlobalTransform(gctx))) {
215 const auto& surfaceRepr = (*surface.surface);
216 const Acts::Polyhedron& polyhedron = surfaceRepr.polyhedronRepresentation(gctx);
217 const Amg::Vector3D& center = surfaceRepr.center(gctx);
218
219 maxZ = std::max(maxZ, center.z());
220 minZ = std::min(minZ, center.z());
221
222 // Outer radius needs to be treated differently due to curvature of cylindrical surface
223 for(const Amg::Vector3D& vertex: polyhedron.vertices){
224 outerRadius = std::max(outerRadius, vertex.perp());
225 }
226 }
227 //create the null blueprint nodes
229 materialNodePtr materialNode;
230 const bool isSingleMdt =
231 (element->readoutEles().size() == 1 &&
232 element->readoutEles().front()->detectorType() == DetectorType::Mdt);
233 //for the single MDT elements we build the material node during the volume construction
234 //and the node returned is the material node already
235 if (isSingleMdt) {
236 // Take ownership of the single existing material node where we have already included the static node as child
237 auto matNode = std::dynamic_pointer_cast<Acts::Experimental::MaterialDesignatorBlueprintNode>(innerStructure.first.front());
238 materialNode = std::move(matNode);
239 } else {
240 //for the non single MDT elements we build the material node that has as child the static node representing the chamber volume
241 materialNode = std::make_shared<Acts::Experimental::MaterialDesignatorBlueprintNode>(element->identString() + "_MaterialNode");
242 configureMaterialFaces(*materialNode, vol->volumeBounds(), getActiveMaterial(*element));
243 node = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(vol));
244 for (auto& childNode : innerStructure.first) {
245 auto staticNode = std::dynamic_pointer_cast<Acts::Experimental::StaticBlueprintNode>(childNode);
246 if (staticNode) {
247 node->addChild(std::move(staticNode));
248 }
249 }
250 materialNode->addChild(node);
251 innerStructure.first.clear();
252 }
253 if (!materialNode) {
254 THROW_EXCEPTION("No blueprint node constructed");
255 }
256 materialNodes.push_back(std::move(materialNode));
257
258 //keep the elements of the stations we want to assign passive material surfaces
259 if(!Acts::rangeContainsValue(passiveStationIds, element->chamberIndex())){
260 continue;
261 }
262
263 DetIdx detIdx = toDetectorRegionIndex(element->chamberIndex(), element->side());
264 elementsPerStation[regionChamberHash(detIdx, element->chamberIndex())].push_back(element);
265 }
266 //construct the surfaces we want to map passive material on using the elements' geometrical parameters
267 passiveSurfaces = getPassiveMaterialSurfaces(gctx, std::move(elementsPerStation));
268
269 }, elements);
270
271 double halfLengthZ = 0.5 * std::abs(maxZ - minZ);
272 ATH_MSG_DEBUG("Inner radius: " << innerRadius<<", outer radius: " << outerRadius
273 <<", max Z: " << maxZ<<", min Z: " << minZ<<", half length Z: " << halfLengthZ);
274
275 Amg::Transform3D trf = Amg::getTranslateZ3D(halfLengthZ + minZ);
276
277 auto bounds = boundsFactory.makeBounds<Acts::CylinderVolumeBounds>(innerRadius, outerRadius, halfLengthZ);
278 auto volume = std::make_unique<Acts::TrackingVolume>(trf, bounds, name);
279 volume->assignGeometryId(id);
280
281 //put the passive material surfaces into the volume
282 std::ranges::for_each(passiveSurfaces, [&volume](auto& surf){
283 volume->addSurface(surf);
284 });
285
286 auto muonNode = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(volume));
287 ATH_MSG_DEBUG("There are " << materialNodes.size() << " nodes");
288 //loop through the nodes-material pairs to add the nodes to the muon node and assign the material to the faces
289 std::ranges::for_each(materialNodes, [&muonNode](const auto& node) {
290 muonNode->addChild(std::move(node));
291 });
292 return muonNode;
293 }
294
295template<typename T>
298 const T& element,
299 const Acts::GeometryIdentifier& chId,
300 Acts::VolumeBoundFactory& boundsFactory) const
301 requires(std::is_same_v<T, MuonGMR4::Chamber> || std::is_same_v<T, MuonGMR4::SpectrometerSector>){
302
303 std::vector<blueprintNodePtr> readoutVolumes;
304 std::vector<surfacePtr> readoutSurfaces;
305 Acts::GeometryIdentifier::Value mdtId{1};
306
307 for (const MuonGMR4::MuonReadoutElement* readoutEle : element.readoutEles()) {
308
309 std::vector<surfacePtr> detSurfaces = readoutEle->getSurfaces();
310 switch(readoutEle->detectorType()){
311 case DetectorType::Mdt: {
312 const auto* mdtReadoutEle = static_cast<const MuonGMR4::MdtReadoutElement*>(readoutEle);
313 const MuonGMR4::MdtReadoutElement::parameterBook& parameters{mdtReadoutEle->getParameters()};
314
315 std::unique_ptr<ActsTrk::VolumePlacement> placement{};
316
317 // create the MDT multilayer volume with the dedicated builder
318 Acts::Experimental::MultiWireVolumeBuilder::Config mwCfg;
319 mwCfg.name = m_detMgr->idHelperSvc()->toStringDetEl(mdtReadoutEle->identify());
320 mwCfg.mlSurfaces = detSurfaces;
321 mwCfg.transform = readoutEle->localToGlobalTransform(gctx);
322
323 //initialize a nullptr material node which will be filled in the case of single MDT readout elements and used to assign the material to the volume and add the static node as child of the material node
324 std::shared_ptr<Acts::Experimental::MaterialDesignatorBlueprintNode> mdtMaterialNode;
325
326 //special treatment of BIS78 MDT multilayer
327 //use different shape because of clashes with EIL chambers
328 if(isBIS78(readoutEle) && mdtReadoutEle->multilayer() == 2){
329
330
331 //find the minimum and the maximum tube length (x dimension of the diamond bounds)
332 std::vector<double> tubeLengths;
333 tubeLengths.reserve(mdtReadoutEle->numTubesInLay());
334 for(std::size_t tube = 1; tube < mdtReadoutEle->numTubesInLay(); ++tube){
336 const auto& surface = mdtReadoutEle->surface(tubeHash);
337 const auto& lBounds = static_cast<const Acts::LineBounds&>(surface.bounds());
338 using BoundEnum = Acts::LineBounds::BoundValues;
339 const double tubeLength = 2.*lBounds.get(BoundEnum::eHalfLengthZ);
340 tubeLengths.push_back(tubeLength);
341 }
342 auto [minX,maxX] = std::ranges::minmax_element(tubeLengths);
343 int nSmallTubes = std::count_if(tubeLengths.begin(), tubeLengths.end(), [minX](double length){
344 return std::abs(*minX-length) < Acts::s_epsilon;
345 });
346
347 //create the diamond bounds for the volume
348 constexpr double extraMargin = 1._cm;
349 double y2 = (nSmallTubes+1.)*parameters.tubePitch;
350 double y1 = 2.*parameters.halfY + extraMargin - y2;
351 if (m_alignableVolumes) {
352 placement = std::make_unique<ActsTrk::VolumePlacement>(*readoutEle,
353 Amg::getTranslateY3D(parameters.halfY + extraMargin -y2));
354 }
355 mwCfg.transform = mwCfg.transform * Amg::getTranslateY3D(parameters.halfY + extraMargin - y2);
356 mwCfg.bounds = boundsFactory.makeBounds<Acts::DiamondVolumeBounds>(0.5*(*maxX), 0.5*(*maxX), 0.5*(*minX),
357 y1, y2, parameters.halfHeight);
358
359 } else {
361 placement = std::make_unique<ActsTrk::VolumePlacement>(*readoutEle);
362 }
363 //check for rectangular or trapezoidal shape bounds
364 if(std::abs(parameters.shortHalfX - parameters.longHalfX) < Acts::s_epsilon){
365 mwCfg.bounds = boundsFactory.makeBounds<Acts::CuboidVolumeBounds>(parameters.shortHalfX,
366 parameters.halfY,
367 parameters.halfHeight);
368 } else {
369 mwCfg.bounds = boundsFactory.makeBounds<Acts::TrapezoidVolumeBounds>(parameters.shortHalfX,
370 parameters.longHalfX,
371 parameters.halfY,
372 parameters.halfHeight);
373 }
374 }
375 mwCfg.alignablePlacement = placement.get();
377 element.addPlacement(std::move(placement));
378 }
379 mwCfg.binning = {{{Acts::AxisDirection::AxisY, Acts::AxisBoundaryType::Bound,
380 -parameters.halfY,
381 parameters.halfY,
382 static_cast<std::size_t>(std::lround(2 * parameters.halfY / parameters.tubePitch))}, 2u},
383 {{Acts::AxisDirection::AxisZ, Acts::AxisBoundaryType::Bound,
384 -parameters.halfHeight,
385 parameters.halfHeight,
386 static_cast<std::size_t>(std::lround(2 * parameters.halfHeight / parameters.tubePitch))}, 1u}};
387 Acts::Experimental::MultiWireVolumeBuilder mdtBuilder{mwCfg};
388 std::unique_ptr<Acts::TrackingVolume> mdtVolume = mdtBuilder.buildVolume();
389
390 mdtVolume->assignGeometryId(chId.withExtra(mdtId++));
391 //create the blueprint node for the mdt multilayers
392 // check if this is a single mdt (single multilayer) chamber so we assign the material directly to the multilayer
393 if(element.readoutEles().size() == 1){
394
395 mdtMaterialNode = std::make_shared<Acts::Experimental::MaterialDesignatorBlueprintNode>(element.identString() + "_MaterialNode");
396 configureMaterialFaces(*mdtMaterialNode, mdtVolume->volumeBounds(), getActiveMaterial(element));
397 auto staticNode = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(mdtVolume));
398 mdtMaterialNode->addChild(std::move(staticNode));
399 readoutVolumes.push_back(std::move(mdtMaterialNode));
400 break;
401 }
402 auto mdtNode = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(mdtVolume));
403 mdtNode->setNavigationPolicyFactory(mdtBuilder.createNavigationPolicyFactory());
404 readoutVolumes.push_back(std::move(mdtNode));
405
406 break;
407
408 } case DetectorType::Rpc:
411 case DetectorType::Mm: {
412
413 readoutSurfaces.insert(readoutSurfaces.end(), std::make_move_iterator(detSurfaces.begin()),
414 std::make_move_iterator(detSurfaces.end()));
415
416 break;
417
418 } default:
419 THROW_EXCEPTION("Unknown detector type for readout element: " << ActsTrk::to_string(readoutEle->detectorType()));
420 break;
421
422 }
423 }
424
425 return std::make_pair(std::move(readoutVolumes), std::move(readoutSurfaces));
426}
427
428
430 return element->detectorType() == ActsTrk::DetectorType::Mdt &&
431 element->chamberIndex() == ChIndex::BIS &&
432 element->stationEta()>=7;
433 }
434
435template<typename ElementSet_t>
436std::vector<std::shared_ptr<Acts::Surface>>
438 const Acts::GeometryContext& gctx,
439 const std::unordered_map<unsigned int, ElementSet_t>& elementsPerStation) const {
440
442 return {};
443 }
444 //this is a margin to put the surfaces along Z
445 //(a margin distance from the corresponding chamber's boundary surface)
446 constexpr double margin{4._mm};
447
448 std::vector<std::shared_ptr<Acts::Surface>> surfaces;
449 surfaces.reserve(elementsPerStation.size());
450 LayIdx layIdx = LayIdx::LayerIndexMax;
451 DetIdx detIdx = DetIdx::DetectorRegionIndexMax;
452
453 const ActsTrk::GeometryContext* context = gctx.get<const ActsTrk::GeometryContext* >();
454
455 //lamda function to reject BIS78 chambers from the extension of the passive surface
456 //otherwise they create overlap with the NSW sectors - stop a little bit before the cylinder of the passive surface
457 const auto rejectBIS78 = [&](const MuonGMR4::MuonReadoutElement* readoutEle) {
458 bool reject{false};
459 switch (readoutEle->detectorType()) {
460 case DetectorType::Mdt: {
461 const auto* techEle =
462 static_cast<const MuonGMR4::MdtReadoutElement*>(readoutEle);
463 if (techEle->multilayer() == 2) {
464 reject = true;
465 }
466 break;
467 }
468 case DetectorType::Rpc: {
469 const auto* techEle =
470 static_cast<const MuonGMR4::RpcReadoutElement*>(readoutEle);
471 if (techEle->doubletZ() == 2) {
472 reject = true;
473 }
474 break;
475 }
476 default:
477 break;
478 }
479 return isBIS78(readoutEle) && reject;
480 };
481
482 for(const auto& [hash, elements] : elementsPerStation){
483
484 //decompose the layer hash to the detector region idx and layer index
485 const auto& [detIdxVal, chIdx] = decomposeRegionChamberHash(hash);
486 layIdx = toLayerIndex(chIdx);
487 detIdx = detIdxVal;
488
489 double maxZ{std::numeric_limits<double>::lowest()};
490 double minZ{std::numeric_limits<double>::max()};
491 double rMin{std::numeric_limits<double>::max()};
492 double rMax{std::numeric_limits<double>::lowest()};
493 //loop through the elements of every station to construct the cylinder/disc surfaces
494 for(const auto& el : elements){
495
496 if(rejectBIS78(el->readoutEles().front())){
497 continue;
498 }
499 const Amg::Transform3D& locToGlobal = el->localToGlobalTransform(*context);
500 const auto& bounds = el->bounds();
501 for(const auto& surface : bounds->orientedSurfaces(locToGlobal)){
502 const auto& surfaceRepr = (*surface.surface);
503 const Amg::Vector3D& center = surfaceRepr.center(gctx);
504 rMin = std::min(rMin, center.perp());
505 minZ = std::min(minZ, center.z());
506 maxZ = std::max(maxZ, center.z());
507 rMax = std::max(rMax, center.perp());
508 }
509
510 }
511 double halfZ = 0.5*std::abs(maxZ-minZ);
512 Amg::Transform3D trf = Amg::Transform3D::Identity();
513 double zShift{0.};
514 // the chambers are groupd per chamber index and detector region(side) -
515 // we can use the first one for the distinction
516 const auto& testCh = elements.front();
517 int8_t side = testCh->side();
518 switch (testCh->chamberIndex()) {
519 //small NSW sectors (disc passive surface in front of NSW and one in front of EMS)
520 case ChIdx::EIS :
521 case ChIdx::EMS :{
522 side > 0 ? zShift = minZ - margin : zShift = maxZ + margin;
523 trf = Amg::getTranslateZ3D(zShift);
524 auto surface = Acts::Surface::makeShared<Acts::DiscSurface>(trf, std::make_shared<Acts::RadialBounds>(0., rMax));
525 const auto [nBins1, nBins2] = getMaterialBins(testCh->chamberIndex());
526 surface->assignSurfaceMaterial(preparePassiveMaterial(surface->bounds(), nBins1, nBins2));
527 surfaces.push_back(surface);
528 break;
529 //large sectors (disc passive surface after NSW/EIL and after EML)
530 } case ChIdx::EIL :
531 case ChIdx::EML : {
532 // HARDCODED!! (maybe think a better solution in the future)
533 // But for the EIL that we put after the EIS/EIL chambers we extend the radius of the disc surface
534 // in order to have a better coverage for the projections from EE
535 if(testCh->chamberIndex() == ChIdx::EIL){
536 rMax += 60*margin;
537 }
538 side > 0 ? zShift = maxZ + margin : zShift = minZ - margin;
539 trf = Amg::getTranslateZ3D(zShift);
540 auto surface = Acts::Surface::makeShared<Acts::DiscSurface>(trf,
541 std::make_shared<Acts::RadialBounds>(0., rMax));
542 const auto [nBins1, nBins2] = getMaterialBins(testCh->chamberIndex());
543 surface->assignSurfaceMaterial(preparePassiveMaterial(surface->bounds(), nBins1, nBins2));
544 surfaces.push_back(surface);
545 break;
546 } case ChIdx::BIS :
547 case ChIdx::BML :
548 case ChIdx::BOL : {
549 trf = Amg::getTranslateZ3D(halfZ + minZ);
550 auto surface = Acts::Surface::makeShared<Acts::CylinderSurface>(trf,
551 std::make_shared<Acts::CylinderBounds>(rMin - margin, halfZ));
552 const auto [nBins1, nBins2] = getMaterialBins(testCh->chamberIndex());
553 surface->assignSurfaceMaterial(preparePassiveMaterial(surface->bounds(), nBins1, nBins2));
554 surfaces.push_back(surface);
555 break;
556 } default :
557 THROW_EXCEPTION("No implementation of passive material surface for this station!!!! - sorry :) ");
558 }
559 ATH_MSG_VERBOSE("Putting passive material surface for station " << layerName(layIdx) << "/ "<< regionName(detIdx) << ": minZ = " << minZ << ", maxZ = " << maxZ<< "and radius "<< rMax);
560 }
561
562 if(msgLvl(MSG::VERBOSE)){
563 std::stringstream stream{};
564 for(const auto& surf : surfaces){
565 stream<< " at position : "<< Amg::toString(surf->center(gctx))
566 << "with bounds "<< surf->bounds()<<std::endl;
567 }
568 ATH_MSG_VERBOSE("Constructed "<< surfaces.size()
569 << " surfaces for passive material description : "<<std::endl<<stream.str());
570 }
571
572 return surfaces;
573}
574
575template<typename T>
576std::shared_ptr<const Acts::ISurfaceMaterial>
578 requires(std::is_same_v<T, MuonGMR4::Chamber> ||
579 std::is_same_v<T, MuonGMR4::SpectrometerSector>) {
580
582 return std::make_shared<Acts::HomogeneousSurfaceMaterial>(Acts::MaterialSlab::Nothing());
583 }
584
585 const float thickness = element.halfZ();
586 PVConstLink parentVolume = element.readoutEles().front()->getMaterialGeom()->getParent();
587 GeoModelTools::GeoMaterialHelper geoMaterialHelper;
588 std::pair<GeoModelTools::GeoMaterialPtr, double> geoMaterials = geoMaterialHelper.collectMaterial(parentVolume);
589
590 const Acts::Material aMat = ActsPlugins::GeoModel::geoMaterialConverter(*geoMaterials.first);
591 Acts::MaterialSlab slab{aMat, thickness};
592 std::shared_ptr<Acts::HomogeneousSurfaceMaterial> material = std::make_shared<Acts::HomogeneousSurfaceMaterial>(slab);
593 material->scale(0.5); // we want to split the active material in two and put it on the two faces of the chamber bounds
594
595 return material;
596
597}
598
599template<typename T>
601 const std::vector<StIdx>& stationIndex,
602 const EndcapSide side) const
603 requires(std::is_same_v<T, MuonGMR4::Chamber> ||
604 std::is_same_v<T, MuonGMR4::SpectrometerSector>) {
605 bool etaSignCorrect = (side == EndcapSide::Both) ||
606 (side == EndcapSide::A && element.side() > 0) ||
607 (side == EndcapSide::C && element.side() < 0);
608 return etaSignCorrect &&
609 Acts::rangeContainsValue(stationIndex, toStationIndex(element.chamberIndex()));
610}
611
612
613std::shared_ptr<Acts::ISurfaceMaterial>
614 MuonBlueprintNodeBuilder::preparePassiveMaterial(const Acts::SurfaceBounds& bounds,
615 const std::size_t nBins1,
616 const std::size_t nBins2) const {
617 if (nBins1 == 0 || nBins2 == 0) {
618 ATH_MSG_ERROR("Cannot create material for "<<bounds
619 <<" as one of the bin dimensions is zero. nBins1: "<<nBins1<<", nBins2: "<<nBins2);
620 return nullptr;
621 }
622 if (nBins1 == 1 && nBins1 == nBins2) {
623 return std::make_shared<Acts::HomogeneousSurfaceMaterial>();
624 }
625
626 std::vector<Acts::DirectedProtoAxis> pmBinning = {};
627
628 switch (bounds.type()) {
629 using enum Acts::SurfaceBounds::BoundsType;
630 case eCylinder: {
631 pmBinning = {{Acts::AxisDirection::AxisZ, Acts::AxisBoundaryType::Bound, nBins1},
632 {Acts::AxisDirection::AxisPhi, Acts::AxisBoundaryType::Bound, nBins2}};
633 break;
634 } case eDisc: {
635 pmBinning = {{Acts::AxisDirection::AxisR, Acts::AxisBoundaryType::Bound, nBins1},
636 {Acts::AxisDirection::AxisPhi, Acts::AxisBoundaryType::Bound, nBins2}};
637
638 break;
639 } default:
640 ATH_MSG_ERROR("Unsupoorted type "<<bounds<<".");
641 return nullptr;
642 }
643 return std::make_shared<Acts::ProtoGridSurfaceMaterial>(pmBinning);
644}
645std::pair<std::size_t, std::size_t>
647 switch(chIdx) {
648 using enum ChIndex;
649 case BIS:
650 case BIL:
651 return std::make_pair(1ul * m_nZBinsBI, 1ul * m_nPhiBinsBI);
652 case BML:
653 case BMS:
654 return std::make_pair(1ul * m_nZBinsBM, 1ul * m_nPhiBinsBM);
655 case BOL:
656 case BOS:
657 return std::make_pair(1ul * m_nZBinsBO, 1ul * m_nPhiBinsBO);
658 case EIS:
659 return std::make_pair(1ul* m_nRBinsEI1, 1ul* m_nPhiBinsEI1);
660 case EIL:
661 return std::make_pair(1ul* m_nRBinsEI2, 1ul* m_nPhiBinsEI2);
662 case EMS:
663 return std::make_pair(1ul* m_nRBinsEM1, 1ul* m_nPhiBinsEM1);
664 case EML:
665 return std::make_pair(1ul* m_nRBinsEM2, 1ul* m_nPhiBinsEM2);
666 default:
667 THROW_EXCEPTION("getMaterialBins() - "<<chName(chIdx)<<" is not yet implemented");
668 }
669 return std::make_pair(0ul, 0ul);
670}
671} //namespace ActsTrk
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
double length(const pvec &v)
double tubeLength
@ BIL
Definition RegSelEnums.h:10
@ BOL
Definition RegSelEnums.h:14
@ BIS
Definition RegSelEnums.h:11
@ BOS
Definition RegSelEnums.h:15
@ BML
Definition RegSelEnums.h:12
@ BMS
Definition RegSelEnums.h:13
virtual DetectorType detectorType() const =0
Returns the detector element type.
Gaudi::Property< std::size_t > m_nPhiBinsEM1
Number of bins in phi direction on the disc before the middle big wheel.
BluePrintSurfPairs_t getSensitiveElements(const ActsTrk::GeometryContext &gctx, const T &element, const Acts::GeometryIdentifier &chId, Acts::VolumeBoundFactory &boundsFactory) const
Get the chamber's sensitive elements.
Gaudi::Property< std::size_t > m_nZBinsBI
Number of bins in Z direction on the BI cylinder surface.
Gaudi::Property< std::size_t > m_nZBinsBM
Number of bins in Z direction on the BM cylinder surface.
std::shared_ptr< Acts::Experimental::BlueprintNode > buildBlueprintNode(const Acts::GeometryContext &gctx, std::shared_ptr< Acts::Experimental::BlueprintNode > &&childNode) override
Build the Muon Blueprint Node.
staticNodePtr buildMuonNode(const Acts::GeometryContext &gctx, const EnvelopeSet_t &elements, const std::string &name, const Acts::GeometryIdentifier &id, Acts::VolumeBoundFactory &boundsFactory, const std::vector< ChIdx > &passiveStationIds={}) const
Build subnodes for the muon system node.
Gaudi::Property< std::size_t > m_nPhiBinsBI
Number of bins in phi direction on the BI cylinder surface.
std::shared_ptr< Acts::ISurfaceMaterial > preparePassiveMaterial(const Acts::SurfaceBounds &bounds, const std::size_t nBins1, const std::size_t nBins2) const
Prepare a binned material which is associated to the surface.
Gaudi::Property< std::size_t > m_nPhiBinsEI1
Number of bins in phi direction on the disc before the NSW.
bool isElementInTheStation(const T &element, const std::vector< StIdx > &stationNames, const EndcapSide side) const
Check if the chamber is in this node.
Gaudi::Property< bool > m_buildPassiveVolumes
Flag to construct the passive material surfaces.
std::vector< surfacePtr > getPassiveMaterialSurfaces(const Acts::GeometryContext &gctx, const std::unordered_map< unsigned int, ElementSet_t > &elementsPerStation) const
Construct and return the surfaces for the passive material description (e.g cylinders for barrel/ dis...
Muon::MuonStationIndex::DetectorRegionIndex DetIdx
Abrivatin for the detector region index.
Gaudi::Property< std::size_t > m_nPhiBinsEM2
Number of bins in phi direction on the disc after the NSW.
Gaudi::Property< bool > m_alignableVolumes
Flag to control if the volumes should be alignable or not.
Gaudi::Property< bool > m_useSectors
Flag to control if we want to build the muon node from sectors or chambers.
std::pair< std::size_t, std::size_t > getMaterialBins(const Muon::MuonStationIndex::ChIndex chIdx) const
std::pair< std::vector< blueprintNodePtr >, std::vector< surfacePtr > > BluePrintSurfPairs_t
Abrivate the vector pair of blue print nodes and associated active surfaces.
std::variant< MuonChamberSet, MuonSectorSet > EnvelopeSet_t
Hide the flexibility to build the tracking geometry from sectors or chambers behind a variant.
Gaudi::Property< std::size_t > m_nPhiBinsEI2
Number of bins in phi direction on the disc after the NSW.
const MuonGMR4::MuonDetectorManager * m_detMgr
the Detector manager
Gaudi::Property< std::size_t > m_nRBinsEI1
Number of bins in R direction on the disc before the NSW.
Gaudi::Property< std::size_t > m_nRBinsEM1
Number of bins in R direction on the disc before the middle big wheel.
std::shared_ptr< Acts::Experimental::StaticBlueprintNode > staticNodePtr
Abrivation of the blue print node pointer.
Gaudi::Property< std::size_t > m_nRBinsEM2
Number of bins in R direction on the disc after the middle big wheel.
Muon::MuonStationIndex::LayerIndex LayIdx
Abrivation for the layer index.
bool isBIS78(const MuonGMR4::MuonReadoutElement *element) const
Helper function determining whether a readout element is BIS78.
Gaudi::Property< bool > m_assignActiveMaterial
Flag to assign active material on the chambers.
Gaudi::Property< std::size_t > m_nPhiBinsBM
Number of bins in phi direction on the BM cylinder surface.
std::shared_ptr< const Acts::ISurfaceMaterial > getActiveMaterial(const T &element) const
Get the active material for a given element representing the chamber/sector.
Gaudi::Property< std::size_t > m_nZBinsBO
Number of bins in Z direction on the BM cylinder surface.
Gaudi::Property< std::size_t > m_nPhiBinsBO
Number of bins in phi direction on the BM cylinder surface.
std::shared_ptr< Acts::Experimental::MaterialDesignatorBlueprintNode > materialNodePtr
Abrivation of the material node pointer.
Gaudi::Property< std::size_t > m_nRBinsEI2
Number of bins in R direction on the disc after the NSW.
This is a "hash" representation of an Identifier.
Readout element to describe the Monitored Drift Tube (Mdt) chambers Mdt chambers usually comrpise out...
static IdentifierHash measurementHash(unsigned layerNumber, unsigned tubeNumber)
Constructs a Measurement hash from layer && tube number.
MuonReadoutElement is an abstract class representing the geometry of a muon detector.
int stationEta() const
Returns the stationEta (positive A site, negative C site)
Muon::MuonStationIndex::ChIndex chamberIndex() const
Returns the chamber index of the Identifier (MMS & STS) have the same chamber Index (EIS)
Definition node.h:24
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
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 getTranslateZ3D(const double Z)
: Returns a shift transformation along the z-axis
Amg::Transform3D getTranslateY3D(const double Y)
: Returns a shift transformation along the y-axis
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
const std::string & layerName(LayerIndex index)
convert LayerIndex into a string
std::pair< DetectorRegionIndex, ChIndex > decomposeRegionChamberHash(unsigned int hash)
decompose the hash into Region and Chamber
StIndex toStationIndex(ChIndex index)
convert ChIndex into StIndex
const std::string & chName(ChIndex index)
convert ChIndex into a string
const std::string & regionName(DetectorRegionIndex index)
convert DetectorRegionIndex into a string
LayerIndex toLayerIndex(ChIndex index)
convert ChIndex into LayerIndex
DetectorRegionIndex toDetectorRegionIndex(ChIndex index, int8_t etaSign)
convert ChamberIndex + etaSign into DetectorRegionIndex
ChIndex
enum to classify the different chamber layers in the muon spectrometer
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10