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