ATLAS Offline Software
Loading...
Searching...
No Matches
MuonBlueprintNodeBuilder.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
7
8#include "Acts/Geometry/BlueprintNode.hpp"
9#include "Acts/Geometry/StaticBlueprintNode.hpp"
10#include "Acts/Geometry/CylinderVolumeBounds.hpp"
11#include <Acts/Geometry/ContainerBlueprintNode.hpp>
12#include "Acts/Geometry/MultiWireVolumeBuilder.hpp"
13#include "Acts/Surfaces/TrapezoidBounds.hpp"
14#include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
15#include <Acts/Geometry/MaterialDesignatorBlueprintNode.hpp>
16#include <Acts/Geometry/GeometryIdentifierBlueprintNode.hpp>
17#include <Acts/Geometry/VolumeAttachmentStrategy.hpp>
18#include "Acts/Geometry/VolumeResizeStrategy.hpp"
19#include <Acts/Geometry/TrackingVolume.hpp>
20#include <Acts/Geometry/TrapezoidVolumeBounds.hpp>
21#include <Acts/Geometry/CuboidVolumeBounds.hpp>
22#include <Acts/Geometry/DiamondVolumeBounds.hpp>
23#include <Acts/Surfaces/PlaneSurface.hpp>
24#include <ActsPlugins/GeoModel/GeoModelMaterialConverter.hpp>
25
26
31
32#include "GeoModelValidation/GeoMaterialHelper.h"
33
34using namespace Acts::UnitLiterals;
35namespace {
36 //Muon System IDs
37constexpr std::size_t s_muonBarrelId = 30;
38constexpr std::size_t s_muonEndcapAId = 31;
39constexpr std::size_t s_muonEndcapCId = 32;
40constexpr std::size_t s_muonEndcapMiddleAId = 33;
41constexpr std::size_t s_muonEndcapMiddleCId = 34;
42}
43
44namespace ActsTrk {
45
47 ATH_CHECK(detStore()->retrieve(m_detMgr));
48 return StatusCode::SUCCESS;
49 }
50
51
52std::shared_ptr<Acts::Experimental::BlueprintNode> MuonBlueprintNodeBuilder::buildBlueprintNode(const Acts::GeometryContext& gctx, std::shared_ptr<Acts::Experimental::BlueprintNode>&& childNode) {
53
54std::variant<MuonChamberSet, MuonSectorSet> elements;
55std::variant<MuonChamberSet, MuonSectorSet> barrelStations, endcapAStations, endcapCStations, endcapMiddleAStations, endcapMiddleCStations;
56
57if (m_useSectors) {
58 elements = m_detMgr->getAllSectors();
59} else {
60 elements = m_detMgr->getAllChambers();
61}
62
63std::visit([&](auto& elems) {
64 using SetType = std::decay_t<decltype(elems)>;
65
66 // Initialize station containers of the same type
67 SetType barrel, endcapA, endcapC, endcapMiddleA, endcapMiddleC;
68
69 for (const auto& element : elems) {
70 if (isElementInTheStation(*element,
71 {StIdx::BI, StIdx::BM, StIdx::BO, StIdx::BE, StIdx::EE, StIdx::EI},
73 barrel.push_back(element);
74 } else if (isElementInTheStation(*element, {StIdx::EO}, EndcapSide::A)) {
75 endcapA.push_back(element);
76 } else if (isElementInTheStation(*element, {StIdx::EO}, EndcapSide::C)) {
77 endcapC.push_back(element);
78 } else if (isElementInTheStation(*element, {StIdx::EM}, EndcapSide::A)) {
79 endcapMiddleA.push_back(element);
80 } else if (isElementInTheStation(*element, {StIdx::EM}, EndcapSide::C)) {
81 endcapMiddleC.push_back(element);
82 } else {
83 ATH_MSG_WARNING("Element " << element->identString()
84 << " not assigned to any station!");
85 }
86 }
87
88 // Assign back into the outer variants
89 barrelStations = std::move(barrel);
90 endcapAStations = std::move(endcapA);
91 endcapCStations = std::move(endcapC);
92 endcapMiddleAStations= std::move(endcapMiddleA);
93 endcapMiddleCStations= std::move(endcapMiddleC);
94
95}, elements);
96
97 // Top level node for the Muon system
98auto muonNode = std::make_shared<Acts::Experimental::CylinderContainerBlueprintNode>("MuonNode", Acts::AxisDirection::AxisZ);
99
100Acts::VolumeBoundFactory boundsFactory{};
101auto barrelNode = buildMuonNode(gctx, barrelStations, "BI_BM_BO_EE_EI",Acts::GeometryIdentifier().withVolume(s_muonBarrelId), boundsFactory);
102auto endcapANode = buildMuonNode(gctx, endcapAStations, "EO_A", Acts::GeometryIdentifier().withVolume(s_muonEndcapAId), boundsFactory);
103auto endcapCNode = buildMuonNode(gctx, endcapCStations, "EO_C", Acts::GeometryIdentifier().withVolume(s_muonEndcapCId), boundsFactory);
104auto endcapMiddleANode = buildMuonNode(gctx, endcapMiddleAStations, "EM_A", Acts::GeometryIdentifier().withVolume(s_muonEndcapMiddleAId), boundsFactory);
105auto endcapMiddleCNode = buildMuonNode(gctx, endcapMiddleCStations, "EM_C", Acts::GeometryIdentifier().withVolume(s_muonEndcapMiddleCId), boundsFactory);
106
107//Add to the muon barrel child node (e.g calo or Itk) - if existed
108if(childNode){
109 barrelNode->addChild(std::move(childNode));
110}
111muonNode->addChild(std::move(barrelNode));
112muonNode->addChild(std::move(endcapANode));
113muonNode->addChild(std::move(endcapCNode));
114muonNode->addChild(std::move(endcapMiddleANode));
115muonNode->addChild(std::move(endcapMiddleCNode));
116
117return muonNode;
118
119}
120
121template<typename MuonElementsSet>
122std::shared_ptr<Acts::Experimental::StaticBlueprintNode>
124 const Acts::GeometryContext& gctx,
125 const MuonElementsSet& elements,
126 const std::string& name,
127 const Acts::GeometryIdentifier& id,
128 Acts::VolumeBoundFactory& boundsFactory) const {
129
130 const ActsTrk::GeometryContext* context = gctx.get<const ActsTrk::GeometryContext* >();
131 std::vector<std::string> stationNames;
132
133 std::vector<std::shared_ptr<Acts::Experimental::StaticBlueprintNode>> nodes;
134
135 double innerRadius = 0.0;
136 double outerRadius = std::numeric_limits<double>::lowest();
137 double maxZ = std::numeric_limits<double>::lowest();
138 double minZ = std::numeric_limits<double>::max();
139 int chamberId = 1;
140 std::visit([&](const auto& elems){
141
142 for(const auto& element : elems){
143 const Amg::Transform3D& transform = element->localToGlobalTransform(*context);
144 std::string volName = element->identString();
145
146 auto vol = std::make_unique<Acts::TrackingVolume>(
147 transform,
148 element->bounds(),
149 volName);
150
151 // Get material per chamber, blend it and place it in the center of the volume
152 auto material = blendMaterial(*element);
153 vol->addSurface(std::move(material));
154 // //the chamber geometry id
155 Acts::GeometryIdentifier chId = id.withLayer(chamberId++);
156 vol->assignGeometryId(chId);
157 std::pair<std::vector<staticNodePtr>,std::vector<surfacePtr>> innerStructure = getSensitiveElements(*context, *element, chId, boundsFactory);
158 for(auto& surface: innerStructure.second){
159 vol->addSurface(surface);
160 }
161 //calculate the bounds of the cylinder container
162 for(const auto& surface: vol->boundarySurfaces()){
163 const auto& surfaceRepr = surface->surfaceRepresentation();
164 const Acts::Polyhedron& polyhedron = surfaceRepr.polyhedronRepresentation(gctx);
165 const Amg::Vector3D& center = surfaceRepr.center(gctx);
166
167 maxZ = std::max(maxZ, center.z());
168 minZ = std::min(minZ, center.z());
169
170 // Outer radius needs to be treated differently due to curvature of cylindrical surface
171 for(const Amg::Vector3D& vertex: polyhedron.vertices){
172 outerRadius = std::max(outerRadius, vertex.perp());
173 }
174 }
175
176 std::shared_ptr<Acts::Experimental::StaticBlueprintNode> node;
177 const bool isSingleMdt =
178 (element->readoutEles().size() == 1 &&
179 element->readoutEles().front()->detectorType() == DetectorType::Mdt);
180
181 if (isSingleMdt) {
182 // Take ownership of the single existing node
183 node = std::move(innerStructure.first.front());
184 } else {
185 node = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(vol));
186 for (auto& childNode : innerStructure.first) {
187 node->addChild(std::move(childNode));
188 }
189 innerStructure.first.clear();
190 }
191 if (!node) {
192 THROW_EXCEPTION("No blueprint node constructed");
193 }
194 nodes.emplace_back(std::move(node));
195 }
196
197 }, elements);
198
199 double halfLengthZ = 0.5 * std::abs(maxZ - minZ);
200 ATH_MSG_DEBUG("Inner radius: " << innerRadius);
201 ATH_MSG_DEBUG("Outer radius: " << outerRadius);
202 ATH_MSG_DEBUG("Max Z: " << maxZ);
203 ATH_MSG_DEBUG("Min Z: " << minZ);
204 ATH_MSG_DEBUG("Half length Z: " << halfLengthZ);
205
206 Amg::Transform3D trf = Amg::getTranslateZ3D(halfLengthZ + minZ);
207
208 auto bounds = boundsFactory.makeBounds<Acts::CylinderVolumeBounds>(innerRadius, outerRadius, halfLengthZ);
209 auto volume = std::make_unique<Acts::TrackingVolume>(trf, bounds, name);
210 volume->assignGeometryId(id);
211 auto muonNode = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(volume));
212
213 ATH_MSG_DEBUG("There are " << nodes.size() << " nodes");
214 std::ranges::for_each(nodes, [&muonNode](auto& node) {
215 muonNode->addChild(std::move(node));
216 });
217 return muonNode;
218 }
219
220template<typename T>
221std::pair<std::vector<staticNodePtr>, std::vector<surfacePtr>>
223 const ActsTrk::GeometryContext& gctx,
224 const T& element,
225 const Acts::GeometryIdentifier& chId,
226 Acts::VolumeBoundFactory& boundsFactory) const {
227
228 std::vector<staticNodePtr> readoutVolumes;
229 std::vector<surfacePtr> readoutSurfaces;
230 Acts::GeometryIdentifier::Value mdtId{1};
231
232 //lamda function for BIS78 MDT case
233 auto isBIS78 = [this](const MuonGMR4::MuonReadoutElement* rElem) {
234
235 if(rElem->detectorType() != DetectorType::Mdt){
236 return false;
237 }
238
239 auto& mdtIdHelper = m_detMgr->idHelperSvc()->mdtIdHelper();
240 const int BIS = mdtIdHelper.stationNameIndex("BIS");
241 int stEta = rElem->stationEta();
242
243 return rElem->stationName() == BIS && std::abs(stEta) >= 7;
244
245 };
246
247 for (const MuonGMR4::MuonReadoutElement* readoutEle : element.readoutEles()) {
248
249 std::vector<surfacePtr> detSurfaces = readoutEle->getSurfaces();
250 switch(readoutEle->detectorType()){
251 case DetectorType::Mdt: {
252 const auto* mdtReadoutEle = static_cast<const MuonGMR4::MdtReadoutElement*>(readoutEle);
253 const MuonGMR4::MdtReadoutElement::parameterBook& parameters{mdtReadoutEle->getParameters()};
254
255 // get the transform to the sector's frame
256 const Amg::Vector3D toChamber = element.globalToLocalTransform(gctx)*mdtReadoutEle->center(gctx);
257 Acts::Transform3 mdtTransform = element.localToGlobalTransform(gctx) * Amg::getTranslate3D(toChamber);
258
259 // create the MDT multilayer volume with the dedicated builder
260 Acts::Experimental::MultiWireVolumeBuilder::Config mwCfg;
261 mwCfg.name = m_detMgr->idHelperSvc()->toStringDetEl(mdtReadoutEle->identify());
262 mwCfg.mlSurfaces = detSurfaces;
263 mwCfg.transform = mdtTransform;
264
265 //special treatment of BIS78 MDT multilayer
266 //use different shape because of clashes with EIL chambers
267 std::shared_ptr<Acts::VolumeBounds> mdtBounds{nullptr};
268 if(isBIS78(readoutEle) && mdtReadoutEle->multilayer() == 2){
269
270 //find the minimum and the maximum tube length (x dimension of the diamond bounds)
271 std::vector<double> tubeLengths;
272 tubeLengths.reserve(mdtReadoutEle->numTubesInLay());
273 for(std::size_t tube = 1; tube < mdtReadoutEle->numTubesInLay(); ++tube){
275 double tubeLength = mdtReadoutEle->tubeLength(tubeHash);
276 tubeLengths.push_back(tubeLength);
277 }
278 auto [minX,maxX] = std::ranges::minmax_element(tubeLengths);
279 int nSmallTubes = std::count_if(tubeLengths.begin(), tubeLengths.end(), [minX](double length){
280 return std::abs(*minX-length) < Acts::s_epsilon;
281 });
282
283 //create the diamond bounds for the volume
284 double y2 = (nSmallTubes+1)*parameters.tubePitch ;
285 double y1 = 2*parameters.halfY - y2;
286 mdtTransform = mdtTransform*Amg::getTranslateY3D(parameters.halfY-y2);
287 mdtBounds = boundsFactory.makeBounds<Acts::DiamondVolumeBounds>(0.5*(*maxX), 0.5*(*maxX), 0.5*(*minX),
288 y1, y2, parameters.halfHeight);
289 }else{
290 //check for rectangular or trapezoidal shape bounds
291 if(std::abs(parameters.shortHalfX - parameters.longHalfX) < Acts::s_epsilon){
292 mdtBounds = boundsFactory.makeBounds<Acts::CuboidVolumeBounds>(parameters.shortHalfX, parameters.halfY, parameters.halfHeight);
293 } else {
294 mdtBounds = boundsFactory.makeBounds<Acts::TrapezoidVolumeBounds>(parameters.shortHalfX,
295 parameters.longHalfX, parameters.halfY, parameters.halfHeight);
296 }
297 }
298 mwCfg.bounds = mdtBounds;
299 mwCfg.transform = mdtTransform;
300 using BoundsV = Acts::TrapezoidVolumeBounds::BoundValues;
301 mwCfg.binning = {{{Acts::AxisDirection::AxisY, Acts::AxisBoundaryType::Bound,
302 -parameters.halfY,
303 parameters.halfY,
304 static_cast<std::size_t>(std::lround(2 * parameters.halfY / parameters.tubePitch))}, 2u},
305 {{Acts::AxisDirection::AxisZ, Acts::AxisBoundaryType::Bound,
306 -parameters.halfHeight,
307 parameters.halfHeight,
308 static_cast<std::size_t>(std::lround(2 * parameters.halfHeight / parameters.tubePitch))}, 1u}};
309 Acts::Experimental::MultiWireVolumeBuilder mdtBuilder{mwCfg};
310 std::unique_ptr<Acts::TrackingVolume> mdtVolume = mdtBuilder.buildVolume();
311
312 mdtVolume->assignGeometryId(chId.withExtra(mdtId++));
313 //create the blueprint node for the mdt multilayers
314 std::shared_ptr<Acts::Experimental::StaticBlueprintNode> mdtNode = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(mdtVolume));
315 mdtNode->setNavigationPolicyFactory(mdtBuilder.createNavigationPolicyFactory());
316 readoutVolumes.push_back(std::move(mdtNode));
317
318 break;
319
320 } case DetectorType::Rpc:
323 case DetectorType::Mm: {
324
325 readoutSurfaces.insert(readoutSurfaces.end(), std::make_move_iterator(detSurfaces.begin()),
326 std::make_move_iterator(detSurfaces.end()));
327
328 break;
329
330 } default:
331 THROW_EXCEPTION("Unknown detector type for readout element: " << ActsTrk::to_string(readoutEle->detectorType()));
332 break;
333
334 }
335 }
336
337 return std::make_pair(std::move(readoutVolumes), std::move(readoutSurfaces));
338}
339
340
341template<typename T>
342std::shared_ptr<Acts::Surface>
344 const T& element) const {
345
346 const float thickness = element.halfZ() * 2;
347 PVConstLink parentVolume = element.readoutEles().front()->getMaterialGeom()->getParent();
348 GeoModelTools::GeoMaterialHelper geoMaterialHelper;
349 std::pair<GeoModelTools::GeoMaterialPtr, double> geoMaterials = geoMaterialHelper.collectMaterial(parentVolume);
350
351 const Acts::Material aMat = ActsPlugins::GeoModel::geoMaterialConverter(*geoMaterials.first);
352 //rotate about the z axis
353 auto constPtr = element.surface().getSharedPtr();
354 //to assign the material shouldnt be const
355 auto ptr = std::const_pointer_cast<Acts::Surface>(constPtr);
356
357 Acts::MaterialSlab slab{aMat, thickness};
358 std::shared_ptr<Acts::HomogeneousSurfaceMaterial> material = std::make_shared<Acts::HomogeneousSurfaceMaterial>(slab);
359 ptr->assignSurfaceMaterial(material);
360 return ptr;
361
362}
363
364template<typename T>
365bool MuonBlueprintNodeBuilder::isElementInTheStation(const T& element, const std::vector<StIdx>& stationIndex, const EndcapSide& side) const {
366 StIdx stationIdx = toStationIndex(element.chamberIndex());
367 auto stationSide = element.side();
368 bool matchesName = std::ranges::any_of(stationIndex.begin(), stationIndex.end(), [&](const auto& n){
369 return stationIdx == n;
370 });
371
372 bool etaSignCorrect = ((stationSide > 0 && side == EndcapSide::A) || (stationSide < 0 && side == EndcapSide::C) || (side == EndcapSide::Both));
373 return matchesName && etaSignCorrect;
374}
375
376
377} //namespace ActsTrk
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
double length(const pvec &v)
double tubeLength
@ BIS
Definition RegSelEnums.h:11
std::shared_ptr< Acts::Experimental::BlueprintNode > buildBlueprintNode(const Acts::GeometryContext &gctx, std::shared_ptr< Acts::Experimental::BlueprintNode > &&childNode) override
Build the Muon Blueprint Node.
std::shared_ptr< Acts::Surface > blendMaterial(const T &element) const
Blend the sector's/chamber's material as plane surface.
bool isElementInTheStation(const T &element, const std::vector< StIdx > &stationNames, const EndcapSide &side) const
Check if the chamber is in this node.
std::shared_ptr< Acts::Experimental::StaticBlueprintNode > buildMuonNode(const Acts::GeometryContext &gctx, const MuonElementsSet &elements, const std::string &name, const Acts::GeometryIdentifier &id, Acts::VolumeBoundFactory &boundsFactory) const
Build subnodes for the muon system node.
std::pair< std::vector< staticNodePtr >, std::vector< surfacePtr > > getSensitiveElements(const ActsTrk::GeometryContext &gctx, const T &element, const Acts::GeometryIdentifier &chId, Acts::VolumeBoundFactory &boundsFactory) const
Get the chamber's sensitive elements.
const MuonGMR4::MuonDetectorManager * m_detMgr
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.
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)
Muon::MuonStationIndex::StIndex StIdx
@ Mm
Maybe not needed in the migration.
@ Tgc
Resitive Plate Chambers.
@ sTgc
Micromegas (NSW)
@ Rpc
Monitored Drift Tubes.
@ Mdt
MuonSpectrometer.
Amg::Transform3D getTranslate3D(const double X, const double Y, const double Z)
: Returns a shift transformation along an arbitrary axis
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
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10