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