ATLAS Offline Software
Loading...
Searching...
No Matches
ActsTrk::MuonBlueprintNodeBuilder Class Reference

Helper class to build a Blueprint node of the muon system. More...

#include <MuonBlueprintNodeBuilder.h>

Inheritance diagram for ActsTrk::MuonBlueprintNodeBuilder:
Collaboration diagram for ActsTrk::MuonBlueprintNodeBuilder:

Public Types

enum class  EndcapSide { A , C , Both }

Public Member Functions

StatusCode initialize () override
std::shared_ptr< Acts::Experimental::BlueprintNode > buildBlueprintNode (const Acts::GeometryContext &gctx, std::shared_ptr< Acts::Experimental::BlueprintNode > &&childNode) override
 Build the Muon Blueprint Node.

Private Member Functions

template<typename T>
std::shared_ptr< Acts::Surface > blendMaterial (const T &element) const
 Blend the sector's/chamber's material as plane surface.
template<typename T>
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.
template<typename T>
bool isElementInTheStation (const T &element, const std::vector< StIdx > &stationNames, const EndcapSide &side) const
 Check if the chamber is in this node.
template<typename MuonElementsSet>
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.

Private Attributes

const MuonGMR4::MuonDetectorManagerm_detMgr {nullptr}
Gaudi::Property< bool > m_useSectors {this, "UseSectors", false}

Detailed Description

Helper class to build a Blueprint node of the muon system.

It builds the whole muon system for PhaseII adding it to the Blueprint as a node.

Definition at line 49 of file MuonBlueprintNodeBuilder.h.

Member Enumeration Documentation

◆ EndcapSide

Enumerator
Both 

Definition at line 53 of file MuonBlueprintNodeBuilder.h.

53 {
54 A,
55 C,
56 Both
57 };
struct color C

Member Function Documentation

◆ blendMaterial()

template<typename T>
std::shared_ptr< Acts::Surface > ActsTrk::MuonBlueprintNodeBuilder::blendMaterial ( const T & element) const
private

Blend the sector's/chamber's material as plane surface.

Parameters
elementThe element for which to blend the material for This function returns a plane surface with the element's (chamber or sector) material assigned to be placed at the center of the element.

Definition at line 343 of file MuonBlueprintNodeBuilder.cxx.

344 {
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}
void * ptr(T *p)
Definition SGImplSvc.cxx:74

◆ buildBlueprintNode()

std::shared_ptr< Acts::Experimental::BlueprintNode > ActsTrk::MuonBlueprintNodeBuilder::buildBlueprintNode ( const Acts::GeometryContext & gctx,
std::shared_ptr< Acts::Experimental::BlueprintNode > && childNode )
override

Build the Muon Blueprint Node.

Parameters
gctxGeometry context
childNodeThe blueprint node as child of this node (for Muon System it should be Calo or Itk).

Definition at line 52 of file MuonBlueprintNodeBuilder.cxx.

52 {
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}
#define ATH_MSG_WARNING(x)
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.
const MuonGMR4::MuonDetectorManager * m_detMgr

◆ buildMuonNode()

template<typename MuonElementsSet>
std::shared_ptr< Acts::Experimental::StaticBlueprintNode > ActsTrk::MuonBlueprintNodeBuilder::buildMuonNode ( const Acts::GeometryContext & gctx,
const MuonElementsSet & elements,
const std::string & name,
const Acts::GeometryIdentifier & id,
Acts::VolumeBoundFactory & boundsFactory ) const
private

Build subnodes for the muon system node.

Parameters
gctxThe geometry context
elementsThe name of the stations to include
sideThe side (A, C or Both)
idThe geometry identifier of this node
boundsFactoryThe factory for volume bounds

Definition at line 123 of file MuonBlueprintNodeBuilder.cxx.

128 {
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 }
#define ATH_MSG_DEBUG(x)
std::shared_ptr< Acts::Surface > blendMaterial(const T &element) const
Blend the sector's/chamber's material as plane surface.
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.
@ Mdt
MuonSpectrometer.
Amg::Transform3D getTranslateZ3D(const double Z)
: Returns a shift transformation along the z-axis
Eigen::Affine3d Transform3D
Amg::Vector3D transform(Amg::Vector3D &v, Amg::Transform3D &tr)
Transform a point from a Trasformation3D.
Eigen::Matrix< double, 3, 1 > Vector3D
#define THROW_EXCEPTION(MESSAGE)
Definition throwExcept.h:10

◆ getSensitiveElements()

template<typename T>
std::pair< std::vector< staticNodePtr >, std::vector< surfacePtr > > ActsTrk::MuonBlueprintNodeBuilder::getSensitiveElements ( const ActsTrk::GeometryContext & gctx,
const T & element,
const Acts::GeometryIdentifier & chId,
Acts::VolumeBoundFactory & boundsFactory ) const
private

Get the chamber's sensitive elements.

Parameters
gctxThe geometry context
elementThe element for which to get the sensitive elements (chamber or sector)
chIdThe geometry identifier of the chamber
boundsFactoryThe factory for volume bounds This function constructs and returns the sensitive elements (volumes and surfaces) of the sector.

Definition at line 222 of file MuonBlueprintNodeBuilder.cxx.

226 {
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){
274 const IdentifierHash tubeHash = MuonGMR4::MdtReadoutElement::measurementHash(1,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}
double length(const pvec &v)
double tubeLength
@ BIS
Definition RegSelEnums.h:11
static IdentifierHash measurementHash(unsigned layerNumber, unsigned tubeNumber)
Constructs a Measurement hash from layer && tube number.
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.
Amg::Transform3D getTranslate3D(const double X, const double Y, const double Z)
: Returns a shift transformation along an arbitrary axis
Amg::Transform3D getTranslateY3D(const double Y)
: Returns a shift transformation along the y-axis

◆ initialize()

StatusCode ActsTrk::MuonBlueprintNodeBuilder::initialize ( )
override

Definition at line 46 of file MuonBlueprintNodeBuilder.cxx.

46 {
48 return StatusCode::SUCCESS;
49 }
#define ATH_CHECK
Evaluate an expression and check for errors.
retrieve(aClass, aKey=None)
Definition PyKernel.py:110

◆ isElementInTheStation()

template<typename T>
bool ActsTrk::MuonBlueprintNodeBuilder::isElementInTheStation ( const T & element,
const std::vector< StIdx > & stationNames,
const EndcapSide & side ) const
private

Check if the chamber is in this node.

Parameters
elementThe element to check (chamber or sector)
stationNamesThe names of the stations to check against
sideThe side of the endcap (A, C or Both) This function checks if the chamber is part of the configured chambers in this node. It is used to filter out chambers that are not part of this muon node.

Definition at line 365 of file MuonBlueprintNodeBuilder.cxx.

365 {
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}
Muon::MuonStationIndex::StIndex StIdx
StIndex toStationIndex(ChIndex index)
convert ChIndex into StIndex

Member Data Documentation

◆ m_detMgr

const MuonGMR4::MuonDetectorManager* ActsTrk::MuonBlueprintNodeBuilder::m_detMgr {nullptr}
private

Definition at line 69 of file MuonBlueprintNodeBuilder.h.

69{nullptr};

◆ m_useSectors

Gaudi::Property<bool> ActsTrk::MuonBlueprintNodeBuilder::m_useSectors {this, "UseSectors", false}
private

Definition at line 71 of file MuonBlueprintNodeBuilder.h.

71{this, "UseSectors", false}; // Flag to control if we want to build the muon node from sectors or chambers

The documentation for this class was generated from the following files: