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

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}

Detailed Description

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

Definition at line 19 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

Definition at line 216 of file ItkBlueprintNodeBuilder.cxx.

218 {
219
220 auto itkNode =
221 std::make_shared<Acts::CylinderContainerBlueprintNode>(
222 "itkNode", AxisZ);
223 itkNode->setAttachmentStrategy(AttachmentStrategy::Gap);
224 itkNode->setResizeStrategy(ResizeStrategy::Gap);
225
226 auto& itk = itkNode->addCylinderContainer("ItkNodeMain", AxisR);
227 itk.setAttachmentStrategy(AttachmentStrategy::Gap);
228 itk.setResizeStrategy(ResizeStrategy::Gap);
229
230 itk.addMaterial("ItkNodeMain_Material", [&](auto& mat) {
231 if (m_buildStrip) {
232 mat.configureFace(NegativeDisc,
233 AxisSpec::DeferredEquidistant(50, AxisR),
234 AxisSpec::DeferredEquidistant(50, AxisPhi));
235 mat.configureFace(PositiveDisc,
236 AxisSpec::DeferredEquidistant(50, AxisR),
237 AxisSpec::DeferredEquidistant(50, AxisPhi));
238 }
239
240 auto& innerContainer = mat.addCylinderContainer("ITkInnerContainer", AxisR);
241 // The radial dead bands between the R-stacked sub-detectors (beampipe /
242 // InnerPixel / OuterPixel / Strip) are genuine empty space, but the default
243 // Midpoint attachment splits each band at its midpoint and lets BOTH
244 // neighbours fill their half with a ResizeStrategy::Gap volume -> two thin
245 // gap volumes per interface. Second makes a single neighbour own the whole
246 // band -> one gap per interface. Material is unaffected: each sub-detector
247 // keeps ResizeStrategy::Gap, so its material-bearing shell stays frozen at
248 // its true edge and only the empty band is merged.
249 innerContainer.setAttachmentStrategy(AttachmentStrategy::Second);
250 innerContainer.setResizeStrategy(ResizeStrategy::Gap);
251 // Beam pipe is passed in as an optional child from BeamPipeBlueprintNodeBuilder
252 if (childNode) {
253 innerContainer.addChild(std::move(childNode));
254 }
255
256 if (m_buildPixel) {
257 buildItkPixelBlueprintNode(gctx, innerContainer);
258 }
259 if (m_buildStrip) {
260 buildItkStripBlueprintNode(gctx, innerContainer);
261 }
262 });
264 Acts::ExtentEnvelope envelope{};
265 envelope[AxisDirection::AxisZ] = {20_mm, 20_mm};
266 envelope[AxisDirection::AxisR] = {1_mm, 2_mm};
267 auto padNode = std::make_shared<Acts::PadBlueprintNode>("ITkEnvelope", envelope);
268 padNode->addChild(itkNode);
269 return padNode;
270}
void buildItkStripBlueprintNode(const Acts::GeometryContext &gctx, Acts::BlueprintNode &node)
void buildItkPixelBlueprintNode(const Acts::GeometryContext &gctx, Acts::BlueprintNode &node)

◆ buildItkPixelBlueprintNode()

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

Definition at line 272 of file ItkBlueprintNodeBuilder.cxx.

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

753 {
754
755 // Get ITkStrip parameters from detector manager
756 if (!m_itkStripMgr) {
757 ATH_MSG_ERROR("ITkStrip manager not available");
758 throw std::runtime_error("ITkStrip manager not available");
759 }
760
761 ATH_MSG_DEBUG("Detector manager has "
762 << m_itkStripMgr->getDetectorElementCollection()->size()
763 << " elements");
764
765 std::vector<std::shared_ptr<ActsDetectorElement>> elements;
766
768 for (const auto* element : *m_itkStripMgr->getDetectorElementCollection()) {
769 const InDetDD::SiDetectorElement* siDetElement =
770 dynamic_cast<const InDetDD::SiDetectorElement*>(element);
771 if (siDetElement == nullptr) {
772 ATH_MSG_ERROR("Detector element was nullptr");
773 throw std::runtime_error{"Corrupt detector element collection"};
774 }
775 elements.push_back(std::make_shared<ActsDetectorElement>(*siDetElement));
776 }
777 ATH_MSG_VERBOSE("Retrieved " << elements.size() << " elements");
778
779 // Copy to service level store to extend lifetime
780 m_elementStore->vector().insert(m_elementStore->vector().end(),
781 elements.begin(), elements.end());
782
783 auto& strip = node.addCylinderContainer("Strip", AxisR);
784 strip.setAttachmentStrategy(AttachmentStrategy::Gap);
785 // Inner edge Expand: extend all Strip cylinders inward onto the OuterPixel
786 // outer shell (316.7), removing Strip::Gap1. The InnerCylinder material
787 // (382.5) is dropped below so the expanded face fuses cleanly with the
788 // OuterPixel OuterCylinder (316.7, the kept lower-r surface). Outer edge stays
789 // Gap (Strip::Gap2, the resize gap up to the ItkNodeMain envelope, carries
790 // the Strip outer material and must not move).
791 strip.setResizeStrategies(ResizeStrategy::Expand, ResizeStrategy::Gap);
792
793 strip.addMaterial("StripMaterial", [&](auto& mat) {
794 mat.configureFace(OuterCylinder,
795 AxisSpec::DeferredEquidistant(50, AxisRPhi),
796 AxisSpec::DeferredEquidistant(100, AxisZ));
797
798 auto& stripContainer = mat.addCylinderContainer("Strip", AxisZ);
799
800 // Add barrel container
801 stripContainer.withGeometryIdentifier([this, &elements](auto& geoId) {
802 geoId.setAllVolumeIdsTo(s_stripVolumeId).incrementLayerIds(1);
803
804 auto& brl_mat =
805 geoId.addMaterial("Strip_Brl_Material", [&](auto& material) {
806 material.configureFace(NegativeDisc,
807 AxisSpec::DeferredEquidistant(50, AxisR),
808 AxisSpec::DeferredEquidistant(50, AxisPhi));
809 material.configureFace(PositiveDisc,
810 AxisSpec::DeferredEquidistant(50, AxisR),
811 AxisSpec::DeferredEquidistant(50, AxisPhi));
812 });
813 brl_mat.addCylinderContainer(
814 "Strip_Brl", AxisR, [this, &elements](auto& barrel) {
815 // Collapse the inter-layer gaps by extending the outer layer inward
816 // (Second); material kept only on OuterCylinder faces (see
817 // addStripBarrelLayer) so it stays at the lower-radius side.
818 barrel.setAttachmentStrategy(AttachmentStrategy::Second);
819 // Inner edge: Expand the innermost layer onto the StripMaterial
820 // inner shell (382.5), removing Strip_Brl::Gap1. The layer's own
821 // InnerCylinder material (385.5, redundant with the full-z shell
822 // 3mm below it) is dropped in addStripBarrelLayer so the expanded
823 // face fuses cleanly (one material side). Outer edge stays Gap
824 // (invariant, see OuterPixel_Brl).
825 barrel.setResizeStrategies(ResizeStrategy::Expand,
826 ResizeStrategy::Gap);
827
828 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
829
830 for (auto& element : elements) {
831 IdentityHelper id = element->identityHelper();
832 if (id.bec() != 0) {
833 continue;
834 }
835
836 int elementLayer = id.layer_disk();
837 layers[elementLayer].push_back(element->surface().getSharedPtr());
838 }
839
840 ATH_MSG_DEBUG("Adding " << layers.size()
841 << " layers to Strip barrel");
842
843 for (const auto& [ilayer, surfaces] : layers) {
844 ATH_MSG_DEBUG("- Layer " << ilayer << " has " << surfaces.size()
845 << " surfaces");
846 addStripBarrelLayer(barrel, ilayer, surfaces);
847 }
848 });
849 });
850
851 // Add endcap containers
852 for (int bec : {-2, 2}) {
853 const std::string s = bec > 0 ? "p" : "n";
854
855 std::map<int, std::vector<std::shared_ptr<Acts::Surface>>> layers{};
856
857 for (auto& element : elements) {
858 IdentityHelper id = element->identityHelper();
859 if (id.bec() * bec <= 0) {
860 continue;
861 }
862
863 layers[id.layer_disk()].push_back(element->surface().getSharedPtr());
864 }
865
866 ATH_MSG_DEBUG("Found " << layers.size() << " layers in Strip " << s
867 << "EC");
868
869 std::vector<std::vector<std::shared_ptr<Acts::Surface>>> sorted_layers;
870 sorted_layers.reserve(layers.size());
871 for (const auto& [key, surfaces] : layers) {
872 sorted_layers.push_back(surfaces);
873 }
874
875 std::sort(sorted_layers.begin(), sorted_layers.end(),
876 [&gctx](const auto& a, const auto& b) {
877 Acts::ProtoLayer pl_a(gctx, makeConstPtrVector(a));
878 Acts::ProtoLayer pl_b(gctx, makeConstPtrVector(b));
879 return std::abs(pl_a.min(AxisZ)) < std::abs(pl_b.min(AxisZ));
880 });
881
882 stripContainer.withGeometryIdentifier([&sorted_layers, bec,
883 &s](auto& geoId) {
884 geoId.setAllVolumeIdsTo(s_stripVolumeId + std::floor(bec / 2))
885 .incrementLayerIds(1);
886
887 geoId.addCylinderContainer(
888 "Strip_" + s + "EC", AxisZ, [&sorted_layers, &s, bec](auto& ec) {
889 // Collapse inter-disk gaps; material kept on outward discs only
890 // (see addStripEndcapLayer), so it stays at the smaller-|z| side.
891 ec.setAttachmentStrategy(bec > 0 ? AttachmentStrategy::Second
892 : AttachmentStrategy::First);
893 // Barrel-facing edge Expand: extend the innermost disk toward the
894 // barrel (smaller |z|), removing the barrel<->endcap gap; far edge
895 // stays Gap. Innermost inward disc dropped (addStripEndcapLayer) so
896 // the expanded face fuses with the barrel end disc.
897 ec.setResizeStrategies(
898 bec > 0 ? ResizeStrategy::Expand : ResizeStrategy::Gap,
899 bec > 0 ? ResizeStrategy::Gap : ResizeStrategy::Expand);
900
901 for (size_t i = 0; i < sorted_layers.size(); ++i) {
902 const auto& surfaces = sorted_layers[i];
903 auto layerName = "Strip_" + s + "EC_" + std::to_string(i);
904 addStripEndcapLayer(ec, layerName, surfaces, bec);
905 }
906 });
907 });
908 };
909 });
910
911 // Explicit spacer volume beyond the strip detector's natural outer radius.
912 // Without it, the outer cylinder carrying the strip container material
913 // coincides with the ITk node boundary, causing volume merging issues.
914 std::vector<std::shared_ptr<Acts::Surface>> allStripSurfaces;
915 allStripSurfaces.reserve(elements.size());
916 for (auto& element : elements) {
917 allStripSurfaces.push_back(element->surface().getSharedPtr());
918 }
919 Acts::ProtoLayer stripExtent(gctx, makeConstPtrVector(allStripSurfaces));
920
921 constexpr double spacerClearance = 5_mm;
922 constexpr double spacerThickness = 5_mm;
923 double spacerRmin = stripExtent.max(AxisR) + spacerClearance;
924 double spacerHalfZ = std::max(std::abs(stripExtent.min(AxisZ)),
925 std::abs(stripExtent.max(AxisZ))) +
926 spacerClearance;
927
928 strip.addStaticVolume(
929 Acts::Transform3::Identity(),
930 std::make_shared<Acts::CylinderVolumeBounds>(
931 spacerRmin, spacerRmin + spacerThickness, spacerHalfZ),
932 "Strip_OuterSpacer");
933}
const InDetDD::SiDetectorManager * m_itkStripMgr
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 203 of file ItkBlueprintNodeBuilder.cxx.

203 {
204 // Retrieve the detector managers from the detector store for enabled systems
205 if (m_buildPixel) {
206 ATH_CHECK(detStore()->retrieve(m_itkPixelMgr, "ITkPixel"));
207 }
208 if (m_buildStrip) {
209 ATH_CHECK(detStore()->retrieve(m_itkStripMgr, "ITkStrip"));
210 }
211 m_elementStore = std::make_shared<ActsElementVector>();
212 return StatusCode::SUCCESS;
213}
#define ATH_CHECK
Evaluate an expression and check for errors.

Member Data Documentation

◆ m_buildPixel

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

Definition at line 41 of file ItkBlueprintNodeBuilder.h.

41{this, "buildPixel", true};

◆ m_buildStrip

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

Definition at line 42 of file ItkBlueprintNodeBuilder.h.

42{this, "buildStrip", true};

◆ m_doEndcapLayerMerging

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

Definition at line 39 of file ItkBlueprintNodeBuilder.h.

39 {this, "doEndcapLayerMerging",
40 true};

◆ m_elementStore

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

Definition at line 37 of file ItkBlueprintNodeBuilder.h.

37{nullptr};

◆ m_itkPixelMgr

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

Definition at line 35 of file ItkBlueprintNodeBuilder.h.

35{nullptr};

◆ m_itkStripMgr

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

Definition at line 36 of file ItkBlueprintNodeBuilder.h.

36{nullptr};

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