ATLAS Offline Software
ItkBlueprintNodeBuilder.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 // This absolutely needs to go first to ensure Eigen plugin is loaded
7 //
8 
10 #include <GeoModelKernel/GeoTube.h>
11 #include <GeoModelKernel/GeoVPhysVol.h>
12 
13 
15 
16 #include <Acts/Definitions/Units.hpp>
17 #include <Acts/Geometry/Blueprint.hpp>
18 #include <Acts/Geometry/BlueprintNode.hpp>
19 #include <Acts/Geometry/ContainerBlueprintNode.hpp>
20 #include <Acts/Geometry/CylinderVolumeBounds.hpp>
21 #include <Acts/Geometry/Extent.hpp>
22 #include <Acts/Geometry/GeometryIdentifierBlueprintNode.hpp>
23 #include <Acts/Geometry/LayerBlueprintNode.hpp>
24 #include <Acts/Geometry/MaterialDesignatorBlueprintNode.hpp>
25 #include <Acts/Geometry/ProtoLayer.hpp>
26 #include <Acts/Geometry/VolumeAttachmentStrategy.hpp>
27 #include "Acts/Geometry/VolumeResizeStrategy.hpp"
28 #include "Acts/Material/HomogeneousSurfaceMaterial.hpp"
29 #include <Acts/Navigation/SurfaceArrayNavigationPolicy.hpp>
30 #include <Acts/Surfaces/SurfaceArray.hpp>
31 #include <Acts/Navigation/TryAllNavigationPolicy.hpp>
32 #include <Acts/Utilities/AxisDefinitions.hpp>
33 
34 
35 #include <format>
36 #include <fstream>
37 #include <ranges>
38 
42 #include "ActsInterop/Logger.h"
45 
46 
47 
48 using namespace Acts::UnitLiterals;
49 
50 
51 namespace{
52 
53 constexpr std::size_t s_stripVolumeId = 20;
54 constexpr std::size_t s_innerPixelVolumeId = 5;
55 constexpr std::size_t s_outerPixelVolumeId = 10;
56 constexpr std::size_t s_beamPipeVolumeId = 1;
57 
58 using enum Acts::CylinderVolumeBounds::Face;
59 using enum Acts::AxisDirection;
60 using enum Acts::AxisBoundaryType;
62 using AttachmentStrategy = Acts::VolumeAttachmentStrategy;
63 using ResizeStrategy = Acts::VolumeResizeStrategy;
64 
65 // Helper function to convert shared_ptr vector to const ptr vector
66 std::vector<const Acts::Surface*> makeConstPtrVector(
67  const std::vector<std::shared_ptr<Acts::Surface>>& surfs) {
68  std::vector<const Acts::Surface*> constPtrs;
69  constPtrs.reserve(surfs.size());
70  for (const auto& surf : surfs) {
71  constPtrs.push_back(surf.get());
72  }
73  return constPtrs;
74 }
75 
76 // Helper struct to keep ProtoLayer and its associated surfaces together
77 struct LayerData {
78  Acts::ProtoLayer protoLayer;
79  std::vector<std::shared_ptr<Acts::Surface>> surfaces;
80 
81  LayerData(const Acts::GeometryContext& gctx,
82  std::vector<std::shared_ptr<Acts::Surface>> surfs)
83  : protoLayer(gctx, makeConstPtrVector(surfs)),
84  surfaces(std::move(surfs)) {}
85 };
86 
87 // Helper function to merge layers that overlap in z
88 std::vector<LayerData> mergeLayers(const Acts::GeometryContext& gctx,
89  std::vector<LayerData> layers) {
90  using enum Acts::AxisDirection;
91 
92  std::vector<LayerData> mergedLayers;
93  if (layers.empty()) {
94  return mergedLayers;
95  }
96 
97  mergedLayers.push_back(std::move(layers.front()));
98 
99  for (size_t i = 1; i < layers.size(); i++) {
100  auto& current = layers[i];
101  auto& prev = mergedLayers.back();
102 
103  // Check if they overlap in z
104  bool overlap =
105  (current.protoLayer.min(AxisZ) <= prev.protoLayer.max(AxisZ) &&
106  current.protoLayer.max(AxisZ) >= prev.protoLayer.min(AxisZ));
107 
108  if (overlap) {
109  // Merge surfaces
110  std::vector<std::shared_ptr<Acts::Surface>> mergedSurfaces;
111  mergedSurfaces.reserve(current.surfaces.size() + prev.surfaces.size());
112  mergedSurfaces.insert(mergedSurfaces.end(), current.surfaces.begin(),
113  current.surfaces.end());
114  mergedSurfaces.insert(mergedSurfaces.end(), prev.surfaces.begin(),
115  prev.surfaces.end());
116 
117  mergedLayers.pop_back();
118  mergedLayers.emplace_back(gctx, std::move(mergedSurfaces));
119  auto& merged = mergedLayers.back();
120  merged.protoLayer.envelope[AxisR] = current.protoLayer.envelope[AxisR];
121  merged.protoLayer.envelope[AxisZ] = current.protoLayer.envelope[AxisZ];
122  } else {
123  mergedLayers.push_back(std::move(current));
124  }
125  }
126 
127  return mergedLayers;
128 }
129 
130 
131 void addStripBarrelLayer(
132  Acts::Experimental::BlueprintNode& parent, std::size_t ilayer,
133  const std::vector<std::shared_ptr<Acts::Surface>>& surfaces) {
135  using enum Acts::CylinderVolumeBounds::Face;
136  using enum Acts::AxisDirection;
137  using enum Acts::AxisBoundaryType;
138 
139  auto addLayer = [ilayer, &surfaces](auto& node) {
140  node.addLayer("Strip_Brl_" + std::to_string(ilayer), [&](auto& layer) {
141  layer.setNavigationPolicyFactory(
142  Acts::NavigationPolicyFactory{}
143  .add<Acts::SurfaceArrayNavigationPolicy>(
144  Acts::SurfaceArrayNavigationPolicy::Config{
145  .layerType = Cylinder, .bins = {30, 10}})
146  .add<Acts::TryAllNavigationPolicy>(
147  Acts::TryAllNavigationPolicy::Config{.sensitives = false})
148  .asUniquePtr());
149 
150  layer.setSurfaces(surfaces);
151  layer.setEnvelope(Acts::ExtentEnvelope{{
152  .z = {5_mm, 5_mm},
153  .r = {2_mm, 2_mm},
154  }});
155  });
156  };
157 
158  if (ilayer < 3) {
159  // Inner 3 layers: add material on outer cylinder
160  parent.addMaterial("Strip_Brl_" + std::to_string(ilayer) + "_Material",
161  [&addLayer](auto& lmat) {
162  lmat.configureFace(OuterCylinder,
163  {AxisRPhi, Bound, 20},
164  {AxisZ, Bound, 20});
165  addLayer(lmat);
166  });
167  } else {
168  addLayer(parent);
169  }
170 }
171 
172 void addStripEndcapLayer(
173  Acts::Experimental::BlueprintNode& parent, int bec, const std::string& name,
174  const std::vector<std::shared_ptr<Acts::Surface>>& surfaces) {
176  using enum Acts::CylinderVolumeBounds::Face;
177  using enum Acts::AxisDirection;
178  using enum Acts::AxisBoundaryType;
179 
180  parent.addMaterial(name + "_Material", [&](auto& mat) {
181  mat.configureFace(bec < 0 ? NegativeDisc : PositiveDisc, {AxisR, Bound, 20},
182  {AxisPhi, Bound, 40});
183 
184  mat.addLayer(name, [&surfaces](auto& layer) {
185  layer.setNavigationPolicyFactory(
186  Acts::NavigationPolicyFactory{}
187  .add<Acts::SurfaceArrayNavigationPolicy>(
188  Acts::SurfaceArrayNavigationPolicy::Config{.layerType = Disc,
189  .bins = {30, 30}})
190  .add<Acts::TryAllNavigationPolicy>(
191  Acts::TryAllNavigationPolicy::Config{.sensitives = false})
192  .asUniquePtr());
193 
194  layer.setSurfaces(surfaces);
195  layer.setEnvelope(Acts::ExtentEnvelope{{
196  .z = {0.1_mm, 0.1_mm},
197  .r = {2_mm, 2_mm},
198  }});
199  });
200  });
201 }
202 } // namespace
203 
204 namespace ActsTrk{
205 
207  ATH_CHECK(detStore()->retrieve(m_itkStripMgr, "ITkStrip"));
208  ATH_CHECK(detStore()->retrieve(m_itkPixelMgr, "ITkPixel"));
209  if(m_buildBeamPipe) {
210  ATH_CHECK(detStore()->retrieve(m_beamPipeMgr, "BeamPipe"));
211  }
212  m_elementStore = std::make_shared<ActsElementVector>();
213  return StatusCode::SUCCESS;
214 
215 }
216 
217 std::shared_ptr<Acts::Experimental::BlueprintNode> ItkBlueprintNodeBuilder::buildBlueprintNode(
218  const Acts::GeometryContext& gctx, std::shared_ptr<Acts::Experimental::BlueprintNode>&& childNode) {
219 
220  if(childNode) {
221  ATH_MSG_ERROR("Child node for the Itk should be null - no child expected");
222  throw std::runtime_error("Child node is not null");
223  }
224 
225  // The itk node is a container node that will hold both the pixel and strip nodes
226  auto itkNode = std::make_shared<Acts::Experimental::CylinderContainerBlueprintNode>("ItkNode", AxisR);
227 
228  // Add the itk pixel to the node
229  buildItkPixelBlueprintNode(gctx, *itkNode);
230 
231  // Add the itk strip to the node
232  buildItkStripBlueprintNode(gctx, *itkNode);
233 
234  //Add the beam pipe to the node
235  if(m_buildBeamPipe) {
236 
237  buildBeamPipeBlueprintNode(gctx, *itkNode);
238 
239  }
240 
241  return itkNode;
242 
243 }
244 
245 void ItkBlueprintNodeBuilder::buildItkPixelBlueprintNode(
246  const Acts::GeometryContext& gctx, Acts::Experimental::BlueprintNode& node) {
247 
248  // Get ITkPixel parameters from detector manager
249  if (!m_itkPixelMgr) {
250  ATH_MSG_ERROR("ITkPixel manager not available");
251  throw std::runtime_error("ITkPixel manager not available");
252  }
253 
254  ATH_MSG_DEBUG("Detector manager has "
255  << m_itkPixelMgr->getDetectorElementCollection()->size()
256  << " elements");
257 
258  std::vector<std::shared_ptr<ActsDetectorElement>> elements;
259 
261  for (const auto* element :
262  *m_itkPixelMgr->getDetectorElementCollection()) {
263  const InDetDD::SiDetectorElement* siDetElement =
264  dynamic_cast<const InDetDD::SiDetectorElement*>(element);
265  if (siDetElement == nullptr) {
266  ATH_MSG_ERROR("Detector element was nullptr");
267  throw std::runtime_error{"Corrupt detector element collection"};
268  }
269  elements.push_back(std::make_shared<ActsDetectorElement>(*siDetElement));
270  }
271  ATH_MSG_VERBOSE("Retrieved " << elements.size() << " elements");
272 
273  // Copy to service level store to extend lifetime
274  m_elementStore->vector().insert(m_elementStore->vector().end(),
275  elements.begin(), elements.end());
276 
277  // Create containers for inner and outer pixel parts
278  node.addMaterial("InnerPixelMaterial", [&](auto& mat) {
279  mat.configureFace(OuterCylinder, {AxisRPhi, Bound, 20}, {AxisZ, Bound, 20});
280 
281  auto& innerPixelContainer = mat.addCylinderContainer("InnerPixel", AxisZ);
282 
283  // Add barrel container
284  auto& barrelGeoId = innerPixelContainer.withGeometryIdentifier();
285  barrelGeoId.setAllVolumeIdsTo(s_innerPixelVolumeId)
286  .incrementLayerIds(1)
287  .sortBy([](auto& a, auto& b) {
288  auto& boundsA =
289  dynamic_cast<const Acts::CylinderVolumeBounds&>(a.volumeBounds());
290  auto& boundsB =
291  dynamic_cast<const Acts::CylinderVolumeBounds&>(b.volumeBounds());
292 
293  using enum Acts::CylinderVolumeBounds::BoundValues;
294  double aMidR = (boundsA.get(eMinR) + boundsA.get(eMaxR)) / 2.0;
295  double bMidR = (boundsB.get(eMinR) + boundsB.get(eMaxR)) / 2.0;
296 
297  return aMidR < bMidR;
298  });
299 
300  auto& barrel = barrelGeoId.addCylinderContainer("InnerPixel_Brl", AxisR);
301  barrel.setAttachmentStrategy(AttachmentStrategy::Gap);
302  barrel.setResizeStrategy(ResizeStrategy::Gap);
303 
304  std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
305 
306  for (auto& element : elements) {
307  IdentityHelper id = element->identityHelper();
308  if (id.bec() != 0) {
309  continue;
310  }
311 
312  if (id.layer_disk() >= 2) {
313  continue;
314  }
315 
316  int elementLayer = id.layer_disk();
317  layers[elementLayer].push_back(element->surface().getSharedPtr());
318  }
319 
320  ATH_MSG_DEBUG("Adding " << layers.size() << " layers to InnerPixel barrel");
321 
322  for (const auto& [ilayer, surfaces] : layers) {
323  ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
324  << " surfaces");
325 
326  barrel.addMaterial(
327  std::format("InnerPixel_Brl_{}_Material", ilayer), [&](auto& lmat) {
328  // Innermost layer: outer cylinder, else inner cylinder
329  lmat.configureFace(OuterCylinder, {AxisRPhi, Bound, 40},
330  {AxisZ, Bound, 20});
331 
332  // Add layer with surfaces
333  auto& layer =
334  lmat.addLayer(std::format("InnerPixel_Brl_{}", ilayer));
335 
336  // Set navigation policy for efficient surface lookup
337  layer.setNavigationPolicyFactory(
338  Acts::NavigationPolicyFactory{}
339  .add<Acts::SurfaceArrayNavigationPolicy>(
340  Acts::SurfaceArrayNavigationPolicy::Config{
341  .layerType = Cylinder,
342  .bins = {30, 10}}) // @TODO: Improve this logic
343  .add<Acts::TryAllNavigationPolicy>(
344  Acts::TryAllNavigationPolicy::Config{.sensitives =
345  false})
346  .asUniquePtr());
347 
348  layer.setSurfaces(surfaces);
349  layer.setEnvelope(Acts::ExtentEnvelope{{
350  .z = {5_mm, 5_mm},
351  .r = {2_mm, 2_mm},
352  }});
353  });
354  }
355 
356  // Add endcap containers
357  for (int bec : {-2, 2}) { // Negative and positive endcaps
358  std::string s = bec > 0 ? "p" : "n";
359  auto& ecGeoId = innerPixelContainer.withGeometryIdentifier();
360  ecGeoId.setAllVolumeIdsTo(s_innerPixelVolumeId + std::floor(bec / 2))
361  .incrementLayerIds(1);
362  auto& ec = ecGeoId.addCylinderContainer("InnerPixel_" + s + "EC", AxisZ);
363  ec.setAttachmentStrategy(AttachmentStrategy::Gap);
364  ec.setResizeStrategy(ResizeStrategy::Expand);
365 
366  std::map<std::tuple<int, int, int>,
367  std::vector<std::shared_ptr<Acts::Surface>>>
368  initialLayers{};
369 
370  for (auto& element : elements) {
371  IdentityHelper id = element->identityHelper();
372  if (id.bec() * bec <= 0) {
373  continue; // Skip if not in the right endcap
374  }
375 
376  if (id.layer_disk() >= 3) {
377  continue; // Only include first 3 disks for inner pixel
378  }
379 
380  std::tuple<int, int, int> key{id.bec(), id.layer_disk(),
381  id.eta_module()};
382  initialLayers[key].push_back(element->surface().getSharedPtr());
383  }
384 
385  ATH_MSG_DEBUG("Found " << initialLayers.size()
386  << " initial layers to InnerPixel " << s << "EC");
387 
388  // Create proto layers from surfaces
389  std::vector<LayerData> protoLayers;
390  protoLayers.reserve(initialLayers.size());
391 
392  for (const auto& [key, surfaces] : initialLayers) {
393  auto& layer = protoLayers.emplace_back(gctx, surfaces);
394  layer.protoLayer.envelope[AxisR] = {2_mm, 2_mm};
395  layer.protoLayer.envelope[AxisZ] = {1_mm, 1_mm};
396  }
397 
398  // Sort by z position
399  std::ranges::sort(protoLayers,
400  [](const LayerData& a, const LayerData& b) {
401  return std::abs(a.protoLayer.medium(AxisZ)) <
402  std::abs(b.protoLayer.medium(AxisZ));
403  });
404 
405  ATH_MSG_DEBUG("Found " << protoLayers.size() << " initial layers");
406 
407  // Merge overlapping layers
408  std::vector<LayerData> mergedLayers;
409  if (m_doEndcapLayerMerging) {
410  mergedLayers = mergeLayers(gctx, std::move(protoLayers));
411  } else {
412  mergedLayers = std::move(protoLayers);
413  }
414 
415  ATH_MSG_DEBUG("After merging: " << mergedLayers.size() << " layers");
416 
417  // Create layers from merged proto layers
418  for (const auto [key, pl] : Acts::enumerate(mergedLayers)) {
419  ATH_MSG_DEBUG("- Layer " << key << " has " << pl.surfaces.size()
420  << " surfaces");
421 
422  pl.protoLayer.medium(AxisZ);
423  auto layerName = std::format("InnerPixel_{}EC_{}", key, s);
424 
425  auto addLayer = [&layerName, &pl](auto& parent) {
426  // Add layer with surfaces
427  auto& layer = parent.addLayer(layerName);
428 
429  // Set navigation policy for efficient surface lookup
430  layer.setNavigationPolicyFactory(
431  Acts::NavigationPolicyFactory{}
432  .add<Acts::SurfaceArrayNavigationPolicy>(
433  Acts::SurfaceArrayNavigationPolicy::Config{
434  .layerType = Disc,
435  .bins = {30, 30}}) // @TODO: Improve this logic
436  .add<Acts::TryAllNavigationPolicy>(
437  Acts::TryAllNavigationPolicy::Config{.sensitives = false})
438  .asUniquePtr());
439 
440  layer.setSurfaces(pl.surfaces);
441  layer.setEnvelope(Acts::ExtentEnvelope{{
442  .z = {1_mm, 1_mm},
443  .r = {2_mm, 2_mm},
444  }});
445  };
446 
447  ATH_MSG_VERBOSE("Add inner pixel layer"
448  << key << " / " << mergedLayers.size()
449  << " at z = " << pl.protoLayer.medium(AxisZ));
450  if (key < mergedLayers.size() - 1) {
451  ATH_MSG_VERBOSE("Adding material for layer "
452  << layerName );
453  ec.addMaterial(layerName + "_Material", [&](auto& lmat) {
454  // Set binning for endcap layer
455  lmat.configureFace(bec < 0 ? NegativeDisc : PositiveDisc,
456  {AxisR, Bound, 40}, {AxisPhi, Bound, 40});
457  addLayer(lmat);
458  });
459  } else {
460  addLayer(ec);
461  }
462  }
463  }
464  });
465 
466  // Add outer pixel part
467  node.addMaterial("OuterPixelMaterial", [&](auto& mat) {
468  mat.configureFace(OuterCylinder, {AxisRPhi, Bound, 20}, {AxisZ, Bound, 20});
469 
470  auto& outerPixelContainer = mat.addCylinderContainer("OuterPixel", AxisZ);
471 
472  // Add barrel container
473  auto& barrelGeoId = outerPixelContainer.withGeometryIdentifier();
474  barrelGeoId.setAllVolumeIdsTo(s_outerPixelVolumeId).incrementLayerIds(1);
475 
476  auto& barrel = barrelGeoId.addCylinderContainer("OuterPixel_Brl", AxisR);
477  barrel.setAttachmentStrategy(AttachmentStrategy::Gap);
478  barrel.setResizeStrategy(ResizeStrategy::Gap);
479 
480  std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
481 
482  for (auto& element : elements) {
483  IdentityHelper id = element->identityHelper();
484  if (id.bec() != 0) {
485  continue;
486  }
487 
488  if (id.layer_disk() <= 1) {
489  continue;
490  }
491 
492  int elementLayer = id.layer_disk();
493  layers[elementLayer].push_back(element->surface().getSharedPtr());
494  }
495 
496  ATH_MSG_DEBUG("Adding " << layers.size() << " layers to OuterPixel barrel");
497 
498  for (const auto& [ilayer, surfaces] : layers) {
499  ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
500  << " surfaces");
501 
502  barrel.addMaterial(
503  std::format("OuterPixel_Brl_{}_Material", ilayer), [&](auto& lmat) {
504  // Innermost layer: outer cylinder, else inner cylinder
505  lmat.configureFace(OuterCylinder, {AxisRPhi, Bound, 40},
506  {AxisZ, Bound, 20});
507 
508  // Add layer with surfaces
509  auto& layer =
510  lmat.addLayer("OuterPixel_Brl_" + std::to_string(ilayer));
511 
512  // Set navigation policy for efficient surface lookup
513  layer.setNavigationPolicyFactory(
514  Acts::NavigationPolicyFactory{}
515  .add<Acts::SurfaceArrayNavigationPolicy>(
516  Acts::SurfaceArrayNavigationPolicy::Config{
517  .layerType = Cylinder,
518  .bins = {30, 10}}) // @TODO: Improve this logic
519  .add<Acts::TryAllNavigationPolicy>(
520  Acts::TryAllNavigationPolicy::Config{.sensitives =
521  false})
522  .asUniquePtr());
523 
524  layer.setSurfaces(surfaces);
525  layer.setEnvelope(Acts::ExtentEnvelope{{
526  .z = {5_mm, 5_mm},
527  .r = {2_mm, 2_mm},
528  }});
529  });
530  }
531 
532  constexpr static auto addEndcapLayer = [](auto& parent, const auto& name,
533  const auto& surfaces) {
534  parent.addLayer(name, [&surfaces](auto& layer) {
535  layer.setNavigationPolicyFactory(
536  Acts::NavigationPolicyFactory{}
537  .add<Acts::SurfaceArrayNavigationPolicy>(
538  Acts::SurfaceArrayNavigationPolicy::Config{
539  .layerType = Disc, .bins = {30, 30}})
540  .add<Acts::TryAllNavigationPolicy>(
541  Acts::TryAllNavigationPolicy::Config{.sensitives = false})
542  .asUniquePtr());
543 
544  layer.setSurfaces(surfaces);
545  });
546  };
547 
548  // Add outer pixel endcaps
549  for (int bec : {-2, 2}) { // Negative and positive endcaps
550  const std::string s = bec > 0 ? "p" : "n";
551 
552  // R stacked Z rings
553 
554  auto& ec_outer_geoId = outerPixelContainer.withGeometryIdentifier();
555  ec_outer_geoId
556  .setAllVolumeIdsTo(s_outerPixelVolumeId + std::floor(bec / 2))
557  .incrementLayerIds(1);
558 
559  auto& ec_outer =
560  ec_outer_geoId.addCylinderContainer("OuterPixel_" + s + "EC", AxisR);
561 
562  // Three groups of disks stacked in R
563  std::array diskGroups{std::pair{3, 4}, std::pair{6, 5}, std::pair{7, 8}};
564 
565  for (size_t idx = 0; idx < diskGroups.size(); ++idx) {
566  auto [disk1, disk2] = diskGroups[idx];
567 
568  auto& ec_stack = ec_outer.addCylinderContainer(
569  "OuterPixel_" + s + "EC_" + std::to_string(idx), AxisZ);
570 
571  ec_stack.setAttachmentStrategy(AttachmentStrategy::Gap);
572  ec_stack.setResizeStrategy(ResizeStrategy::Expand);
573 
574  // Group sensors by eta rings
575  std::map<std::tuple<int, int>,
576  std::vector<std::shared_ptr<Acts::Surface>>>
577  eta_rings;
578 
579  for (auto& element : elements) {
580  IdentityHelper id = element->identityHelper();
581  if (id.bec() != bec ||
582  (id.layer_disk() != disk1 && id.layer_disk() != disk2)) {
583  continue;
584  }
585  std::tuple<int, int> key{id.layer_disk(), id.eta_module()};
586  eta_rings[key].push_back(element->surface().getSharedPtr());
587  }
588 
589  ATH_MSG_DEBUG("Found " << eta_rings.size() << " eta rings in group "
590  << idx);
591 
592  // Sort rings by absolute z position
593  std::vector<std::vector<std::shared_ptr<Acts::Surface>>> sorted_rings;
594  sorted_rings.reserve(eta_rings.size());
595  for (const auto& [key, surfaces] : eta_rings) {
596  sorted_rings.push_back(surfaces);
597  }
598 
599  std::ranges::sort(sorted_rings, [&gctx](const auto& a, const auto& b) {
600  Acts::ProtoLayer pl_a(gctx, makeConstPtrVector(a));
601  Acts::ProtoLayer pl_b(gctx, makeConstPtrVector(b));
602  return std::abs(pl_a.min(AxisZ)) < std::abs(pl_b.min(AxisZ));
603  });
604 
605  // Create layers from sorted rings
606  for (size_t i = 0; i < sorted_rings.size(); ++i) {
607  const auto& surfaces = sorted_rings[i];
608  auto layerName = "OuterPixel_" + s + "EC_" + std::to_string(idx) +
609  "_" + std::to_string(i);
610 
611  if (i < sorted_rings.size() - 1) {
612  ec_stack.addMaterial(layerName + "_Material", [&](auto& mat) {
613  mat.configureFace(bec > 0 ? PositiveDisc : NegativeDisc,
614  {AxisR, Bound, 20}, {AxisPhi, Bound, 40});
615  addEndcapLayer(mat, layerName, surfaces);
616  });
617  } else {
618  addEndcapLayer(ec_stack, layerName, surfaces);
619  }
620  }
621  }
622  }
623  });
624 
625 }
626 
627 void ItkBlueprintNodeBuilder::buildItkStripBlueprintNode(const Acts::GeometryContext& gctx,
628  Acts::Experimental::BlueprintNode& node) {
629 
630  // Get ITkStrip parameters from detector manager
631  if (!m_itkStripMgr) {
632  ATH_MSG_ERROR("ITkStrip manager not available");
633  throw std::runtime_error("ITkStrip manager not available");
634  }
635 
636  ATH_MSG_DEBUG("Detector manager has "
637  << m_itkStripMgr->getDetectorElementCollection()->size()
638  << " elements");
639 
640  std::vector<std::shared_ptr<ActsDetectorElement>> elements;
641 
643  for (const auto* element :
644  *m_itkStripMgr->getDetectorElementCollection()) {
645  const InDetDD::SiDetectorElement* siDetElement =
646  dynamic_cast<const InDetDD::SiDetectorElement*>(element);
647  if (siDetElement == nullptr) {
648  ATH_MSG_ERROR("Detector element was nullptr");
649  throw std::runtime_error{"Corrupt detector element collection"};
650  }
651  elements.push_back(std::make_shared<ActsDetectorElement>(*siDetElement));
652  }
653  ATH_MSG_VERBOSE("Retrieved " << elements.size() << " elements");
654 
655  // Copy to service level store to extend lifetime
656  m_elementStore->vector().insert(m_elementStore->vector().end(),
657  elements.begin(), elements.end());
658 
659  // Create container for strip part
660  node.addMaterial("StripMaterial", [&](auto& mat) {
661  mat.configureFace(OuterCylinder, {AxisRPhi, Bound, 20}, {AxisZ, Bound, 20});
662 
663  mat.addCylinderContainer("Strip", AxisZ, [&](auto& strips) {
664  // Add barrel container
665  strips.withGeometryIdentifier([this, &elements](auto& geoId) {
666  geoId.setAllVolumeIdsTo(s_stripVolumeId).incrementLayerIds(1);
667 
668  geoId.addCylinderContainer(
669  "Strip_Brl", AxisR, [this, &elements](auto& barrel) {
670  barrel.setAttachmentStrategy(AttachmentStrategy::Gap);
671  barrel.setResizeStrategy(ResizeStrategy::Gap);
672 
673  std::map<int, std::vector<std::shared_ptr<Acts::Surface>>>
674  layers{};
675 
676  for (auto& element : elements) {
677  IdentityHelper id = element->identityHelper();
678  if (id.bec() != 0) {
679  continue;
680  }
681 
682  int elementLayer = id.layer_disk();
683  layers[elementLayer].push_back(
684  element->surface().getSharedPtr());
685  }
686 
687  ATH_MSG_DEBUG("Adding " << layers.size()
688  << " layers to Strip barrel");
689 
690  for (const auto& [ilayer, surfaces] : layers) {
691  ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
692  << " surfaces");
693  addStripBarrelLayer(barrel, ilayer, surfaces);
694  }
695  });
696  });
697 
698  // Add endcap containers
699  for (int bec : {-2, 2}) { // Negative and positive endcaps
700  const std::string s = bec > 0 ? "p" : "n";
701 
702  std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
703 
704  for (auto& element : elements) {
705  IdentityHelper id = element->identityHelper();
706  if (id.bec() * bec <= 0) {
707  continue; // Skip if not in the right endcap
708  }
709 
710  layers[id.layer_disk()].push_back(element->surface().getSharedPtr());
711  }
712 
713  ATH_MSG_DEBUG("Found " << layers.size() << " layers in Strip " << s
714  << "EC");
715 
716  // Sort layers by absolute z position
717  std::vector<std::vector<std::shared_ptr<Acts::Surface>>> sorted_layers;
718  sorted_layers.reserve(layers.size());
719  for (const auto& [key, surfaces] : layers) {
720  sorted_layers.push_back(surfaces);
721  }
722 
723  std::sort(sorted_layers.begin(), sorted_layers.end(),
724  [&gctx](const auto& a, const auto& b) {
725  Acts::ProtoLayer pl_a(gctx, makeConstPtrVector(a));
726  Acts::ProtoLayer pl_b(gctx, makeConstPtrVector(b));
727  return std::abs(pl_a.min(AxisZ)) <
728  std::abs(pl_b.min(AxisZ));
729  });
730 
731  strips.withGeometryIdentifier([&sorted_layers, bec, &s](auto& geoId) {
732  geoId.setAllVolumeIdsTo(s_stripVolumeId + std::floor(bec / 2))
733  .incrementLayerIds(1);
734 
735  geoId.addCylinderContainer(
736  "Strip_" + s + "EC", AxisZ, [&sorted_layers, bec, &s](auto& ec) {
737  ec.setAttachmentStrategy(AttachmentStrategy::Gap);
738  ec.setResizeStrategy(ResizeStrategy::Gap);
739 
740  // Create layers from sorted layers
741  for (size_t i = 0; i < sorted_layers.size(); ++i) {
742  const auto& surfaces = sorted_layers[i];
743  auto layerName = "Strip_" + s + "EC_" + std::to_string(i);
744 
745  addStripEndcapLayer(ec, bec, layerName, surfaces);
746  }
747  });
748  });
749  }
750 
751  });
752  });
753  }
754 
755  void ItkBlueprintNodeBuilder::buildBeamPipeBlueprintNode(const Acts::GeometryContext& /*gctx*/,
756  Acts::Experimental::BlueprintNode& node) {
757 
758 
759  // Get beam pipe parameters from existing code
760  PVConstLink beamPipeTopVolume = m_beamPipeMgr->getTreeTop(0);
761  if (m_beamPipeMgr->getNumTreeTops() == 1) {
762  beamPipeTopVolume =
763  m_beamPipeMgr->getTreeTop(0)->getChildVol(0)->getChildVol(0);
764  }
765 
766  const Amg::Transform3D beamPipeTransform{Amg::getTranslate3D(beamPipeTopVolume->getX().translation())};
767 
768  // Extract radius similar to makeBeamPipeConfig
769  double beamPipeRadius = 20; // Default value
770 
771  const GeoLogVol* beamPipeLogVolume = beamPipeTopVolume->getLogVol();
772  // This should always be set, but let's be safe
773  if (beamPipeLogVolume == nullptr) {
774  ATH_MSG_ERROR("Beam pipe volume has no log volume");
775  throw std::runtime_error("Beam pipe volume has no log volume");
776  }
777 
778  // Get the geoShape and translate
779  const GeoTube* beamPipeTube =
780  dynamic_cast<const GeoTube*>(beamPipeLogVolume->getShape());
781  if (beamPipeTube == nullptr) {
782  ATH_MSG_ERROR("BeamPipeLogVolume was not of type GeoTube");
783  throw std::runtime_error{"BeamPipeLogVolume was not of type GeoTube"};
784  }
785 
786  // Look for SectionC03 to get the actual radius
787  for (unsigned int i = 0; i < beamPipeTopVolume->getNChildVols(); i++) {
788  auto childName = beamPipeTopVolume->getNameOfChildVol(i);
789  if (childName != "SectionC03") {
790  continue; // Skip if not SectionC03
791  }
792  PVConstLink childTopVolume = beamPipeTopVolume->getChildVol(i);
793  const GeoLogVol* childLogVolume = childTopVolume->getLogVol();
794  const GeoTube* childTube =
795  dynamic_cast<const GeoTube*>(childLogVolume->getShape());
796  if (childTube) {
797  beamPipeRadius = 0.5 * (childTube->getRMax() + childTube->getRMin());
798  break;
799  }
800 
801  }
802 
803 
805  "BeamPipe constructed from Database: translation (yes) - radius "
806  << (beamPipeTube ? "(yes)" : "(no)") << " - r = " << beamPipeRadius);
807 
808  ATH_MSG_VERBOSE("BeamPipe shift estimated as : "
809  << beamPipeTransform.translation().transpose());
810 
811  // Add to blueprint following pattern from blueprint_itk.py
812  node.withGeometryIdentifier([&](auto& geoId) {
813  geoId.setAllVolumeIdsTo(s_beamPipeVolumeId);
814 
815  geoId.addMaterial("BeamPipe_Material", [&](auto& mat) {
816  mat.configureFace(OuterCylinder, {AxisRPhi, Bound, 20},
817  {AxisZ, Bound, 20});
818 
819  // Add static volume for beam pipe
820  mat.addStaticVolume(beamPipeTransform,
821  std::make_shared<Acts::CylinderVolumeBounds>(
822  0, beamPipeRadius * 1_mm, 3 * 1_m),
823  "BeamPipe");
824  });
825  });
826 }
827 
828 } //namespace ActsTrk
python.PyKernel.retrieve
def retrieve(aClass, aKey=None)
Definition: PyKernel.py:110
fillPileUpNoiseLumi.current
current
Definition: fillPileUpNoiseLumi.py:52
createLinkingScheme.iter
iter
Definition: createLinkingScheme.py:62
DataModel_detail::const_iterator
Const iterator class for DataVector/DataList.
Definition: DVLIterator.h:82
LayerType
LayerType
Definition: sTGCenumeration.h:55
vtune_athena.format
format
Definition: vtune_athena.py:14
AthMsgStreamMacros.h
ParticleGun_SamplingFraction.bec
int bec
Definition: ParticleGun_SamplingFraction.py:89
IdentityHelper.h
mat
GeoMaterial * mat
Definition: LArDetectorConstructionTBEC.cxx:55
initialize
void initialize()
Definition: run_EoverP.cxx:894
IdentityHelper::layer_disk
int layer_disk() const
Definition: IdentityHelper.cxx:49
module_driven_slicing.layers
layers
Definition: module_driven_slicing.py:113
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
BeamPipeDetectorManager.h
GeoPrimitives.h
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
lumiFormat.i
int i
Definition: lumiFormat.py:85
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
TRT::Hit::layer
@ layer
Definition: HitInfo.h:79
Amg::Transform3D
Eigen::Affine3d Transform3D
Definition: GeoPrimitives.h:46
test_pyathena.parent
parent
Definition: test_pyathena.py:15
xAOD::Disc
@ Disc
Definition: TrackingPrimitives.h:555
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
PyPoolBrowser.node
node
Definition: PyPoolBrowser.py:131
ActsDetectorElement.h
lumiFormat.array
array
Definition: lumiFormat.py:91
python.PyKernel.detStore
detStore
Definition: PyKernel.py:41
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:240
xAOD::Cylinder
@ Cylinder
Definition: TrackingPrimitives.h:554
ActsTrk::to_string
std::string to_string(const DetectorType &type)
Definition: GeometryDefs.h:34
plotBeamSpotMon.b
b
Definition: plotBeamSpotMon.py:76
InDetDD::SiDetectorElement
Definition: SiDetectorElement.h:109
a
TList * a
Definition: liststreamerinfos.cxx:10
GeoPrimitivesHelpers.h
ItkBlueprintNodeBuilder.h
LArNewCalib_DelayDump_OFC_Cali.idx
idx
Definition: LArNewCalib_DelayDump_OFC_Cali.py:69
python.SystemOfUnits.s
float s
Definition: SystemOfUnits.py:147
ActsTrk
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
Definition: MSTrackingVolumeBuilder.cxx:25
Logger.h
IdentityHelper
Definition: IdentityHelper.h:14
ActsElementVector.h
Helper to hold elements for deletion.
node
Definition: node.h:21
Amg::getTranslate3D
Amg::Transform3D getTranslate3D(const double X, const double Y, const double Z)
: Returns a shift transformation along an arbitrary axis
Definition: GeoPrimitivesHelpers.h:289
mapkey::key
key
Definition: TElectronEfficiencyCorrectionTool.cxx:37