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/ConvexPolygonVolumeBounds.hpp>
23#include <Acts/Surfaces/PlaneSurface.hpp>
24#include <ActsPlugins/GeoModel/GeoModelMaterialConverter.hpp>
25#include <Acts/Visualization/ObjVisualization3D.hpp>
26
27
31
32#include "GeoModelValidation/GeoMaterialHelper.h"
33
34namespace {
35 //Muon System IDs
36constexpr std::size_t s_muonBarrelId = 30;
37constexpr std::size_t s_muonEndcapAId = 31;
38constexpr std::size_t s_muonEndcapCId = 32;
39constexpr std::size_t s_muonEndcapMiddleAId = 33;
40constexpr std::size_t s_muonEndcapMiddleCId = 34;
41}
42
43namespace ActsTrk {
44
46 ATH_CHECK(detStore()->retrieve(m_detMgr));
47 return StatusCode::SUCCESS;
48 }
49
50
51std::shared_ptr<Acts::Experimental::BlueprintNode> MuonBlueprintNodeBuilder::buildBlueprintNode(const Acts::GeometryContext& gctx, std::shared_ptr<Acts::Experimental::BlueprintNode>&& childNode) {
52
53std::variant<MuonChamberSet, MuonSectorSet> elements;
54std::variant<MuonChamberSet, MuonSectorSet> barrelStations, endcapAStations, endcapCStations, endcapMiddleAStations, endcapMiddleCStations;
55
56if (m_useSectors) {
57 elements = m_detMgr->getAllSectors();
58} else {
59 elements = m_detMgr->getAllChambers();
60}
61
62std::visit([&](auto& elems) {
63 using SetType = std::decay_t<decltype(elems)>;
64
65 // Initialize station containers of the same type
66 SetType barrel, endcapA, endcapC, endcapMiddleA, endcapMiddleC;
67
68 for (const auto& element : elems) {
69 if (isElementInTheStation(*element,
70 {StIdx::BI, StIdx::BM, StIdx::BO, StIdx::BE, StIdx::EE, StIdx::EI},
72 barrel.push_back(element);
73 } else if (isElementInTheStation(*element, {StIdx::EO}, EndcapSide::A)) {
74 endcapA.push_back(element);
75 } else if (isElementInTheStation(*element, {StIdx::EO}, EndcapSide::C)) {
76 endcapC.push_back(element);
77 } else if (isElementInTheStation(*element, {StIdx::EM}, EndcapSide::A)) {
78 endcapMiddleA.push_back(element);
79 } else if (isElementInTheStation(*element, {StIdx::EM}, EndcapSide::C)) {
80 endcapMiddleC.push_back(element);
81 } else {
82 ATH_MSG_WARNING("Element " << element->identString()
83 << " not assigned to any station!");
84 }
85 }
86
87 // Assign back into the outer variants
88 barrelStations = std::move(barrel);
89 endcapAStations = std::move(endcapA);
90 endcapCStations = std::move(endcapC);
91 endcapMiddleAStations= std::move(endcapMiddleA);
92 endcapMiddleCStations= std::move(endcapMiddleC);
93
94}, elements);
95
96 // Top level node for the Muon system
97auto muonNode = std::make_shared<Acts::Experimental::CylinderContainerBlueprintNode>("MuonNode", Acts::AxisDirection::AxisZ);
98
99Acts::VolumeBoundFactory boundsFactory{};
100
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}
111
112muonNode->addChild(std::move(barrelNode));
113muonNode->addChild(std::move(endcapANode));
114muonNode->addChild(std::move(endcapCNode));
115muonNode->addChild(std::move(endcapMiddleANode));
116muonNode->addChild(std::move(endcapMiddleCNode));
117
118return muonNode;
119
120}
121
122template<typename MuonElementsSet>
123std::shared_ptr<Acts::Experimental::StaticBlueprintNode>
125 const Acts::GeometryContext& gctx,
126 const MuonElementsSet& elements,
127 const std::string& name,
128 const Acts::GeometryIdentifier& id,
129 Acts::VolumeBoundFactory& boundsFactory) const {
130
131 const ActsTrk::GeometryContext* context = gctx.get<const ActsTrk::GeometryContext* >();
132 std::vector<std::string> stationNames;
133
134 std::vector<std::shared_ptr<Acts::Experimental::StaticBlueprintNode>> nodes;
135
136 double innerRadius = 0.0;
137 double outerRadius = std::numeric_limits<double>::lowest();
138 double maxZ = std::numeric_limits<double>::lowest();
139 double minZ = std::numeric_limits<double>::max();
140
141 int chamberId = 1;
142
143 std::visit([&](const auto& elems){
144
145 for(const auto& element : elems){
146 const Amg::Transform3D& transform = element->localToGlobalTrans(*context);
147 std::string volName = element->identString();
148
149 auto vol = std::make_unique<Acts::TrackingVolume>(
150 transform,
151 element->bounds(),
152 volName);
153
154 // Get material per chamber, blend it and place it in the center of the volume
155 auto material = blendMaterial(*element);
156 vol->addSurface(std::move(material));
157 // //the chamber geometry id
158 Acts::GeometryIdentifier chId = id.withLayer(chamberId++);
159 vol->assignGeometryId(chId);
160
161 std::pair<std::vector<staticNodePtr>,std::vector<surfacePtr>> innerStructure = getSensitiveElements(*context, *element, chId, boundsFactory);
162
163 for(auto& surface: innerStructure.second){
164 vol->addSurface(surface);
165 }
166
167 //calculate the bounds of the cylinder container
168 for(const auto& surface: vol->boundarySurfaces()){
169 const auto& surfaceRepr = surface->surfaceRepresentation();
170 const Acts::Polyhedron& polyhedron = surfaceRepr.polyhedronRepresentation(gctx);
171 const Amg::Vector3D& center = surfaceRepr.center(gctx);
172
173 maxZ = std::max(maxZ, center.z());
174 minZ = std::min(minZ, center.z());
175
176 // Outer radius needs to be treated differently due to curvature of cylindrical surface
177 for(const Amg::Vector3D& vertex: polyhedron.vertices){
178 outerRadius = std::max(outerRadius, vertex.perp());
179 }
180 }
181 if(m_dumpVolumes){
182 //for visualizing each chamber volume individually
183 Acts::ObjVisualization3D helper;
184 vol->visualize(helper, gctx, {.visible = true},
185 {.visible = true}, {.visible = true});
186 helper.write(volName + ".obj");
187 helper.clear();
188 }
189
190 std::shared_ptr<Acts::Experimental::StaticBlueprintNode> node;
191
192 const bool isSingleMdt =
193 (element->readoutEles().size() == 1 &&
194 element->readoutEles().front()->detectorType() == DetectorType::Mdt);
195
196 if (isSingleMdt) {
197 // Take ownership of the single existing node
198 node = std::move(innerStructure.first.front());
199 innerStructure.first.clear();
200 } else {
201 node = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(vol));
202
203 for (auto& childNode : innerStructure.first) {
204 node->addChild(std::move(childNode));
205 }
206 innerStructure.first.clear();
207 }
208
209 if (!node) {
210 THROW_EXCEPTION("No blueprint node constructed");
211 }
212
213 nodes.emplace_back(std::move(node));
214
215 }
216
217 }, elements);
218
219 double halfLengthZ = 0.5 * std::abs(maxZ - minZ);
220 ATH_MSG_DEBUG("Inner radius: " << innerRadius);
221 ATH_MSG_DEBUG("Outer radius: " << outerRadius);
222 ATH_MSG_DEBUG("Max Z: " << maxZ);
223 ATH_MSG_DEBUG("Min Z: " << minZ);
224 ATH_MSG_DEBUG("Half length Z: " << halfLengthZ);
225
226 Amg::Transform3D trf = Amg::getTranslateZ3D(halfLengthZ + minZ);
227
228 auto bounds = boundsFactory.makeBounds<Acts::CylinderVolumeBounds>(innerRadius, outerRadius, halfLengthZ);
229 auto volume = std::make_unique<Acts::TrackingVolume>(trf, bounds, name);
230 volume->assignGeometryId(id);
231 auto muonNode = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(volume));
232
233 ATH_MSG_DEBUG("There are " << nodes.size() << " nodes");
234 std::ranges::for_each(nodes, [&muonNode](auto& node) {
235 muonNode->addChild(std::move(node));
236 });
237 return muonNode;
238 }
239
240template<typename T>
241std::pair<std::vector<staticNodePtr>, std::vector<surfacePtr>>
243 const ActsTrk::GeometryContext& gctx,
244 const T& element,
245 const Acts::GeometryIdentifier& chId,
246 Acts::VolumeBoundFactory& boundsFactory) const {
247
248 std::vector<staticNodePtr> readoutVolumes;
249 std::vector<surfacePtr> readoutSurfaces;
250 Acts::GeometryIdentifier::Value mdtId{1};
251
252 for (const MuonGMR4::MuonReadoutElement* readoutEle : element.readoutEles()) {
253
254 std::vector<surfacePtr> detSurfaces = readoutEle->getSurfaces();
255 switch(readoutEle->detectorType()){
256 case DetectorType::Mdt: {
257 const auto* mdtReadoutEle = static_cast<const MuonGMR4::MdtReadoutElement*>(readoutEle);
258 const MuonGMR4::MdtReadoutElement::parameterBook& parameters{mdtReadoutEle->getParameters()};
259
260 // get the transform to the sector's frame
261 const Amg::Vector3D toChamber = element.globalToLocalTrans(gctx)*mdtReadoutEle->center(gctx);
262 const Acts::Transform3 mdtTransform = element.localToGlobalTrans(gctx) * Amg::getTranslate3D(toChamber);
263
264 // create the MDT multilayer volume with the dedicated builder
265 Acts::Experimental::MultiWireVolumeBuilder::Config mwCfg;
266 mwCfg.name = m_detMgr->idHelperSvc()->toStringDetEl(mdtReadoutEle->identify());
267 mwCfg.mlSurfaces = detSurfaces;
268 mwCfg.transform = mdtTransform;
269
270 //check for rectangular or trapezoidal shape bounds
271 std::shared_ptr<Acts::VolumeBounds> mdtBounds{nullptr};
272
273 if(std::abs(parameters.shortHalfX - parameters.longHalfX) < Acts::s_epsilon){
274
275 mdtBounds = boundsFactory.makeBounds<Acts::CuboidVolumeBounds>(parameters.shortHalfX, parameters.halfY, parameters.halfHeight);
276
277 } else {
278
279 mdtBounds = boundsFactory.makeBounds<Acts::TrapezoidVolumeBounds>(parameters.shortHalfX,
280 parameters.longHalfX, parameters.halfY, parameters.halfHeight);
281 }
282
283 mwCfg.bounds = mdtBounds;
284 using BoundsV = Acts::TrapezoidVolumeBounds::BoundValues;
285 mwCfg.binning = {{{Acts::AxisDirection::AxisY, Acts::AxisBoundaryType::Bound,
286 -parameters.halfY,
287 parameters.halfY,
288 static_cast<std::size_t>(std::lround(2 * parameters.halfY / parameters.tubePitch))}, 2u},
289 {{Acts::AxisDirection::AxisZ, Acts::AxisBoundaryType::Bound,
290 -parameters.halfHeight,
291 parameters.halfHeight,
292 static_cast<std::size_t>(std::lround(2 * parameters.halfHeight / parameters.tubePitch))}, 1u}};
293 Acts::Experimental::MultiWireVolumeBuilder mdtBuilder{mwCfg};
294 std::unique_ptr<Acts::TrackingVolume> mdtVolume = mdtBuilder.buildVolume();
295
296 mdtVolume->assignGeometryId(chId.withExtra(mdtId++));
297 //create the blueprint node for the mdt multilayers
298 std::shared_ptr<Acts::Experimental::StaticBlueprintNode> mdtNode = std::make_shared<Acts::Experimental::StaticBlueprintNode>(std::move(mdtVolume));
299 mdtNode->setNavigationPolicyFactory(mdtBuilder.createNavigationPolicyFactory());
300 readoutVolumes.push_back(std::move(mdtNode));
301
302 break;
303
304 } case DetectorType::Rpc:
307 case DetectorType::Mm: {
308
309 readoutSurfaces.insert(readoutSurfaces.end(), std::make_move_iterator(detSurfaces.begin()),
310 std::make_move_iterator(detSurfaces.end()));
311
312 break;
313
314 } default:
315 THROW_EXCEPTION("Unknown detector type for readout element: " << ActsTrk::to_string(readoutEle->detectorType()));
316 break;
317
318 }
319 }
320
321 return std::make_pair(std::move(readoutVolumes), std::move(readoutSurfaces));
322}
323
324
325template<typename T>
326std::shared_ptr<Acts::Surface>
328 const T& element) const {
329
330 const float thickness = element.halfZ() * 2;
331 PVConstLink parentVolume = element.readoutEles().front()->getMaterialGeom()->getParent();
332 GeoModelTools::GeoMaterialHelper geoMaterialHelper;
333 std::pair<GeoModelTools::GeoMaterialPtr, double> geoMaterials = geoMaterialHelper.collectMaterial(parentVolume);
334
335 const Acts::Material aMat = ActsPlugins::GeoModel::geoMaterialConverter(*geoMaterials.first);
336 //rotate about the z axis
337 auto constPtr = element.surface().getSharedPtr();
338 //to assign the material shouldnt be const
339 auto ptr = std::const_pointer_cast<Acts::Surface>(constPtr);
340
341 Acts::MaterialSlab slab{aMat, thickness};
342 std::shared_ptr<Acts::HomogeneousSurfaceMaterial> material = std::make_shared<Acts::HomogeneousSurfaceMaterial>(slab);
343 ptr->assignSurfaceMaterial(material);
344 return ptr;
345
346}
347
348template<typename T>
349bool MuonBlueprintNodeBuilder::isElementInTheStation(const T& element, const std::vector<StIdx>& stationIndex, const EndcapSide& side) const {
350 StIdx stationIdx = toStationIndex(element.chamberIndex());
351 auto stationSide = element.side();
352 bool matchesName = std::ranges::any_of(stationIndex.begin(), stationIndex.end(), [&](const auto& n){
353 return stationIdx == n;
354 });
355
356 bool etaSignCorrect = ((stationSide > 0 && side == EndcapSide::A) || (stationSide < 0 && side == EndcapSide::C) || (side == EndcapSide::Both));
357 return matchesName && etaSignCorrect;
358}
359
360
361} //namespace ActsTrk
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
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
The MuonReadoutElement is an abstract class representing the geometry representing the 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
Eigen::Affine3d Transform3D
Eigen::Matrix< double, 3, 1 > Vector3D
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10