Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-17 07:46:20

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 /// Implementation of BlueprintBuilder template methods.
0010 /// Include this header only in TUs that perform explicit template instantiation
0011 /// (e.g. geometry plugin .cpp files). Do not include from the main
0012 /// BlueprintBuilder.hpp.
0013 
0014 #pragma once
0015 
0016 #include "Acts/Geometry/BlueprintBuilder.hpp"
0017 #include "Acts/Utilities/FunctionComposition.hpp"
0018 
0019 namespace Acts::Experimental {
0020 
0021 namespace detail {
0022 
0023 template <typename ElementT>
0024 struct LayerBuildInputs {
0025   std::vector<ElementT> layerElements;
0026   std::optional<std::string> deducedContainerName;
0027 };
0028 
0029 template <detail::BlueprintBackend BackendT>
0030 LayerBuildInputs<typename BackendT::Element> resolveLayerBuildInputs(
0031     const BlueprintBuilder<BackendT>& builder,
0032     const std::optional<typename BackendT::Element>& container,
0033     const std::optional<std::vector<typename BackendT::Element>>&
0034         explicitLayerElements,
0035     const std::optional<std::regex>& filter) {
0036   using Element = typename BackendT::Element;
0037   LayerBuildInputs<Element> inputs;
0038 
0039   if (container.has_value()) {
0040     inputs.deducedContainerName = builder.backend().nameOf(container.value());
0041   }
0042 
0043   if (explicitLayerElements.has_value()) {
0044     inputs.layerElements = *explicitLayerElements;
0045 
0046     if (filter.has_value()) {
0047       std::vector<Element> filteredLayerElements;
0048       filteredLayerElements.reserve(inputs.layerElements.size());
0049       for (const auto& layerElement : inputs.layerElements) {
0050         const std::string layerElementName =
0051             builder.backend().nameOf(layerElement);
0052         if (std::regex_match(layerElementName, filter.value())) {
0053           filteredLayerElements.push_back(layerElement);
0054         }
0055       }
0056       inputs.layerElements = std::move(filteredLayerElements);
0057     }
0058     return inputs;
0059   }
0060 
0061   if (!container.has_value()) {
0062     throw std::runtime_error("Container not set in ElementLayerAssembler");
0063   }
0064   if (!filter.has_value()) {
0065     throw std::runtime_error("Pattern not set in ElementLayerAssembler");
0066   }
0067 
0068   inputs.layerElements =
0069       builder.findDetElementByNamePattern(container.value(), filter.value());
0070   return inputs;
0071 }
0072 
0073 // Both ElementLayerAssembler and SensorLayerAssembler use the same underlying
0074 // LayerCustomizer type (a std::function with identical signature), so a single
0075 // template deducing CustomizerT covers both.
0076 template <detail::BlueprintBackend BackendT, typename CustomizerT>
0077 std::shared_ptr<Acts::Experimental::LayerBlueprintNode> finalizeLayer(
0078     const std::optional<typename BackendT::Element>& layerElement,
0079     std::shared_ptr<Acts::Experimental::LayerBlueprintNode> layer,
0080     const std::optional<Acts::ExtentEnvelope>& envelope,
0081     const CustomizerT& onLayer) {
0082   if (envelope.has_value()) {
0083     layer->setEnvelope(envelope.value());
0084   }
0085   if (onLayer) {
0086     layer = onLayer(layerElement, std::move(layer));
0087   }
0088   return layer;
0089 }
0090 
0091 }  // namespace detail
0092 
0093 // ElementLayerAssembler
0094 template <detail::BlueprintBackend BackendT>
0095 ElementLayerAssembler<BackendT>::ElementLayerAssembler(const Builder& builder)
0096     : m_builder{&builder} {}
0097 
0098 template <detail::BlueprintBackend BackendT>
0099 ElementLayerAssembler<BackendT>&& ElementLayerAssembler<BackendT>::setLayerType(
0100     LayerType layerType) && {
0101   m_layerType = layerType;
0102   return std::move(*this);
0103 }
0104 
0105 template <detail::BlueprintBackend BackendT>
0106 ElementLayerAssembler<BackendT>&& ElementLayerAssembler<BackendT>::endcap() && {
0107   return std::move(*this).setLayerType(LayerType::Disc);
0108 }
0109 
0110 template <detail::BlueprintBackend BackendT>
0111 ElementLayerAssembler<BackendT>&& ElementLayerAssembler<BackendT>::barrel() && {
0112   return std::move(*this).setLayerType(LayerType::Cylinder);
0113 }
0114 
0115 template <detail::BlueprintBackend BackendT>
0116 ElementLayerAssembler<BackendT>&& ElementLayerAssembler<BackendT>::planar() && {
0117   return std::move(*this).setLayerType(LayerType::Plane);
0118 }
0119 
0120 template <detail::BlueprintBackend BackendT>
0121 ElementLayerAssembler<BackendT>&&
0122 ElementLayerAssembler<BackendT>::setLayerFilter(const std::string& pattern) && {
0123   return std::move(*this).setLayerFilter(std::regex{pattern});
0124 }
0125 
0126 template <detail::BlueprintBackend BackendT>
0127 ElementLayerAssembler<BackendT>&&
0128 ElementLayerAssembler<BackendT>::setLayerFilter(const std::regex& pattern) && {
0129   m_filter = pattern;
0130   return std::move(*this);
0131 }
0132 
0133 template <detail::BlueprintBackend BackendT>
0134 ElementLayerAssembler<BackendT>&& ElementLayerAssembler<BackendT>::setContainer(
0135     const Element& container) && {
0136   m_container = container;
0137   return std::move(*this);
0138 }
0139 
0140 template <detail::BlueprintBackend BackendT>
0141 ElementLayerAssembler<BackendT>&&
0142 ElementLayerAssembler<BackendT>::setContainerName(
0143     std::string containerName) && {
0144   m_containerName = std::move(containerName);
0145   return std::move(*this);
0146 }
0147 
0148 template <detail::BlueprintBackend BackendT>
0149 ElementLayerAssembler<BackendT>&&
0150 ElementLayerAssembler<BackendT>::setLayerNameSuffix(
0151     const std::optional<std::string>& layerNameSuffix) && {
0152   m_layerSpec.layerName = layerNameSuffix;
0153   return std::move(*this);
0154 }
0155 
0156 template <detail::BlueprintBackend BackendT>
0157 ElementLayerAssembler<BackendT>&&
0158 ElementLayerAssembler<BackendT>::setLayerElements(
0159     std::vector<Element> layerElements) && {
0160   m_layerElements = std::move(layerElements);
0161   return std::move(*this);
0162 }
0163 
0164 template <detail::BlueprintBackend BackendT>
0165 ElementLayerAssembler<BackendT>&& ElementLayerAssembler<BackendT>::setContainer(
0166     const std::string& name) && {
0167   m_container = m_builder->findDetElementByName(name);
0168   if (!m_container.has_value()) {
0169     throw std::runtime_error("Could not find DetElement with name " + name +
0170                              " in ElementLayerAssembler");
0171   }
0172   return std::move(*this);
0173 }
0174 
0175 template <detail::BlueprintBackend BackendT>
0176 ElementLayerAssembler<BackendT>&& ElementLayerAssembler<BackendT>::setEnvelope(
0177     const Acts::ExtentEnvelope& envelope) && {
0178   m_envelope = envelope;
0179   return std::move(*this);
0180 }
0181 
0182 template <detail::BlueprintBackend BackendT>
0183 ElementLayerAssembler<BackendT>&& ElementLayerAssembler<BackendT>::setEmptyOk(
0184     bool emptyOk) && {
0185   m_emptyOk = emptyOk;
0186   return std::move(*this);
0187 }
0188 
0189 template <detail::BlueprintBackend BackendT>
0190 ElementLayerAssembler<BackendT>&&
0191 ElementLayerAssembler<BackendT>::setAttachmentStrategy(
0192     std::optional<Acts::VolumeAttachmentStrategy> strategy) && {
0193   m_attachmentStrategy = strategy;
0194   return std::move(*this);
0195 }
0196 
0197 template <detail::BlueprintBackend BackendT>
0198 void ElementLayerAssembler<BackendT>::addTo(
0199     Acts::Experimental::BlueprintNode& node) const&& {
0200   node.addChild(build());
0201 }
0202 
0203 template <detail::BlueprintBackend BackendT>
0204 std::shared_ptr<Acts::Experimental::ContainerBlueprintNode>
0205 ElementLayerAssembler<BackendT>::build() const {
0206   const auto& logger = m_builder->logger();
0207 
0208   if (!m_layerType.has_value()) {
0209     throw std::runtime_error("Layer type not set in ElementLayerAssembler");
0210   }
0211 
0212   if constexpr (detail::HasAxisDefinition<BackendT>) {
0213     if (!m_layerSpec.axes.has_value()) {
0214       throw std::runtime_error("Axes not set in ElementLayerAssembler");
0215     }
0216   }
0217 
0218   if (!m_filter.has_value() && !m_layerElements.has_value()) {
0219     throw std::runtime_error(
0220         "Neither filter nor layer elements set in ElementLayerAssembler");
0221   }
0222 
0223   // Resolve the concrete layer-element set and optional deduced container name.
0224   auto inputs = detail::resolveLayerBuildInputs<BackendT>(
0225       *m_builder, m_container, m_layerElements, m_filter);
0226 
0227   // Resolve the final output container name (manual override wins).
0228   std::string containerName;
0229   if (m_containerName.has_value()) {
0230     containerName = m_containerName.value();
0231   } else if (inputs.deducedContainerName.has_value()) {
0232     containerName = inputs.deducedContainerName.value();
0233   } else {
0234     throw std::runtime_error(
0235         "Container name is not set in ElementLayerAssembler. Provide "
0236         "setContainerName() or setContainer().");
0237   }
0238 
0239   if (inputs.layerElements.empty()) {
0240     ACTS_LOG(m_emptyOk ? Acts::Logging::INFO : Acts::Logging::ERROR,
0241              "No layers found in container " << containerName
0242                                              << " matching pattern");
0243     if (!m_emptyOk) {
0244       throw std::runtime_error(std::format(
0245           "No layers found in container {} matching pattern", containerName));
0246     }
0247   }
0248 
0249   std::shared_ptr<Acts::Experimental::ContainerBlueprintNode> node;
0250   if (m_layerType != LayerType::Plane) {
0251     const Acts::AxisDirection axisDir = m_layerType == LayerType::Cylinder
0252                                             ? Acts::AxisDirection::AxisR
0253                                             : Acts::AxisDirection::AxisZ;
0254     node = std::make_shared<Acts::Experimental::CylinderContainerBlueprintNode>(
0255         containerName, axisDir);
0256   } else {
0257     node = std::make_shared<Acts::Experimental::CuboidContainerBlueprintNode>(
0258         containerName, Acts::AxisDirection::AxisZ);
0259   }
0260 
0261   if (m_attachmentStrategy.has_value()) {
0262     node->setAttachmentStrategy(m_attachmentStrategy.value());
0263   }
0264 
0265   for (const auto& layerElement : inputs.layerElements) {
0266     LayerSpec resolvedLayerSpec = m_layerSpec;
0267     std::string fullLayerName = m_builder->getPathToElementName(layerElement);
0268     if (resolvedLayerSpec.layerName.has_value() &&
0269         !resolvedLayerSpec.layerName->empty()) {
0270       fullLayerName += "|" + *resolvedLayerSpec.layerName;
0271     }
0272     resolvedLayerSpec.layerName = std::move(fullLayerName);
0273     auto layer = m_builder->makeLayer(layerElement, resolvedLayerSpec);
0274     layer->setLayerType(m_layerType.value());
0275     node->addChild(detail::finalizeLayer<BackendT>(
0276         std::optional<Element>{layerElement}, std::move(layer), m_envelope,
0277         m_onLayer));
0278   }
0279 
0280   return node;
0281 }
0282 
0283 // SensorLayerAssembler
0284 template <detail::BlueprintBackend BackendT>
0285 SensorLayerAssembler<BackendT>::SensorLayerAssembler(const Builder& builder)
0286     : m_builder{&builder} {}
0287 
0288 template <detail::BlueprintBackend BackendT>
0289 SensorLayerAssembler<BackendT>&& SensorLayerAssembler<BackendT>::setLayerType(
0290     LayerType layerType) && {
0291   m_layerType = layerType;
0292   return std::move(*this);
0293 }
0294 
0295 template <detail::BlueprintBackend BackendT>
0296 SensorLayerAssembler<BackendT>&& SensorLayerAssembler<BackendT>::endcap() && {
0297   return std::move(*this).setLayerType(LayerType::Disc);
0298 }
0299 
0300 template <detail::BlueprintBackend BackendT>
0301 SensorLayerAssembler<BackendT>&& SensorLayerAssembler<BackendT>::barrel() && {
0302   return std::move(*this).setLayerType(LayerType::Cylinder);
0303 }
0304 
0305 template <detail::BlueprintBackend BackendT>
0306 SensorLayerAssembler<BackendT>&& SensorLayerAssembler<BackendT>::planar() && {
0307   return std::move(*this).setLayerType(LayerType::Plane);
0308 }
0309 
0310 template <detail::BlueprintBackend BackendT>
0311 SensorLayerAssembler<BackendT>&& SensorLayerAssembler<BackendT>::setSensors(
0312     std::vector<Element> sensors) && {
0313   m_sensors = std::move(sensors);
0314   return std::move(*this);
0315 }
0316 
0317 template <detail::BlueprintBackend BackendT>
0318 SensorLayerAssembler<BackendT>&& SensorLayerAssembler<BackendT>::groupBy(
0319     typename SensorLayerAssembler<BackendT>::LayerGrouper grouper) && {
0320   m_groupBy = std::move(grouper);
0321   return std::move(*this);
0322 }
0323 
0324 template <detail::BlueprintBackend BackendT>
0325 SensorLayerAssembler<BackendT>&&
0326 SensorLayerAssembler<BackendT>::setContainerName(std::string containerName) && {
0327   m_containerName = std::move(containerName);
0328   return std::move(*this);
0329 }
0330 
0331 template <detail::BlueprintBackend BackendT>
0332 SensorLayerAssembler<BackendT>&& SensorLayerAssembler<BackendT>::setEnvelope(
0333     const Acts::ExtentEnvelope& envelope) && {
0334   m_envelope = envelope;
0335   return std::move(*this);
0336 }
0337 
0338 template <detail::BlueprintBackend BackendT>
0339 SensorLayerAssembler<BackendT>&&
0340 SensorLayerAssembler<BackendT>::setAttachmentStrategy(
0341     std::optional<Acts::VolumeAttachmentStrategy> strategy) && {
0342   m_attachmentStrategy = strategy;
0343   return std::move(*this);
0344 }
0345 
0346 template <detail::BlueprintBackend BackendT>
0347 void SensorLayerAssembler<BackendT>::addTo(
0348     Acts::Experimental::BlueprintNode& node) const&& {
0349   node.addChild(build());
0350 }
0351 
0352 template <detail::BlueprintBackend BackendT>
0353 std::shared_ptr<Acts::Experimental::ContainerBlueprintNode>
0354 SensorLayerAssembler<BackendT>::build() const {
0355   using enum Acts::AxisDirection;
0356 
0357   if (!m_layerType.has_value()) {
0358     throw std::runtime_error("Layer type not set in SensorLayerAssembler");
0359   }
0360 
0361   if constexpr (detail::HasAxisDefinition<BackendT>) {
0362     if (!m_layerSpec.axes.has_value()) {
0363       throw std::runtime_error(
0364           std::format("Axes not set in SensorLayerAssembler (backend: {})",
0365                       BackendT::kIdentifier));
0366     }
0367   }
0368 
0369   if (!m_sensors.has_value()) {
0370     throw std::runtime_error("Sensors not set in SensorLayerAssembler");
0371   }
0372   if (!m_containerName.has_value()) {
0373     throw std::runtime_error(
0374         "Container name not set in SensorLayerAssembler. "
0375         "Call setContainerName().");
0376   }
0377   if (!m_groupBy) {
0378     throw std::runtime_error(
0379         "SensorLayerAssembler requires groupBy(). For a single layer without "
0380         "grouping, use BlueprintBuilder::layerFromSensors() instead.");
0381   }
0382 
0383   std::shared_ptr<Acts::Experimental::ContainerBlueprintNode> node;
0384   if (m_layerType != LayerType::Plane) {
0385     const AxisDirection axisDir =
0386         m_layerType == LayerType::Cylinder ? AxisR : AxisZ;
0387     node = std::make_shared<Acts::Experimental::CylinderContainerBlueprintNode>(
0388         m_containerName.value(), axisDir);
0389   } else {
0390     node = std::make_shared<Acts::Experimental::CuboidContainerBlueprintNode>(
0391         m_containerName.value(), AxisZ);
0392   }
0393 
0394   if (m_attachmentStrategy.has_value()) {
0395     node->setAttachmentStrategy(m_attachmentStrategy.value());
0396   }
0397 
0398   struct GroupData {
0399     std::string key;
0400     std::vector<Element> sensors;
0401   };
0402 
0403   // Keep first-seen group order deterministic.
0404   std::vector<GroupData> groups;
0405   for (const auto& sensor : *m_sensors) {
0406     const std::string key = m_groupBy(sensor);
0407     auto it = std::ranges::find_if(
0408         groups, [&](const GroupData& g) { return g.key == key; });
0409     if (it == groups.end()) {
0410       groups.push_back(GroupData{.key = key, .sensors = {}});
0411       it = std::prev(groups.end());
0412     }
0413     it->sensors.push_back(sensor);
0414   }
0415 
0416   for (const auto& group : groups) {
0417     if (group.key.empty()) {
0418       throw std::runtime_error(
0419           "groupBy() key must be non-empty for all sensors in "
0420           "SensorLayerAssembler");
0421     }
0422     LayerSpec layerSpec = m_layerSpec;
0423     layerSpec.layerName = group.key;
0424     auto layer = m_builder->makeLayer(std::span<const Element>{group.sensors},
0425                                       layerSpec);
0426     layer->setLayerType(m_layerType.value());
0427     node->addChild(detail::finalizeLayer<BackendT>(
0428         std::nullopt, std::move(layer), m_envelope, m_onLayer));
0429   }
0430 
0431   return node;
0432 }
0433 
0434 // SensorLayer
0435 template <detail::BlueprintBackend BackendT>
0436 SensorLayer<BackendT>::SensorLayer(const Builder& builder)
0437     : m_builder{&builder} {}
0438 
0439 template <detail::BlueprintBackend BackendT>
0440 SensorLayer<BackendT>&& SensorLayer<BackendT>::setLayerType(
0441     LayerType layerType) && {
0442   m_layerType = layerType;
0443   return std::move(*this);
0444 }
0445 
0446 template <detail::BlueprintBackend BackendT>
0447 SensorLayer<BackendT>&& SensorLayer<BackendT>::endcap() && {
0448   return std::move(*this).setLayerType(LayerType::Disc);
0449 }
0450 
0451 template <detail::BlueprintBackend BackendT>
0452 SensorLayer<BackendT>&& SensorLayer<BackendT>::barrel() && {
0453   return std::move(*this).setLayerType(LayerType::Cylinder);
0454 }
0455 
0456 template <detail::BlueprintBackend BackendT>
0457 SensorLayer<BackendT>&& SensorLayer<BackendT>::planar() && {
0458   return std::move(*this).setLayerType(LayerType::Plane);
0459 }
0460 
0461 template <detail::BlueprintBackend BackendT>
0462 SensorLayer<BackendT>&& SensorLayer<BackendT>::setSensors(
0463     std::vector<Element> sensors) && {
0464   m_sensors = std::move(sensors);
0465   return std::move(*this);
0466 }
0467 
0468 template <detail::BlueprintBackend BackendT>
0469 SensorLayer<BackendT>&& SensorLayer<BackendT>::setLayerName(
0470     std::string name) && {
0471   m_layerName = std::move(name);
0472   return std::move(*this);
0473 }
0474 
0475 template <detail::BlueprintBackend BackendT>
0476 SensorLayer<BackendT>&& SensorLayer<BackendT>::setEnvelope(
0477     const Acts::ExtentEnvelope& envelope) && {
0478   m_envelope = envelope;
0479   return std::move(*this);
0480 }
0481 
0482 template <detail::BlueprintBackend BackendT>
0483 void SensorLayer<BackendT>::addTo(
0484     Acts::Experimental::BlueprintNode& node) const&& {
0485   node.addChild(build());
0486 }
0487 
0488 template <detail::BlueprintBackend BackendT>
0489 std::shared_ptr<Acts::Experimental::LayerBlueprintNode>
0490 SensorLayer<BackendT>::build() const {
0491   if (!m_layerType.has_value()) {
0492     throw std::runtime_error("Layer type not set in SensorLayer");
0493   }
0494 
0495   if constexpr (detail::HasAxisDefinition<BackendT>) {
0496     if (!m_layerSpec.axes.has_value()) {
0497       throw std::runtime_error("Axes not set in SensorLayer");
0498     }
0499   }
0500 
0501   if (!m_sensors.has_value()) {
0502     throw std::runtime_error("Sensors not set in SensorLayer");
0503   }
0504   if (!m_layerName.has_value() || m_layerName->empty()) {
0505     throw std::runtime_error(
0506         "Layer name not set in SensorLayer. Call setLayerName().");
0507   }
0508 
0509   LayerSpec layerSpec = m_layerSpec;
0510   layerSpec.layerName = m_layerName;
0511   auto layer =
0512       m_builder->makeLayer(std::span<const Element>{*m_sensors}, layerSpec);
0513   layer->setLayerType(m_layerType.value());
0514   return detail::finalizeLayer<BackendT>(std::nullopt, std::move(layer),
0515                                          m_envelope, m_onLayer);
0516 }
0517 
0518 // BarrelEndcapAssembler
0519 template <detail::BlueprintBackend BackendT>
0520 BarrelEndcapAssembler<BackendT>::BarrelEndcapAssembler(const Builder& builder)
0521     : m_builder{&builder} {}
0522 
0523 template <detail::BlueprintBackend BackendT>
0524 void BarrelEndcapAssembler<BackendT>::addTo(
0525     Acts::Experimental::BlueprintNode& node) const&&
0526   requires(detail::HasBarrelEndcapClassifier<BackendT>)
0527 {
0528   node.addChild(build());
0529 }
0530 
0531 template <detail::BlueprintBackend BackendT>
0532 BarrelEndcapAssembler<BackendT>&& BarrelEndcapAssembler<BackendT>::setAssembly(
0533     const Element& assembly) && {
0534   m_assembly = assembly;
0535   return std::move(*this);
0536 }
0537 
0538 template <detail::BlueprintBackend BackendT>
0539 BarrelEndcapAssembler<BackendT>&&
0540 BarrelEndcapAssembler<BackendT>::setSensorAxes(
0541     typename BarrelEndcapAssembler<BackendT>::AxisDefinition barrel,
0542     typename BarrelEndcapAssembler<BackendT>::AxisDefinition endcap) &&
0543   requires(detail::HasAxisDefinition<BackendT>)
0544 {
0545   m_barrelAxes = std::move(barrel);
0546   m_endcapAxes = std::move(endcap);
0547   return std::move(*this);
0548 }
0549 
0550 template <detail::BlueprintBackend BackendT>
0551 BarrelEndcapAssembler<BackendT>&&
0552 BarrelEndcapAssembler<BackendT>::setEndcapAxes(
0553     typename BarrelEndcapAssembler<BackendT>::AxisDefinition axes) &&
0554   requires(detail::HasAxisDefinition<BackendT>)
0555 {
0556   m_endcapAxes = std::move(axes);
0557   return std::move(*this);
0558 }
0559 
0560 template <detail::BlueprintBackend BackendT>
0561 BarrelEndcapAssembler<BackendT>&&
0562 BarrelEndcapAssembler<BackendT>::setLayerFilter(const std::regex& pattern) && {
0563   m_layerFilter = pattern;
0564   return std::move(*this);
0565 }
0566 
0567 template <detail::BlueprintBackend BackendT>
0568 std::shared_ptr<Acts::Experimental::CylinderContainerBlueprintNode>
0569 BarrelEndcapAssembler<BackendT>::build() const
0570   requires(detail::HasBarrelEndcapClassifier<BackendT>)
0571 {
0572   using enum Acts::AxisDirection;
0573 
0574   const auto& logger = m_builder->logger();
0575 
0576   if (!m_assembly.has_value()) {
0577     throw std::runtime_error(
0578         "Assembly detector element not set in BarrelEndcapAssembler");
0579   }
0580 
0581   if constexpr (detail::HasAxisDefinition<BackendT>) {
0582     if (!m_barrelAxes.has_value()) {
0583       throw std::runtime_error("Barrel axes not set in BarrelEndcapAssembler");
0584     }
0585 
0586     if (!m_endcapAxes.has_value()) {
0587       throw std::runtime_error("Endcap axes not set in BarrelEndcapAssembler");
0588     }
0589   }
0590 
0591   if (!m_layerFilter.has_value()) {
0592     throw std::runtime_error("Layer pattern not set in BarrelEndcapAssembler");
0593   }
0594 
0595   const auto& assembly = m_assembly.value();
0596   const std::string assemblyName = m_builder->backend().nameOf(assembly);
0597 
0598   ACTS_INFO("Converting barrel-endcap assembly from element: " << assemblyName);
0599   auto barrels = m_builder->findBarrelElements(assembly);
0600 
0601   ACTS_DEBUG("Have " << barrels.size() << " barrel elements in assembly "
0602                      << assemblyName);
0603   if (barrels.size() > 1) {
0604     ACTS_ERROR("Expected exactly zero or one barrel in assembly "
0605                << assemblyName << ", found " << barrels.size());
0606     throw std::runtime_error(std::format(
0607         "Expected exactly zero or one barrel in assembly {}", assemblyName));
0608   }
0609 
0610   auto endcaps = m_builder->findEndcapElements(assembly);
0611 
0612   ACTS_DEBUG("Have " << endcaps.size() << " endcap elements in assembly "
0613                      << assemblyName);
0614 
0615   auto node =
0616       std::make_shared<Acts::Experimental::CylinderContainerBlueprintNode>(
0617           assemblyName, Acts::AxisDirection::AxisZ);
0618 
0619   auto maybeAddAxes = [](const auto& axes) {
0620     return [&axes]<typename T>(T&& assembler) {
0621       if constexpr (detail::HasAxisDefinition<BackendT>) {
0622         return std::forward<T>(assembler).setSensorAxes(axes.value());
0623       } else {
0624         return std::forward<T>(assembler);
0625       }
0626     };
0627   };
0628 
0629   auto build = []<typename T>(T&& assembler) {
0630     return std::forward<T>(assembler).build();
0631   };
0632 
0633   auto addTo =
0634       std::bind_front(&Acts::Experimental::BlueprintNode::addChild, node.get());
0635 
0636   for (const auto& barrel : barrels) {
0637     auto compose = Acts::compose(addTo, std::bind_front(m_onContainer, barrel),
0638                                  build, maybeAddAxes(m_barrelAxes));
0639 
0640     compose(m_builder->layers()
0641                 .barrel()
0642                 .setLayerFilter(m_layerFilter.value())
0643                 .setContainer(barrel)
0644                 .onLayer(m_onLayer));
0645   }
0646 
0647   for (const auto& endcap : endcaps) {
0648     auto compose = Acts::compose(addTo, std::bind_front(m_onContainer, endcap),
0649                                  build, maybeAddAxes(m_endcapAxes));
0650 
0651     compose(m_builder->layers()
0652                 .endcap()
0653                 .setLayerFilter(m_layerFilter.value())
0654                 .setContainer(endcap)
0655                 .onLayer(m_onLayer));
0656   }
0657 
0658   return node;
0659 }
0660 
0661 // BlueprintBuilder
0662 template <detail::BlueprintBackend BackendT>
0663 BlueprintBuilder<BackendT>::BlueprintBuilder(
0664     const typename Backend::Config& cfg,
0665     std::unique_ptr<const Acts::Logger> logger_)
0666     : m_logger(logger_ ? std::move(logger_)
0667                        : Acts::getDefaultLogger("BlueprintBuilder",
0668                                                 Acts::Logging::INFO)),
0669       m_backend(cfg, *m_logger) {}
0670 
0671 template <detail::BlueprintBackend BackendT>
0672 std::shared_ptr<Acts::Experimental::LayerBlueprintNode>
0673 BlueprintBuilder<BackendT>::makeLayer(const Element& layerElement,
0674                                       const LayerSpec& layerSpec) const {
0675   auto sensitives = resolveSensitives(layerElement);
0676   return makeLayer(layerElement, sensitives, layerSpec);
0677 }
0678 
0679 template <detail::BlueprintBackend BackendT>
0680 typename BlueprintBuilder<BackendT>::ElementLayerAssembler
0681 BlueprintBuilder<BackendT>::layers() const {
0682   return ElementLayerAssembler(*this);
0683 }
0684 
0685 template <detail::BlueprintBackend BackendT>
0686 typename BlueprintBuilder<BackendT>::SensorLayerAssembler
0687 BlueprintBuilder<BackendT>::layersFromSensors() const {
0688   return SensorLayerAssembler(*this);
0689 }
0690 
0691 template <detail::BlueprintBackend BackendT>
0692 typename BlueprintBuilder<BackendT>::SensorLayer
0693 BlueprintBuilder<BackendT>::layerFromSensors() const {
0694   return SensorLayer(*this);
0695 }
0696 
0697 template <detail::BlueprintBackend BackendT>
0698 typename BlueprintBuilder<BackendT>::BarrelEndcapAssembler
0699 BlueprintBuilder<BackendT>::barrelEndcap() const
0700   requires(detail::HasBarrelEndcapClassifier<BackendT>)
0701 {
0702   return BarrelEndcapAssembler(*this);
0703 }
0704 
0705 template <detail::BlueprintBackend BackendT>
0706 const Acts::Logger& BlueprintBuilder<BackendT>::logger() const {
0707   return *m_logger;
0708 }
0709 
0710 template <detail::BlueprintBackend BackendT>
0711 const typename BlueprintBuilder<BackendT>::Backend&
0712 BlueprintBuilder<BackendT>::backend() const {
0713   return m_backend;
0714 }
0715 
0716 template <detail::BlueprintBackend BackendT>
0717 std::optional<typename BlueprintBuilder<BackendT>::Element>
0718 BlueprintBuilder<BackendT>::findDetElementByName(
0719     const Element& parent, const std::string& name) const {
0720   if (m_backend.nameOf(parent) == name) {
0721     return parent;
0722   }
0723 
0724   for (const auto& child : m_backend.children(parent)) {
0725     auto result = findDetElementByName(child, name);
0726     if (result.has_value()) {
0727       return result;
0728     }
0729   }
0730 
0731   return std::nullopt;
0732 }
0733 
0734 template <detail::BlueprintBackend BackendT>
0735 std::optional<typename BlueprintBuilder<BackendT>::Element>
0736 BlueprintBuilder<BackendT>::findDetElementByName(
0737     const std::string& name) const {
0738   return findDetElementByName(m_backend.world(), name);
0739 }
0740 
0741 template <detail::BlueprintBackend BackendT>
0742 std::string BlueprintBuilder<BackendT>::getPathToElementName(
0743     const Element& elem, std::string_view separator) const {
0744   std::vector<std::string> names;
0745   names.emplace_back(m_backend.nameOf(elem));
0746 
0747   const auto world = m_backend.world();
0748   auto current = elem;
0749   while (current != world) {
0750     current = m_backend.parent(current);
0751     if (current == world) {
0752       break;
0753     }
0754     names.emplace_back(m_backend.nameOf(current));
0755   }
0756 
0757   std::ranges::reverse(names);
0758   std::string path;
0759   for (std::size_t i = 0; i < names.size(); ++i) {
0760     if (i > 0) {
0761       path += separator;
0762     }
0763     path += names[i];
0764   }
0765   return path;
0766 }
0767 
0768 template <detail::BlueprintBackend BackendT>
0769 std::vector<typename BlueprintBuilder<BackendT>::Element>
0770 BlueprintBuilder<BackendT>::findDetElementByNamePattern(
0771     const Element& parent, const std::regex& pattern) const {
0772   std::vector<Element> matches;
0773 
0774   std::function<void(const Element&)> visit = [&](const Element& elem) {
0775     if (const std::string elemName = m_backend.nameOf(elem);
0776         std::regex_match(elemName, pattern)) {
0777       matches.push_back(elem);
0778     }
0779     for (const auto& child : m_backend.children(elem)) {
0780       visit(child);
0781     }
0782   };
0783   visit(parent);
0784 
0785   return matches;
0786 }
0787 
0788 template <detail::BlueprintBackend BackendT>
0789 std::vector<typename BlueprintBuilder<BackendT>::Element>
0790 BlueprintBuilder<BackendT>::findBarrelElements(const Element& assembly) const
0791   requires(detail::HasBarrelEndcapClassifier<BackendT>)
0792 {
0793   std::vector<Element> barrels;
0794 
0795   std::function<void(const Element&)> visit = [&](const Element& elem) {
0796     if (m_backend.isTracker(elem) && m_backend.isBarrel(elem)) {
0797       barrels.push_back(elem);
0798     }
0799     for (const auto& child : m_backend.children(elem)) {
0800       visit(child);
0801     }
0802   };
0803   visit(assembly);
0804   return barrels;
0805 }
0806 
0807 template <detail::BlueprintBackend BackendT>
0808 std::vector<typename BlueprintBuilder<BackendT>::Element>
0809 BlueprintBuilder<BackendT>::findEndcapElements(const Element& assembly) const
0810   requires(detail::HasBarrelEndcapClassifier<BackendT>)
0811 {
0812   std::vector<Element> endcaps;
0813 
0814   std::function<void(const Element&)> visit = [&](const Element& elem) {
0815     if (m_backend.isTracker(elem) && m_backend.isEndcap(elem)) {
0816       endcaps.push_back(elem);
0817     }
0818     for (const auto& child : m_backend.children(elem)) {
0819       visit(child);
0820     }
0821   };
0822   visit(assembly);
0823   return endcaps;
0824 }
0825 
0826 template <detail::BlueprintBackend BackendT>
0827 std::shared_ptr<Acts::Experimental::LayerBlueprintNode>
0828 BlueprintBuilder<BackendT>::makeLayer(const Element& parent,
0829                                       std::span<const Element> sensitives,
0830                                       const LayerSpec& layerSpec) const {
0831   const std::string nodeName =
0832       layerSpec.layerName.value_or(m_backend.nameOf(parent));
0833   auto node =
0834       std::make_shared<Acts::Experimental::LayerBlueprintNode>(nodeName);
0835   node->setSurfaces(m_backend.makeSurfaces(sensitives, layerSpec));
0836 
0837   if constexpr (detail::HasLayerTransformLookup<BackendT>) {
0838     if (const auto transform =
0839             m_backend.lookupLayerTransform(parent, layerSpec);
0840         transform.has_value()) {
0841       node->setTransform(transform.value());
0842     }
0843   }
0844 
0845   return node;
0846 }
0847 
0848 template <detail::BlueprintBackend BackendT>
0849 std::shared_ptr<Acts::Experimental::LayerBlueprintNode>
0850 BlueprintBuilder<BackendT>::makeLayer(std::span<const Element> sensitives,
0851                                       const LayerSpec& layerSpec) const {
0852   if (!layerSpec.layerName.has_value() || layerSpec.layerName->empty()) {
0853     throw std::runtime_error(
0854         "BlueprintBuilder::makeLayer(sensitives, layerSpec): "
0855         "layerSpec.layerName must be set");
0856   }
0857 
0858   auto node = std::make_shared<Acts::Experimental::LayerBlueprintNode>(
0859       layerSpec.layerName.value());
0860   node->setSurfaces(m_backend.makeSurfaces(sensitives, layerSpec));
0861   return node;
0862 }
0863 
0864 template <detail::BlueprintBackend BackendT>
0865 std::vector<typename BlueprintBuilder<BackendT>::Element>
0866 BlueprintBuilder<BackendT>::resolveSensitives(const Element& detElement) const {
0867   std::vector<Element> sensitives;
0868 
0869   std::function<void(const Element&)> visit = [&](const Element& elem) {
0870     if (m_backend.isSensitive(elem)) {
0871       sensitives.push_back(elem);
0872     }
0873     for (const auto& child : m_backend.children(elem)) {
0874       visit(child);
0875     }
0876   };
0877   visit(detElement);
0878   return sensitives;
0879 }
0880 
0881 }  // namespace Acts::Experimental