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

Helper class to build the ItkBlueprint node It adds the system as a node to the Blueprint. More...

#include <ItkBlueprintNodeBuilder.h>

Inheritance diagram for ActsTrk::ItkBlueprintNodeBuilder:
Collaboration diagram for ActsTrk::ItkBlueprintNodeBuilder:

Public Member Functions

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

Private Member Functions

Acts::SurfaceArrayNavigationPolicy::Config surfaceArrayConfig (Acts::SurfaceArrayNavigationPolicy::LayerType layerType, const Gaudi::Property< std::vector< unsigned int > > &neighborWindow) const
 Configuration of the surface array navigation policy of one layer.
void buildItkStripBlueprintNode (const Acts::GeometryContext &gctx, Acts::BlueprintNode &node)
void buildItkPixelBlueprintNode (const Acts::GeometryContext &gctx, Acts::BlueprintNode &node)

Private Attributes

const InDetDD::SiDetectorManager * m_itkPixelMgr {nullptr}
const InDetDD::SiDetectorManager * m_itkStripMgr {nullptr}
std::shared_ptr< ActsElementVector > m_elementStore {nullptr}
Gaudi::Property< bool > m_doEndcapLayerMerging
Gaudi::Property< bool > m_buildPixel {this, "buildPixel", true}
Gaudi::Property< bool > m_buildStrip {this, "buildStrip", true}
Gaudi::Property< std::vector< unsigned int > > m_itkPixelInnerBarrelNeighborWindow
 Most bins the surface array lookup serves on each side of the crossing, as {phi, z} for the barrel and {r, phi} for the endcap.
Gaudi::Property< std::vector< unsigned int > > m_itkPixelInnerEndcapNeighborWindow
Gaudi::Property< std::vector< unsigned int > > m_itkPixelOuterBarrelNeighborWindow
Gaudi::Property< std::vector< unsigned int > > m_itkPixelOuterEndcapNeighborWindow
Gaudi::Property< std::vector< unsigned int > > m_itkStripBarrelNeighborWindow
Gaudi::Property< std::vector< unsigned int > > m_itkStripEndcapNeighborWindow
Gaudi::Property< unsigned int > m_surfaceArrayOverfill
 Extra cells per direction and axis that each surface fills around its footprint.

Detailed Description

Helper class to build the ItkBlueprint node It adds the system as a node to the Blueprint.

Definition at line 23 of file ItkBlueprintNodeBuilder.h.

Member Function Documentation

◆ buildBlueprintNode()

std::shared_ptr< Acts::BlueprintNode > ActsTrk::ItkBlueprintNodeBuilder::buildBlueprintNode ( const Acts::GeometryContext & gctx,
std::shared_ptr< Acts::BlueprintNode > && child )
override

Build the Itk Blueprint Node.

Parameters
gctxGeometry context
childOptional beam pipe node to nest inside the ITk AxisR container.

Wrap the ITk into a separate volume

Setup a fixed geometry Identifier

Definition at line 253 of file ItkBlueprintNodeBuilder.cxx.

255 {
256
257 auto itkNode =
258 std::make_shared<Acts::CylinderContainerBlueprintNode>(
259 "itkNode", AxisZ);
260 itkNode->setAttachmentStrategy(AttachmentStrategy::Gap);
261 itkNode->setResizeStrategy(ResizeStrategy::Gap);
262
263 auto& itk = itkNode->addCylinderContainer("ItkNodeMain", AxisR);
264 itk.setAttachmentStrategy(AttachmentStrategy::Gap);
265 itk.setResizeStrategy(ResizeStrategy::Gap);
266
267 itk.addMaterial("ItkNodeMain_Material", [&](auto& mat) {
268 if (m_buildStrip) {
269 mat.configureFace(NegativeDisc,
270 AxisSpec::DeferredEquidistant(50, AxisR),
271 AxisSpec::DeferredEquidistant(50, AxisPhi),
272 "ItkNodeMain_Material_NegativeDisc");
273 mat.configureFace(PositiveDisc,
274 AxisSpec::DeferredEquidistant(50, AxisR),
275 AxisSpec::DeferredEquidistant(50, AxisPhi),
276 "ItkNodeMain_Material_PositiveDisc");
277 }
278
279 auto& innerContainer = mat.addCylinderContainer("ITkInnerContainer", AxisR);
280 // The radial dead bands between the R-stacked sub-detectors (beampipe /
281 // InnerPixel / OuterPixel / Strip) are genuine empty space, but the default
282 // Midpoint attachment splits each band at its midpoint and lets BOTH
283 // neighbours fill their half with a ResizeStrategy::Gap volume -> two thin
284 // gap volumes per interface. Second makes a single neighbour own the whole
285 // band -> one gap per interface. Material is unaffected: each sub-detector
286 // keeps ResizeStrategy::Gap, so its material-bearing shell stays frozen at
287 // its true edge and only the empty band is merged.
288 innerContainer.setAttachmentStrategy(AttachmentStrategy::Second);
289 innerContainer.setResizeStrategy(ResizeStrategy::Gap);
290 // Beam pipe is passed in as an optional child from BeamPipeBlueprintNodeBuilder
291 if (childNode) {
292 innerContainer.addChild(std::move(childNode));
293 }
294
295 if (m_buildPixel) {
296 buildItkPixelBlueprintNode(gctx, innerContainer);
297 }
298 if (m_buildStrip) {
299 buildItkStripBlueprintNode(gctx, innerContainer);
300 }
301 });
303 Acts::ExtentEnvelope envelope{};
304 envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
305 envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
306 auto padNode = std::make_shared<Acts::PadBlueprintNode>("ITkEnvelope", envelope);
307 padNode->addChild(itkNode);
309 auto idNode = std::make_shared<Acts::GeometryIdentifierBlueprintNode>();
310 idNode->setDirectChildVolumeIdTo(s_ITkEnvelopeId);
311 idNode->addChild(std::move(padNode));
312 return idNode;
313}
void buildItkStripBlueprintNode(const Acts::GeometryContext &gctx, Acts::BlueprintNode &node)
void buildItkPixelBlueprintNode(const Acts::GeometryContext &gctx, Acts::BlueprintNode &node)
constexpr std::size_t s_ITkEnvelopeId

◆ buildItkPixelBlueprintNode()

void ActsTrk::ItkBlueprintNodeBuilder::buildItkPixelBlueprintNode ( const Acts::GeometryContext & gctx,
Acts::BlueprintNode & node )
private

Definition at line 315 of file ItkBlueprintNodeBuilder.cxx.

317 {
318
319 // Get ITkPixel parameters from detector manager
320 if (!m_itkPixelMgr) {
321 ATH_MSG_ERROR("ITkPixel manager not available");
322 throw std::runtime_error("ITkPixel manager not available");
323 }
324
325 ATH_MSG_DEBUG("Detector manager has "
326 << m_itkPixelMgr->getDetectorElementCollection()->size()
327 << " elements");
328
329 std::vector<std::shared_ptr<ActsDetectorElement>> elements;
330
332 for (const auto* element : *m_itkPixelMgr->getDetectorElementCollection()) {
333 const InDetDD::SiDetectorElement* siDetElement =
334 dynamic_cast<const InDetDD::SiDetectorElement*>(element);
335 if (siDetElement == nullptr) {
336 ATH_MSG_ERROR("Detector element was nullptr");
337 throw std::runtime_error{"Corrupt detector element collection"};
338 }
339 elements.push_back(std::make_shared<ActsDetectorElement>(*siDetElement));
340 }
341 ATH_MSG_VERBOSE("Retrieved " << elements.size() << " elements");
342
343 // Copy to service level store to extend lifetime
344 m_elementStore->vector().insert(m_elementStore->vector().end(),
345 elements.begin(), elements.end());
346
347 // Inner pixel: 2 innermost barrel layers + inner endcap disks
348 auto& innerPixel = node.addCylinderContainer("InnerPixel", AxisR);
349 innerPixel.setAttachmentStrategy(AttachmentStrategy::Gap);
350 // Inner edge Expand: extend all InnerPixel cylinders (endcaps + barrel_0)
351 // inward onto the beam pipe, removing InnerPixel::Gap1. The InnerCylinder
352 // material (30.9) is dropped below so the expanded face fuses cleanly with
353 // the beam pipe outer material (23.9, the kept lower-r surface). Outer edge
354 // stays Gap.
355 innerPixel.setResizeStrategies(ResizeStrategy::Expand, ResizeStrategy::Gap);
356
357 innerPixel.addMaterial("InnerPixelMaterial", [&](auto& mat) {
358 mat.configureFace(OuterCylinder,
359 AxisSpec::DeferredEquidistant(50, AxisRPhi),
360 AxisSpec::DeferredEquidistant(100, AxisZ),
361 "InnerPixelMaterial");
362
363 auto& innerPixelContainer = mat.addCylinderContainer("InnerPixel", AxisZ);
364
365 // Add barrel container
366 auto& barrelGeoId = innerPixelContainer.withGeometryIdentifier();
367 barrelGeoId.setAllVolumeIdsTo(s_innerPixelVolumeId)
368 .incrementLayerIds(1)
369 .sortBy([](auto& a, auto& b) {
370 auto& boundsA =
371 dynamic_cast<const Acts::CylinderVolumeBounds&>(a.volumeBounds());
372 auto& boundsB =
373 dynamic_cast<const Acts::CylinderVolumeBounds&>(b.volumeBounds());
374
375 using enum Acts::CylinderVolumeBounds::BoundValues;
376 double aMidR = (boundsA.get(eMinR) + boundsA.get(eMaxR)) / 2.0;
377 double bMidR = (boundsB.get(eMinR) + boundsB.get(eMaxR)) / 2.0;
378
379 return aMidR < bMidR;
380 });
381
382 auto& brl_mat =
383 barrelGeoId.addMaterial("InnerPixel_Material", [&](auto& material) {
384 material.configureFace(NegativeDisc,
385 AxisSpec::DeferredEquidistant(50, AxisR),
386 AxisSpec::DeferredEquidistant(50, AxisPhi),
387 "InnerPixel_Material_NegativeDisc");
388 material.configureFace(PositiveDisc,
389 AxisSpec::DeferredEquidistant(50, AxisR),
390 AxisSpec::DeferredEquidistant(50, AxisPhi),
391 "InnerPixel_Material_PositiveDisc");
392 });
393 auto& barrel = brl_mat.addCylinderContainer("InnerPixel_Brl", AxisR);
394
395 // Barrel layers carry material only on their OuterCylinder face. Attaching
396 // with Second extends the outer (higher-R) neighbour's material-free inner
397 // face inward to meet each layer's outer face, collapsing the inter-layer
398 // gap volumes while leaving every material surface fixed.
399 barrel.setAttachmentStrategy(AttachmentStrategy::Second);
400 // Inner edge Expand: Brl_0's inner cylinder is material-free, so it extends
401 // inward onto the beam pipe (the InnerPixel container also expands inward,
402 // so the endcaps reach the beam pipe too) removing the barrel's share of the
403 // InnerPixel beam-pipe gap.
404 // Outer edge Expand: the outermost layer grows out onto the InnerPixel
405 // OuterCylinder shell (123.7), removing InnerPixel_Brl::Gap1 (the gap after
406 // the last barrel layer). Its own OuterCylinder material is dropped below
407 // (kept only up to the second-outermost layer) so the expanded face fuses
408 // cleanly with the container shell (the kept surface).
409 barrel.setResizeStrategies(ResizeStrategy::Expand, ResizeStrategy::Expand);
410
411 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
412
413 for (auto& element : elements) {
414 IdentityHelper id = element->identityHelper();
415 if (id.bec() != 0) {
416 continue;
417 }
418
419 if (id.layer_disk() >= 2) {
420 continue;
421 }
422
423 int elementLayer = id.layer_disk();
424 layers[elementLayer].push_back(element->surface().getSharedPtr());
425 }
426
427 ATH_MSG_DEBUG("Adding " << layers.size() << " layers to InnerPixel barrel");
428
429 const int outermostBrlLayer = layers.rbegin()->first;
430 for (const auto& [ilayer, surfaces] : layers) {
431 ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
432 << " surfaces");
433
434 auto configureLayer = [&](auto& node) {
435 auto& layer = node.addLayer(std::format("InnerPixel_Brl_{}", ilayer));
436 layer.setNavigationPolicyFactory(
437 Acts::NavigationPolicyFactory{}
438 .add<Acts::SurfaceArrayNavigationPolicy>(surfaceArrayConfig(
440 .add<Acts::CylinderNavigationPolicy>()
441 .asUniquePtr());
442 layer.setSurfaces(surfaces);
443 layer.setEnvelope(Acts::ExtentEnvelope{{
444 .z = {5_mm, 5_mm},
445 .r = {2_mm, 2_mm},
446 }});
447 };
448
449 // Outermost layer expands onto the container OuterCylinder shell, so
450 // drop its own OuterCylinder; skip the MaterialDesignator wrapper to
451 // avoid empty-designator warnings.
452 if (ilayer != outermostBrlLayer) {
453 const std::string materialName =
454 std::format("InnerPixel_Brl_{}_Material", ilayer);
455 barrel.addMaterial(
456 materialName,
457 [&](auto& lmat) {
458 lmat.configureFace(OuterCylinder,
459 AxisSpec::DeferredEquidistant(50, AxisRPhi),
460 AxisSpec::DeferredEquidistant(100, AxisZ),
461 materialName);
462 configureLayer(lmat);
463 });
464 } else {
465 configureLayer(barrel);
466 }
467 }
468
469 // Add endcap containers
470 for (int bec : {-2, 2}) {
471 std::string s = bec > 0 ? "p" : "n";
472 auto& ecGeoId = innerPixelContainer.withGeometryIdentifier();
473 ecGeoId.setAllVolumeIdsTo(s_innerPixelVolumeId + std::floor(bec / 2))
474 .incrementLayerIds(1);
475 auto& ec = ecGeoId.addCylinderContainer(std::format("InnerPixel_{:}EC", s), AxisZ);
476 // Collapse the inter-disk gaps by extending the outer disk inward onto
477 // its neighbour (Second for +z, First for -z), keeping the material
478 // surface at the smaller-|z| side of each gap (see per-disk material
479 // handling below).
480 ec.setAttachmentStrategy(bec > 0 ? AttachmentStrategy::Second
481 : AttachmentStrategy::First);
482 // Barrel-facing edge Expand: extend the innermost disk toward the barrel
483 // (smaller |z|), removing the barrel<->endcap transition gap. That edge is
484 // the low-z (inner) side for +z endcaps and the high-z (outer) side for -z
485 // endcaps. The far (z-end) edge stays Gap. The innermost disk's inward disc
486 // material is dropped below so the expanded face fuses with the barrel end
487 // disc (the kept, smaller-|z| surface).
488 ec.setResizeStrategies(
489 bec > 0 ? ResizeStrategy::Expand : ResizeStrategy::Gap,
490 bec > 0 ? ResizeStrategy::Gap : ResizeStrategy::Expand);
491
492 std::map<std::tuple<int, int, int>,
493 std::vector<std::shared_ptr<Acts::Surface>>>
494 initialLayers{};
495
496 for (auto& element : elements) {
497 IdentityHelper id = element->identityHelper();
498 if (id.bec() * bec <= 0) {
499 continue;
500 }
501
502 if (id.layer_disk() >= 3) {
503 continue;
504 }
505
506 std::tuple<int, int, int> key{id.bec(), id.layer_disk(),
507 id.eta_module()};
508 initialLayers[key].push_back(element->surface().getSharedPtr());
509 }
510
511 ATH_MSG_DEBUG("Found " << initialLayers.size()
512 << " initial layers to InnerPixel " << s << "EC");
513
514 std::vector<LayerData> protoLayers;
515 protoLayers.reserve(initialLayers.size());
516
517 for (const auto& [key, surfaces] : initialLayers) {
518 auto& layer = protoLayers.emplace_back(gctx, surfaces);
519 layer.protoLayer.envelope[AxisR] = {2_mm, 2_mm};
520 layer.protoLayer.envelope[AxisZ] = {1_mm, 1_mm};
521 }
522
523 std::ranges::sort(protoLayers,
524 [](const LayerData& a, const LayerData& b) {
525 return std::abs(a.protoLayer.medium(AxisZ)) <
526 std::abs(b.protoLayer.medium(AxisZ));
527 });
528
529 ATH_MSG_DEBUG("Found " << protoLayers.size() << " initial layers");
530
531 std::vector<LayerData> mergedLayers;
533 mergedLayers = mergeLayers(gctx, std::move(protoLayers));
534 } else {
535 mergedLayers = std::move(protoLayers);
536 }
537
538 ATH_MSG_DEBUG("After merging: " << mergedLayers.size() << " layers");
539
540 for (const auto [key, pl] : Acts::enumerate(mergedLayers)) {
541 ATH_MSG_DEBUG("- Layer " << key << " has " << pl.surfaces.size()
542 << " surfaces");
543
544 pl.protoLayer.medium(AxisZ);
545 auto layerName = std::format("InnerPixel_{}EC_{}", key, s);
546
547 auto addLayer = [this, &layerName, &pl](auto& parent) {
548 auto& layer = parent.addLayer(layerName);
549
550 layer.setNavigationPolicyFactory(
551 Acts::NavigationPolicyFactory{}
552 .add<Acts::SurfaceArrayNavigationPolicy>(surfaceArrayConfig(
554 .add<Acts::CylinderNavigationPolicy>()
555 .asUniquePtr());
556
557 layer.setSurfaces(pl.surfaces);
558 layer.setEnvelope(Acts::ExtentEnvelope{{
559 .z = {1_mm, 1_mm},
560 .r = {2_mm, 2_mm},
561 }});
562 };
563
564 ATH_MSG_VERBOSE("Add inner pixel layer"
565 << key << " / " << mergedLayers.size()
566 << " at z = " << pl.protoLayer.medium(AxisZ));
567 ATH_MSG_VERBOSE("Adding material for layer " << layerName);
568 // Keep material only on each disk's outward disc (away from the IP);
569 // the inward disc is dropped so it can fuse with the adjacent disk's
570 // kept outward disc without the "both portals carry material" fuse
571 // error. The innermost disk drops its inward disc too: it is expanded
572 // toward the barrel and fuses with the barrel end disc (the kept,
573 // smaller-|z| surface).
574 const auto outwardDisc = (bec > 0) ? PositiveDisc : NegativeDisc;
575 const std::string materialName = layerName + "_Material";
576 ec.addMaterial(materialName, [&](auto& lmat) {
577 lmat.configureFace(outwardDisc,
578 AxisSpec::DeferredEquidistant(50, AxisR),
579 AxisSpec::DeferredEquidistant(50, AxisPhi),
580 materialName);
581 addLayer(lmat);
582 });
583 }
584 }
585 });
586
587 // Outer pixel: 3 outer barrel layers + outer endcaps
588 auto& outerPixel = node.addCylinderContainer("OuterPixel", AxisR);
589 outerPixel.setAttachmentStrategy(AttachmentStrategy::Gap);
590 // Inner edge Expand: extend all OuterPixel cylinders inward onto the
591 // InnerPixel outer shell (123.7), removing OuterPixel::Gap1. The InnerCylinder
592 // material (147.7) is dropped below so the expanded face fuses cleanly with
593 // the InnerPixel OuterCylinder (123.7, the kept lower-r surface). Outer edge
594 // stays Gap (the OuterPixel/Strip interface is handled from the Strip side).
595 outerPixel.setResizeStrategies(ResizeStrategy::Expand, ResizeStrategy::Gap);
596
597 outerPixel.addMaterial("OuterPixelMaterial", [&](auto& opmat) {
598 opmat.configureFace(OuterCylinder,
599 AxisSpec::DeferredEquidistant(50, AxisRPhi),
600 AxisSpec::DeferredEquidistant(100, AxisZ),
601 "OuterPixelMaterial");
602
603 auto& outerPixelContainer = opmat.addCylinderContainer("OuterPixel", AxisZ);
604
605 auto& barrelGeoId = outerPixelContainer.withGeometryIdentifier();
606 barrelGeoId.setAllVolumeIdsTo(s_outerPixelVolumeId).incrementLayerIds(1);
607
608 auto& brl_mat =
609 barrelGeoId.addMaterial("OuterPixel_Material", [&](auto& bmat) {
610 bmat.configureFace(NegativeDisc,
611 AxisSpec::DeferredEquidistant(50, AxisR),
612 AxisSpec::DeferredEquidistant(50, AxisPhi),
613 "OuterPixel_Material_NegativeDisc");
614 bmat.configureFace(PositiveDisc,
615 AxisSpec::DeferredEquidistant(50, AxisR),
616 AxisSpec::DeferredEquidistant(50, AxisPhi),
617 "OuterPixel_Material_PositiveDisc");
618 });
619 auto& barrel = brl_mat.addCylinderContainer("OuterPixel_Brl", AxisR);
620
621 // See InnerPixel_Brl: layers carry material only on OuterCylinder, so
622 // Second attachment removes the inter-layer gaps without moving any
623 // material surface.
624 barrel.setAttachmentStrategy(AttachmentStrategy::Second);
625 // Inner edge Expand: the innermost layer's inner cylinder is material-free,
626 // so it extends inward onto the container inner shell and fuses with the
627 // OuterPixelMaterial InnerCylinder (one material side, nothing moves).
628 // Outer edge Expand: the outermost layer grows out onto the OuterPixel
629 // OuterCylinder shell (316.7), removing the gap after the last barrel layer.
630 // Its own OuterCylinder material is dropped below so the expanded face fuses
631 // cleanly with the container shell (the kept surface).
632 barrel.setResizeStrategies(ResizeStrategy::Expand, ResizeStrategy::Expand);
633
634 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
635
636 for (auto& element : elements) {
637 IdentityHelper id = element->identityHelper();
638 if (id.bec() != 0) {
639 continue;
640 }
641
642 if (id.layer_disk() <= 1) {
643 continue;
644 }
645
646 int elementLayer = id.layer_disk();
647 layers[elementLayer].push_back(element->surface().getSharedPtr());
648 }
649
650 ATH_MSG_DEBUG("Adding " << layers.size() << " layers to OuterPixel barrel");
651
652 const int outermostBrlLayer = layers.rbegin()->first;
653 for (const auto& [ilayer, surfaces] : layers) {
654 ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
655 << " surfaces");
656
657 auto configureLayer = [&](auto& node) {
658 auto& layer = node.addLayer(std::format("OuterPixel_Brl_{:}", ilayer));
659 layer.setNavigationPolicyFactory(
660 Acts::NavigationPolicyFactory{}
661 .add<Acts::SurfaceArrayNavigationPolicy>(surfaceArrayConfig(
663 .add<Acts::CylinderNavigationPolicy>()
664 .asUniquePtr());
665 layer.setSurfaces(surfaces);
666 layer.setEnvelope(Acts::ExtentEnvelope{{
667 .z = {5_mm, 5_mm},
668 .r = {2_mm, 2_mm},
669 }});
670 };
671
672 // Outermost layer expands onto the container OuterCylinder shell, so
673 // drop its own OuterCylinder; skip the MaterialDesignator wrapper to
674 // avoid empty-designator warnings.
675 if (ilayer != outermostBrlLayer) {
676 const std::string materialName =
677 std::format("OuterPixel_Brl_{}_Material", ilayer);
678 barrel.addMaterial(
679 materialName,
680 [&](auto& lmat) {
681 lmat.configureFace(OuterCylinder,
682 AxisSpec::DeferredEquidistant(50, AxisRPhi),
683 AxisSpec::DeferredEquidistant(100, AxisZ),
684 materialName);
685 configureLayer(lmat);
686 });
687 } else {
688 configureLayer(barrel);
689 }
690 }
691
692 const auto endcapSurfaceArrayConfig =
694 auto addEndcapLayer = [&endcapSurfaceArrayConfig](
695 auto& parent, const auto& name,
696 const auto& surfaces) {
697 parent.addLayer(name, [&surfaces, &endcapSurfaceArrayConfig](auto& layer) {
698 layer.setNavigationPolicyFactory(
699 Acts::NavigationPolicyFactory{}
700 .add<Acts::SurfaceArrayNavigationPolicy>(endcapSurfaceArrayConfig)
701 .add<Acts::CylinderNavigationPolicy>()
702 .asUniquePtr());
703
704 layer.setSurfaces(surfaces);
705 });
706 };
707
708 for (int bec : {-2, 2}) {
709 const std::string s = bec > 0 ? "p" : "n";
710
711 auto& ec_outer_geoId = outerPixelContainer.withGeometryIdentifier();
712 ec_outer_geoId
713 .setAllVolumeIdsTo(s_outerPixelVolumeId + std::floor(bec / 2))
714 .incrementLayerIds(1);
715
716 auto& ec_outer =
717 ec_outer_geoId.addCylinderContainer(std::format("OuterPixel_{:}EC", s), AxisR);
718
719 // Three groups of disks stacked in R
720 std::array diskGroups{std::pair{3, 4}, std::pair{6, 5}, std::pair{7, 8}};
721
722 for (size_t idx = 0; idx < diskGroups.size(); ++idx) {
723 auto [disk1, disk2] = diskGroups[idx];
724
725 Acts::MaterialDesignatorBlueprintNode* material = nullptr;
726 if (idx < (diskGroups.size() - 1)) {
727 const std::string materialName =
728 "OuterPixel_" + s + "EC_" + std::to_string(idx) + "_Material";
729 material = &ec_outer.addMaterial(
730 materialName,
731 [&](auto& mat) {
732 mat.configureFace(OuterCylinder,
733 AxisSpec::DeferredEquidistant(50, AxisRPhi),
734 AxisSpec::DeferredEquidistant(100, AxisZ),
735 materialName);
736 });
737 }
738
739 auto& ec_stack =
740 material
741 ? material->addCylinderContainer(
742 std::format("OuterPixel_{:}EC_{:}", s, idx), AxisZ)
743 : ec_outer.addCylinderContainer(
744 std::format("OuterPixel_{:}EC_{:}", s, idx), AxisZ);
745
746 // Collapse inter-disk gaps by extending the outer disk inward (Second
747 // for +z, First for -z); material kept only on outward discs (see
748 // below), so surviving material sits at the smaller-|z| side of each gap.
749 ec_stack.setAttachmentStrategy(bec > 0 ? AttachmentStrategy::Second
750 : AttachmentStrategy::First);
751 // Barrel-facing edge Expand: extend the innermost disk of each R-group
752 // toward the barrel (smaller |z|), removing the barrel<->endcap gap; far
753 // (z-end) edge stays Gap. Innermost inward disc dropped below so the
754 // expanded face fuses with the barrel end disc (kept, smaller |z|).
755 ec_stack.setResizeStrategies(
756 bec > 0 ? ResizeStrategy::Expand : ResizeStrategy::Gap,
757 bec > 0 ? ResizeStrategy::Gap : ResizeStrategy::Expand);
758
759 std::map<std::tuple<int, int>,
760 std::vector<std::shared_ptr<Acts::Surface>>>
761 eta_rings;
762
763 for (auto& element : elements) {
764 IdentityHelper id = element->identityHelper();
765 if (id.bec() != bec ||
766 (id.layer_disk() != disk1 && id.layer_disk() != disk2)) {
767 continue;
768 }
769 std::tuple<int, int> key{id.layer_disk(), id.eta_module()};
770 eta_rings[key].push_back(element->surface().getSharedPtr());
771 }
772
773 ATH_MSG_DEBUG("Found " << eta_rings.size() << " eta rings in group "
774 << idx);
775
776 std::vector<std::vector<std::shared_ptr<Acts::Surface>>> sorted_rings;
777 sorted_rings.reserve(eta_rings.size());
778 for (const auto& [key, surfaces] : eta_rings) {
779 sorted_rings.push_back(surfaces);
780 }
781
782 std::ranges::sort(sorted_rings, [&gctx](const auto& a, const auto& b) {
783 Acts::ProtoLayer pl_a(gctx, Acts::unpackConstSmartPointers(a));
784 Acts::ProtoLayer pl_b(gctx, Acts::unpackConstSmartPointers(b));
785 return std::abs(pl_a.min(AxisZ)) < std::abs(pl_b.min(AxisZ));
786 });
787
788 for (size_t i = 0; i < sorted_rings.size(); ++i) {
789 const auto& surfaces = sorted_rings[i];
790 const std::string layerName = std::format("OuterPixel_{:}EC_{:}_{:}", s,idx, i);
791
792 const auto outwardDisc = (bec > 0) ? PositiveDisc : NegativeDisc;
793 const std::string materialName = layerName + "_Material";
794 ec_stack.addMaterial(materialName, [&](auto& mat) {
795 mat.configureFace(outwardDisc,
796 AxisSpec::DeferredEquidistant(50, AxisR),
797 AxisSpec::DeferredEquidistant(50, AxisPhi),
798 materialName);
799 addEndcapLayer(mat, layerName, surfaces);
800 });
801 }
802 }
803 }
804 });
805}
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_VERBOSE(x,...)
static Double_t a
Gaudi::Property< std::vector< unsigned int > > m_itkPixelInnerEndcapNeighborWindow
Gaudi::Property< std::vector< unsigned int > > m_itkPixelOuterBarrelNeighborWindow
Gaudi::Property< std::vector< unsigned int > > m_itkPixelOuterEndcapNeighborWindow
const InDetDD::SiDetectorManager * m_itkPixelMgr
std::shared_ptr< ActsElementVector > m_elementStore
Acts::SurfaceArrayNavigationPolicy::Config surfaceArrayConfig(Acts::SurfaceArrayNavigationPolicy::LayerType layerType, const Gaudi::Property< std::vector< unsigned int > > &neighborWindow) const
Configuration of the surface array navigation policy of one layer.
Gaudi::Property< std::vector< unsigned int > > m_itkPixelInnerBarrelNeighborWindow
Most bins the surface array lookup serves on each side of the crossing, as {phi, z}...
Gaudi::Property< bool > m_doEndcapLayerMerging
DataModel_detail::const_iterator< DataVector > const_iterator
Definition DataVector.h:861
constexpr std::size_t s_outerPixelVolumeId
constexpr std::size_t s_innerPixelVolumeId
const std::string & layerName(LayerIndex index)
convert LayerIndex into a string
@ layer
Definition HitInfo.h:79

◆ buildItkStripBlueprintNode()

void ActsTrk::ItkBlueprintNodeBuilder::buildItkStripBlueprintNode ( const Acts::GeometryContext & gctx,
Acts::BlueprintNode & node )
private

Definition at line 807 of file ItkBlueprintNodeBuilder.cxx.

809 {
810
811 // Get ITkStrip parameters from detector manager
812 if (!m_itkStripMgr) {
813 ATH_MSG_ERROR("ITkStrip manager not available");
814 throw std::runtime_error("ITkStrip manager not available");
815 }
816
817 ATH_MSG_DEBUG("Detector manager has "
818 << m_itkStripMgr->getDetectorElementCollection()->size()
819 << " elements");
820
821 std::vector<std::shared_ptr<ActsDetectorElement>> elements;
822
824 for (const auto* element : *m_itkStripMgr->getDetectorElementCollection()) {
825 const InDetDD::SiDetectorElement* siDetElement =
826 dynamic_cast<const InDetDD::SiDetectorElement*>(element);
827 if (siDetElement == nullptr) {
828 ATH_MSG_ERROR("Detector element was nullptr");
829 throw std::runtime_error{"Corrupt detector element collection"};
830 }
831 elements.push_back(std::make_shared<ActsDetectorElement>(*siDetElement));
832 }
833 ATH_MSG_VERBOSE("Retrieved " << elements.size() << " elements");
834
835 // Copy to service level store to extend lifetime
836 m_elementStore->vector().insert(m_elementStore->vector().end(),
837 elements.begin(), elements.end());
838
839 auto& strip = node.addCylinderContainer("Strip", AxisR);
840 strip.setAttachmentStrategy(AttachmentStrategy::Gap);
841 // Inner edge Expand: extend all Strip cylinders inward onto the OuterPixel
842 // outer shell (316.7), removing Strip::Gap1. The InnerCylinder material
843 // (382.5) is dropped below so the expanded face fuses cleanly with the
844 // OuterPixel OuterCylinder (316.7, the kept lower-r surface). Outer edge stays
845 // Gap (Strip::Gap2, the resize gap up to the ItkNodeMain envelope, carries
846 // the Strip outer material and must not move).
847 strip.setResizeStrategies(ResizeStrategy::Expand, ResizeStrategy::Gap);
848
849 strip.addMaterial("StripMaterial", [&](auto& mat) {
850 mat.configureFace(OuterCylinder,
851 AxisSpec::DeferredEquidistant(50, AxisRPhi),
852 AxisSpec::DeferredEquidistant(100, AxisZ),
853 "StripMaterial");
854
855 auto& stripContainer = mat.addCylinderContainer("Strip", AxisZ);
856
857 // Add barrel container
858 stripContainer.withGeometryIdentifier([this, &elements](auto& geoId) {
859 geoId.setAllVolumeIdsTo(s_stripVolumeId).incrementLayerIds(1);
860
861 auto& brl_mat =
862 geoId.addMaterial("Strip_Brl_Material", [&](auto& material) {
863 material.configureFace(NegativeDisc,
864 AxisSpec::DeferredEquidistant(50, AxisR),
865 AxisSpec::DeferredEquidistant(50, AxisPhi),
866 "Strip_Brl_Material_NegativeDisc");
867 material.configureFace(PositiveDisc,
868 AxisSpec::DeferredEquidistant(50, AxisR),
869 AxisSpec::DeferredEquidistant(50, AxisPhi),
870 "Strip_Brl_Material_PositiveDisc");
871 });
872 brl_mat.addCylinderContainer(
873 "Strip_Brl", AxisR, [this, &elements](auto& barrel) {
874 // Collapse the inter-layer gaps by extending the outer layer inward
875 // (Second); material kept only on OuterCylinder faces (see
876 // addStripBarrelLayer) so it stays at the lower-radius side.
877 barrel.setAttachmentStrategy(AttachmentStrategy::Second);
878 // Inner edge: Expand the innermost layer onto the StripMaterial
879 // inner shell (382.5), removing Strip_Brl::Gap1. The layer's own
880 // InnerCylinder material (385.5, redundant with the full-z shell
881 // 3mm below it) is dropped in addStripBarrelLayer so the expanded
882 // face fuses cleanly (one material side). Outer edge stays Gap
883 // (invariant, see OuterPixel_Brl).
884 barrel.setResizeStrategies(ResizeStrategy::Expand,
885 ResizeStrategy::Gap);
886
887 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
888
889 for (auto& element : elements) {
890 IdentityHelper id = element->identityHelper();
891 if (id.bec() != 0) {
892 continue;
893 }
894
895 int elementLayer = id.layer_disk();
896 layers[elementLayer].push_back(element->surface().getSharedPtr());
897 }
898
899 ATH_MSG_DEBUG("Adding " << layers.size()
900 << " layers to Strip barrel");
901
902 for (const auto& [ilayer, surfaces] : layers) {
903 ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
904 << " surfaces");
905 addStripBarrelLayer(
906 barrel, ilayer, surfaces,
908 }
909 });
910 });
911
912 // Add endcap containers
913 for (int bec : {-2, 2}) {
914 const std::string s = bec > 0 ? "p" : "n";
915
916 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
917
918 for (auto& element : elements) {
919 IdentityHelper id = element->identityHelper();
920 if (id.bec() * bec <= 0) {
921 continue;
922 }
923
924 layers[id.layer_disk()].push_back(element->surface().getSharedPtr());
925 }
926
927 ATH_MSG_DEBUG("Found " << layers.size() << " layers in Strip " << s
928 << "EC");
929
930 std::vector<std::vector<std::shared_ptr<Acts::Surface>>> sorted_layers;
931 sorted_layers.reserve(layers.size());
932 for (const auto& [key, surfaces] : layers) {
933 sorted_layers.push_back(surfaces);
934 }
935
936 std::sort(sorted_layers.begin(), sorted_layers.end(),
937 [&gctx](const auto& a, const auto& b) {
938 Acts::ProtoLayer pl_a(gctx, Acts::unpackConstSmartPointers(a));
939 Acts::ProtoLayer pl_b(gctx, Acts::unpackConstSmartPointers(b));
940 return std::abs(pl_a.min(AxisZ)) < std::abs(pl_b.min(AxisZ));
941 });
942
943 const auto endcapSurfaceArrayConfig =
945 stripContainer.withGeometryIdentifier([&sorted_layers, bec, &s,
946 &endcapSurfaceArrayConfig](auto& geoId) {
947 geoId.setAllVolumeIdsTo(s_stripVolumeId + std::floor(bec / 2))
948 .incrementLayerIds(1);
949
950 geoId.addCylinderContainer(
951 std::format("Strip_{:}_EC", s), AxisZ,
952 [&sorted_layers, &s, bec, &endcapSurfaceArrayConfig](auto& ec) {
953 // Collapse inter-disk gaps; material kept on outward discs only
954 // (see addStripEndcapLayer), so it stays at the smaller-|z| side.
955 ec.setAttachmentStrategy(bec > 0 ? AttachmentStrategy::Second
956 : AttachmentStrategy::First);
957 // Barrel-facing edge Expand: extend the innermost disk toward the
958 // barrel (smaller |z|), removing the barrel<->endcap gap; far edge
959 // stays Gap. Innermost inward disc dropped (addStripEndcapLayer) so
960 // the expanded face fuses with the barrel end disc.
961 ec.setResizeStrategies(
962 bec > 0 ? ResizeStrategy::Expand : ResizeStrategy::Gap,
963 bec > 0 ? ResizeStrategy::Gap : ResizeStrategy::Expand);
964
965 for (size_t i = 0; i < sorted_layers.size(); ++i) {
966 const auto& surfaces = sorted_layers[i];
967 auto layerName = std::format("Strip_{:}EC_{:}", s, i);
968 addStripEndcapLayer(ec, layerName, surfaces, bec,
969 endcapSurfaceArrayConfig);
970 }
971 });
972 });
973 };
974 });
975
976 // Explicit spacer volume beyond the strip detector's natural outer radius.
977 // Without it, the outer cylinder carrying the strip container material
978 // coincides with the ITk node boundary, causing volume merging issues.
979 std::vector<std::shared_ptr<Acts::Surface>> allStripSurfaces;
980 allStripSurfaces.reserve(elements.size());
981 for (auto& element : elements) {
982 allStripSurfaces.push_back(element->surface().getSharedPtr());
983 }
984 Acts::ProtoLayer stripExtent(gctx, Acts::unpackConstSmartPointers(allStripSurfaces));
985
986 constexpr double spacerClearance = 5_mm;
987 constexpr double spacerThickness = 5_mm;
988 double spacerRmin = stripExtent.max(AxisR) + spacerClearance;
989 double spacerHalfZ = std::max(std::abs(stripExtent.min(AxisZ)),
990 std::abs(stripExtent.max(AxisZ))) +
991 spacerClearance;
992
993 strip.addStaticVolume(
994 Acts::Transform3::Identity(),
995 std::make_shared<Acts::CylinderVolumeBounds>(
996 spacerRmin, spacerRmin + spacerThickness, spacerHalfZ),
997 "Strip_OuterSpacer");
998}
Gaudi::Property< std::vector< unsigned int > > m_itkStripBarrelNeighborWindow
const InDetDD::SiDetectorManager * m_itkStripMgr
Gaudi::Property< std::vector< unsigned int > > m_itkStripEndcapNeighborWindow
constexpr std::size_t s_stripVolumeId
Volume Ids used within the ITk.
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.

◆ initialize()

StatusCode ActsTrk::ItkBlueprintNodeBuilder::initialize ( )
override

Definition at line 211 of file ItkBlueprintNodeBuilder.cxx.

211 {
212 for (const Gaudi::Property<std::vector<unsigned int>>* window :
216 if (!toNeighborWindow(window->value()).has_value()) {
217 ATH_MSG_FATAL((*window)
218 << " must hold two bin counts of at most "
219 << static_cast<unsigned int>(std::numeric_limits<std::uint8_t>::max()));
220 return StatusCode::FAILURE;
221 }
222 }
223 if (m_surfaceArrayOverfill.value() > std::numeric_limits<std::uint8_t>::max()) {
225 << " exceeds " << static_cast<unsigned int>(std::numeric_limits<std::uint8_t>::max()));
226 return StatusCode::FAILURE;
227 }
228
229 // Retrieve the detector managers from the detector store for enabled systems
230 if (m_buildPixel) {
231 ATH_CHECK(detStore()->retrieve(m_itkPixelMgr, "ITkPixel"));
232 }
233 if (m_buildStrip) {
234 ATH_CHECK(detStore()->retrieve(m_itkStripMgr, "ITkStrip"));
235 }
236 m_elementStore = std::make_shared<ActsElementVector>();
237 return StatusCode::SUCCESS;
238}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_FATAL(x,...)
Gaudi::Property< unsigned int > m_surfaceArrayOverfill
Extra cells per direction and axis that each surface fills around its footprint.

◆ surfaceArrayConfig()

Acts::SurfaceArrayNavigationPolicy::Config ActsTrk::ItkBlueprintNodeBuilder::surfaceArrayConfig ( Acts::SurfaceArrayNavigationPolicy::LayerType layerType,
const Gaudi::Property< std::vector< unsigned int > > & neighborWindow ) const
private

Configuration of the surface array navigation policy of one layer.

Parameters
layerTypeThe layer type of the surface array
neighborWindowThe neighbor window bound property, checked in initialize()

Definition at line 241 of file ItkBlueprintNodeBuilder.cxx.

243 {
244 return Acts::SurfaceArrayNavigationPolicy::Config{
245 .layerType = layerType,
246 .bins = {0, 0},
247 .numberOfBinsFactor = 5.0,
248 .neighborWindow = *toNeighborWindow(neighborWindow.value()),
249 .overfill = static_cast<std::uint8_t>(m_surfaceArrayOverfill.value())};
250}

Member Data Documentation

◆ m_buildPixel

Gaudi::Property<bool> ActsTrk::ItkBlueprintNodeBuilder::m_buildPixel {this, "buildPixel", true}
private

Definition at line 45 of file ItkBlueprintNodeBuilder.h.

45{this, "buildPixel", true};

◆ m_buildStrip

Gaudi::Property<bool> ActsTrk::ItkBlueprintNodeBuilder::m_buildStrip {this, "buildStrip", true}
private

Definition at line 46 of file ItkBlueprintNodeBuilder.h.

46{this, "buildStrip", true};

◆ m_doEndcapLayerMerging

Gaudi::Property<bool> ActsTrk::ItkBlueprintNodeBuilder::m_doEndcapLayerMerging
private
Initial value:
{this, "doEndcapLayerMerging",
true}

Definition at line 43 of file ItkBlueprintNodeBuilder.h.

43 {this, "doEndcapLayerMerging",
44 true};

◆ m_elementStore

std::shared_ptr<ActsElementVector> ActsTrk::ItkBlueprintNodeBuilder::m_elementStore {nullptr}
private

Definition at line 41 of file ItkBlueprintNodeBuilder.h.

41{nullptr};

◆ m_itkPixelInnerBarrelNeighborWindow

Gaudi::Property<std::vector<unsigned int> > ActsTrk::ItkBlueprintNodeBuilder::m_itkPixelInnerBarrelNeighborWindow
private
Initial value:
{
this, "ITkPixelInnerBarrelNeighborWindow", {1, 8},
"Neighbor window bound {phi, z} of the ITk inner pixel barrel layers"}

Most bins the surface array lookup serves on each side of the crossing, as {phi, z} for the barrel and {r, phi} for the endcap.

The lookup sizes the window from the crossing angle and the layer thickness, so these values only cap it. The defaults cover the steepest crossing for |z0| < 200 mm at a bin factor of 5.

Definition at line 53 of file ItkBlueprintNodeBuilder.h.

53 {
54 this, "ITkPixelInnerBarrelNeighborWindow", {1, 8},
55 "Neighbor window bound {phi, z} of the ITk inner pixel barrel layers"};

◆ m_itkPixelInnerEndcapNeighborWindow

Gaudi::Property<std::vector<unsigned int> > ActsTrk::ItkBlueprintNodeBuilder::m_itkPixelInnerEndcapNeighborWindow
private
Initial value:
{
this, "ITkPixelInnerEndcapNeighborWindow", {2, 1},
"Neighbor window bound {r, phi} of the ITk inner pixel endcap layers"}

Definition at line 56 of file ItkBlueprintNodeBuilder.h.

56 {
57 this, "ITkPixelInnerEndcapNeighborWindow", {2, 1},
58 "Neighbor window bound {r, phi} of the ITk inner pixel endcap layers"};

◆ m_itkPixelMgr

const InDetDD::SiDetectorManager* ActsTrk::ItkBlueprintNodeBuilder::m_itkPixelMgr {nullptr}
private

Definition at line 39 of file ItkBlueprintNodeBuilder.h.

39{nullptr};

◆ m_itkPixelOuterBarrelNeighborWindow

Gaudi::Property<std::vector<unsigned int> > ActsTrk::ItkBlueprintNodeBuilder::m_itkPixelOuterBarrelNeighborWindow
private
Initial value:
{
this, "ITkPixelOuterBarrelNeighborWindow", {1, 3},
"Neighbor window bound {phi, z} of the ITk outer pixel barrel layers"}

Definition at line 59 of file ItkBlueprintNodeBuilder.h.

59 {
60 this, "ITkPixelOuterBarrelNeighborWindow", {1, 3},
61 "Neighbor window bound {phi, z} of the ITk outer pixel barrel layers"};

◆ m_itkPixelOuterEndcapNeighborWindow

Gaudi::Property<std::vector<unsigned int> > ActsTrk::ItkBlueprintNodeBuilder::m_itkPixelOuterEndcapNeighborWindow
private
Initial value:
{
this, "ITkPixelOuterEndcapNeighborWindow", {4, 1},
"Neighbor window bound {r, phi} of the ITk outer pixel endcap and inclined layers"}

Definition at line 62 of file ItkBlueprintNodeBuilder.h.

62 {
63 this, "ITkPixelOuterEndcapNeighborWindow", {4, 1},
64 "Neighbor window bound {r, phi} of the ITk outer pixel endcap and inclined layers"};

◆ m_itkStripBarrelNeighborWindow

Gaudi::Property<std::vector<unsigned int> > ActsTrk::ItkBlueprintNodeBuilder::m_itkStripBarrelNeighborWindow
private
Initial value:
{
this, "ITkStripBarrelNeighborWindow", {1, 4},
"Neighbor window bound {phi, z} of the ITk strip barrel layers"}

Definition at line 65 of file ItkBlueprintNodeBuilder.h.

65 {
66 this, "ITkStripBarrelNeighborWindow", {1, 4},
67 "Neighbor window bound {phi, z} of the ITk strip barrel layers"};

◆ m_itkStripEndcapNeighborWindow

Gaudi::Property<std::vector<unsigned int> > ActsTrk::ItkBlueprintNodeBuilder::m_itkStripEndcapNeighborWindow
private
Initial value:
{
this, "ITkStripEndcapNeighborWindow", {2, 1},
"Neighbor window bound {r, phi} of the ITk strip endcap layers"}

Definition at line 68 of file ItkBlueprintNodeBuilder.h.

68 {
69 this, "ITkStripEndcapNeighborWindow", {2, 1},
70 "Neighbor window bound {r, phi} of the ITk strip endcap layers"};

◆ m_itkStripMgr

const InDetDD::SiDetectorManager* ActsTrk::ItkBlueprintNodeBuilder::m_itkStripMgr {nullptr}
private

Definition at line 40 of file ItkBlueprintNodeBuilder.h.

40{nullptr};

◆ m_surfaceArrayOverfill

Gaudi::Property<unsigned int> ActsTrk::ItkBlueprintNodeBuilder::m_surfaceArrayOverfill
private
Initial value:
{this, "SurfaceArrayOverfill", 0,
"Extra cells per direction and axis that each surface fills in the surface arrays"}

Extra cells per direction and axis that each surface fills around its footprint.

Definition at line 73 of file ItkBlueprintNodeBuilder.h.

73 {this, "SurfaceArrayOverfill", 0,
74 "Extra cells per direction and axis that each surface fills in the surface arrays"};

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