ATLAS Offline Software
Loading...
Searching...
No Matches
ItkBlueprintNodeBuilder.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 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 <Acts/Definitions/Units.hpp>
10#include <Acts/Geometry/Blueprint.hpp>
11#include <Acts/Geometry/BlueprintNode.hpp>
12#include <Acts/Geometry/ContainerBlueprintNode.hpp>
13#include <Acts/Geometry/CylinderVolumeBounds.hpp>
14#include <Acts/Geometry/Extent.hpp>
15#include <Acts/Geometry/GeometryIdentifierBlueprintNode.hpp>
16#include <Acts/Geometry/LayerBlueprintNode.hpp>
17#include <Acts/Geometry/MaterialDesignatorBlueprintNode.hpp>
18#include <Acts/Geometry/ProtoLayer.hpp>
19#include <Acts/Geometry/VolumeAttachmentStrategy.hpp>
20#include <Acts/Navigation/SurfaceArrayNavigationPolicy.hpp>
21#include <Acts/Navigation/TryAllNavigationPolicy.hpp>
22#include <Acts/Surfaces/SurfaceArray.hpp>
23#include <Acts/Utilities/AxisDefinitions.hpp>
24#include <cstddef>
25#include <format>
26#include <ranges>
27
28#include "Acts/Geometry/VolumeResizeStrategy.hpp"
34
35using namespace Acts;
36using namespace Acts::Experimental;
37using namespace Acts::UnitLiterals;
38
39namespace {
40
41using enum Acts::CylinderVolumeBounds::Face;
42using enum Acts::AxisDirection;
43using enum Acts::AxisBoundaryType;
44using enum Acts::SurfaceArrayNavigationPolicy::LayerType;
45using AttachmentStrategy = Acts::VolumeAttachmentStrategy;
46using ResizeStrategy = Acts::VolumeResizeStrategy;
47using namespace ActsTrk::detail::GeoVolIds;
48
49// Helper function to convert shared_ptr vector to const ptr vector
50std::vector<const Acts::Surface*> makeConstPtrVector(
51 const std::vector<std::shared_ptr<Acts::Surface>>& surfs) {
52 std::vector<const Acts::Surface*> constPtrs;
53 constPtrs.reserve(surfs.size());
54 for (const auto& surf : surfs) {
55 constPtrs.push_back(surf.get());
56 }
57 return constPtrs;
58}
59
60// Helper struct to keep ProtoLayer and its associated surfaces together
61struct LayerData {
62 Acts::ProtoLayer protoLayer;
63 std::vector<std::shared_ptr<Acts::Surface>> surfaces;
64
65 LayerData(const Acts::GeometryContext& gctx,
66 std::vector<std::shared_ptr<Acts::Surface>> surfs)
67 : protoLayer(gctx, makeConstPtrVector(surfs)),
68 surfaces(std::move(surfs)) {}
69};
70
71// Helper function to merge layers that overlap in z
72std::vector<LayerData> mergeLayers(const Acts::GeometryContext& gctx,
73 std::vector<LayerData> layers) {
74 using enum Acts::AxisDirection;
75
76 std::vector<LayerData> mergedLayers;
77 if (layers.empty()) {
78 return mergedLayers;
79 }
80
81 mergedLayers.push_back(std::move(layers.front()));
82
83 for (size_t i = 1; i < layers.size(); i++) {
84 auto& current = layers[i];
85 auto& prev = mergedLayers.back();
86
87 // Check if they overlap in z
88 bool overlap =
89 (current.protoLayer.min(AxisZ) <= prev.protoLayer.max(AxisZ) &&
90 current.protoLayer.max(AxisZ) >= prev.protoLayer.min(AxisZ));
91
92 if (overlap) {
93 // Merge surfaces
94 std::vector<std::shared_ptr<Acts::Surface>> mergedSurfaces;
95 mergedSurfaces.reserve(current.surfaces.size() + prev.surfaces.size());
96 mergedSurfaces.insert(mergedSurfaces.end(), current.surfaces.begin(),
97 current.surfaces.end());
98 mergedSurfaces.insert(mergedSurfaces.end(), prev.surfaces.begin(),
99 prev.surfaces.end());
100
101 mergedLayers.pop_back();
102 mergedLayers.emplace_back(gctx, std::move(mergedSurfaces));
103 auto& merged = mergedLayers.back();
104 merged.protoLayer.envelope[AxisR] = current.protoLayer.envelope[AxisR];
105 merged.protoLayer.envelope[AxisZ] = current.protoLayer.envelope[AxisZ];
106 } else {
107 mergedLayers.push_back(std::move(current));
108 }
109 }
110
111 return mergedLayers;
112}
113
114void addStripBarrelLayer(
115 Acts::Experimental::BlueprintNode& parent, std::size_t ilayer,
116 const std::vector<std::shared_ptr<Acts::Surface>>& surfaces) {
117 using enum Acts::SurfaceArrayNavigationPolicy::LayerType;
118 using enum Acts::CylinderVolumeBounds::Face;
119 using enum Acts::AxisDirection;
120 using enum Acts::AxisBoundaryType;
121
122 auto addLayer = [ilayer, &surfaces](auto& node) {
123 node.addLayer("Strip_Brl_" + std::to_string(ilayer), [&](auto& layer) {
124 layer.setNavigationPolicyFactory(
125 Acts::NavigationPolicyFactory{}
126 .add<Acts::SurfaceArrayNavigationPolicy>(
127 Acts::SurfaceArrayNavigationPolicy::Config{
128 .layerType = Cylinder, .bins = {30, 10}})
129 .add<Acts::TryAllNavigationPolicy>(
130 Acts::TryAllNavigationPolicy::Config{.sensitives = false})
131 .asUniquePtr());
132
133 layer.setSurfaces(surfaces);
134 layer.setEnvelope(Acts::ExtentEnvelope{{
135 .z = {5_mm, 5_mm},
136 .r = {2_mm, 2_mm},
137 }});
138 });
139 };
140
141 // Inner 3 layers: add material on inner and outer cylinders
142 // Outermost layer: add material only on inner cylinder
143 parent.addMaterial("Strip_Brl_" + std::to_string(ilayer) + "_Material",
144 [&addLayer, &ilayer](auto& lmat) {
145 if (ilayer < 3) {
146 lmat.configureFace(OuterCylinder,
147 {AxisRPhi, Closed, 20},
148 {AxisZ, Bound, 20});
149 }
150 lmat.configureFace(InnerCylinder, {AxisRPhi, Closed, 20},
151 {AxisZ, Bound, 20});
152 addLayer(lmat);
153 });
154}
155
156void addStripEndcapLayer(
157 Acts::Experimental::BlueprintNode& parent, const std::string& name,
158 const std::vector<std::shared_ptr<Acts::Surface>>& surfaces) {
159 using enum Acts::SurfaceArrayNavigationPolicy::LayerType;
160 using enum Acts::CylinderVolumeBounds::Face;
161 using enum Acts::AxisDirection;
162 using enum Acts::AxisBoundaryType;
163
164 parent.addMaterial(name + "_Material", [&](auto& mat) {
165 mat.configureFace(PositiveDisc, {AxisR, Bound, 20}, {AxisPhi, Closed, 40});
166 mat.configureFace(NegativeDisc, {AxisR, Bound, 20}, {AxisPhi, Closed, 40});
167
168 mat.addLayer(name, [&surfaces](auto& layer) {
169 layer.setNavigationPolicyFactory(
170 Acts::NavigationPolicyFactory{}
171 .add<Acts::SurfaceArrayNavigationPolicy>(
172 Acts::SurfaceArrayNavigationPolicy::Config{.layerType = Disc,
173 .bins = {30, 30}})
174 .add<Acts::TryAllNavigationPolicy>(
175 Acts::TryAllNavigationPolicy::Config{.sensitives = false})
176 .asUniquePtr());
177
178 layer.setSurfaces(surfaces);
179 layer.setEnvelope(Acts::ExtentEnvelope{{
180 .z = {0.1_mm, 0.1_mm},
181 .r = {2_mm, 2_mm},
182 }});
183 });
184 });
185}
186} // namespace
187
188namespace ActsTrk {
189
191 // Retrieve the detector managers from the detector store for enabled systems
192 if (m_buildPixel) {
193 ATH_CHECK(detStore()->retrieve(m_itkPixelMgr, "ITkPixel"));
194 }
195 if (m_buildStrip) {
196 ATH_CHECK(detStore()->retrieve(m_itkStripMgr, "ITkStrip"));
197 }
198 m_elementStore = std::make_shared<ActsElementVector>();
199 return StatusCode::SUCCESS;
200}
201
202std::shared_ptr<Acts::Experimental::BlueprintNode>
204 const Acts::GeometryContext& gctx,
205 std::shared_ptr<Acts::Experimental::BlueprintNode>&& childNode) {
206
207 auto itkNode =
208 std::make_shared<Acts::Experimental::CylinderContainerBlueprintNode>(
209 "itkNode", AxisZ);
210 itkNode->setAttachmentStrategy(AttachmentStrategy::Gap);
211 itkNode->setResizeStrategy(ResizeStrategy::Gap);
212
213 auto& itk = itkNode->addCylinderContainer("ItkNodeMain", AxisR);
214 itk.setAttachmentStrategy(AttachmentStrategy::Gap);
215 itk.setResizeStrategy(ResizeStrategy::Gap);
216
217 itk.addMaterial("ItkNodeMain_Material", [&](auto& mat) {
218 if (m_buildStrip) {
219 mat.configureFace(NegativeDisc, {AxisR, Bound, 20}, {AxisPhi, Closed, 40});
220 mat.configureFace(PositiveDisc, {AxisR, Bound, 20}, {AxisPhi, Closed, 40});
221 }
222
223 auto& innerContainer = mat.addCylinderContainer("ITkInnerContainer", AxisR);
224
225 // Beam pipe is passed in as an optional child from BeamPipeBlueprintNodeBuilder
226 if (childNode) {
227 innerContainer.addChild(std::move(childNode));
228 }
229
230 if (m_buildPixel) {
231 buildItkPixelBlueprintNode(gctx, innerContainer);
232 }
233 if (m_buildStrip) {
234 buildItkStripBlueprintNode(gctx, innerContainer);
235 }
236 });
237
238 return itkNode;
239}
240
242 const Acts::GeometryContext& gctx,
243 Acts::Experimental::BlueprintNode& node) {
244
245 // Get ITkPixel parameters from detector manager
246 if (!m_itkPixelMgr) {
247 ATH_MSG_ERROR("ITkPixel manager not available");
248 throw std::runtime_error("ITkPixel manager not available");
249 }
250
251 ATH_MSG_DEBUG("Detector manager has "
252 << m_itkPixelMgr->getDetectorElementCollection()->size()
253 << " elements");
254
255 std::vector<std::shared_ptr<ActsDetectorElement>> elements;
256
258 for (const auto* element : *m_itkPixelMgr->getDetectorElementCollection()) {
259 const InDetDD::SiDetectorElement* siDetElement =
260 dynamic_cast<const InDetDD::SiDetectorElement*>(element);
261 if (siDetElement == nullptr) {
262 ATH_MSG_ERROR("Detector element was nullptr");
263 throw std::runtime_error{"Corrupt detector element collection"};
264 }
265 elements.push_back(std::make_shared<ActsDetectorElement>(*siDetElement));
266 }
267 ATH_MSG_VERBOSE("Retrieved " << elements.size() << " elements");
268
269 // Copy to service level store to extend lifetime
270 m_elementStore->vector().insert(m_elementStore->vector().end(),
271 elements.begin(), elements.end());
272
273 // Inner pixel: 2 innermost barrel layers + inner endcap disks
274 auto& innerPixel = node.addCylinderContainer("InnerPixel", AxisR);
275 innerPixel.setAttachmentStrategy(AttachmentStrategy::Gap);
276 innerPixel.setResizeStrategy(ResizeStrategy::Gap);
277
278 innerPixel.addMaterial("InnerPixelMaterial", [&](auto& mat) {
279 mat.configureFace(OuterCylinder, {AxisRPhi, Closed, 20},
280 {AxisZ, Bound, 20});
281 mat.configureFace(InnerCylinder, {AxisRPhi, Closed, 20},
282 {AxisZ, Bound, 20});
283
284 auto& innerPixelContainer = mat.addCylinderContainer("InnerPixel", AxisZ);
285
286 // Add barrel container
287 auto& barrelGeoId = innerPixelContainer.withGeometryIdentifier();
288 barrelGeoId.setAllVolumeIdsTo(s_innerPixelVolumeId)
289 .incrementLayerIds(1)
290 .sortBy([](auto& a, auto& b) {
291 auto& boundsA =
292 dynamic_cast<const Acts::CylinderVolumeBounds&>(a.volumeBounds());
293 auto& boundsB =
294 dynamic_cast<const Acts::CylinderVolumeBounds&>(b.volumeBounds());
295
296 using enum Acts::CylinderVolumeBounds::BoundValues;
297 double aMidR = (boundsA.get(eMinR) + boundsA.get(eMaxR)) / 2.0;
298 double bMidR = (boundsB.get(eMinR) + boundsB.get(eMaxR)) / 2.0;
299
300 return aMidR < bMidR;
301 });
302
303 auto& brl_mat =
304 barrelGeoId.addMaterial("InnerPixel_Material", [&](auto& mat) {
305 mat.configureFace(NegativeDisc, {AxisR, Bound, 10},
306 {AxisPhi, Closed, 10});
307 mat.configureFace(PositiveDisc, {AxisR, Bound, 10},
308 {AxisPhi, Closed, 10});
309 });
310 auto& barrel = brl_mat.addCylinderContainer("InnerPixel_Brl", AxisR);
311
312 barrel.setAttachmentStrategy(AttachmentStrategy::Gap);
313 barrel.setResizeStrategy(ResizeStrategy::Gap);
314
315 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
316
317 for (auto& element : elements) {
318 IdentityHelper id = element->identityHelper();
319 if (id.bec() != 0) {
320 continue;
321 }
322
323 if (id.layer_disk() >= 2) {
324 continue;
325 }
326
327 int elementLayer = id.layer_disk();
328 layers[elementLayer].push_back(element->surface().getSharedPtr());
329 }
330
331 ATH_MSG_DEBUG("Adding " << layers.size() << " layers to InnerPixel barrel");
332
333 for (const auto& [ilayer, surfaces] : layers) {
334 ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
335 << " surfaces");
336
337 barrel.addMaterial(
338 std::format("InnerPixel_Brl_{}_Material", ilayer), [&](auto& lmat) {
339 lmat.configureFace(OuterCylinder, {AxisRPhi, Closed, 40},
340 {AxisZ, Bound, 20});
341
342 auto& layer =
343 lmat.addLayer(std::format("InnerPixel_Brl_{}", ilayer));
344
345 layer.setNavigationPolicyFactory(
346 Acts::NavigationPolicyFactory{}
347 .add<Acts::SurfaceArrayNavigationPolicy>(
348 Acts::SurfaceArrayNavigationPolicy::Config{
349 .layerType = Cylinder,
350 .bins = {30, 10}})
351 .add<Acts::TryAllNavigationPolicy>(
352 Acts::TryAllNavigationPolicy::Config{.sensitives =
353 false})
354 .asUniquePtr());
355
356 layer.setSurfaces(surfaces);
357 layer.setEnvelope(Acts::ExtentEnvelope{{
358 .z = {5_mm, 5_mm},
359 .r = {2_mm, 2_mm},
360 }});
361 });
362 }
363
364 // Add endcap containers
365 for (int bec : {-2, 2}) {
366 std::string s = bec > 0 ? "p" : "n";
367 auto& ecGeoId = innerPixelContainer.withGeometryIdentifier();
368 ecGeoId.setAllVolumeIdsTo(s_innerPixelVolumeId + std::floor(bec / 2))
369 .incrementLayerIds(1);
370 auto& ec = ecGeoId.addCylinderContainer("InnerPixel_" + s + "EC", AxisZ);
371 ec.setAttachmentStrategy(AttachmentStrategy::Gap);
372 ec.setResizeStrategy(ResizeStrategy::Gap);
373
374 std::map<std::tuple<int, int, int>,
375 std::vector<std::shared_ptr<Acts::Surface>>>
376 initialLayers{};
377
378 for (auto& element : elements) {
379 IdentityHelper id = element->identityHelper();
380 if (id.bec() * bec <= 0) {
381 continue;
382 }
383
384 if (id.layer_disk() >= 3) {
385 continue;
386 }
387
388 std::tuple<int, int, int> key{id.bec(), id.layer_disk(),
389 id.eta_module()};
390 initialLayers[key].push_back(element->surface().getSharedPtr());
391 }
392
393 ATH_MSG_DEBUG("Found " << initialLayers.size()
394 << " initial layers to InnerPixel " << s << "EC");
395
396 std::vector<LayerData> protoLayers;
397 protoLayers.reserve(initialLayers.size());
398
399 for (const auto& [key, surfaces] : initialLayers) {
400 auto& layer = protoLayers.emplace_back(gctx, surfaces);
401 layer.protoLayer.envelope[AxisR] = {2_mm, 2_mm};
402 layer.protoLayer.envelope[AxisZ] = {1_mm, 1_mm};
403 }
404
405 std::ranges::sort(protoLayers,
406 [](const LayerData& a, const LayerData& b) {
407 return std::abs(a.protoLayer.medium(AxisZ)) <
408 std::abs(b.protoLayer.medium(AxisZ));
409 });
410
411 ATH_MSG_DEBUG("Found " << protoLayers.size() << " initial layers");
412
413 std::vector<LayerData> mergedLayers;
415 mergedLayers = mergeLayers(gctx, std::move(protoLayers));
416 } else {
417 mergedLayers = std::move(protoLayers);
418 }
419
420 ATH_MSG_DEBUG("After merging: " << mergedLayers.size() << " layers");
421
422 for (const auto [key, pl] : Acts::enumerate(mergedLayers)) {
423 ATH_MSG_DEBUG("- Layer " << key << " has " << pl.surfaces.size()
424 << " surfaces");
425
426 pl.protoLayer.medium(AxisZ);
427 auto layerName = std::format("InnerPixel_{}EC_{}", key, s);
428
429 auto addLayer = [&layerName, &pl](auto& parent) {
430 auto& layer = parent.addLayer(layerName);
431
432 layer.setNavigationPolicyFactory(
433 Acts::NavigationPolicyFactory{}
434 .add<Acts::SurfaceArrayNavigationPolicy>(
435 Acts::SurfaceArrayNavigationPolicy::Config{
436 .layerType = Disc,
437 .bins = {30, 30}})
438 .add<Acts::TryAllNavigationPolicy>(
439 Acts::TryAllNavigationPolicy::Config{.sensitives = false})
440 .asUniquePtr());
441
442 layer.setSurfaces(pl.surfaces);
443 layer.setEnvelope(Acts::ExtentEnvelope{{
444 .z = {1_mm, 1_mm},
445 .r = {2_mm, 2_mm},
446 }});
447 };
448
449 ATH_MSG_VERBOSE("Add inner pixel layer"
450 << key << " / " << mergedLayers.size()
451 << " at z = " << pl.protoLayer.medium(AxisZ));
452 ATH_MSG_VERBOSE("Adding material for layer " << layerName);
453 ec.addMaterial(layerName + "_Material", [&](auto& lmat) {
454 lmat.configureFace(NegativeDisc, {AxisR, Bound, 10},
455 {AxisPhi, Closed, 40});
456 lmat.configureFace(PositiveDisc, {AxisR, Bound, 10},
457 {AxisPhi, Closed, 40});
458 addLayer(lmat);
459 });
460 }
461 }
462 });
463
464 // Outer pixel: 3 outer barrel layers + outer endcaps
465 auto& outerPixel = node.addCylinderContainer("OuterPixel", AxisR);
466 outerPixel.setAttachmentStrategy(AttachmentStrategy::Gap);
467 outerPixel.setResizeStrategy(ResizeStrategy::Gap);
468
469 outerPixel.addMaterial("OuterPixelMaterial", [&](auto& opmat) {
470 opmat.configureFace(OuterCylinder, {AxisRPhi, Bound, 20}, {AxisZ, Bound, 20});
471 opmat.configureFace(InnerCylinder, {AxisRPhi, Bound, 20}, {AxisZ, Bound, 20});
472
473 auto& outerPixelContainer = opmat.addCylinderContainer("OuterPixel", AxisZ);
474
475 auto& barrelGeoId = outerPixelContainer.withGeometryIdentifier();
476 barrelGeoId.setAllVolumeIdsTo(s_outerPixelVolumeId).incrementLayerIds(1);
477
478 auto& brl_mat =
479 barrelGeoId.addMaterial("OuterPixel_Material", [&](auto& bmat) {
480 bmat.configureFace(NegativeDisc, {AxisR, Bound, 10},
481 {AxisPhi, Closed, 10});
482 bmat.configureFace(PositiveDisc, {AxisR, Bound, 10},
483 {AxisPhi, Closed, 10});
484 });
485 auto& barrel = brl_mat.addCylinderContainer("OuterPixel_Brl", AxisR);
486
487 barrel.setAttachmentStrategy(AttachmentStrategy::Gap);
488 barrel.setResizeStrategy(ResizeStrategy::Gap);
489
490 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
491
492 for (auto& element : elements) {
493 IdentityHelper id = element->identityHelper();
494 if (id.bec() != 0) {
495 continue;
496 }
497
498 if (id.layer_disk() <= 1) {
499 continue;
500 }
501
502 int elementLayer = id.layer_disk();
503 layers[elementLayer].push_back(element->surface().getSharedPtr());
504 }
505
506 ATH_MSG_DEBUG("Adding " << layers.size() << " layers to OuterPixel barrel");
507
508 for (const auto& [ilayer, surfaces] : layers) {
509 ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
510 << " surfaces");
511
512 barrel.addMaterial(
513 std::format("OuterPixel_Brl_{}_Material", ilayer), [&](auto& lmat) {
514 lmat.configureFace(OuterCylinder, {AxisRPhi, Closed, 40},
515 {AxisZ, Bound, 20});
516
517 auto& layer =
518 lmat.addLayer("OuterPixel_Brl_" + std::to_string(ilayer));
519
520 layer.setNavigationPolicyFactory(
521 Acts::NavigationPolicyFactory{}
522 .add<Acts::SurfaceArrayNavigationPolicy>(
523 Acts::SurfaceArrayNavigationPolicy::Config{
524 .layerType = Cylinder,
525 .bins = {30, 10}})
526 .add<Acts::TryAllNavigationPolicy>(
527 Acts::TryAllNavigationPolicy::Config{.sensitives =
528 false})
529 .asUniquePtr());
530
531 layer.setSurfaces(surfaces);
532 layer.setEnvelope(Acts::ExtentEnvelope{{
533 .z = {5_mm, 5_mm},
534 .r = {2_mm, 2_mm},
535 }});
536 });
537 }
538
539 constexpr static auto addEndcapLayer = [](auto& parent, const auto& name,
540 const auto& surfaces) {
541 parent.addLayer(name, [&surfaces](auto& layer) {
542 layer.setNavigationPolicyFactory(
543 Acts::NavigationPolicyFactory{}
544 .add<Acts::SurfaceArrayNavigationPolicy>(
545 Acts::SurfaceArrayNavigationPolicy::Config{
546 .layerType = Disc, .bins = {30, 30}})
547 .add<Acts::TryAllNavigationPolicy>(
548 Acts::TryAllNavigationPolicy::Config{.sensitives = false})
549 .asUniquePtr());
550
551 layer.setSurfaces(surfaces);
552 });
553 };
554
555 for (int bec : {-2, 2}) {
556 const std::string s = bec > 0 ? "p" : "n";
557
558 auto& ec_outer_geoId = outerPixelContainer.withGeometryIdentifier();
559 ec_outer_geoId
560 .setAllVolumeIdsTo(s_outerPixelVolumeId + std::floor(bec / 2))
561 .incrementLayerIds(1);
562
563 auto& ec_outer =
564 ec_outer_geoId.addCylinderContainer("OuterPixel_" + s + "EC", AxisR);
565
566 // Three groups of disks stacked in R
567 std::array diskGroups{std::pair{3, 4}, std::pair{6, 5}, std::pair{7, 8}};
568
569 for (size_t idx = 0; idx < diskGroups.size(); ++idx) {
570 auto [disk1, disk2] = diskGroups[idx];
571
572 Acts::Experimental::MaterialDesignatorBlueprintNode* material = nullptr;
573 if (idx < (diskGroups.size() - 1)) {
574 material = &ec_outer.addMaterial(
575 "OuterPixel_" + s + "EC_" + std::to_string(idx) + "_Material",
576 [&](auto& mat) {
577 mat.configureFace(OuterCylinder, {AxisRPhi, Closed, 20},
578 {AxisZ, Bound, 20});
579 });
580 }
581
582 auto& ec_stack =
583 material
584 ? material->addCylinderContainer(
585 "OuterPixel_" + s + "EC_" + std::to_string(idx), AxisZ)
586 : ec_outer.addCylinderContainer(
587 "OuterPixel_" + s + "EC_" + std::to_string(idx), AxisZ);
588
589 ec_stack.setAttachmentStrategy(AttachmentStrategy::Gap);
590 ec_stack.setResizeStrategy(ResizeStrategy::Gap);
591
592 std::map<std::tuple<int, int>,
593 std::vector<std::shared_ptr<Acts::Surface>>>
594 eta_rings;
595
596 for (auto& element : elements) {
597 IdentityHelper id = element->identityHelper();
598 if (id.bec() != bec ||
599 (id.layer_disk() != disk1 && id.layer_disk() != disk2)) {
600 continue;
601 }
602 std::tuple<int, int> key{id.layer_disk(), id.eta_module()};
603 eta_rings[key].push_back(element->surface().getSharedPtr());
604 }
605
606 ATH_MSG_DEBUG("Found " << eta_rings.size() << " eta rings in group "
607 << idx);
608
609 std::vector<std::vector<std::shared_ptr<Acts::Surface>>> sorted_rings;
610 sorted_rings.reserve(eta_rings.size());
611 for (const auto& [key, surfaces] : eta_rings) {
612 sorted_rings.push_back(surfaces);
613 }
614
615 std::ranges::sort(sorted_rings, [&gctx](const auto& a, const auto& b) {
616 Acts::ProtoLayer pl_a(gctx, makeConstPtrVector(a));
617 Acts::ProtoLayer pl_b(gctx, makeConstPtrVector(b));
618 return std::abs(pl_a.min(AxisZ)) < std::abs(pl_b.min(AxisZ));
619 });
620
621 for (size_t i = 0; i < sorted_rings.size(); ++i) {
622 const auto& surfaces = sorted_rings[i];
623 auto layerName = "OuterPixel_" + s + "EC_" + std::to_string(idx) +
624 "_" + std::to_string(i);
625
626 ec_stack.addMaterial(layerName + "_Material", [&](auto& mat) {
627 mat.configureFace(PositiveDisc, {AxisR, Bound, 10},
628 {AxisPhi, Closed, 40});
629 mat.configureFace(NegativeDisc, {AxisR, Bound, 10},
630 {AxisPhi, Closed, 40});
631 addEndcapLayer(mat, layerName, surfaces);
632 });
633 }
634 }
635 }
636 });
637}
638
640 const Acts::GeometryContext& gctx,
641 Acts::Experimental::BlueprintNode& node) {
642
643 // Get ITkStrip parameters from detector manager
644 if (!m_itkStripMgr) {
645 ATH_MSG_ERROR("ITkStrip manager not available");
646 throw std::runtime_error("ITkStrip manager not available");
647 }
648
649 ATH_MSG_DEBUG("Detector manager has "
650 << m_itkStripMgr->getDetectorElementCollection()->size()
651 << " elements");
652
653 std::vector<std::shared_ptr<ActsDetectorElement>> elements;
654
656 for (const auto* element : *m_itkStripMgr->getDetectorElementCollection()) {
657 const InDetDD::SiDetectorElement* siDetElement =
658 dynamic_cast<const InDetDD::SiDetectorElement*>(element);
659 if (siDetElement == nullptr) {
660 ATH_MSG_ERROR("Detector element was nullptr");
661 throw std::runtime_error{"Corrupt detector element collection"};
662 }
663 elements.push_back(std::make_shared<ActsDetectorElement>(*siDetElement));
664 }
665 ATH_MSG_VERBOSE("Retrieved " << elements.size() << " elements");
666
667 // Copy to service level store to extend lifetime
668 m_elementStore->vector().insert(m_elementStore->vector().end(),
669 elements.begin(), elements.end());
670
671 auto& strip = node.addCylinderContainer("Strip", AxisR);
672 strip.setAttachmentStrategy(AttachmentStrategy::Gap);
673 strip.setResizeStrategy(ResizeStrategy::Gap);
674
675 strip.addMaterial("StripMaterial", [&](auto& mat) {
676 mat.configureFace(OuterCylinder, {AxisRPhi, Closed, 20},
677 {AxisZ, Bound, 20});
678 mat.configureFace(InnerCylinder, {AxisRPhi, Closed, 20},
679 {AxisZ, Bound, 20});
680
681 auto& stripContainer = mat.addCylinderContainer("Strip", AxisZ);
682
683 // Add barrel container
684 stripContainer.withGeometryIdentifier([this, &elements](auto& geoId) {
685 geoId.setAllVolumeIdsTo(s_stripVolumeId).incrementLayerIds(1);
686
687 auto& brl_mat = geoId.addMaterial("Strip_Brl_Material", [&](auto& mat) {
688 mat.configureFace(NegativeDisc, {AxisR, Bound, 10},
689 {AxisPhi, Closed, 10});
690 mat.configureFace(PositiveDisc, {AxisR, Bound, 10},
691 {AxisPhi, Closed, 10});
692 });
693 brl_mat.addCylinderContainer(
694 "Strip_Brl", AxisR, [this, &elements](auto& barrel) {
695 barrel.setAttachmentStrategy(AttachmentStrategy::Gap);
696 barrel.setResizeStrategy(ResizeStrategy::Gap);
697
698 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
699
700 for (auto& element : elements) {
701 IdentityHelper id = element->identityHelper();
702 if (id.bec() != 0) {
703 continue;
704 }
705
706 int elementLayer = id.layer_disk();
707 layers[elementLayer].push_back(element->surface().getSharedPtr());
708 }
709
710 ATH_MSG_DEBUG("Adding " << layers.size()
711 << " layers to Strip barrel");
712
713 for (const auto& [ilayer, surfaces] : layers) {
714 ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
715 << " surfaces");
716 addStripBarrelLayer(barrel, ilayer, surfaces);
717 }
718 });
719 });
720
721 // Add endcap containers
722 for (int bec : {-2, 2}) {
723 const std::string s = bec > 0 ? "p" : "n";
724
725 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
726
727 for (auto& element : elements) {
728 IdentityHelper id = element->identityHelper();
729 if (id.bec() * bec <= 0) {
730 continue;
731 }
732
733 layers[id.layer_disk()].push_back(element->surface().getSharedPtr());
734 }
735
736 ATH_MSG_DEBUG("Found " << layers.size() << " layers in Strip " << s
737 << "EC");
738
739 std::vector<std::vector<std::shared_ptr<Acts::Surface>>> sorted_layers;
740 sorted_layers.reserve(layers.size());
741 for (const auto& [key, surfaces] : layers) {
742 sorted_layers.push_back(surfaces);
743 }
744
745 std::sort(sorted_layers.begin(), sorted_layers.end(),
746 [&gctx](const auto& a, const auto& b) {
747 Acts::ProtoLayer pl_a(gctx, makeConstPtrVector(a));
748 Acts::ProtoLayer pl_b(gctx, makeConstPtrVector(b));
749 return std::abs(pl_a.min(AxisZ)) < std::abs(pl_b.min(AxisZ));
750 });
751
752 stripContainer.withGeometryIdentifier([&sorted_layers, bec,
753 &s](auto& geoId) {
754 geoId.setAllVolumeIdsTo(s_stripVolumeId + std::floor(bec / 2))
755 .incrementLayerIds(1);
756
757 geoId.addCylinderContainer(
758 "Strip_" + s + "EC", AxisZ, [&sorted_layers, &s](auto& ec) {
759 ec.setAttachmentStrategy(AttachmentStrategy::Gap);
760 ec.setResizeStrategy(ResizeStrategy::Gap);
761
762 for (size_t i = 0; i < sorted_layers.size(); ++i) {
763 const auto& surfaces = sorted_layers[i];
764 auto layerName = "Strip_" + s + "EC_" + std::to_string(i);
765 addStripEndcapLayer(ec, layerName, surfaces);
766 }
767 });
768 });
769 };
770 });
771
772 // Explicit spacer volume beyond the strip detector's natural outer radius.
773 // Without it, the outer cylinder carrying the strip container material
774 // coincides with the ITk node boundary, causing volume merging issues.
775 std::vector<std::shared_ptr<Acts::Surface>> allStripSurfaces;
776 allStripSurfaces.reserve(elements.size());
777 for (auto& element : elements) {
778 allStripSurfaces.push_back(element->surface().getSharedPtr());
779 }
780 Acts::ProtoLayer stripExtent(gctx, makeConstPtrVector(allStripSurfaces));
781
782 constexpr double spacerClearance = 5_mm;
783 constexpr double spacerThickness = 5_mm;
784 double spacerRmin = stripExtent.max(AxisR) + spacerClearance;
785 double spacerHalfZ = std::max(std::abs(stripExtent.min(AxisZ)),
786 std::abs(stripExtent.max(AxisZ))) +
787 spacerClearance;
788
789 strip.addStaticVolume(
790 Acts::Transform3::Identity(),
791 std::make_shared<Acts::CylinderVolumeBounds>(
792 spacerRmin, spacerRmin + spacerThickness, spacerHalfZ),
793 "Strip_OuterSpacer");
794}
795
796} // 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)
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)
const InDetDD::SiDetectorManager * m_itkStripMgr
const InDetDD::SiDetectorManager * m_itkPixelMgr
std::shared_ptr< ActsElementVector > m_elementStore
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
Define the volume parts of the GeometryIdentifier for each ATLAS subsystem centrally.
constexpr std::size_t s_outerPixelVolumeId
constexpr std::size_t s_stripVolumeId
Volume Ids used within the ITk.
constexpr std::size_t s_innerPixelVolumeId
The AlignStoreProviderAlg loads the rigid alignment corrections and pipes them through the readout ge...
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.